2006-12-22 10:46:01 -05:00
|
|
|
|
PEP: 3107
|
|
|
|
|
Title: Function Annotations
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Collin Winter <collinw@gmail.com>,
|
|
|
|
|
Tony Lownds <tony@pagedna.com>
|
|
|
|
|
Status: Draft
|
|
|
|
|
Type: Standards Track
|
|
|
|
|
Requires: 362
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 2-Dec-2006
|
|
|
|
|
Python-Version: 3.0
|
|
|
|
|
Post-History:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
This PEP introduces a syntax for adding arbitrary metadata annotations
|
|
|
|
|
to Python functions [#functerm]_.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
Because Python's 2.x series lacks a standard way of annotating a
|
|
|
|
|
function's parameters and return values (e.g., with information about
|
2006-12-23 01:10:01 -05:00
|
|
|
|
what type a function's return value should be), a variety of tools
|
2006-12-22 10:46:01 -05:00
|
|
|
|
and libraries have appeared to fill this gap [#tailexamp]_. Some
|
|
|
|
|
utilise the decorators introduced in "PEP 318", while others parse a
|
|
|
|
|
function's docstring, looking for annotations there.
|
|
|
|
|
|
|
|
|
|
This PEP aims to provide a single, standard way of specifying this
|
|
|
|
|
information, reducing the confusion caused by the wide variation in
|
|
|
|
|
mechanism and syntax that has existed until this point.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Fundamentals of Function Annotations
|
|
|
|
|
====================================
|
|
|
|
|
|
|
|
|
|
Before launching into a discussion of the precise ins and outs of
|
|
|
|
|
Python 3.0's function annotations, let's first talk broadly about
|
|
|
|
|
what annotations are and are not:
|
|
|
|
|
|
|
|
|
|
1. Function annotations, both for parameters and return values, are
|
|
|
|
|
completely optional.
|
|
|
|
|
|
|
|
|
|
2. Function annotations are nothing more than a way of associating
|
|
|
|
|
arbitrary Python expressions with various parts of a function at
|
|
|
|
|
compile-time.
|
|
|
|
|
|
|
|
|
|
By itself, Python does not attach any particular meaning or
|
|
|
|
|
significance to annotations. Left to its own, Python simply makes
|
|
|
|
|
these expressions available as the values in the
|
|
|
|
|
``__signature__.annotations`` mapping described in `Accessing
|
|
|
|
|
Function Annotations`_ below.
|
|
|
|
|
|
|
|
|
|
The only way that annotations take on meaning is when they are
|
|
|
|
|
interpreted by third-party libraries. These annotation consumers
|
|
|
|
|
can do anything they want with a function's annotations. For
|
|
|
|
|
example, one library might use string-based annotations to provide
|
|
|
|
|
improved help messages, like so::
|
|
|
|
|
|
|
|
|
|
def compile(source: "something compilable",
|
|
|
|
|
filename: "where the compilable thing comes from",
|
|
|
|
|
mode: "is this a single statement or a suite?"):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
Another library might be used to provide typechecking for Python
|
|
|
|
|
functions and methods. This library could use annotations to
|
|
|
|
|
indicate the function's expected input and return types, possibly
|
2006-12-23 01:10:01 -05:00
|
|
|
|
something like::
|
2006-12-22 10:46:01 -05:00
|
|
|
|
|
|
|
|
|
def haul(item: Haulable, *vargs: PackAnimal) -> Distance:
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
However, neither the strings in the first example nor the
|
|
|
|
|
type information in the second example have any meaning on their
|
2006-12-23 01:10:01 -05:00
|
|
|
|
own; meaning comes from third-party libraries alone.
|
2006-12-22 10:46:01 -05:00
|
|
|
|
|
|
|
|
|
3. Following from point 2, this PEP makes no attempt to introduce
|
|
|
|
|
any kind of standard semantics, even for the built-in types.
|
|
|
|
|
This work will be left to third-party libraries.
|
|
|
|
|
|
|
|
|
|
There is no worry that these libraries will assign semantics at
|
|
|
|
|
random, or that a variety of libraries will appear, each with
|
|
|
|
|
varying semantics and interpretations of what, say, a tuple of
|
|
|
|
|
strings means. The difficulty inherent in writing annotation
|
|
|
|
|
interpreting libraries will keep their number low and their
|
|
|
|
|
authorship in the hands of people who, frankly, know what they're
|
|
|
|
|
doing.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Syntax
|
|
|
|
|
======
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
Annotations for parameters take the form of optional expressions that
|
|
|
|
|
follow the parameter name. This example indicates that parameters 'a'
|
2006-12-23 01:10:01 -05:00
|
|
|
|
and 'c' should both be a ``Number``, while parameter 'b' should
|
2006-12-22 10:46:01 -05:00
|
|
|
|
be a ``Mapping``::
|
|
|
|
|
|
|
|
|
|
def foo(a: Number, b: Mapping, c: Number = 5):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
(Number, Mapping and Sequence are used thoughout this PEP and
|
|
|
|
|
represent the standard `numeric`_, `mapping`_ and `sequence
|
|
|
|
|
protocols`_.)
|
|
|
|
|
|
|
|
|
|
In pseudo-grammar, parameters now look like ``identifier [:
|
|
|
|
|
expression] [= expression]``. That is, type annotations always
|
|
|
|
|
precede a parameter's default value and both type annotations and
|
|
|
|
|
default values are optional. Just like how equal signs are used to
|
|
|
|
|
indicate a default value, colons are used to mark annotations. All
|
|
|
|
|
annotation expressions are evaluated when the function definition is
|
|
|
|
|
executed.
|
|
|
|
|
|
|
|
|
|
Annotations for excess parameters (i.e., ``*args`` and ``**kwargs``)
|
|
|
|
|
are indicated similarly. In the following function definition,
|
|
|
|
|
``*args`` is flagged as a list of ``Number``, and ``**kwargs`` is
|
|
|
|
|
marked as a dict whose keys are strings and whose values are of type
|
|
|
|
|
``Sequence``. ::
|
|
|
|
|
|
|
|
|
|
def foo(*args: Number, **kwargs: Sequence):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
Note that, depending on what annotation-interpreting library you're
|
|
|
|
|
using, the following might also be a valid spelling of the above::
|
|
|
|
|
|
|
|
|
|
def foo(*args: [Number], **kwargs: {str: Sequence}):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
Only the first, however, has the BDFL's blessing [#blessedexcess]_ as
|
|
|
|
|
the One Obvious Way.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Return Values
|
|
|
|
|
-------------
|
|
|
|
|
|
|
|
|
|
The examples thus far have omitted examples of how to annotate the
|
|
|
|
|
type of a function's return value. This is done like so::
|
|
|
|
|
|
|
|
|
|
def sum(*args: Number) -> Number:
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
The parameter list can now be followed by a literal ``->`` and a
|
|
|
|
|
Python expression. Like the annotations for parameters, this
|
|
|
|
|
expression will be evaluated when the function definition is executed.
|
|
|
|
|
|
2006-12-23 01:10:01 -05:00
|
|
|
|
The grammar for function definitions [#grammar]_ is now something like::
|
2006-12-22 10:46:01 -05:00
|
|
|
|
|
|
|
|
|
funcdef ::= [decorators] "def" funcname "("
|
|
|
|
|
[parameter_list] ")" ["->" expression] ":" suite
|
|
|
|
|
decorators ::= decorator+
|
|
|
|
|
decorator ::= "@" dotted_name ["(" [argument_list
|
|
|
|
|
[","]] ")"] NEWLINE
|
|
|
|
|
dotted_name ::= identifier ("." identifier)*
|
|
|
|
|
parameter_list ::= (defparameter ",")*
|
|
|
|
|
( "*" identifier [":" expression]
|
|
|
|
|
[, "**" identifier [":" expression] ]
|
|
|
|
|
| "**" identifier [":" expression]
|
|
|
|
|
| defparameter [","] )
|
|
|
|
|
defparameter ::= parameter [":" expression] ["=" expression]
|
|
|
|
|
sublist ::= parameter ("," parameter)* [","]
|
|
|
|
|
parameter ::= identifier | "(" sublist ")"
|
|
|
|
|
funcname ::= identifier
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Accessing Function Annotations
|
|
|
|
|
==============================
|
|
|
|
|
|
|
|
|
|
Once compiled, a function's annotations are available via the
|
|
|
|
|
function's ``__signature__`` attribute, introduced by PEP 362.
|
|
|
|
|
Signature objects include an attribute just for annotations,
|
|
|
|
|
appropriately called ``annotations``. This attribute is a dictionary,
|
|
|
|
|
mapping parameter names to an object representing the evaluated
|
|
|
|
|
annotation expression.
|
|
|
|
|
|
|
|
|
|
There is a special key in the ``annotations`` mapping, ``"return"``.
|
|
|
|
|
This key is present only if an annotation was supplied for the
|
|
|
|
|
function's return value.
|
|
|
|
|
|
|
|
|
|
For example, the following annotation::
|
|
|
|
|
|
|
|
|
|
def foo(a: Number, b: 5 + 6, c: list) -> String:
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
would result in a ``__signature__.annotations`` mapping of ::
|
|
|
|
|
|
|
|
|
|
{'a': Number,
|
|
|
|
|
'b': 11,
|
|
|
|
|
'c': list,
|
|
|
|
|
'return': String}
|
|
|
|
|
|
|
|
|
|
The ``return`` key was chosen because it cannot conflict with the name
|
|
|
|
|
of a parameter; any attempt to use ``return`` as a parameter name
|
|
|
|
|
would result in a ``SyntaxError``.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Implementation
|
|
|
|
|
==============
|
|
|
|
|
|
|
|
|
|
A sample implementation has been provided [#implementation]_ by Tony
|
|
|
|
|
Lownds.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rejected Proposals
|
|
|
|
|
==================
|
|
|
|
|
|
|
|
|
|
+ The BDFL rejected the author's idea for a special syntax for adding
|
|
|
|
|
annotations to generators as being "too ugly" [#rejectgensyn]_.
|
|
|
|
|
|
|
|
|
|
+ Though discussed early on ([#threadgen]_, [#threadhof]_), including
|
|
|
|
|
special objects in the stdlib for annotating generator functions and
|
|
|
|
|
higher-order functions was ultimately rejected as being more
|
|
|
|
|
appropriate for third-party libraries; including them in the
|
|
|
|
|
standard library raised too many thorny issues.
|
|
|
|
|
|
|
|
|
|
+ Despite considerable discussion about a standard type
|
|
|
|
|
parameterisation syntax, it was decided that this should also be
|
|
|
|
|
left to third-party libraries. ([#threadimmlist]_, [#threadmixing]_,
|
|
|
|
|
[#emphasistpls]_)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
References and Footnotes
|
|
|
|
|
========================
|
|
|
|
|
|
|
|
|
|
.. [#functerm] Unless specifically stated, "function" is generally
|
|
|
|
|
used as a synonym for "callable" throughout this document.
|
|
|
|
|
|
|
|
|
|
.. [#tailexamp] The author's typecheck_ library makes use of
|
|
|
|
|
decorators, while `Maxime Bourget's own typechecker`_ utilises
|
|
|
|
|
parsed docstrings.
|
|
|
|
|
|
|
|
|
|
.. [#blessedexcess]
|
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-May/002173.html
|
|
|
|
|
|
|
|
|
|
.. [#rejectgensyn]
|
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-May/002103.html
|
|
|
|
|
|
|
|
|
|
.. _typecheck:
|
|
|
|
|
http://oakwinter.com/code/typecheck/
|
|
|
|
|
|
|
|
|
|
.. _Maxime Bourget's own typechecker:
|
|
|
|
|
http://maxrepo.info/taxonomy/term/3,6/all
|
|
|
|
|
|
|
|
|
|
.. [#threadgen]
|
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-May/002091.html
|
|
|
|
|
|
|
|
|
|
.. [#threadhof]
|
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-May/001972.html
|
|
|
|
|
|
|
|
|
|
.. [#threadimmlist]
|
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-May/002105.html
|
|
|
|
|
|
|
|
|
|
.. [#threadmixing]
|
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-May/002209.html
|
|
|
|
|
|
|
|
|
|
.. [#emphasistpls]
|
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-June/002438.html
|
|
|
|
|
|
|
|
|
|
.. [#implementation]
|
|
|
|
|
http://python.org/sf/1607548
|
|
|
|
|
|
|
|
|
|
.. _numeric:
|
|
|
|
|
http://docs.python.org/lib/typesnumeric.html
|
|
|
|
|
|
|
|
|
|
.. _mapping:
|
|
|
|
|
http://docs.python.org/lib/typesmapping.html
|
|
|
|
|
|
|
|
|
|
.. _sequence protocols:
|
|
|
|
|
http://docs.python.org/lib/typesseq.html
|
|
|
|
|
|
|
|
|
|
.. [#grammar]
|
|
|
|
|
http://www.python.org/doc/current/ref/function.html
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
coding: utf-8
|
|
|
|
|
End:
|