Imported core, m, ag and serial for refactoring
This commit is contained in:
parent
6f28ac0382
commit
5baa9b75d0
25 changed files with 918 additions and 4 deletions
0
ag/__init__.py
Normal file
0
ag/__init__.py
Normal file
160
ag/ag.Aggregate.pyx
Normal file
160
ag/ag.Aggregate.pyx
Normal file
|
@ -0,0 +1,160 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
cdef class Aggregate:
|
||||
def __init__(Aggregate self, *modules):
|
||||
self._attrs = {} # attr_name, module
|
||||
self._modules = OrderedDict() # module_name, module
|
||||
self._provided = {} # keyword, module
|
||||
self._common = {} # method_name, method_wrapper
|
||||
|
||||
for module in modules:
|
||||
self._link_module(module)
|
||||
|
||||
def _list_modules(Aggregate self):
|
||||
d = {}
|
||||
|
||||
for attr, module in self._attrs.items():
|
||||
if module not in d:
|
||||
d[module] = set()
|
||||
|
||||
d[module].add(attr)
|
||||
|
||||
return d
|
||||
|
||||
def _link_module(Aggregate self, Module module):
|
||||
# check for name (=type) collision
|
||||
if module._name in self._modules.keys():
|
||||
raise ModuleCollision(module._name, None, "name", None)
|
||||
|
||||
# check if requirements are satisfied
|
||||
unsatisfied_deps = module._requires - set(self._provided.keys())
|
||||
if unsatisfied_deps:
|
||||
raise ModuleDependencyError(module._name, unsatisfied_deps)
|
||||
|
||||
# check for new module declaring a common method that we already provide as non-common
|
||||
new_commons = module._common - set(self._common.keys())
|
||||
common_collisions = {nc for nc in new_commons if nc in self._attrs and not nc in self._common.keys()}
|
||||
if common_collisions:
|
||||
colliding_module_names = {self._attrs[x]._name for x in common_collisions}
|
||||
raise ModuleCollision(module._name, colliding_module_names, "non-common method", common_collisions)
|
||||
|
||||
# check for an attr collision
|
||||
module_attrs = {x for x in dir(module) if x[0] != "_"}
|
||||
attr_collisions = (module_attrs - module._common) & (set(self._attrs.keys()) | set(self._common.keys()))
|
||||
if attr_collisions:
|
||||
colliding_module_names = set()
|
||||
for collision in attr_collisions:
|
||||
if collision in self._attrs:
|
||||
colliding_module_names.add(self._attrs[collision]._name)
|
||||
|
||||
if collision in self._common:
|
||||
colliding_module_names.add(self._common[collision]._name)
|
||||
|
||||
raise ModuleCollision(module._name, colliding_module_names, "attribute", attr_collisions)
|
||||
|
||||
# check for a provided keyword collision
|
||||
provided_collisions = module._provides & set(self._provided.keys())
|
||||
if provided_collisions:
|
||||
colliding_module_names = {self._provided[x]._name for x in provided_collisions}
|
||||
raise ModuleCollision(module._name, colliding_module_names, "provided keyword", provided_collisions)
|
||||
|
||||
# link the module
|
||||
self._modules[module._name] = module
|
||||
|
||||
for keyword in module._provides:
|
||||
self._provided[keyword] = module
|
||||
|
||||
for attr in (module_attrs - module._common):
|
||||
self._attrs[attr] = module
|
||||
|
||||
# create and/or populate CommonMethod wrappers to common methods
|
||||
for method_name in module._common:
|
||||
if method_name not in module_attrs:
|
||||
raise CommonMethodMissing(method_name, module._name)
|
||||
|
||||
if method_name not in self._common:
|
||||
self._common[method_name] = CommonMethod(method_name)
|
||||
|
||||
self._common[method_name].link_module(module)
|
||||
|
||||
# hand the module a reference to us
|
||||
module._top = self
|
||||
|
||||
# call the module's _on_link method, if it has one
|
||||
if hasattr(module, "_on_link"):
|
||||
module._on_link()
|
||||
|
||||
def _unlink_module(Aggregate self, str module_name):
|
||||
if not module_name in self._modules:
|
||||
raise ModuleDoesntExist(module_name)
|
||||
|
||||
module = self._modules[module_name]
|
||||
|
||||
# check reverse dependencies
|
||||
global_deps = set()
|
||||
for m in self._modules.values():
|
||||
global_deps.update(m._requires)
|
||||
|
||||
reverse_deps = module._provides & global_deps
|
||||
if reverse_deps:
|
||||
raise ModuleDependencyError(module_name, reverse_deps, unlink=True)
|
||||
|
||||
# remove from all pools
|
||||
for aname, mod in list(self._attrs.items()):
|
||||
if mod._name == module_name:
|
||||
del self._attrs[aname]
|
||||
|
||||
del self._modules[module_name]
|
||||
|
||||
for ename in module._provides:
|
||||
del self._provided[ename]
|
||||
|
||||
# remove _common wrappers
|
||||
for method_name in module._common:
|
||||
self._common[method_name].unlink_module(module_name)
|
||||
|
||||
# clear _top reference
|
||||
module._top = None
|
||||
|
||||
def _merge_in(Aggregate self, Aggregate other_ag):
|
||||
for module_name, module in other_ag._modules.items():
|
||||
if module_name not in self._modules:
|
||||
self._link_module(module)
|
||||
|
||||
def __getattr__(Aggregate self, str aname):
|
||||
if aname in self._attrs:
|
||||
return getattr(self._attrs[aname], aname)
|
||||
elif aname in self._common:
|
||||
return self._common[aname]
|
||||
else:
|
||||
raise AttributeError("Aggregate has no attribute '%s'" %(aname))
|
||||
|
||||
def __setattr__(Aggregate self, str aname, avalue):
|
||||
if aname not in self._attrs:
|
||||
raise AttributeError("Aggregate has no attribute '%s'" %(aname))
|
||||
else:
|
||||
setattr(self._attrs[aname], aname, avalue)
|
||||
|
||||
def _get_type(Aggregate self):
|
||||
return tuple(self._modules.keys())
|
||||
|
||||
def __repr__(Aggregate self):
|
||||
module_count = len(self._modules)
|
||||
|
||||
if module_count:
|
||||
lines = ["Aggregate("]
|
||||
|
||||
|
||||
for i, module in enumerate(self._modules.values()):
|
||||
if i + 1 < module_count:
|
||||
comma = ","
|
||||
else:
|
||||
comma = ""
|
||||
|
||||
lines.append(" %s%s" %(repr(module), comma))
|
||||
|
||||
lines.append(")")
|
||||
|
||||
return "\n".join(lines)
|
||||
else:
|
||||
return "Aggregate()"
|
30
ag/ag.CommonMethod.pyx
Normal file
30
ag/ag.CommonMethod.pyx
Normal file
|
@ -0,0 +1,30 @@
|
|||
cdef class CommonMethod:
|
||||
def __init__(CommonMethod self, str method_name):
|
||||
self.method_name = method_name
|
||||
self.module_names = []
|
||||
self.modules = []
|
||||
|
||||
def link_module(CommonMethod self, Module module):
|
||||
self.module_names.append(module._name)
|
||||
self.modules.append(module)
|
||||
|
||||
def unlink_module(CommonMethod self, module_name):
|
||||
i = self.module_names.index(module_name)
|
||||
|
||||
del self.module_names[i]
|
||||
del self.modules[i]
|
||||
|
||||
def __call__(CommonMethod self, *args):
|
||||
return_values = []
|
||||
|
||||
for module in self.modules:
|
||||
return_values.append(getattr(module, self.method_name).__call__(*args))
|
||||
|
||||
return return_values
|
||||
|
||||
def __repr__(self):
|
||||
return "CommonMethod(name=%s)" %(self.method_name)
|
||||
|
||||
@property
|
||||
def _name(self):
|
||||
return repr(self)
|
20
ag/ag.Module.pyx
Normal file
20
ag/ag.Module.pyx
Normal file
|
@ -0,0 +1,20 @@
|
|||
cdef class Module:
|
||||
def __init__(self, name, provides=set(), requires=set(), common=set()):
|
||||
"""
|
||||
* Modules are identified by their name which has to be unique within the Aggregate's namespace.
|
||||
* The provides sequence contains keywords which are copied into Aggregate's _provided list.
|
||||
Two modules providing the same export can't be linked into the same Aggregate.
|
||||
* The requires sequence contains keywords that have to be already present in the Aggregate's
|
||||
_provided list before linking.
|
||||
* All names in the Module's namespace that don't begin with an underscore will be exported
|
||||
into the Aggregate's namespace.
|
||||
* If you want to call one method on multiple modules, these modules must all export the method name
|
||||
in their "common" set. Otherwise a name collision is raised. Their methods will be called in the
|
||||
same order in which the modules were linked.
|
||||
"""
|
||||
|
||||
self._top = None
|
||||
self._name = name
|
||||
self._provides = set(provides)
|
||||
self._requires = set(requires)
|
||||
self._common = set(common)
|
49
ag/ag.header.pyx
Normal file
49
ag/ag.header.pyx
Normal file
|
@ -0,0 +1,49 @@
|
|||
"""
|
||||
Overwatch Aggregate Object type
|
||||
|
||||
TODO Method overrides: _common gets renamed to _call_all and we add _call_last.
|
||||
"""
|
||||
|
||||
class ModuleCollision(Exception):
|
||||
def __init__(self, colliding_module_name, resident_module_names, item_type, items):
|
||||
self.colliding_module_name = colliding_module_name
|
||||
self.resident_module_names = resident_module_names
|
||||
self.item_type = item_type
|
||||
self.items = items
|
||||
|
||||
def __str__(self):
|
||||
if self.item_type == "name":
|
||||
return "Unable to link module '%s': name already present in Aggregate." %(self.colliding_module_name)
|
||||
else:
|
||||
return "Unable to link module '%s': %s(s) '%s' already provided by module(s) '%s'." %(self.colliding_module_name,
|
||||
self.item_type, "', '".join(self.items), "', '".join(self.resident_module_names))
|
||||
|
||||
class ModuleDependencyError(Exception):
|
||||
def __init__(self, module_name, dependencies, unlink=False):
|
||||
self.module_name = module_name
|
||||
self.dependencies = dependencies
|
||||
self.unlink = unlink
|
||||
|
||||
def __str__(self):
|
||||
if self.unlink:
|
||||
return "Unable to unlink module '%s': Aggregate depends on its export(s) '%s'." %(self.module_name,
|
||||
"', '".join(self.dependencies))
|
||||
else:
|
||||
return "Unable to link module '%s': requirement '%s' unsatisfied by Aggregate." %(self.module_name,
|
||||
"', '".join(self.dependencies))
|
||||
|
||||
class CommonMethodMissing(Exception):
|
||||
def __init__(self, method_name, module_name):
|
||||
self.method_name = method_name
|
||||
self.module_name = module_name
|
||||
|
||||
def __str__(self):
|
||||
return "Unable to link module '%s': method '%s' (declared as common) does not exist in module." %(
|
||||
self.module_name, self.method_name)
|
||||
|
||||
class ModuleDoesntExist(Exception):
|
||||
def __init__(self, module_name):
|
||||
self.module_name = module_name
|
||||
|
||||
def __str__(self):
|
||||
return "Unable to unlink module '%s': not linked to Aggregate." %(self.module_name)
|
0
ag/build.sh
Executable file
0
ag/build.sh
Executable file
Loading…
Add table
Add a link
Reference in a new issue