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.
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,))