beanbag.namespace

The beanbag.namespace module allows defining classes that provide arbitrary namespace behaviour. This is what allows the other beanbag modules to provide their clever syntactic sugar.

A entry in a namespace is identified by two components: a base and a path. The base is constructed once for a namespace and is common to all entries in the namespace, and each entry’s path is used to differentiate them. For example, with AttrDict, the base is the underlying dictionary (d), while the path is the sequence of references into that dictionary (eg, ("foo", "bar") corresponding to d["foo"]["bar"]). The reason for splitting these apart is mostly efficiency – the path element needs to be cheap and easy to construct and copy since that may need to happen for an attribute access.

To define a namespace you provide a class that inherits from beanbag.namespace.Namespace and defines the methods the base class should have. The NamespaceMeta metaclass then creates a new base class containing these methods, and builds the namespace class on top of that base class, mapping Python’s special method names to the corresponding base class methods, minus the underscores. For example, to define the behavour of the ~ operator (aka __invert__(self)), the Base class defines a method:

def invert(self, path):
    ...

The code can rely on the base value being self, and the path being path, then do whatever calculation is necessary to create a result. If that result should be a different entry in the same namespace, that can be created by invoking self.namespace(newpath).

In order to make inplace operations work more smoothly, returning None from those options will be automatically treated as returning the original namespace object (ie self.namespace(path), without the overhead of reconstructing the object). This is primarily to make it easier to avoid the “double setting” behaviour of python’s inplace operations, ie where a[i] += j is converted into:

tmp = a.__getitem__(i)   # tmp = a[i]
res = tmp.__iadd__(j)    # tmp += j
a.__setitem__(i, res)    # a[i] = tmp

In particular, implementations of setitem and setattr can avoid poor behaviour here by testing whether the value being set (res) is already the existing value, and performing a no-op if so. The SettableHierarchialNS class implements this behaviour.

NamespaceMeta

The NamespaceMeta metaclass provides the magic for creating arbitrary namespaces from Base classes as discussed above. When set as the metaclass for a class, it will turn a base class into a namespace class directly, while constructing an appropriate base class for the namespace to use.

class beanbag.namespace.NamespaceMeta
__invert__()

Obtain base class for namespace

__module__ = 'beanbag.namespace'
static __new__(mcls, name, bases, nmspc)
classmethod deferfn(mcls, cls, nsdict, basefnname, inum=False, attr=False)
classmethod make_namespace(mcls, cls)

create a unique Namespace class based on provided class

ops = ['repr', 'str', 'call', 'bool', 'getitem', 'setitem', 'delitem', 'len', 'iter', 'reversed', 'contains', 'enter', 'exit', 'pos', 'neg', 'invert', 'eq', 'ne', 'lt', 'le', 'gt', 'ge', 'add', 'sub', 'mul', 'pow', 'div', 'floordiv', 'lshift', 'rshift', 'and', 'or', 'xor', 'radd', 'rsub', 'rmul', 'rpow', 'rdiv', 'rfloordiv', 'rlshift', 'rrshift', 'rand', 'ror', 'rxor']
ops_attr = ['getattr', 'setattr', 'delattr']
ops_inum = ['iadd', 'isub', 'imul', 'ipow', 'idiv', 'ifloordiv', 'ilshift', 'irshift', 'iand', 'ior', 'ixor']
static wrap_path_fn(basefn)
static wrap_path_fn_attr(basefn)
static wrap_path_fn_inum(basefn)

NamespaceBase

The generated base class will inherit from NamespaceBase (or the base class corresponding to any namespaces the namespace class inherits from), and will have a Namespace attribute referencing the namespace class. Further, the generated base class can be accessed by using the inverse opertor on the namespace class, ie MyNamespaceBase = ~MyNamespace.

class beanbag.namespace.NamespaceBase

Base class for user-defined namespace classes’ bases

Namespace = None

Replaced in subclasses by the corresponding namespace class

namespace(path=None)

Used to create a new Namespace object from the Base class

Namespace

Namespace provides a trivial Base implementation. It’s primarily useful as a parent class for inheritance, so that you don’t have explicitly set NamespaceMeta as your metaclass.

class beanbag.namespace.Namespace(*args, **kwargs)

HierarchialNS

HierarchialNS provides a simple basis for producing namespaces with freeform attribute and item hierarchies, eg, where you might have something like ns.foo.bar["baz"].

By default, this class specifies a path as a tuple of attributes, but this can be changed by overriding the path and _get methods. If some conversion is desired on either attribute or item access, the attr and item methods can be overridden respectively.

Otherwise, to get useful behaviour from this class, you probably want to provide some additional methods, such as __call__.

class beanbag.namespace.HierarchialNS

Bases: beanbag.namespace.Namespace

__eq__(other)

self == other

__getattr__(attr)

self.attr

__getitem__(item)

self[attr]

__init__()
__module__ = 'beanbag.namespace'
__ne__(other)

self != other

__repr__()

Human readable representation of object

__str__()

Returns path joined by dots

SettableHierarchialNS

SettableHierarchialNS is intended to make life slightly easier if you want to be able to assign to your hierarchial namespace. It provides set and delete methods that you can implement, without having to go to the trouble of implementing both item and attribute variants of both functions.

This class implements the check for “setting to self” mentioned earlier in order to prevent inplace operations having two effects. It uses the eq method to test for equality.

class beanbag.namespace.SettableHierarchialNS(*args, **kwargs)

Bases: beanbag.namespace.HierarchialNS

__delattr__(attr)

del self.attr

__delitem__(item)

del self[item]

__eq__(other)

self == other

__getattr__(attr)

self.attr

__getitem__(item)

self[attr]

__init__(*args, **kwargs)
__module__ = 'beanbag.namespace'
__ne__(other)

self != other

__repr__()

Human readable representation of object

__setattr__(attr, val)

self.attr = val

__setitem__(item, val)

self[item] = val

__str__()

Returns path joined by dots

sig_adapt

This is a helper function to make that generated methods in the namespace object provide more useful help.

beanbag.namespace.sig_adapt(sigfn, dropargs=None, name=None)

Function decorator that changes the name and (optionally) signature of a function to match another function. This is useful for making the help of generic wrapper functions match the functions they’re wrapping. For example:

def foo(a, b, c, d=None):
    pass

@sig_adapt(foo)
def myfn(*args, **kwargs):
    pass

The optional “name” parameter allows renaming the function to something different to the original function’s name.

The optional “dropargs” parameter allows dropping arguments by position or name. (Note positions are 0 based, so to convert foo(self, a, b) to foo(a, b) specify dropargs=(“self”,) or dropargs=(0,))