PEP 614: Relaxing Grammar Restrictions On Decorators (#1303)

This commit is contained in:
Brandt Bucher 2020-02-10 22:47:06 -08:00 committed by GitHub
parent 0fcb222a7c
commit 85c431c827
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 248 additions and 0 deletions

248
pep-0614.rst Normal file
View File

@ -0,0 +1,248 @@
PEP: 614
Title: Relaxing Grammar Restrictions On Decorators
Version: $Revision$
Last-Modified: $Date$
Author: Brandt Bucher <brandtbucher@gmail.com>
Sponsor: Guido van Rossum <guido@python.org>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 10-Feb-2020
Python-Version: 3.9
Post-History: 10-Feb-2020
Resolution:
Abstract
========
This PEP proposes allowing functions and classes to be decorated with
any valid expression.
Motivation
==========
When decorators were first being introduced, `Guido described
<https://mail.python.org/archives/list/python-dev@python.org/message/P3JD24UFFPZUUDANOAI6GZAPIGY4CVK7>`_
the motivation to limit their syntax as a preference, not a technical
requirement:
I have a gut feeling about this one. I'm not sure where it comes
from, but I have it... So while it would be quite easy to change
the syntax to ``@test`` in the future, I'd like to stick to with
the more restricted form unless a real use case is presented where
allowing ``@test`` would increase readability.
While these limitations were rarely encountered in practice, `BPO
issues <https://bugs.python.org/issue19660>`_ and `mailing list posts
<https://mail.python.org/archives/list/python-ideas@python.org/thread/UQOCJH3KOPBP7P3AVNS3OYBGZPR3V2WO/#CAOXYF4GV76AFJNCYSYMQTBM7CIPPH5M>`_
have consistently surfaced over the years requesting that they be
removed. The `most recent one
<https://mail.python.org/archives/list/python-ideas@python.org/thread/WOWD4P323DYDIGUQVWMESDWUG6QOW4MP>`_
(which `prompted this proposal
<https://mail.python.org/archives/list/python-ideas@python.org/message/FKE7ZFGUDCU5WVOE2QTD5XGMCNCOMETV>`_)
contained a good example of code using the ``PyQt5`` library that
would become more readable, idiomatic, and maintainable if the
existing restrictions were relaxed. Slightly modified::
buttons = [QPushButton(f'Button {i}') for i in range(10)]
# Do stuff with the list of buttons...
@buttons[0].clicked.connect
def spam():
...
@buttons[1].clicked.connect
def eggs():
...
# Do stuff with the list of buttons...
Currently, these decorations must be rewritten as something like::
button_0 = buttons[0]
@button_0.clicked.connect
def spam():
...
button_1 = buttons[1]
@button_1.clicked.connect
def eggs():
...
Further, the current grammar is already loose enough that it's trivial
to hack more complicated decorator expressions together. So rather
than disallow arbitrarily complex expressions, as intended, the
current restrictions only make them uglier and less efficient::
# Identity function hack:
def _(x):
return x
@_(buttons[0].clicked.connect)
def spam():
...
# eval hack:
@eval("buttons[1].clicked.connect")
def eggs():
...
Rationale
=========
Allowing Any Expression
-----------------------
The decision to allow *any* valid expression (and not just relaxing
the current restrictions to allow, for example, subscripting) has
been considered as the next logical step in the evolution of decorator
grammar for quite some time. As `Guido noted
<https://mail.python.org/archives/list/python-ideas@python.org/message/CAOXYF4GV76AFJNCYSYMQTBM7CIPPH5M>`_,
during yet another mailing list thread:
I don't think it's reasonable to constrain it less than it
currently is but more than a general expression.
Special-casing the grammar to allow *some* useful cases would only
complicate the current situation, and all but guarantee that the
process would repeat itself sometime in the future. Further, one
purpose of this grammatical change is to discourage the temptation to
use hacks like the ``eval`` and identity-function anti-patterns shown
above.
In short: if we're removing somewhat arbitrary restrictions, we should
remove *all* of them.
What Counts As An "Expression"
------------------------------
Throughout this document, the word "expression" is used as defined in
the `Python Language Reference
<https://docs.python.org/3.9/reference/expressions.html#grammar-token-expression>`_.
This can be summarized as "anything that's valid as a test in ``if``,
``elif``, and ``while`` blocks". This differs subtly from a perhaps
more popular `definition
<https://docs.python.org/3/glossary.html#term-expression>`_, which can
be summarized as "anything that's valid as string input to ``eval``".
This definition of "expression" is convenient in that it fits our
needs well, and reuses the allowed grammar of existing language
constructs. It has two subtle differences from the other definition:
Tuple Displays *Must* Be Parenthesized
''''''''''''''''''''''''''''''''''''''
This is based on an observation Guido made in the same email.
Continued immediately from above:
Though I wouldn't allow commas-- there's no way that
.. code::
@f, g
def pooh(): ...
can make sense.
Indeed, it may even lead inexperienced readers to conclude that
several decorators are being applied, as if they were stacked.
Requiring parentheses here makes the (admittedly nonsensical) intent
clear without imposing further restrictions and grammar complications.
Named Expressions *Need Not* Be Parenthesized
'''''''''''''''''''''''''''''''''''''''''''''
Here, the the choice of syntax is unambiguous. :pep:`572` explains
why it requires parentheses around top-level expression statements:
This rule is included to simplify the choice for the user between
an assignment statement and an assignment expression -- there is
no syntactic position where both are valid.
Since an assignment statement is not valid here, assignment
expressions should not be unnecessarily burdened with parentheses.
Specification
=============
The grammar for decorators is currently::
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
This PEP proposes that it be simplified to::
decorator: '@' namedexpr_test NEWLINE
Backwards Compatibility
=======================
This new grammar is fully backward-compatible with the existing
grammar.
Binary Matrix Multiplication
----------------------------
One interesting case is the binary ``@`` operator, which was added to
the language after decorators were first introduced. With the proposed
change to the grammar, the following decorator expression will be
unambiguous legal syntax::
@a @ b
def f():
...
However, this pathological case is unlikely to occur in practice, as
there are no known examples of binary matrix multiplication results
which may used as decorators. Likely the only people who would do this
are those who currently write similarly obfuscated toy code::
@_(a
@b)
def f():
...
How To Teach This
=================
Decorators can continue to be taught as they always have; the average
Python programmer is likely unaware that the current restriction even
exists.
Reference Implementation
========================
The author has drafted a CPython implementation, which can be found on
`GitHub <https://github.com/brandtbucher/cpython/tree/decorators>`_.
Copyright
=========
This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
coding: utf-8
End: