cdxcore 0.1.13__tar.gz → 0.1.15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cdxcore might be problematic. Click here for more details.

Files changed (44) hide show
  1. {cdxcore-0.1.13 → cdxcore-0.1.15}/PKG-INFO +1 -1
  2. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/__init__.py +1 -1
  3. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/crman.py +1 -1
  4. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/deferred.py +329 -143
  5. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/dynaplot.py +14 -264
  6. cdxcore-0.1.15/cdxcore/dynaplotlimits.py +245 -0
  7. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/util.py +5 -5
  8. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/version.py +3 -3
  9. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore.egg-info/PKG-INFO +1 -1
  10. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore.egg-info/SOURCES.txt +2 -0
  11. {cdxcore-0.1.13 → cdxcore-0.1.15}/docs/source/conf.py +1 -6
  12. {cdxcore-0.1.13 → cdxcore-0.1.15}/pyproject.toml +1 -1
  13. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_deferred.py +23 -3
  14. cdxcore-0.1.15/tmp/deferred-hierarchical.py +682 -0
  15. {cdxcore-0.1.13 → cdxcore-0.1.15}/LICENSE +0 -0
  16. {cdxcore-0.1.13 → cdxcore-0.1.15}/README.md +0 -0
  17. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/config.py +0 -0
  18. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/err.py +0 -0
  19. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/jcpool.py +0 -0
  20. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/pretty.py +0 -0
  21. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/pretty.py_bak.py +0 -0
  22. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/subdir.py +0 -0
  23. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/uniquehash.py +0 -0
  24. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore/verbose.py +0 -0
  25. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore.egg-info/dependency_links.txt +0 -0
  26. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore.egg-info/requires.txt +0 -0
  27. {cdxcore-0.1.13 → cdxcore-0.1.15}/cdxcore.egg-info/top_level.txt +0 -0
  28. {cdxcore-0.1.13 → cdxcore-0.1.15}/setup.cfg +0 -0
  29. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_config.py +0 -0
  30. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_crman.py +0 -0
  31. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_err.py +0 -0
  32. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_jcpool.py +0 -0
  33. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_pretty.py +0 -0
  34. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_subdir.py +0 -0
  35. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_uniquehash.py +0 -0
  36. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_util.py +0 -0
  37. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_verbose.py +0 -0
  38. {cdxcore-0.1.13 → cdxcore-0.1.15}/tests/test_version.py +0 -0
  39. {cdxcore-0.1.13 → cdxcore-0.1.15}/tmp/filelock.py +0 -0
  40. {cdxcore-0.1.13 → cdxcore-0.1.15}/tmp/np.py +0 -0
  41. {cdxcore-0.1.13 → cdxcore-0.1.15}/tmp/npio.py +0 -0
  42. {cdxcore-0.1.13 → cdxcore-0.1.15}/tmp/sharedarray.py +0 -0
  43. {cdxcore-0.1.13 → cdxcore-0.1.15}/up/git_message.py +0 -0
  44. {cdxcore-0.1.13 → cdxcore-0.1.15}/up/pip_modify_setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cdxcore
3
- Version: 0.1.13
3
+ Version: 0.1.15
4
4
  Summary: Basic Python Tools; upgraded cdxbasics
5
5
  Author-email: Hans Buehler <github@buehler.london>
6
6
  License-Expression: MIT
@@ -4,4 +4,4 @@ Created on June 2022
4
4
  @author: hansb
5
5
  """
6
6
 
7
- __version__ = "0.1.13" # auto-updated by setup.py
7
+ __version__ = "0.1.15" # auto-updated by setup.py
@@ -142,7 +142,7 @@ class CRMan(object):
142
142
  """
143
143
  Return current string.
144
144
 
145
- This is the string that ``CRMan``is currently visible to the user
145
+ This is the string that ``CRMan`` is currently visible to the user
146
146
  since the last time a new line was printed.
147
147
  """
148
148
  return self._current
@@ -1,10 +1,153 @@
1
- """
1
+ r"""
2
2
  Overview
3
3
  --------
4
4
 
5
5
  Framework for delayed execution of a Python code tree.
6
6
 
7
- Used by :mod:`cdxcore.dnaplot`.
7
+ Used by :mod:`cdxcore.dnaplot`.
8
+
9
+ Illustation
10
+ ^^^^^^^^^^^
11
+
12
+ **The Deferred Dependency Graph**
13
+
14
+ Assume we have a class ``A`` and a function ``F``
15
+ operates on instances ``a`` of ``A``:
16
+
17
+ .. code-block:: python
18
+
19
+ class A(object):
20
+ def __init__(self, x):
21
+ self.x = x
22
+ def func(self, y=1):
23
+ return self.x * y
24
+ def __call__(self, y):
25
+ return self.x + y
26
+ def __eq__(self, z):
27
+ return self.x==z
28
+
29
+ def F(a : A):
30
+ _ = a.f(2)
31
+ _ = _+a(3)
32
+ return _, _ == 1
33
+
34
+ Deferred execution means that instead of using an instance of ``A`` to evaluate
35
+ ``F``, we use a :class:`cdxcore.deferred.Deferred` object:
36
+
37
+ .. code-block:: python
38
+
39
+ from cdxcore.defer1ed import Deferred
40
+
41
+ a = Deferred("a") # <- name "a" is arbitrary but important for error messages
42
+ r1, r2 = F(a)
43
+
44
+ The returned ``r1`` and ``r2`` are of type :class:`cdxcore.deferred.DeferredAction`. We can print
45
+ the overall dependency tree from ``a`` using :meth:`cdxcore.deferred.DeferredAction.deferred_print_dependency_tree`:
46
+
47
+ .. code-block:: python
48
+
49
+ a.deferred_print_dependency_tree( with_sources = True )
50
+
51
+ yields::
52
+
53
+ 00: $a <= $a
54
+ 01: $a.f <= $a
55
+ 02: $a.f(2) <= $a
56
+ 03: ($a.f(2)+$a(3)) <= $a
57
+ 04: (($a.f(2)+$a(3))==1) <= $a
58
+ 01: $a(3) <= $a
59
+
60
+ The ``$`` sign indicates top level source elements.
61
+ Note that this tree shows the depth of the dependencies, not execution dependencies: the term ``$a(3)``
62
+ must be executed before ``$a.f(2)+=$a(3)``.
63
+ This order of terms a seen from the top level ``a`` can be retrieved using :meth:`cdxcore.deferred.DeferredAction.deferred_source_dependants`:
64
+ calling ``print( [ _.deferred_info for _ in a.deferred_source_dependants ] )`` prints::
65
+
66
+ ['$a.f', '$a.f(2)', '$a(3)', '$a.f(2)+=$a(3)', '($a.f(2)==2)']
67
+
68
+ **Evaluating the Graph**
69
+
70
+ To execute the graph, use :meth:`cdxcore.deferred.Deferred.deferred_resolve` at top level (note that graphs can only
71
+ be resolved once):
72
+
73
+ .. code-block:: python
74
+
75
+ a.deferred_resolve(A(x=2))
76
+ print(r1.deferred_result, ",", r2.deferred_result) # -> 9 , False
77
+
78
+ Validation by directly execution ``F`` with an object of type ``A`` confirms the result:
79
+
80
+ .. code-block:: python
81
+
82
+ t1, t2 = F(A(x=2))
83
+ print(, t1, ",", t2) # -> 9 , False
84
+
85
+ Limitations
86
+ ^^^^^^^^^^^
87
+
88
+ The framework relies on replacing any Python element by a :class:`cdxcore.deferred.DeferredAction` object and then
89
+ catche and track all actions applied to it, be that reading an attribute, an item, calling ``__call__`` or accessing
90
+ any of the standard Python attributes such as ``__eq__``.
91
+
92
+ This appraoch has a number of short comings:
93
+
94
+ * **Gap in Implementation:** please let us know.
95
+
96
+ * **Flow controls:** any code flow which depends on the actual value of some computation is not supported.
97
+
98
+ * **Core Python Operators:** some Python operators
99
+ must return a certain type: ``__bool__``, ``__str__``, ``__repr__`` and ``__index__`` as far as we are aware.
100
+ That means we cannot wrap the result into a
101
+ :class:`cdxcore.deferred.DeferredAction`.
102
+
103
+ Considering ``__str__`` and ``__repr__`` are used throughtout the Python stack
104
+ a ``DeferredAction`` will return strings describing itself. Be aware of
105
+ corresponding error messages.
106
+
107
+ If ``__bool__`` or ``__index__`` is called a :class:`cdxcore.deferred.NotSupportedError`
108
+ is raised.
109
+
110
+ * **Python atomic types:** Python optimizes various operations for its atomic types such as ``int`` etc. That means if a deferred
111
+ action catches an operation on an element which represents an atomic element, it might not be able to translate the action
112
+ into the atomic's equivalent.
113
+
114
+ Here is an example for ``int`` with ``+=``:
115
+
116
+ .. code-block:: python
117
+
118
+ a = Deferred("a")
119
+ a += 1
120
+ a.deferred_resolve(int(1))
121
+
122
+ This produces an error:
123
+
124
+ .. code-block:: python
125
+
126
+ AttributeError: ("Cannot resolve '$a+=1': the concrete element of type 'int' provided by '$a' does not contain the action '__iadd__'", "'int' object has no attribute '__iadd__'")
127
+
128
+ The reason here is that the operation ``a += 1`` was caught as an ``__iadd__`` action.
129
+ However, ``getattr`` cannot obtain this operator from an ``int``:
130
+
131
+ .. code-block:: python
132
+
133
+ getattr( int(1), "__iadd__" ) # -> AttributeError: 'int' object has no attribute '__iadd__'
134
+
135
+ Interestingly, other operators such as ``__add__`` do work.
136
+
137
+ **Core Python Elements**
138
+
139
+ Some Python functionality requires more complexity: catching and tracing core
140
+ operatores like ``__bool__``, ``is_`` etc does not add value to the use cases we have in mind, and complicate the framework
141
+ considerably.
142
+
143
+ Class Interface
144
+ ^^^^^^^^^^^^^^^
145
+
146
+ The class interfaces for both :class:`cdxcore.deferred.Deferred` and :class:`cdxcore.deferred.DeferredAction`
147
+ are pretty clunky in that all member functions and attribues are pre-fixed with ``deferred`` or ``_deferred_``, respectively.
148
+ The reason is that the objects will catch and track all member function calls and attribute access meant for objects
149
+ of type ``A``.
150
+
8
151
 
9
152
  Import
10
153
  ------
@@ -13,6 +156,8 @@ Import
13
156
 
14
157
  from cdxcore.deferrred import Deferred
15
158
 
159
+ Documentation
160
+ -------------
16
161
  """
17
162
 
18
163
  from .err import verify
@@ -31,80 +176,20 @@ class ResolutionDependencyError(RuntimeError):
31
176
  """
32
177
  pass
33
178
 
179
+ class NotSupportedError(RuntimeError):
180
+ """
181
+ Exeception in case of an attempted resolution of unsupported
182
+ operators.
183
+ """
184
+ pass
185
+
34
186
  class DeferredAction(object):
35
187
  """
36
- A deferred action keeps track of an action dependency tree
188
+ A deferred action
37
189
  for an object which has not been created, yet.
38
-
39
- This class is returned by :class:`cdxcore.deferred.Deferred`.
40
-
41
- The aim is to be able to record a sequence of actions on a placeholder
42
- for an object which does not yet exist
43
- such as function calls, item access, or attribute access,
44
- and store the resulting logical dependency tree. Once the target
45
- element is available, execute the dependency tree iteratively.
46
-
47
- A basic example is as follows: assume we have a class ``A`` of which
48
- we will create an object ``a``, but only at a later stage. We
49
- wish to record a list of deferred actions on ``a`` ahead of its creation.
50
-
51
- Define the class::
52
-
53
- class A(object):
54
- def __init__(self, x):
55
- self.x = x
56
- def a_func(self, y):
57
- return self.x * y
58
- @staticmethod
59
- def a_static(y):
60
- return y
61
- @property
62
- def a_propery(self):
63
- return self.x
64
- def __getitem__(self, y):
65
- return self.x * y
66
- def __call__(self, y):
67
- return self.x * y
68
- def another(self):#
69
- return A(x=self.x*2)
70
-
71
- Use :class:`cdxcore.deferred.Deferred` to create a root ``DeferredAction`` object::
72
-
73
- from cdxcore.deferred import Deferred
74
- da = Deferred("A")
75
-
76
- Record a few actions::
77
190
 
78
- af = da.a_func(1) # defer function call to a_func(1)
79
- ai = da[2] # defer item access
80
- aa = da.x # defer attribute access
81
-
82
- :class:`cdxcore.deferred.DeferredAction` works iteratively, e.g. all values returned from a function call, ``__call__``, or ``__getitem__``
83
- are themselves deferred automatically.`:class:`cdxcore.deferred.DeferredAction` is also able to defer item and attribute assignments.
84
-
85
- As an example::
191
+ See :class:`cdxcore.deferred.Deferred` for a full discussion.
86
192
 
87
- an = da.another()
88
- an = an.another() # call a.another().another() --> an.x = 1*2*2 = 4
89
-
90
- Finally, resolve the execution by instantiating ``A`` and calling :meth:`cdxcore.deferred.DeferredAction.deferred_resolve`::
91
-
92
- a = A()
93
- da.deferred_resolve( a )
94
-
95
- print( an.x ) # -> 4
96
-
97
- This functionality is used in :mod:`cdxcore.dynaplot`::
98
-
99
- from cdxcore.dynaplot import figure
100
-
101
- fig = figure() # the DynanicFig returned by figure() is derived from DeferredAction
102
- ax = fig.add_subplot() # Deferred call for add_subplot()
103
- lns = ax.plot( x, y )[0] # This is a deferred call to plot() and then [0].
104
- fig.render() # renders the figure and executes plot() then [0]
105
- lns.set_ydata( y2 ) # we can now access the resulting Line2D object via the DeferredAction wrapper
106
- fig.close() # update graph
107
-
108
193
  **Constructor**
109
194
 
110
195
  External applications do not usually need to directly create objects
@@ -148,7 +233,7 @@ class DeferredAction(object):
148
233
  self._deferred_live = None # once the object exists, it goes here.
149
234
  self._deferred_was_resolved = False # whether the action was executed.
150
235
  self._deferred_dependants = [] # list of all direct dependent actions
151
- self._deferred_src_dependants = src_dependants
236
+ self._deferred_src_dependants = src_dependants #
152
237
 
153
238
  if action == "":
154
239
  info = "$"+info
@@ -160,6 +245,29 @@ class DeferredAction(object):
160
245
 
161
246
  self.__dict__["_ready_for_the_deferred_magic_"] = 1
162
247
 
248
+ def __str__(self) -> str:
249
+ """
250
+ Return descriptive string.
251
+
252
+ ``__str__`` cannot be deferred as it must return a string.
253
+ We therefore return some information on ``self``
254
+ """
255
+ return self._deferred_info
256
+
257
+ def __repr__(self) -> str:
258
+ """
259
+ Return description.
260
+
261
+ ``__repr__`` cannot be deferred as it must return a string.
262
+ We therefore return some information on ``self``
263
+ """
264
+ sources = list(self._deferred_sources.values())
265
+ s = ""
266
+ for src in sources:
267
+ s += src + ","
268
+ s = s[:-1]
269
+ return f"DeferredAction[{self._deferred_info} <- {s}]"
270
+
163
271
  @property
164
272
  def _deferred_parent_info(self) -> str:
165
273
  return self._deferred_parent._deferred_info if not self._deferred_parent is None else ""
@@ -239,6 +347,14 @@ class DeferredAction(object):
239
347
  """ Retrieve list of dependant :class:`cdxcore.deferred.DeferredAction` objects. """
240
348
  return self._deferred_dependants
241
349
 
350
+ @property
351
+ def deferred_source_dependants(self) -> list:
352
+ """
353
+ Retrieve list of dependant :class:`cdxcore.deferred.DeferredAction` objects at the level of the original parent source.
354
+ This list is in order.
355
+ """
356
+ return self._deferred_src_dependants
357
+
242
358
  @property
243
359
  def deferred_sources(self) -> dict:
244
360
  """
@@ -253,7 +369,7 @@ class DeferredAction(object):
253
369
  several :func:`Deferred` elements.
254
370
 
255
371
  Most users will prefer a simple list of names of sources.
256
- In that case, use :meth:`cdxcore.deferred.Deferred.deferred_sources_names`.
372
+ In that case, use :meth:`cdxcore.deferred.DeferredAction.deferred_sources_names`.
257
373
 
258
374
  """
259
375
  return self._deferred_sources
@@ -287,7 +403,7 @@ class DeferredAction(object):
287
403
  return list( self.deferred_sources.values() )
288
404
 
289
405
  @property
290
- def deferred_action_result(self):
406
+ def deferred_result(self):
291
407
  """
292
408
  Returns the result of the deferred action, if available.
293
409
 
@@ -524,18 +640,19 @@ class DeferredAction(object):
524
640
 
525
641
  def __getattr__(self, attr ):
526
642
  """ Deferred attribute access """
527
- private_str = "_deferred_"
643
+ private_str = "deferred_"
528
644
  #print("__getattr__", attr, self.__dict__.get(private_str+"info", "?"))
529
645
  #print(" ", self.__dict__)
530
646
  if attr in self.__dict__ or\
531
647
  attr[:2] == "__" or\
532
648
  attr[:len(private_str)] == private_str or\
649
+ attr[1:1+len(private_str)] == private_str or\
533
650
  not "_ready_for_the_deferred_magic_" in self.__dict__:
534
651
  #print("__getattr__: direct", attr)
535
652
  try:
536
653
  return self.__dict__[attr]
537
654
  except KeyError as e:
538
- raise AttributeError(*e.args, self.__dict__[private_str+"info"]) from e
655
+ raise AttributeError(*e.args, self.__dict__["_deferred_info"]) from e
539
656
  if not self._deferred_live is None:
540
657
  #print("__getattr__: live", attr)
541
658
  return getattr(self._deferred_live, attr)
@@ -544,16 +661,17 @@ class DeferredAction(object):
544
661
 
545
662
  def __setattr__(self, attr, value):
546
663
  """ Deferred attribute access """
547
- private_str = "_deferred_"
664
+ private_str = "deferred_"
548
665
  #print("__getattr__", attr, self.__dict__.get(private_str+"info", "?"))
549
666
  if attr in self.__dict__ or\
550
667
  attr[:2] == "__" or\
551
668
  attr[:len(private_str)] == private_str or\
669
+ attr[1:1+len(private_str)] == private_str or\
552
670
  not "_ready_for_the_deferred_magic_" in self.__dict__:
553
671
  try:
554
672
  self.__dict__[attr] = value
555
673
  except KeyError as e:
556
- raise AttributeError(*e.args, self.__dict__[private_str+"info"]) from e
674
+ raise AttributeError(*e.args, self.__dict__["_deferred_info"]) from e
557
675
  #print("__setattr__: direct", attr)
558
676
  return
559
677
  if not self._deferred_live is None:
@@ -569,6 +687,7 @@ class DeferredAction(object):
569
687
  if attr in self.__dict__ or\
570
688
  attr[:2] == "__" or\
571
689
  attr[:len(private_str)] == private_str or\
690
+ attr[1:1+len(private_str)] == private_str or\
572
691
  not "_ready_for_the_deferred_magic_" in self.__dict__:
573
692
  #print("__delattr__: direct", attr)
574
693
  try:
@@ -583,7 +702,7 @@ class DeferredAction(object):
583
702
  return self._act("__delattr__", args=[attr], num_args=1, fmt="del {parent}.{arg0}")
584
703
 
585
704
  @staticmethod
586
- def _generate_handle( action : str,
705
+ def _deferred_handle( action : str,
587
706
  return_deferred : bool = True, *,
588
707
  num_args : int = None,
589
708
  fmt : str = None ):
@@ -594,80 +713,152 @@ class DeferredAction(object):
594
713
  act.__doc__ = f"Deferred ``{action}`` action"
595
714
  return act
596
715
 
716
+ @staticmethod
717
+ def _deferred_unsupported( action : str, reason : str ):
718
+ def act(self, *args, **kwargs):
719
+ raise NotSupportedError(action, f"Deferring action `{action}` is not possible: {reason}")
720
+ act.__name__ = action
721
+ act.__doc__ = f"Deferred ``{action}`` action"
722
+ return act
723
+
724
+ # not supported
725
+ __bool__ = _deferred_unsupported("__bool__", "'__bool__' must return a 'bool'.")
726
+ __index__ = _deferred_unsupported("__index__", "'__index__' must return an 'int'.")
727
+ # __str__ : implemented
728
+ # __repr__ : implemented
729
+
597
730
  # core functionality
598
- __call__ = _generate_handle("__call__", num_args=None, fmt="{parent}({args})")
599
- __setitem__ = _generate_handle("__setitem__", num_args=2, fmt="{parent}[{arg0}]={arg1}")
600
- __getitem__ = _generate_handle("__getitem__", num_args=1, fmt="{parent}[{arg0}]")
731
+ __call__ = _deferred_handle("__call__", num_args=None, fmt="{parent}({args})")
732
+ __setitem__ = _deferred_handle("__setitem__", num_args=2, fmt="{parent}[{arg0}]={arg1}")
733
+ __getitem__ = _deferred_handle("__getitem__", num_args=1, fmt="{parent}[{arg0}]")
601
734
 
602
735
  # collections
603
- __contains__ = _generate_handle("__contains__", num_args=1, fmt="({arg0} in {parent})")
604
- __iter__ = _generate_handle("__iter__")
605
- __len__ = _generate_handle("__len__", num_args=0, fmt="len({parent})")
606
- __hash__ = _generate_handle("__hash__", num_args=0, fmt="hash({parent})")
607
-
608
- # cannot implement __bool__, __str__, or, __repr__ as these
609
- # have defined return types
736
+ __contains__ = _deferred_handle("__contains__", num_args=1, fmt="({arg0} in {parent})")
737
+ __iter__ = _deferred_handle("__iter__")
738
+ __len__ = _deferred_handle("__len__", num_args=0, fmt="len({parent})")
739
+ __hash__ = _deferred_handle("__hash__", num_args=0, fmt="hash({parent})")
610
740
 
611
741
  # comparison operators
612
- __neq__ = _generate_handle("__neq__", num_args=1, fmt="({parent}!={arg0})")
613
- __eq__ = _generate_handle("__eq__", num_args=1, fmt="({parent}=={arg0})")
614
- __ge__ = _generate_handle("__ge__", num_args=1, fmt="({parent}>={arg0})")
615
- __le__ = _generate_handle("__le__", num_args=1, fmt="({parent}<={arg0})")
616
- __gt__ = _generate_handle("__gt__", num_args=1, fmt="({parent}>{arg0})")
617
- __lt__ = _generate_handle("__lt__", num_args=1, fmt="({parent}<{arg0})")
742
+ __neq__ = _deferred_handle("__neq__", num_args=1, fmt="({parent}!={arg0})")
743
+ __eq__ = _deferred_handle("__eq__", num_args=1, fmt="({parent}=={arg0})")
744
+ __ge__ = _deferred_handle("__ge__", num_args=1, fmt="({parent}>={arg0})")
745
+ __le__ = _deferred_handle("__le__", num_args=1, fmt="({parent}<={arg0})")
746
+ __gt__ = _deferred_handle("__gt__", num_args=1, fmt="({parent}>{arg0})")
747
+ __lt__ = _deferred_handle("__lt__", num_args=1, fmt="({parent}<{arg0})")
748
+
749
+ # unitary
750
+ __abs__ = _deferred_handle("__abs__", True, num_args=0, fmt="|{parent}|")
618
751
 
619
752
  # i*
620
- __ior__ = _generate_handle("__ior__", False, num_args=1, fmt="{parent}|={arg0}")
621
- __iand__ = _generate_handle("__iand__", False, num_args=1, fmt="{parent}&={arg0}")
622
- __ixor__ = _generate_handle("__iand__", False, num_args=1, fmt="{parent}^={arg0}")
623
- __imod__ = _generate_handle("__imod__", False, num_args=1, fmt="{parent}%={arg0}")
624
- __iadd__ = _generate_handle("__iadd__", False, num_args=1, fmt="{parent}+={arg0}")
625
- __iconcat__ = _generate_handle("__iconcat__", False, num_args=1, fmt="{parent}+={arg0}")
626
- __isub__ = _generate_handle("__isub__", False, num_args=1, fmt="{parent}-={arg0}")
627
- __imul__ = _generate_handle("__imul__", False, num_args=1, fmt="{parent}*={arg0}")
628
- __imatmul__ = _generate_handle("__imatmul__", False, num_args=1, fmt="{parent}@={arg0}")
629
- __ipow__ = _generate_handle("__ipow__", False, num_args=1, fmt="{parent}**={arg0}")
630
- __itruediv__ = _generate_handle("__itruediv__", False, num_args=1, fmt="{parent}/={arg0}")
631
- __ifloordiv__ = _generate_handle("__ifloordiv__", False, num_args=1, fmt="{parent}//={arg0}")
632
- # Py2 __idiv__ = _generate_handle("__idiv__", False, num_args=1, fmt="{parent}/={arg0}")
753
+ __ior__ = _deferred_handle("__ior__", False, num_args=1, fmt="{parent}|={arg0}")
754
+ __iand__ = _deferred_handle("__iand__", False, num_args=1, fmt="{parent}&={arg0}")
755
+ __ixor__ = _deferred_handle("__iand__", False, num_args=1, fmt="{parent}^={arg0}")
756
+ __imod__ = _deferred_handle("__imod__", False, num_args=1, fmt="{parent}%={arg0}")
757
+ __iadd__ = _deferred_handle("__iadd__", False, num_args=1, fmt="{parent}+={arg0}")
758
+ __iconcat__ = _deferred_handle("__iconcat__", False, num_args=1, fmt="{parent}+={arg0}")
759
+ __isub__ = _deferred_handle("__isub__", False, num_args=1, fmt="{parent}-={arg0}")
760
+ __imul__ = _deferred_handle("__imul__", False, num_args=1, fmt="{parent}*={arg0}")
761
+ __imatmul__ = _deferred_handle("__imatmul__", False, num_args=1, fmt="{parent}@={arg0}")
762
+ __ipow__ = _deferred_handle("__ipow__", False, num_args=1, fmt="{parent}**={arg0}")
763
+ __itruediv__ = _deferred_handle("__itruediv__", False, num_args=1, fmt="{parent}/={arg0}")
764
+ __ifloordiv__ = _deferred_handle("__ifloordiv__", False, num_args=1, fmt="{parent}//={arg0}")
765
+ # Py2 __idiv__ = _deferred_handle("__idiv__", False, num_args=1, fmt="{parent}/={arg0}")
633
766
 
634
767
  # binary
635
- __or__ = _generate_handle("__or__", num_args=1, fmt="({parent}|{arg0})")
636
- __and__ = _generate_handle("__and__", num_args=1, fmt="({parent}&{arg0})")
637
- __xor__ = _generate_handle("__xor__", num_args=1, fmt="({parent}^{arg0})")
638
- __mod__ = _generate_handle("__mod__", num_args=1, fmt="({parent}%{arg0})")
639
- __add__ = _generate_handle("__add__", num_args=1, fmt="({parent}+{arg0})")
640
- __concat__ = _generate_handle("__concat__", num_args=1, fmt="({parent}+{arg0})")
641
- __sub__ = _generate_handle("__sub__", num_args=1, fmt="({parent}-{arg0})")
642
- __mul__ = _generate_handle("__mul__", num_args=1, fmt="({parent}*{arg0})")
643
- __pow__ = _generate_handle("__pow__", num_args=1, fmt="({parent}**{arg0})")
644
- __matmul__ = _generate_handle("__matmul__", num_args=1, fmt="({parent}@{arg0})")
645
- __truediv__ = _generate_handle("__truediv__", num_args=1, fmt="({parent}/{arg0})")
646
- __floordiv__ = _generate_handle("__floordiv__", num_args=1, fmt="({parent}//{arg0})")
647
- # Py2__div__ = _generate_handle("__div__", num_args=1, fmt="({parent}/{arg0})")
768
+ __or__ = _deferred_handle("__or__", num_args=1, fmt="({parent}|{arg0})")
769
+ __and__ = _deferred_handle("__and__", num_args=1, fmt="({parent}&{arg0})")
770
+ __xor__ = _deferred_handle("__xor__", num_args=1, fmt="({parent}^{arg0})")
771
+ __mod__ = _deferred_handle("__mod__", num_args=1, fmt="({parent}%{arg0})")
772
+ __add__ = _deferred_handle("__add__", num_args=1, fmt="({parent}+{arg0})")
773
+ __concat__ = _deferred_handle("__concat__", num_args=1, fmt="({parent}+{arg0})")
774
+ __sub__ = _deferred_handle("__sub__", num_args=1, fmt="({parent}-{arg0})")
775
+ __mul__ = _deferred_handle("__mul__", num_args=1, fmt="({parent}*{arg0})")
776
+ __pow__ = _deferred_handle("__pow__", num_args=1, fmt="({parent}**{arg0})")
777
+ __matmul__ = _deferred_handle("__matmul__", num_args=1, fmt="({parent}@{arg0})")
778
+ __truediv__ = _deferred_handle("__truediv__", num_args=1, fmt="({parent}/{arg0})")
779
+ __floordiv__ = _deferred_handle("__floordiv__", num_args=1, fmt="({parent}//{arg0})")
780
+ # Py2__div__ = _deferred_handle("__div__", num_args=1, fmt="({parent}/{arg0})")
648
781
 
649
782
  # rbinary
650
- __ror__ = _generate_handle("__ror__", num_args=1, fmt="({arg0}|{parent})")
651
- __rand__ = _generate_handle("__rand__", num_args=1, fmt="({arg0}&{parent})")
652
- __rxor__ = _generate_handle("__rxor__", num_args=1, fmt="({arg0}^{parent})")
653
- __rmod__ = _generate_handle("__rmod__", num_args=1, fmt="({arg0}%{parent})")
654
- __radd__ = _generate_handle("__radd__", num_args=1, fmt="({arg0}+{parent})")
655
- __rconcat__ = _generate_handle("__rconcat__", num_args=1, fmt="({arg0}+{parent})")
656
- __rsub__ = _generate_handle("__rsub__", num_args=1, fmt="({arg0}-{parent})")
657
- __rmul__ = _generate_handle("__rmul__", num_args=1, fmt="({arg0}*{parent})")
658
- __rpow__ = _generate_handle("__rpow__", num_args=1, fmt="({arg0}**{parent})")
659
- __rmatmul__ = _generate_handle("__rmatmul__", num_args=1, fmt="({arg0}@{parent})")
660
- __rtruediv__ = _generate_handle("__rtruediv__", num_args=1, fmt="({arg0}/{parent})")
661
- __rfloordiv__ = _generate_handle("__rfloordiv__", num_args=1,fmt="({arg0}//{parent})")
662
- # Py2__rdiv__ = _generate_handle("__rdiv__", num_args=1, fmt="({arg0}/{parent})")
783
+ __ror__ = _deferred_handle("__ror__", num_args=1, fmt="({arg0}|{parent})")
784
+ __rand__ = _deferred_handle("__rand__", num_args=1, fmt="({arg0}&{parent})")
785
+ __rxor__ = _deferred_handle("__rxor__", num_args=1, fmt="({arg0}^{parent})")
786
+ __rmod__ = _deferred_handle("__rmod__", num_args=1, fmt="({arg0}%{parent})")
787
+ __radd__ = _deferred_handle("__radd__", num_args=1, fmt="({arg0}+{parent})")
788
+ __rconcat__ = _deferred_handle("__rconcat__", num_args=1, fmt="({arg0}+{parent})")
789
+ __rsub__ = _deferred_handle("__rsub__", num_args=1, fmt="({arg0}-{parent})")
790
+ __rmul__ = _deferred_handle("__rmul__", num_args=1, fmt="({arg0}*{parent})")
791
+ __rpow__ = _deferred_handle("__rpow__", num_args=1, fmt="({arg0}**{parent})")
792
+ __rmatmul__ = _deferred_handle("__rmatmul__", num_args=1, fmt="({arg0}@{parent})")
793
+ __rtruediv__ = _deferred_handle("__rtruediv__", num_args=1, fmt="({arg0}/{parent})")
794
+ __rfloordiv__ = _deferred_handle("__rfloordiv__", num_args=1,fmt="({arg0}//{parent})")
795
+ # Py2__rdiv__ = _deferred_handle("__rdiv__", num_args=1, fmt="({arg0}/{parent})")
663
796
 
664
797
  class Deferred(DeferredAction):
665
798
  """
666
- Create a :class:`cdxcore.deferred.DeferredAction` object to keep track
667
- of a dependency tree of a sequence of actions performed an object
668
- that does not yet exist.
799
+ Create a top level placeholder object to keep track
800
+ of a sequence of actions performed
801
+ on an object that does not yet exist.
802
+
803
+ Actions such as function calls, item access, or attribute access,
804
+ feed the resulting logical dependency tree. Once the target
805
+ element is available, execute the dependency tree iteratively.
806
+
807
+ A basic example is as follows: assume we have a class ``A`` of which
808
+ we will create an object ``a``, but only at a later stage. We
809
+ wish to record a list of deferred actions on ``a`` ahead of its creation.
810
+
811
+ Define the class
812
+
813
+ .. code-block:: python
814
+
815
+ class A(object):
816
+ def __init__(self, x):
817
+ self.x = x
818
+ def a_func(self, y):
819
+ return self.x * y
820
+ @staticmethod
821
+ def a_static(y):
822
+ return y
823
+ @property
824
+ def a_propery(self):
825
+ return self.x
826
+ def __getitem__(self, y):
827
+ return self.x * y
828
+ def __call__(self, y):
829
+ return self.x * y
830
+ def another(self):#
831
+ return A(x=self.x*2)
832
+
833
+ Use :class:`cdxcore.deferred.Deferred` to create a root ``DeferredAction`` object:
834
+
835
+ .. code-block:: python
836
+
837
+ from cdxcore.deferred import Deferred
838
+ da = Deferred("A")
839
+
840
+ Record a few actions:
841
+
842
+ .. code-block:: python
843
+
844
+ af = da.a_func(1) # defer function call to a_func(1)
845
+ ai = da[2] # defer item access
846
+ aa = da.x # defer attribute access
847
+
848
+ :class:`cdxcore.deferred.DeferredAction` works iteratively, e.g. all values returned from a function call, ``__call__``, or ``__getitem__``
849
+ are themselves deferred automatically.`:class:`cdxcore.deferred.DeferredAction` is also able to defer item and attribute assignments.
669
850
 
670
- See :class:`cdxcore.deferred.DeferredAction` for a comprehensive discussion.
851
+ As an example::
852
+
853
+ an = da.another()
854
+ an = an.another() # call a.another().another() --> an.x = 1*2*2 = 4
855
+
856
+ Finally, resolve the execution by instantiating ``A`` and calling :meth:`cdxcore.deferred.Deferred.deferred_resolve`::
857
+
858
+ a = A()
859
+ da.deferred_resolve( a )
860
+
861
+ print( an.x ) # -> 4
671
862
 
672
863
  Parameters
673
864
  ----------
@@ -677,11 +868,6 @@ class Deferred(DeferredAction):
677
868
 
678
869
  max_err_len : int, optional
679
870
  Maximum length of output information. The default is 100.
680
-
681
- Returns
682
- -------
683
- :class:`cdxcore.deferred.DeferredAction`
684
- A deferred action.
685
871
  """
686
872
 
687
873
  def __init__(self, info : str,