co3.co3 module¶
CO3 is an abstract base class for scaffolding object hierarchies and managing operations with associated database schemas. It facilitates something like a “lightweight ORM” for classes/tables/states with fixed transformations of interest. The canonical use case is managing hierarchical document relations, format conversions, and syntactical components.
Generic collation syntax:
class Type(CO3):
@collate
def group(self, key):
# disambiguate key
...
@collate('key', groups=['group1', 'group2'])
def key(self):
# key-specific logic
...
- class co3.co3.CO3[source]¶
Bases:
object
Base class supporting the central “COllate, COllect, COmpose” paradigm.
Collate: organize and transform conversion outputs, possibly across class components
Collect: gather core attributes, conversion data, and subcomponents for DB insertion
Compose: construct object-associated DB table references through the class hierarchy
on action groups
Group keys are simply named collections to make it easy for storage components to be attached to action subsets. They do _not_ augment the action registration namespace, meaning the action key should still be unique; the group key is purely auxiliary.
Action methods can also be attached to several groups, in case there is overlapping utility within or across schemas or storage media. In this case, it becomes particularly critical to ensure registered
collate
methods really are just “gathering results” from possibly heavy-duty operations, rather than performing them when called, so as to reduce wasted computation.New: collation caching
To help facilitate the common pattern of storing collation results, a
collate_cache
parameter has been added to store key-group indexed collation results. (Note: now requires explicit superclass instantiation.)- property attributes¶
Method to define how a subtype’s inserts should be handled under
collect
for canonical attributes, i.e., inserts to the type’s table.
- collate(key, group=None, args=None, kwargs=None)[source]¶
Note
This method is sensitive to group specification. By default, the provided key will be checked against the default
None
group, even if that key is only attached to non-default groups. Collation actions are unique on key-group pairs, so more specificity is generally required to correctly execute desired actions (otherwise, rely more heavily on the default group).
- collation_attributes(key, group)[source]¶
Return “connective” collation component data, possibly dependent on instance-specific attributes and the action arguments. This is typically the auxiliary structure that may be needed to attach to responses from registered
collate
calls to complete inserts.Note: this method is primarily used by
Mapper.collect()
, and is called just prior to collector send-off for collation inserts and injected alongside collation data. Common structure in collation components can make this function easy to define, independent of action group for instance.
- property components¶
Method to define how a subtype’s inserts should be handled under
collect
for constituent components that need handling.
- group_registry = {}¶
- key_registry = {}¶
- class co3.co3.FormatRegistryMeta(name, bases, attrs)[source]¶
Bases:
type
Metaclass handling collation registry at the class level.
- co3.co3.collate(key, groups=None)[source]¶
Collation decorator for CO3 subtype action registry.
Dynamic decorator; can be used as
collate
without any arguments, or with all. In the former case,key
will be a function, so we check for this.Usage
Collation registration is the process of exposing various actions for use in hierarchical collection (see
Mapper.collect
). Collation keys are unique identifiers of a particular action that emits data. Keys can belong to an arbitrary number of groups, which serve as semantically meaningful collections of similar actions. Group assignment also determines the associated collation component to be used as a storage target; the results of actions $K_G$ belonging to group $G$ will all be stored in the attached $G$-component. Specification of key-group relations can be done in a few ways:Explicit key-group specification: a specific key and associated groups can be provided as arguments to the decorator:
@collate('key', groups=['group1', 'group2']) def _key(self): # key-specific logic ...
The registry dictionaries will then have the following items:
key_registry = { ..., 'key': (_key, ['group1', 'group2']), ... } group_registry = { ..., 'group1': [..., 'key', ...], 'group2': [..., 'key', ...], ... }
If
groups
is left unspecified, the key will be attached to the defaultNone
group.Implicit key-group association: in some cases, you may want to support an entire “action class,” and associate any operations under the class to the same storage component. Here we still use the notion of connecting groups to components, but allow the key to be dynamically specified and passed through to the collation method:
@collate def group(self, key): # disambiguate key ...
and in the registries:
key_registry = { ..., None: {..., 'group': group, ...}, ... } group_registry = { ..., 'group': [..., None, ...], ... }
A few important notes:
Implicit key-group specifications attach the group to a single method, whereas in the explicit case, groups can be affiliated with many keys. When explicitly provided, only those exact key values are supported. But in the implicit case, any key is allowed; the group still remains a proxy for the entire action class, but without needing to map from specifically stored key values. That is, the utility of the group remains consistent across implicit and explicit cases, but stores the associations differently.
The
None
key, rather than point to a(<method>, <group-list>)
tuple, instead points to a dictionary ofgroup
-method
pairs. When attempting execute a key under a particular group, the group registry indicates whether the key is explicitly supported. IfNone
is present for the group, thenkey_registry[None][<group-name>]
can be used to recover the method implicitly affiliated with the key (along with any other key under the group).When any method has been implicitly registered, any key (even when attempting to specify an explicit key) will match that group. This can effectively mean keys are not unique when an implicit group has been registered. There is a protection in place here, however; in methods like
CO3.collate
andMapper.collect
, an implicit group must be directly named in order for a given key to be considered. That is, when attempting collation outside specific group context, provided keys will only be considered against explicitly registered keys.