PEP: 3107 Title: Function Annotations Version: $Revision$ Last-Modified:
$Date$ Author: Collin Winter <collinwinter@google.com>, Tony Lownds
<tony@lownds.com> Status: Final Type: Standards Track 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, a variety of tools and
libraries have appeared to fill this gap. 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 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
    something like::

         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 own;
    meaning comes from third-party libraries alone.

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.

Syntax
======

Parameters
----------

Annotations for parameters take the form of optional expressions that
follow the parameter name::

    def foo(a: expression, b: expression = 5):
        ...

In pseudo-grammar, parameters now look like
`identifier [: expression] [= expression]`. That is, annotations always
precede a parameter's default value and both 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, just
like default values.

Annotations for excess parameters (i.e., `*args` and `**kwargs`) are
indicated similarly::

    def foo(*args: expression, **kwargs: expression):
        ...

Annotations for nested parameters always follow the name of the
parameter, not the last parenthesis. Annotating all parameters of a
nested parameter is not required::

    def foo((x1, y1: expression),
            (x2: expression, y2: expression)=(None, None)):
        ...

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() -> expression:
        ...

That is, 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.

The grammar for function definitions \[\#grammar\]\_ is now::

    decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
    decorators: decorator+
    funcdef: [decorators] 'def' NAME parameters ['->' test] ':' suite
    parameters: '(' [typedargslist] ')'
    typedargslist: ((tfpdef ['=' test] ',')*
                    ('*' [tname] (',' tname ['=' test])* [',' '**' tname]
                     | '**' tname)
                    | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
    tname: NAME [':' test]
    tfpdef: tname | '(' tfplist ')'
    tfplist: tfpdef (',' tfpdef)* [',']

Lambda
------

`lambda`'s syntax does not support annotations. The syntax of `lambda`
could be changed to support annotations, by requiring parentheses around
the parameter list. However it was decided \[\#lambda\]\_ not to make
this change because:

1.  It would be an incompatible change.
2.  Lambdas are neutered anyway.
3.  The lambda can always be changed to a function.

Accessing Function Annotations
==============================

Once compiled, a function's annotations are available via the function's
`__annotations__` attribute. This attribute is a mutable 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: 'x', b: 5 + 6, c: list) -> max(2, 9):
        ...

would result in an `__annotations__` mapping of ::

    {'a': 'x',
     'b': 11,
     'c': list,
     'return': 9}

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`.

`__annotations__` is an empty, mutable dictionary if there are no
annotations on the function or if the functions was created from a
`lambda` expression.

Use Cases
=========

In the course of discussing annotations, a number of use-cases have been
raised. Some of these are presented here, grouped by what kind of
information they convey. Also included are examples of existing products
and packages that could make use of annotations.

-   Providing typing information

    -   Type checking (\[\#typecheck\]*, \[\#maxime\]*)
    -   Let IDEs show what types a function expects and returns
        (\[\#idle\]\_)
    -   Function overloading / generic functions (\[\#scaling\]\_)
    -   Foreign-language bridges (\[\#jython\]*, \[\#ironpython\]*)
    -   Adaptation (\[\#adaptationpost\]*, \[\#pyprotocols\]*)
    -   Predicate logic functions
    -   Database query mapping
    -   RPC parameter marshaling (\[\#rpyc\]\_)

-   Other information

    -   Documentation for parameters and return values (\[\#pydoc\]\_)

Standard Library
================

pydoc and inspect
-----------------

The `pydoc` module should display the function annotations when
displaying help for a function. The `inspect` module should change to
support annotations.

Relation to Other PEPs
======================

Function Signature Objects \[\#pep-362\]\_
--------------------------------------

Function Signature Objects should expose the function's annotations. The
`Parameter` object may change or other changes may be warranted.

Implementation
==============

A reference implementation has been checked into the py3k (formerly
"p3yk") branch as revision 53170 \[\#implementation\]\_.

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\]\_).

-   Despite yet more discussion, it was decided not to standardize a
    mechanism for annotation interoperability. Standardizing
    interoperability conventions at this point would be premature. We
    would rather let these conventions develop organically, based on
    real-world usage and necessity, than try to force all users into
    some contrived scheme. (\[\#interop0\]*, \[\#interop1\]*,
    \[\#interop2\]\_).

References and Footnotes
========================

.. \[\#functerm\] Unless specifically stated, "function" is generally
used as a synonym for "callable" throughout this document.

.. \[\#rejectgensyn\]
https://mail.python.org/pipermail/python-3000/2006-May/002103.html

.. \[\#typecheck\]
http://web.archive.org/web/20070730120117/http://oakwinter.com/code/typecheck/

.. \[\#maxime\]
http://web.archive.org/web/20070603221429/http://maxrepo.info/

.. \[\#threadgen\]
https://mail.python.org/pipermail/python-3000/2006-May/002091.html

.. \[\#threadhof\]
https://mail.python.org/pipermail/python-3000/2006-May/001972.html

.. \[\#threadimmlist\]
https://mail.python.org/pipermail/python-3000/2006-May/002105.html

.. \[\#threadmixing\]
https://mail.python.org/pipermail/python-3000/2006-May/002209.html

.. \[\#emphasistpls\]
https://mail.python.org/pipermail/python-3000/2006-June/002438.html

.. \[\#implementation\] http://svn.python.org/view?rev=53170&view=rev

.. \[\#grammar\]
http://docs.python.org/reference/compound\_stmts.html\#function-definitions

.. \[\#lambda\]
https://mail.python.org/pipermail/python-3000/2006-May/001613.html

.. \[\#pep-362\] http://www.python.org/dev/peps/pep-0362/

.. \[\#interop0\]
https://mail.python.org/pipermail/python-3000/2006-August/002895.html

.. \[\#interop1\]
https://mail.python.org/pipermail/python-ideas/2007-January/000032.html

.. \[\#interop2\]
https://mail.python.org/pipermail/python-list/2006-December/420645.html

.. \[\#idle\] http://www.python.org/idle/doc/idle2.html\#Tips

.. \[\#jython\] http://www.jython.org/Project/index.html

.. \[\#ironpython\]
http://www.codeplex.com/Wiki/View.aspx?ProjectName=IronPython

.. \[\#pyprotocols\] http://peak.telecommunity.com/PyProtocols.html

.. \[\#adaptationpost\]
http://www.artima.com/weblogs/viewpost.jsp?thread=155123

.. \[\#scaling\]
http://www-128.ibm.com/developerworks/library/l-cppeak2/

.. \[\#rpyc\] http://rpyc.wikispaces.com/

.. \[\#pydoc\] http://docs.python.org/library/pydoc.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:
