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:
