PEP: 232 Title: Function Attributes Version: $Revision$ Last-Modified:
$Date$ Author: Barry Warsaw <barry@python.org> Status: Final Type:
Standards Track Content-Type: text/x-rst Created: 02-Dec-2000
Python-Version: 2.1 Post-History: 20-Feb-2001

Introduction
============

This PEP describes an extension to Python, adding attribute dictionaries
to functions and methods. This PEP tracks the status and ownership of
this feature. It contains a description of the feature and outlines
changes necessary to support the feature. This PEP summarizes
discussions held in mailing list forums, and provides URLs for further
information, where appropriate. The CVS revision history of this file
contains the definitive historical record.

Background
==========

Functions already have a number of attributes, some of which are
writable, e.g. `func_doc`, a.k.a. `func.__doc__`. `func_doc` has the
interesting property that there is special syntax in function (and
method) definitions for implicitly setting the attribute. This
convenience has been exploited over and over again, overloading
docstrings with additional semantics.

For example, John Aycock has written a system where docstrings are used
to define parsing rules. \[1\]\_ Zope's ZPublisher ORB \[2\]\_ uses
docstrings to signal publishable methods, i.e. methods that can be
called through the web.

The problem with this approach is that the overloaded semantics may
conflict with each other. For example, if we wanted to add a doctest
unit test to a Zope method that should not be publishable through the
web.

Proposal
========

This proposal adds a new dictionary to function objects, called
`func_dict` (a.k.a. `__dict__`). This dictionary can be set and get
using ordinary attribute set and get syntax.

Methods also gain getter syntax, and they currently access the attribute
through the dictionary of the underlying function object. It is not
possible to set attributes on bound or unbound methods, except by doing
so explicitly on the underlying function object. See the
`Future Directions`\_ discussion below for approaches in subsequent
versions of Python.

A function object's `__dict__` can also be set, but only to a dictionary
object. Deleting a function's `__dict__`, or setting it to anything
other than a concrete dictionary object results in a `TypeError`. If no
function attributes have ever been set, the function's `__dict__` will
be empty.

Examples
========

Here are some examples of what you can do with this feature.

::

    def a():
        pass

    a.publish = 1
    a.unittest = '''...'''

    if a.publish:
        print a()

    if hasattr(a, 'unittest'):
        testframework.execute(a.unittest)

    class C:
        def a(self):
            'just a docstring'
            a.publish = 1

    c = C()
    if c.a.publish:
        publish(c.a())

Other Uses
----------

Paul Prescod enumerated a bunch of other uses on the
`python-dev thread`\_.

.. \_python-dev thread:
https://mail.python.org/pipermail/python-dev/2000-April/003364.html

Future Directions
=================

Here are a number of future directions to consider. Any adoption of
these ideas would require a new PEP, which referenced this one, and
would have to be targeted at a Python version subsequent to the 2.1
release.

-   A previous version of this PEP allowed for both setter and getter of
    attributes on unbound methods, and only getter on bound methods. A
    number of problems were discovered with this policy.

    Because method attributes were stored in the underlying function,
    this caused several potentially surprising results::

        class C:
            def a(self): pass

        c1 = C()
        c2 = C()
        c1.a.publish = 1
        # c2.a.publish would now be == 1 also!

    Because a change to `a` bound `c1` also caused a change to `a` bound
    to `c2`, setting of attributes on bound methods was disallowed.
    However, even allowing setting of attributes on unbound methods has
    its ambiguities::

        class D(C): pass
        class E(C): pass

        D.a.publish = 1
        # E.a.publish would now be == 1 also!

    For this reason, the current PEP disallows setting attributes on
    either bound or unbound methods, but does allow for getting
    attributes on either -- both return the attribute value on the
    underlying function object.

    A future PEP might propose to implement setting (bound or unbound)
    method attributes by setting attributes on the instance or class,
    using special naming conventions. I.e.::

        class C:
            def a(self): pass

        C.a.publish = 1
        C.__a_publish__ == 1 # true

        c = C()
        c.a.publish = 2
        c.__a_publish__ == 2 # true

        d = C()
        d.__a_publish__ == 1 # true

    Here, a lookup on the instance would look to the instance's
    dictionary first, followed by a lookup on the class's dictionary,
    and finally a lookup on the function object's dictionary.

-   Currently, Python supports function attributes only on Python
    functions (i.e. those that are written in Python, not those that are
    built-in). Should it be worthwhile, a separate patch can be crafted
    that will add function attributes to built-ins.

-   `__doc__` is the only function attribute that currently has
    syntactic support for conveniently setting. It may be worthwhile to
    eventually enhance the language for supporting easy function
    attribute setting. Here are some syntaxes suggested by PEP
    reviewers::

        def a {
            'publish' : 1,
            'unittest': '''...''',
            }
            (args):
            # ...

        def a(args):
            """The usual docstring."""
            {'publish' : 1,
             'unittest': '''...''',
             # etc.
             }

        def a(args) having (publish = 1):
            # see reference [3]
            pass

    The BDFL is currently against any such special syntactic support for
    setting arbitrary function attributes. Any syntax proposals would
    have to be outlined in new PEPs.

Dissenting Opinion
==================

When this was discussed on the python-dev mailing list in April 2000, a
number of dissenting opinions were voiced. For completeness, the
discussion thread starts on `python-dev`\_.

.. \_python-dev:
https://mail.python.org/pipermail/python-dev/2000-April/003361.html

The dissenting arguments appear to fall under the following categories:

-   no clear purpose (what does it buy you?)
-   other ways to do it (e.g. mappings as class attributes)
-   useless until syntactic support is included

Countering some of these arguments is the observation that with vanilla
Python 2.0, `__doc__` can in fact be set to any type of object, so some
semblance of writable function attributes are already feasible. But that
approach is yet another corruption of `__doc__`.

And while it is of course possible to add mappings to class objects (or
in the case of function attributes, to the function's module), it is
more difficult and less obvious how to extract the attribute values for
inspection.

Finally, it may be desirable to add syntactic support, much the same way
that `__doc__` syntactic support exists. This can be considered
separately from the ability to actually set and get function attributes.

Reference Implementation
========================

This PEP has been accepted and the implementation has been integrated
into Python 2.1.

References
==========

.. \[1\] Aycock, "Compiling Little Languages in Python",
http://www.foretec.com/python/workshops/1998-11/proceedings/papers/aycock-little/aycock-little.html

.. \[2\] http://classic.zope.org:8080/Documentation/Reference/ORB

.. \[3\] Hudson, Michael, SourceForge patch implementing this syntax,
http://sourceforge.net/tracker/index.php?func=detail&aid=403441&group\_id=5470&atid=305470

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:
