Source code for mtoolbox.autoname

# -*- coding: utf-8 -*-

"""Access an object's name as a property

Autoname is a data-descriptor, which automatically looks up the
name under which the object on which the descriptor is accessed
is known by.

Import the descriptor using ``from mtoolbox.autoname import Autoname``.

Example:

>>> class Object(object):
...     name = Autoname()
>>> obj1 = Object()
>>> obj1.name
'obj1'
>>> obj2 = Object()
>>> obj2.name
'obj2'

By default Autoname will return the outer-most name that was defined
for the object:

>>> class Object(object):
...     name = Autoname()
>>> def func(anobject):
...     return anobject.name
>>> o = Object()
>>> func(o)
'o'

You can change this behaviour by using the 'inner' keyword:

>>> class Object(object):
...     name = Autoname(inner=True)
>>> o = Object()
>>> def func(anobject):
...     return anobject.name
>>> func(o)
'anobject'

Note:
    Please be aware, that getting the inner-most name, is not what you
    want in most cases:

    >>> class Object(object):
    ...     name = Autoname(inner=True)
    ...     def printname(self):
    ...         print(self.name)
    >>> o = Object()
    >>> o.printname()
    self

    When in automatic mode (see the class documentation below) the
    descriptor will always return a name, that is in some callframe
    dictionary. If you delete a name, it will use another one, that
    is still in use:

    >>> class Object(object):
    ...     name = Autoname()
    >>> o = Object()
    >>> o.name
    'o'
    >>> g = o
    >>> del o
    >>> g.name
    'g'

    This can be helped a bit by using the 'bind' keyword argument and
    calling <object>.name with the name that should be used first:

    >>> class Object(object):
    ...     name = Autoname(bind=True)
    >>> o = Object()
    >>> o.name
    'o'
    >>> g = o
    >>> del o
    >>> g.name
    'o'

Warning:
    Defining multiple names for an object in the same call frame (which is
    easily said the same level of indention in your program) will
    cause undetermined behaviour, depending on the Python interpreter:

    >>> class Object(object):
    ...     name = Autoname()
    >>> o = Object()
    >>> g = o
    >>> o.name in ['o', 'g']
    True
"""

import doctest
import inspect


[docs]class Autoname(object): """Create a new Autoname descriptor Args: initval (str, bool, None): The initial name inner (bool): Return the inner-most name of the object (or not) bind (bool): Bind the descriptor to the first name it returns Returns: Autoname: An Autoname instance """ def __init__(self, initval=True, inner=False, bind=False): self.val = None self.inner = inner self.bind = bind self.__set__(None, initval)
[docs] def __get__(self, theobject, objtype): """Return the name of theobject or None Returns: str or None: the name of the object Usage: >>> class Object(object): ... name = Autoname() >>> obj = Object() >>> obj.name 'obj' >>> obj.name = 'another name' >>> obj.name 'another name' """ if isinstance(self.val, str): return self.val elif self.val is False or self.val is None: return None else: # If we really didn't find a name, we return None thename = None # There is at least one frame in the callstack, in which # the calling object is a local variable, so we climb up # the callstack, to find the name of the object. for count, frametuple in enumerate(inspect.stack()): # skip the first frame - this is our __get__ if count == 0: continue for name, obj in frametuple[0].f_locals.items(): # found a name, but keep searching in order to get # the outer-most name unless inner == True if obj is theobject: thename = name if self.inner: self.__bind_if_wanted(thename) return thename self.__bind_if_wanted(thename) return thename
def __bind_if_wanted(self, name): if self.bind: self.__set__(None, name)
[docs] def __set__(self, theobject, val): """Set the name of the theobject Args: theobject (object): The object to which's class the descriptor is attached to val (str, bool or None): Sets the name to depending on the type: str sets the name to this str. False or None sets the name to None. True sets the name to automatically lookup. Returns: None Raises: TypeError if type(val) is invalid Usage: >>> class Object(object): ... name = Autoname() >>> o = Object() >>> o.name = 'k' >>> o.name 'k' >>> o.name = True >>> o.name 'o' >>> o.name = False >>> str(o.name) 'None' >>> o.name = 4 Traceback (most recent call last): ... TypeError: Autoname must be set to str, bool, NoneType """ types = (str, bool, type(None)) if not isinstance(val, types): raise TypeError("Autoname must be set to %s" % ", ".join( [t.__name__ for t in types])) self.val = val
if __name__ == '__main__': doctest.testmod()