Steven Bethard wrote:
Should namespace chaining be supported?  One suggestion would add a
NamespaceChain object to the module::


This does have the advantage of keeping the basic namespace simple. However, it may also be worth having native chaining support in Namespace:


I think I prefer the separate NamespaceChain object because it allows you to chain namespaces other than just Namespace objects -- any object that supports getattr is okay. If chaining is builtin, all namespaces (except the last one?) have to be Namespace objects...

I like NamespaceChain too - I was simply thinking it might be good to have a recursive chaining method associated with actual Namespace objects as well.

However, now that I look more closely at NamespaceChain, it makes more sense to
me to explicitly make the 'head' of the chain a namespace view in its own right.
This should also make the local binding, chained lookup behaviour fairly obvious
(since it will work just as it does for any class with __getattr__ defined, but
not __setattr__ or __delattr__).


That is, something like:

    class NamespaceChain(NamespaceView):
        def __init__(self, head, *args):
            NamespaceView.__init__(self, head)
            self.__namespaces__ = args

        def __getattr__(self, name):
            """Return the first such attribute found in the object list

            This is only invoked for attributes not found in the head
            namespace.
            """
            for obj in self.__namespaces__:
                try:
                    return getattr(obj, name)
                except AttributeError:
                    pass
            raise AttributeError(name)


Python gives us the local set and local del for free.

The 'nested namespaces' approach that prompted my original suggestion can then
be spelt by using:

  parent = Namespace()
  child1 = NamespaceChain({}, parent)
  child2 = NamespaceChain({}, child1)

There *is* a problem with using __getattr__ though - any attribute in the chained namespaces that is shadowed by a class attribute (like 'update') will be picked up from the class, not from the chained namespaces. So we do need to use __getattribute__ to change that lookup order. However, given the amount of grief the default lookup behaviour causes, I think the place to do that is in Namespace itself:

    class Namespace(object):
        # otherwise unchanged
        def __getattribute__(self, name):
            """Namespaces do NOT default to looking in their class dict
            for non-magic attributes
            """
            getattribute = super(Namespace, self).__getattribute__
            try:
                return getattribute("__dict__")[name]
            except KeyError:
                if name.startswith('__') and name.endswith('__'):
                    # Employ the default lookup system for magic names
                    return getattribute(name)
                else:
                    # Skip the default lookup system for normal names
                    if hasattr(self, "__getattr__"):
                        return getattribute("__getattr__")(name)
                    else:
                        raise AttributeError(name)

The above is a pretty simple approach - it completely bypasses the descriptor machinery for non-magic names. This means that the instance namespace does NOT get polluted by any non-magic names in the class dictionary, but magic names can be accessed normally. And subclasses can add their own methods, and this feature will continue to 'just work'. For example:

Py> ns = namespaces.Namespace()
Py> ns.update
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "namespaces.py", line 30, in __getattribute__
    raise AttributeError(name)
AttributeError: update
Py> type(ns).update
<unbound method Namespace.update>
Py> ns.__init__
<bound method Namespace.__init__ of Namespace()>

(side note: I don't think I have ever written a __getattribute__ method without introducing an infinite recursion on the first attempt. . .)

Anyway, it is probably worth digging into the descriptor machinery a bit further in order to design a lookup scheme that is most appropriate for namespaces, but the above example has convinced me that object.__getattribute__ is NOT it :)

Cheers,
Nick.

--
Nick Coghlan   |   [EMAIL PROTECTED]   |   Brisbane, Australia
---------------------------------------------------------------
            http://boredomandlaziness.skystorm.net

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to