-
-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Environment Variables #145
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
# -*- coding: utf-8 -*- | ||
# ----------------------------------------------------------------------------- | ||
# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details. | ||
# | ||
# Distributed under the terms of the BSD license. | ||
# | ||
# The full license is in the file LICENCE, distributed with this software. | ||
# ----------------------------------------------------------------------------- | ||
from collections import Iterable | ||
|
||
from atom.api import (Atom, List, Signal, Bool, Section, Typed) | ||
|
||
from ...utils.atom_util import (tagged_members, member_to_pref, | ||
members_from_preferences) | ||
from ...utils.container_change import ContainerChange | ||
|
||
#: Id used to identify dependencies type. | ||
DEP_TYPE = 'exopy.envvar' | ||
|
||
|
||
class EnvNode(Atom): | ||
"""Environment variable composed of several sub env_var. | ||
|
||
""" | ||
#: List of all the children of the env_var. The list should not be | ||
#: manipulated directly by user code. | ||
#: The tag 'child' is used to mark that a member can contain child env_var | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tag is not needed in that case, since we ruled out the need for multiple child location. |
||
#: and is used to gather children for operation which must occur on all of | ||
#: them. | ||
children = List().tag(child=100) | ||
|
||
#: Signal emitted when the list of children change, the payload will be a | ||
#: ContainerChange instance. | ||
#: The tag 'child_notifier' is used to mark that a member emmit | ||
#: notifications about modification of another 'child' member. This allow | ||
#: editors to correctly track all of those. | ||
children_changed = Signal().tag(child_notifier='children') | ||
|
||
def add_child_envvar(self, index, child): | ||
"""Add a child envvar at the given index. | ||
|
||
Parameters | ||
---------- | ||
index : int | ||
Index at which to insert the new child env_var. | ||
|
||
child : BaseEnvVar | ||
Env_var to insert in the list of children env_var. | ||
|
||
""" | ||
self.children.insert(index, child) | ||
|
||
# In the absence of a root envvar do nothing else than inserting the | ||
# child. | ||
if self.has_root: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
child.depth = self.depth + 1 | ||
child.path = self._child_path() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those attributes need to be defined. |
||
|
||
# Give him its root so that it can proceed to any child | ||
# registration it needs to. | ||
child.parent = self | ||
child.root = self.root | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here those need to be defined. |
||
|
||
change = ContainerChange(obj=self, name='children', | ||
added=[(index, child)]) | ||
self.children_changed(change) | ||
|
||
def move_child_envvar(self, old, new): | ||
"""Move a child env_var. | ||
|
||
Parameters | ||
---------- | ||
old : int | ||
Index at which the child to move is currently located. | ||
|
||
new : BaseEnvVar | ||
Index at which to insert the child env_var. | ||
|
||
""" | ||
child = self.children.pop(old) | ||
self.children.insert(new, child) | ||
|
||
# In the absence of a root env_var do nothing else than moving the | ||
# child. | ||
if self.has_root: | ||
|
||
change = ContainerChange(obj=self, name='children', | ||
moved=[(old, new, child)]) | ||
self.children_changed(change) | ||
|
||
def remove_child_envvar(self, index): | ||
"""Remove a child env_var from the children list. | ||
|
||
Parameters | ||
---------- | ||
index : int | ||
Index at which the child to remove is located. | ||
|
||
""" | ||
child = self.children.pop(index) | ||
|
||
# Cleanup database, update preferences | ||
child.root = None | ||
child.parent = None | ||
|
||
change = ContainerChange(obj=self, name='children', | ||
removed=[(index, child)]) | ||
self.children_changed(change) | ||
|
||
def preferences_from_members(self): | ||
""" | ||
|
||
""" | ||
prefs = super().preferences_from_members() | ||
for i, child in enumerate(self.children): | ||
prefs["child%i" % i] = child.preferences_from_members() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For homogeneity a |
||
|
||
return prefs | ||
|
||
@classmethod | ||
def build_from_config(cls, config, dependencies): | ||
"""Create a new instance using the provided infos for initialisation. | ||
|
||
Parameters | ||
---------- | ||
config : dict(str) | ||
Dictionary holding the new values to give to the members in string | ||
format, or dictionnary like for instance with prefs. | ||
|
||
dependencies : dict | ||
Dictionary holding the necessary classes needed when rebuilding. | ||
This is assembled by the EnvVarManager. | ||
|
||
Returns | ||
------- | ||
envvar : BaseEnvVar | ||
Newly created and initiliazed env_var. | ||
|
||
Notes | ||
----- | ||
This method is fairly powerful and can handle a lot of cases so | ||
don't override it without checking that it works. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not apply here. |
||
|
||
""" | ||
envvar = cls() | ||
members_from_preferences(envvar, config) | ||
for name, member in tagged_members(envvar, 'child').items(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be heavily simplified ! |
||
|
||
if isinstance(member, List): | ||
i = 0 | ||
pref = name + '_{}' | ||
validated = [] | ||
while True: | ||
child_name = pref.format(i) | ||
if child_name not in config: | ||
break | ||
child_config = config[child_name] | ||
child_class_name = child_config.pop('envvar_id') | ||
child_cls = dependencies[DEP_TYPE][child_class_name] | ||
child = child_cls.build_from_config(child_config, | ||
dependencies) | ||
validated.append(child) | ||
i += 1 | ||
|
||
else: | ||
if name not in config: | ||
continue | ||
child_config = config[name] | ||
child_class_name = child_config.pop('envvar_id') | ||
child_class = dependencies[DEP_TYPE][child_class_name] | ||
validated = child_class.build_from_config(child_config, | ||
dependencies) | ||
|
||
setattr(envvar, name, validated) | ||
|
||
return envvar |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# -*- coding: utf-8 -*- | ||
# ----------------------------------------------------------------------------- | ||
# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details. | ||
# | ||
# Distributed under the terms of the BSD license. | ||
# | ||
# The full license is in the file LICENCE, distributed with this software. | ||
# ----------------------------------------------------------------------------- | ||
|
||
from atom.api import (Unicode, ForwardTyped) | ||
from configobj import ConfigObj | ||
|
||
from ..utils.atom_util import HasPrefAtom | ||
|
||
|
||
def environment_plugin(): | ||
"""Delayed to avoid circular references. | ||
|
||
""" | ||
from .plugin import EnvironmentPlugin | ||
return EnvironmentPlugin | ||
|
||
|
||
class Environment(HasPrefAtom): | ||
|
||
#: Name of the environment. | ||
name = Unicode().tag(pref=True) | ||
|
||
#: Reference to the environment plugin managing this environment. | ||
plugin = ForwardTyped(environment_plugin) | ||
|
||
def __init__(self, **kwargs): | ||
|
||
super(Environment, self).__init__(**kwargs) | ||
|
||
def save(self, path=None): | ||
"""Save the environment as a ConfigObj object. | ||
|
||
Parameters | ||
---------- | ||
path : unicode | ||
Path of the file to which save the environment. | ||
|
||
""" | ||
config = ConfigObj(indent_type=' ', encoding='utf-8') | ||
config.update(self.node.preferences_from_members()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
# -*- coding: utf-8 -*- | ||
# ----------------------------------------------------------------------------- | ||
# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details. | ||
# | ||
# Distributed under the terms of the BSD license. | ||
# | ||
# The full license is in the file LICENCE, distributed with this software. | ||
# ----------------------------------------------------------------------------- | ||
|
||
from atom.api import (Atom, Constant, Str, Dict, Value, | ||
ForwardTyped, Property) | ||
|
||
from enaml.core.api import d_ | ||
|
||
from .infos import EnvVarInfos | ||
|
||
from ..utils.declarator import Declarator, import_and_get | ||
|
||
|
||
DEP_TYPE = 'exopy.envvar' | ||
|
||
|
||
class BaseEnvVar(Atom): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should inherit from HasPrefsAtom |
||
"""Base class defining common members of all Environment Variables. | ||
|
||
This class basically defines the minimal skeleton of an Environment | ||
variable in term of members and methods. | ||
|
||
""" | ||
#: Identifier for the build dependency collector | ||
dep_type = Constant(DEP_TYPE).tag(pref=True) | ||
|
||
#: Name of the class, used for persistence. | ||
envvar_id = Str().tag(pref=True) | ||
|
||
#: Id of the editor. | ||
editor_id = Str().tag(pref=True) | ||
|
||
#: Name of the env_var. This should be unique in hierarchy. | ||
name = Str().tag(pref=True) | ||
|
||
#: Reference to the root env_var in the hierarchy. | ||
root = ForwardTyped(lambda: RootEnvVar) | ||
|
||
#: Refrence to the parent env_var. | ||
parent = ForwardTyped(lambda: BaseEnvVar) | ||
|
||
value = Value().tag(pref=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will need doc. |
||
|
||
metadata = Dict().tag(pref=True) | ||
|
||
def preferences_from_members(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented in HasPrefsAtom |
||
"""Update the entries in the preference object. | ||
|
||
""" | ||
raise NotImplementedError() | ||
|
||
@classmethod | ||
def build_from_config(cls, config, dependencies): | ||
"""Create a new instance using the provided infos for initialisation. | ||
|
||
Parameters | ||
---------- | ||
config : dict(str) | ||
Dictionary holding the new values to give to the members in string | ||
format, or dictionnary like for instance with prefs. | ||
|
||
dependencies : dict | ||
Dictionary holding the necessary classes needed when rebuilding.. | ||
|
||
""" | ||
raise NotImplementedError() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be implemented using |
||
|
||
|
||
class EnvVar(Declarator): | ||
"""Declarator used to contribute a env_var. | ||
|
||
""" | ||
#: Path to the env_var object. Path should be dot separated and the class | ||
#: name preceded by ':'. | ||
envvar = d_(Str()) | ||
|
||
#: Path to the view object associated with the env_var. | ||
#: The path of any parent GroupDeclarator object will be prepended to it. | ||
view = d_(Str()) | ||
|
||
#: Metadata associated to the env_var. | ||
metadata = d_(Dict()) | ||
|
||
#: Id of the env_var computed from the top-level package and the env_var | ||
#: name | ||
id = Property(cached=True) | ||
|
||
def register(self, collector, traceback): | ||
"""Collect env_var and view and add infos to the DeclaratorCollector | ||
contributions member. | ||
|
||
The group declared by a parent if any is taken into account. All | ||
Interface children are also registered. | ||
|
||
""" | ||
# Build the env_var id by assembling the package name and the class | ||
# name | ||
envvar_id = self.id | ||
|
||
# If the env_var only specifies a name update the matching infos. | ||
if ':' not in self.EnvVar: | ||
if self.envvar not in collector.contributions: | ||
collector._delayed.append(self) | ||
return | ||
|
||
infos = collector.contributions[envvar_id] | ||
infos.metadata.update(self.metadata) | ||
|
||
self.is_registered = True | ||
return | ||
|
||
# Determine the path to the env_var. | ||
path = self.get_path() | ||
try: | ||
e_path, envvar = (path + '.' + self.envvar | ||
if path else self.envvar).split(':') | ||
|
||
except ValueError: | ||
msg = 'Incorrect %s (%s), path must be of the form a.b.c:Class' | ||
err_id = e_path.split('.', 1)[0] + '.' + envvar | ||
|
||
traceback[err_id] = msg | ||
return | ||
|
||
# Check that the env_var does not already exist. | ||
if envvar_id in collector.contributions or envvar_id in traceback: | ||
i = 1 | ||
while True: | ||
err_id = '%s_duplicate%d' % (envvar_id, i) | ||
if err_id not in traceback: | ||
break | ||
|
||
msg = 'Duplicate definition of {}, found in {}' | ||
traceback[err_id] = msg.format(envvar, e_path) | ||
return | ||
|
||
infos = EnvVarInfos(metadata=self.metadata) | ||
|
||
# Get the env_var class. | ||
t_cls = import_and_get(e_path, envvar, traceback, envvar_id) | ||
if t_cls is None: | ||
return | ||
|
||
# Add group and add to collector | ||
infos.metadata['group'] = self.get_group() | ||
collector.contributions[envvar_id] = infos | ||
|
||
self.is_registered = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The copyright should not predate the creation of the file ideally.