order.unique

Classes that define unique objects and the index to store them.

Class UniqueObject

class UniqueObject(name, id, context=None)

Bases: UniqueObject

An unique object defined by a name and an id. The purpose of this class is to provide a simple interface for objects that

  1. are used programatically and should therefore have a unique, human-readable name, and

  2. have a unique identifier that can be saved to files, such as (e.g.) ROOT trees.

Both, name and id should have unique values within a certain uniqueness context. This context defaults to either the current one as set by uniqueness_context(), the default_context of this class, or, whem empty, the lower-case class name. See get_default_context() for more info.

Arguments

name, id and context initialize the same-named attributes.

Copy behavior

When an inheriting class inherits also from CopyMixin certain copy rules apply for the name, id and context attributes as duplicate names or ids within the same context would directly cause an exception to be thrown (which is the desired behavior, see examples below). Two ways are in general recommended to copy a unique object:

import order as od

class MyClass(od.UniqueObject, od.CopyMixin):
    pass

orig = MyClass("foo", 1)

# 1. use the same context, explicitely change name and id
copy = orig.copy(name="bar", id=2)

# 2. use a different context, optionally set different name or id
with od.unqiueness_context("other"):
    copy = orig.copy()
    copy2 = orig.copy(name="baz")

Example

import order as od

foo = od.UniqueObject("foo", 1)

print(foo.name)
# -> "foo"
print(foo.id)
# -> 1

# name and id must be strictly string and integer types, respectively
od.UniqueObject(123, 1)
# -> TypeError: invalid name: 123
UniqueObject("foo", "mystring")
# -> TypeError: invalid id: mystring

# the name "foo" and the id 1 can no longer be used
# (at least not within the same uniqueness context, which is the default one when not set)
bar = UniqueObject("foo", 2)
# -> DuplicateNameException: 'order.unique.UniqueObject' object with name 'foo' already
#                            exists in the uniqueness context 'uniqueobject'

bar = UniqueObject("bar", 1)
# -> DuplicateIdException: 'order.unique.UniqueObject' object with id '1' already exists in
#                          the uniqueness context 'uniqueobject'

# everything is fine when an other context is provided
bar = UniqueObject("bar", 1, context="myNewContext")
# works!

# unique objects can als be compared by name and id
foo == 1
# -> True

bar == "bar"
# -> True

foo == bar
# -> False

# automatically use the next highest possible id
obj = UniqueObject("baz", id=UniqueObject.AUTO_ID)

obj.id
# -> 2  # 1 is the highest used id in the default context, see above

Members

classattributedefault_context
type: arbitrary (hashable)

The default context of uniqueness when none is given in the instance constructor. Two instances are only allowed to have the same name or the same id if their classes have different contexts. This member is not inherited when creating a sub-class.

classattributecls_name_singular
type: str

The name of the unique object class in singular form, e.g. for producing automatic messages.

classattributecls_name_plural
type: str

The name of the unique object class in plural form, e.g. for producing automatic messages.

context
type: arbitrary (hashable)

The uniqueness context of this instance.

name
type: str
read-only

The unique name.

id
type: int
read-only

The unique id.

classmethod get_instance(obj, default=no_default, context=None)

Returns an object that was instantiated by this class before. obj might be a name, id, or an instance of cls. If default is given, it is used as the default return value if no such object was found. Otherwise, an error is raised. context defaults to the default_context of this class.

classmethod auto_id(name, context)

Method to create an automatic id for instances that are created with id="+". The default recipe is max(ids) + 1.

Class UniqueObjectIndex

class UniqueObjectIndex(cls, objects=None, default_context=None)

Bases: CopyMixin

Index of UniqueObject instances for faster lookup by either name or id. The instances are stored for different uniqueness context, so most methods have a context argument which usually defaults to the return value of UniqueObject.get_default_context().

Arguments

cls must be a subclass of UniqueObject, which is used for type validation when a new object is added to the index. The default_context is used in case no context argument is set in most methods of this index object. It defaults to the return value of the clsUniqueObject.get_default_context().

Example

import order as od

idx = od.UniqueObjectIndex()
foo = idx.add("foo", 1)
bar = idx.add("bar", 2)

len(idx)
# -> 2

idx.names()
# -> ["foo", "bar"]

idx.ids()
# -> [1, 2]

idx.get(1) == foo
# -> True

# add objects for an other uniqueness context
# note: for the default context, the redundant use of the id 1 would have caused an error!
baz = idx.add("baz", 1, context="other")

len(idx)
# -> 3

# idx.len() (with a context argument) returns the number of objects contained with the
# default context
idx.len()
# -> 2

idx.len(context="other")
# -> 1

# get ids of objects for all contexts (which might contain duplicates)
idx.ids(context=all)
# -> [1, 2, 1]

Members

classattributeALL

The flag that denotes that all contexts should be tranversed in methods that accept a context argument. It defaults to the built-in function all.

cls
type: class
read-only

Class of objects hold by this index.

default_context
type: string

The default context that is used when no context argument is provided in most methods.

n
type: DotAccessProxy
read-only

An object that provides simple attribute access to contained objects via name in the default context.

ALL()

Return True if bool(x) is True for all values x in the iterable.

If the iterable is empty, return True.

len(context=None)

Returns the length of the index stored for context. When None, the default_context is used. When context is all, the sum of lengths of all indices is returned, which is equivalent to __len__().

contexts()

Returns a list of all contexts for whom indices are stored.

names(context=None)

Returns the names of the contained objects in the index stored for context. When None, the default_context is used. When context is all, a list of tuples (name, context) are returned with names from all indices. Note that the returned context refers to the one the object is stored in, rather than the uniqueness context of the object itself.

ids(context=None)

Returns the names of the contained objects in the index stored for context. When None, the default_context is used. When context is all, a list of tuples (id, context) are returned with ids from all indices. Note that the returned context refers to the one the object is stored in, rather than the uniqueness context of the object itself.

keys(context=None)

Returns pairs containing name and id of the currently contained objects in the index stored for context. When None, the default_context is used. When context is all, tuples (name, id, context) are returned with objects from all indices. Note that the returned context refers to the one the object is stored in, rather than the uniqueness context of the object itself.

values(context=None)

Returns all contained objects in the index stored for context. When None, the default_context is used. When context is all, tuples (value, context) are returned with objects from all indices. Note that the returned context refers to the one the object is stored in, rather than the uniqueness context of the object itself.

items(context=None)

Returns a list of (nested) tuples ((name, id), value) of all objects in the index stored for context. When context is all, tuples ((name, id), value, context) are returned with objects from all indices. Note that the returned context refers to the one the object is stored in, rather than the uniqueness context of the object itself.

add(*args, **kwargs)

Adds a new object to the index for a certain context. When the first arg is not an instance of cls, all args and kwargs are passed to the cls constructor to create a new object. In this case, the kwargs may contain index_context to define the context if the index in which the newly created object should be stored. When not set, default_context is used. Otherwise, when the first arg is already an object and to be added, the context is either index_context or contex. The former has priority for consistency with the case described above. In both cases the added object is returned.

extend(objs, context=None)

Adds multiple new objects to the index for context. All elements of the sequence objs are forwarded to add() and returns the added objects in a list. When an object is a dictionary or a tuple, it is expanded for the invocation of add(). When context is None, the default_context is used.

get(obj, default=no_default, context=None)

Returns an object that is stored in the index for context. obj might be a name, id, or an instance of cls. If default is given, it is used as the default return value if no such object could be found. Otherwise, an error is raised. When context is None, the default_context is used.

get_first(default=no_default, context=None)

Returns the first object that is stored in the index for context. If default is given, it is used as the default return value if no object could be found. Otherwise, an error is raised. When context is None, the default_context is used.

get_last(default=no_default, context=None)

Returns the last object that is stored in the index for context. If default is given, it is used as the default return value if no object could be found. Otherwise, an error is raised. When context is None, the default_context is used.

has(obj, context=None)

Checks if an object is contained in the index for context. obj might be a name, id, or an instance of the wrapped cls. When context is None, the default_context is used.

index(obj, context=None)

Returns the position of an object in the index for context. When context is None, the default_context is used. obj might be a name, id, or an instance of cls. When the object is not found in the index, an error is raised.

remove(obj, context=None, silent=False)

Removes an object from the index for context. obj might be a name, id, or an instance of cls. Returns the removed object. Unless silent is True, an error is raised if the object could not be found. When context is None, the default_context is used.

clear(context=None)

Clears the index for context by removing all elements. When None, the default_context is used. When context is all, the indices for all contexts are cleared.

Class UniqueObjectMeta

class UniqueObjectMeta(class_name, bases, class_dict)

Bases: type

Meta class definition that adds an instance cache to every class inheriting from UniqueObject.

Function uniqueness_context

uniqueness_context(context)

Adds the uniqueness context on top of the list of the current contexts, which is priotized in the UniqueObject constructor when no context is configured.

obj = UniqueObject("myObj", 1, context="myContext")

obj.context
# -> "myContext"

with uniqueness_context("otherContext"):
    obj2 = UniqueObject("otherObj", 2)

obj2.context
# -> "otherContext"

Decorator unique_tree

unique_tree(cls=None, parents=1, deep_children=False, deep_parents=False, skip=None)

Decorator that adds attributes and methods to the decorated class to provide tree features, i.e., parent-child relations. Example:

@unique_tree()
class MyNode(UniqueObject):
    cls_name_singular = "node"
    cls_name_plural = "nodes"
    default_context = "myclass"

# now, MyNode has the following attributes and methods:
# nodes,          parent_nodes,
# has_node(),     has_parent_node(),
# add_node(),     add_parent_node(),
# remove_node(),  remove_parent_node(),
# walk_nodes(),   walk_parent_nodes(),
# get_node(),     get_parent_node(),
# has_nodes,      has_parent_nodes,
# is_leaf_node,   is_root_node

c1 = MyNode("nodeA", 1)
c2 = c1.add_node("nodeB", 2)

c1.has_node(2)
# -> True

c2.has_parent_node("nodeA")
# -> True

c2.remove_parent_node(c1)
c2.has_parent_node("nodeA")
# -> False

cls denotes the type of instances the tree should hold and defaults to the decorated class itself. When parents is False, the additional features are reduced to provide only child relations. When parents is an integer, it is interpreted as the maximim number of parents a child can have. Negative numbers mean that an unlimited amount of parents is allowed. Additional convenience methods are added when parents is True or exactly 1. When deep_children (deep_parents) is True, get_* and has_* child (parent) methods will have recursive features. When skip is a sequence, it can contain names of attributes to skip that would normally be created.

A class can be decorated multiple times. Internally, the objects are stored in a separated UniqueObjectIndex instance per added tree functionality.

Doc strings are automatically created.