Source code for fito.ioc
import os
import yaml
from fito import Spec, DictDataStore
from fito.specs.utils import general_iterator, general_new, is_iterable, recursive_map, general_append
[docs]def recursive_load(strings, paths=None):
"""
Load strings or file contents
:param strings: Represents yaml strings
:param paths: Optionally context paths for each string, used for solving imports
:return: an instance of ApplicationContext
"""
if paths is not None:
assert len(paths) == len(strings)
assert all(map(os.path.exists, paths))
parsed_strings = map(yaml.load, strings)
included_files = []
res = {}
for i, parsed_string in enumerate(parsed_strings):
# Collect imports
imports = parsed_string.pop('import', [])
if isinstance(imports, basestring): imports = [imports]
for fname in imports:
if paths is None: raise RuntimeError('Can not handle imports without paths')
fname = os.path.join(paths[i], fname)
included_files.append(fname)
# Do not allow overrides between files, might be caotic
for obj_name, obj in parsed_string.iteritems():
if obj_name in res:
raise RuntimeError(
"The object name {} is defined more than once.".format(obj_name) +
"\nOverrides can only be expressed via imports"
)
res[obj_name] = obj
# After having processed everything, let's consider the included files
if included_files:
tmp_ctx = ApplicationContext.load(*included_files)
for obj_name, obj in tmp_ctx.objects.iteritems():
res[obj_name] = obj
return res
[docs]class ApplicationContext(object):
def __init__(self, objects):
self.objects = objects
@classmethod
[docs] def load(cls, *fnames):
fnames_contents = []
paths = []
for fname in fnames:
paths.append(os.path.abspath(os.path.dirname(fname)))
with open(fname) as f:
fnames_contents.append(f.read())
return cls.load_from_strings(fnames_contents, paths=paths)
@classmethod
[docs] def load_from_strings(cls, strings, paths=None):
objects = recursive_load(strings, paths)
return cls(objects)
[docs] def alias(self, key, object_name):
if object_name not in self.objects:
raise ValueError('Added an alias to a not existing object ("{}")'.format(object_name))
if key in self.objects:
raise ValueError('Invalid alias name "{}" already exists'.format(key))
self.objects[key] = '${}'.format(object_name)
cache = DictDataStore()
@cache.autosave(method_type='instance')
def get(self, name):
res = self._get_raw(name)
if isinstance(res, dict) and 'type' in res:
res = Spec.dict2spec(res)
return res
def _get_raw(self, name):
res = self.objects[name]
if is_iterable(res):
res = self.resolve(res)
else:
res = self._try_load(res)
return res
[docs] def resolve(self, obj):
res = general_new(obj)
for k, v in general_iterator(obj):
if is_iterable(v):
v = recursive_map(v, self._try_load)
else:
v = self._try_load(v)
general_append(res, k, v)
return res
def _try_load(self, v):
if isinstance(v, basestring) and v.startswith('$'):
return self._get_raw(v[1:])
else:
return v