PEP: 3130 Title: Access to Current Module/Class/Function Version:
$Revision$ Last-Modified: $Date$ Author: Jim J. Jewett
<jimjjewett@gmail.com> Status: Rejected Type: Standards Track
Content-Type: text/x-rst Created: 22-Apr-2007 Python-Version: 3.0
Post-History: 22-Apr-2007

Rejection Notice
================

This PEP is rejected. It is not clear how it should be implemented or
what the precise semantics should be in edge cases, and there aren't
enough important use cases given. response has been lukewarm at best.

Abstract
========

It is common to need a reference to the current module, class, or
function, but there is currently no entirely correct way to do this.
This PEP proposes adding the keywords `__module__`, `__class__`, and
`__function__`.

Rationale for `__module__`
==========================

Many modules export various functions, classes, and other objects, but
will perform additional activities (such as running unit tests) when run
as a script. The current idiom is to test whether the module's name has
been set to magic value.

::

    if __name__ == "__main__": ...

More complicated introspection requires a module to (attempt to) import
itself. If importing the expected name actually produces a different
module, there is no good workaround.

::

    # __import__ lets you use a variable, but... it gets more
    # complicated if the module is in a package.
    __import__(__name__)

    # So just go to sys modules... and hope that the module wasn't
    # hidden/removed (perhaps for security), that __name__ wasn't
    # changed, and definitely hope that no other module with the
    # same name is now available.
    class X(object):
        pass

    import sys
    mod = sys.modules[__name__]
    mod = sys.modules[X.__class__.__module__]

Proposal: Add a `__module__` keyword which refers to the module
currently being defined (executed). (But see open issues.)

::

    # XXX sys.main is still changing as draft progresses.  May
    # really need sys.modules[sys.main]
    if __module__ is sys.main:    # assumes PEP (3122), Cannon
        ...

Rationale for `__class__`
=========================

Class methods are passed the current instance; from this they can
determine `self.__class__` (or cls, for class methods). Unfortunately,
this reference is to the object's actual class, which may be a subclass
of the defining class. The current workaround is to repeat the name of
the class, and assume that the name will not be rebound.

::

    class C(B):

        def meth(self):
            super(C, self).meth() # Hope C is never rebound.

    class D(C):

        def meth(self):
            # ?!? issubclass(D,C), so it "works":
            super(C, self).meth()

Proposal: Add a `__class__` keyword which refers to the class currently
being defined (executed). (But see open issues.)

::

    class C(B):
        def meth(self):
            super(__class__, self).meth()

Note that super calls may be further simplified by the "New Super" PEP
(Spealman). The `__class__` (or `__this_class__`) attribute came up in
attempts to simplify the explanation and/or implementation of that PEP,
but was separated out as an independent decision.

Note that `__class__` (or `__this_class__`) is not quite the same as the
`__thisclass__` property on bound super objects. The existing
`super.__thisclass__` property refers to the class from which the Method
Resolution Order search begins. In the above class D, it would refer to
(the current reference of name) C.

Rationale for `__function__`
============================

Functions (including methods) often want access to themselves, usually
for a private storage location or true recursion. While there are
several workarounds, all have their drawbacks.

::

    def counter(_total=[0]):
        # _total shouldn't really appear in the
        # signature at all; the list wrapping and
        # [0] unwrapping obscure the code
        _total[0] += 1
        return _total[0]

    @annotate(total=0)
    def counter():
        # Assume name counter is never rebound:
        counter.total += 1
        return counter.total

    # class exists only to provide storage:
    class _wrap(object):

        __total = 0

        def f(self):
            self.__total += 1
            return self.__total

    # set module attribute to a bound method:
    accum = _wrap().f

    # This function calls "factorial", which should be itself --
    # but the same programming styles that use heavy recursion
    # often have a greater willingness to rebind function names.
    def factorial(n):
        return (n * factorial(n-1) if n else 1)

Proposal: Add a `__function__` keyword which refers to the function (or
method) currently being defined (executed). (But see open issues.)

::

    @annotate(total=0)
    def counter():
        # Always refers to this function obj:
        __function__.total += 1
        return __function__.total

    def factorial(n):
        return (n * __function__(n-1) if n else 1)

Backwards Compatibility
=======================

While a user could be using these names already, double-underscore names
( `__anything__` ) are explicitly reserved to the interpreter. It is
therefore acceptable to introduce special meaning to these names within
a single feature release.

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

Ideally, these names would be keywords treated specially by the bytecode
compiler.

Guido has suggested \[1\]\_ using a cell variable filled in by the
metaclass.

Michele Simionato has provided a prototype using bytecode hacks \[2\]\_.
This does not require any new bytecode operators; it just modifies the
which specific sequence of existing operators gets run.

Open Issues
===========

-   Are `__module__`, `__class__`, and `__function__` the right names?
    In particular, should the names include the word "this", either as
    `__this_module__`, `__this_class__`, and `__this_function__`,
    (format discussed on the python-3000 and python-ideas lists) or as
    `__thismodule__`, `__thisclass__`, and `__thisfunction__` (inspired
    by, but conflicting with, current usage of super.`__thisclass__`).

-   Are all three keywords needed, or should this enhancement be limited
    to a subset of the objects? Should methods be treated separately
    from other functions?

References
==========

.. \[1\] Fixing super anyone? Guido van Rossum
https://mail.python.org/pipermail/python-3000/2007-April/006671.html

.. \[2\] Descriptor/Decorator challenge, Michele Simionato
http://groups.google.com/group/comp.lang.python/browse\_frm/thread/a6010c7494871bb1/62a2da68961caeb6?lnk=gst&q=simionato+challenge&rnum=1&hl=en\#62a2da68961caeb6

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:
