-
Notifications
You must be signed in to change notification settings - Fork 85
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
Wrap single observer to a separate expression #1093
Changes from 5 commits
9bc1135
d74dcdf
8509b07
e254d21
54ea677
583488e
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 |
---|---|---|
|
@@ -17,6 +17,8 @@ | |
ObserverGraph as _ObserverGraph, | ||
) | ||
|
||
# Expression is a public user interface for constructing ObserverGraph. | ||
|
||
|
||
class Expression: | ||
""" | ||
|
@@ -25,25 +27,8 @@ class Expression: | |
``HasTraits.observe`` method or the ``observe`` decorator. | ||
|
||
An Expression is typically created using one of the top-level functions | ||
provided in this module, e.g.``trait``. | ||
provided in this module, e.g. ``trait``. | ||
""" | ||
def __init__(self): | ||
# ``_levels`` is a list of list of IObserver. | ||
# Each item corresponds to a layer of branches in the ObserverGraph. | ||
# The last item is the most nested level. | ||
# e.g. _levels = [[observer1, observer2], [observer3, observer4]] | ||
# observer3 and observer4 are both leaf nodes of a tree, and they are | ||
# "siblings" of each other. Each of observer3 and observer4 has two | ||
# parents: observer1 and observer2. | ||
# When ObserverGraph(s) are constructured from this expression, one | ||
# starts from the end of this list, to the top, and then continues to | ||
# the prior_expressions | ||
self._levels = [] | ||
|
||
# Represent prior expressions to be combined in series (JOIN) | ||
# or in parallel (OR). This is either an instance of _SeriesExpression | ||
# or an instance of _ParallelExpression. | ||
self._prior_expression = None | ||
|
||
def __eq__(self, other): | ||
""" Return true if the other value is an Expression with equivalent | ||
|
@@ -73,10 +58,8 @@ def __or__(self, expression): | |
new_expression : traits.observers.expression.Expression | ||
""" | ||
if self == expression: | ||
return self._copy() | ||
new = Expression() | ||
new._prior_expression = _ParallelExpression([self, expression]) | ||
return new | ||
return 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. Could we drop this branch? Is it required for correctness, or is it more for optimization? 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. (And if it's required for correctness, what happens with something like 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 is an optimization and yes we can drop it. |
||
return ParallelExpression(self, expression) | ||
|
||
def then(self, expression): | ||
""" Create a new expression by extending this expression with | ||
|
@@ -93,14 +76,7 @@ def then(self, expression): | |
------- | ||
new_expression : traits.observers.expression.Expression | ||
""" | ||
|
||
if self._prior_expression is None and not self._levels: | ||
# this expression is empty... | ||
new = expression._copy() | ||
else: | ||
new = Expression() | ||
new._prior_expression = _SeriesExpression([self, expression]) | ||
return new | ||
return SeriesExpression(self, expression) | ||
|
||
def trait(self, name, notify=True, optional=False): | ||
""" Create a new expression for observing a trait with the exact | ||
|
@@ -135,162 +111,75 @@ def _as_graphs(self): | |
------- | ||
graphs : list of ObserverGraph | ||
""" | ||
return _create_graphs(self) | ||
return self._create_graphs(branches=[]) | ||
|
||
def _new_with_branches(self, nodes): | ||
""" Create a new Expression with a new leaf nodes. | ||
def _create_graphs(self, branches): | ||
""" Return a list of ObserverGraph with the given branches. | ||
|
||
Parameters | ||
---------- | ||
nodes : list of IObserver | ||
|
||
Returns | ||
------- | ||
new_expression : traits.observers.expression.Expression | ||
""" | ||
expression = self._copy() | ||
expression._levels.append(nodes) | ||
return expression | ||
|
||
def _copy(self): | ||
""" Return a copy of this expression. | ||
branches : list of ObserverGraph | ||
Graphs to be used as branches. | ||
|
||
Returns | ||
------- | ||
new_expression : traits.observers.expression.Expression | ||
graphs : list of ObserverGraph | ||
""" | ||
expression = Expression() | ||
expression._levels = self._levels.copy() | ||
if self._prior_expression is not None: | ||
expression._prior_expression = self._prior_expression._copy() | ||
return expression | ||
|
||
|
||
def _create_graphs(expression, graphs=None): | ||
""" Create ObserverGraphs from a given expression. | ||
raise NotImplementedError("'_create_graphs' must be implemented.") | ||
|
||
Parameters | ||
---------- | ||
expression : traits.observers.expression.Expression | ||
graphs : collection of ObserverGraph | ||
Leaf graphs to be added. | ||
Needed when this function is called recursively. | ||
|
||
Returns | ||
------- | ||
graphs : list of ObserverGraph | ||
New graphs | ||
class SingleObserverExpression(Expression): | ||
""" Container of Expression for wrapping a single observer. | ||
""" | ||
if graphs is None: | ||
graphs = [] | ||
|
||
for nodes in expression._levels[::-1]: | ||
graphs = [ | ||
_ObserverGraph(node=node, children=graphs) for node in nodes | ||
] | ||
|
||
if expression._prior_expression is not None: | ||
graphs = expression._prior_expression._create_graphs( | ||
graphs=graphs, | ||
) | ||
return graphs | ||
def __init__(self, observer): | ||
self.observer = observer | ||
|
||
def _create_graphs(self, branches): | ||
return [ | ||
_ObserverGraph(node=self.observer, children=branches), | ||
] | ||
|
||
# _SeriesExpression and _ParallelExpression share an undeclared interface | ||
# which require the classes to have implemented ``copy`` and ``_create_graphs`` | ||
|
||
class _SeriesExpression: | ||
class SeriesExpression(Expression): | ||
""" Container of Expression for joining expressions in series. | ||
Used internally in this module. | ||
|
||
Parameters | ||
---------- | ||
expressions : list of Expression | ||
List of Expression to be combined in series. | ||
first : traits.observers.expression.Expression | ||
Left expression to be joined in series. | ||
second : traits.observers.expression.Expression | ||
Right expression to be joined in series. | ||
""" | ||
|
||
def __init__(self, expressions): | ||
self.expressions = expressions.copy() | ||
|
||
def _copy(self): | ||
""" Return a copy of this instance. | ||
The internal ``expressions`` list is copied so it can be mutated. | ||
def __init__(self, first, second): | ||
self._first = first | ||
self._second = second | ||
|
||
Returns | ||
------- | ||
series_expression : _SeriesExpression | ||
""" | ||
return _SeriesExpression(self.expressions) | ||
def _create_graphs(self, branches): | ||
branches = self._second._create_graphs(branches=branches) | ||
return self._first._create_graphs(branches=branches) | ||
|
||
def _create_graphs(self, graphs): | ||
""" | ||
Create new ObserverGraph(s) from the joined expressions. | ||
|
||
Parameters | ||
---------- | ||
graphs : collection of ObserverGraph | ||
Leaf graphs to be added. | ||
Needed when this function is called recursively. | ||
|
||
Returns | ||
------- | ||
graphs : list of ObserverGraph | ||
New graphs | ||
""" | ||
for expr in self.expressions[::-1]: | ||
graphs = _create_graphs( | ||
expr, | ||
graphs=graphs, | ||
) | ||
return graphs | ||
|
||
|
||
class _ParallelExpression: | ||
class ParallelExpression(Expression): | ||
""" Container of Expression for joining expressions in parallel. | ||
Used internally in this module. | ||
|
||
Parameters | ||
---------- | ||
expressions : list of Expression | ||
List of Expression to be combined in parallel. | ||
left : traits.observers.expression.Expression | ||
Left expression to be joined in parallel. | ||
right : traits.observers.expression.Expression | ||
Right expression to be joined in parallel. | ||
""" | ||
|
||
def __init__(self, expressions): | ||
self.expressions = expressions.copy() | ||
def __init__(self, left, right): | ||
self._left = left | ||
self._right = right | ||
|
||
def _copy(self): | ||
""" Return a copy of this instance. | ||
The internal ``expressions`` list is copied so it can be mutated. | ||
|
||
Returns | ||
------- | ||
parallel_expression : _ParallelExpression | ||
""" | ||
return _ParallelExpression(self.expressions) | ||
|
||
def _create_graphs(self, graphs): | ||
""" | ||
Create new ObserverGraph(s) from the joined expressions. | ||
|
||
Parameters | ||
---------- | ||
graphs : collection of ObserverGraph | ||
Leaf graphs to be added. | ||
Needed when this function is called recursively. | ||
|
||
Returns | ||
------- | ||
graphs : list of ObserverGraph | ||
New graphs | ||
""" | ||
new_graphs = [] | ||
for expr in self.expressions: | ||
or_graphs = _create_graphs( | ||
expr, | ||
graphs=graphs, | ||
) | ||
new_graphs.extend(or_graphs) | ||
return new_graphs | ||
def _create_graphs(self, branches): | ||
left_graphs = self._left._create_graphs(branches=branches) | ||
right_graphs = self._right._create_graphs(branches=branches) | ||
return left_graphs + right_graphs | ||
|
||
|
||
def join_(*expressions): | ||
|
@@ -330,4 +219,4 @@ def trait(name, notify=True, optional=False): | |
""" | ||
observer = _NamedTraitObserver( | ||
name=name, notify=notify, optional=optional) | ||
return Expression()._new_with_branches(nodes=[observer]) | ||
return SingleObserverExpression(observer) |
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.
Drive-by: Without the space Sphinx refuses to display
trait
as code.