sier2 0.17__py3-none-any.whl

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 sier2 might be problematic. Click here for more details.

sier2/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from ._block import Block, BlockError
2
+ from ._dag import Connection, Dag, BlockState
3
+ from ._library import Library, Info
4
+ from ._version import __version__
sier2/__main__.py ADDED
@@ -0,0 +1,96 @@
1
+ import argparse
2
+ from importlib.metadata import version
3
+
4
+ from sier2 import Library
5
+ from ._library import _find_blocks, _find_dags, run_dag
6
+ from ._util import block_doc_text, dag_doc_text
7
+
8
+ BOLD = '' # '\x1b[1;37m'
9
+ NORM = '' # '\x1b[0m'
10
+
11
+ def _pkg(module):
12
+ return module.split('.')[0]
13
+
14
+ def blocks_cmd(args):
15
+ """Display the blocks found via plugin entry points."""
16
+
17
+ seen = set()
18
+ curr_ep = None
19
+ for entry_point, gi in _find_blocks():
20
+ show = not args.block or gi.key.endswith(args.block)
21
+ if curr_ep is None or entry_point!=curr_ep:
22
+ if show:
23
+ pkg = _pkg(entry_point.module)
24
+ s = f'In {pkg} v{version(pkg)}'
25
+ u = '' # '\n' + '#' * len(s)
26
+ print(f'\n{BOLD}{s}{u}{NORM}')
27
+ # print(f'\x1b[1mIn {entry_point.module} v{version(entry_point.module)}:\x1b[0m')
28
+ curr_ep = entry_point
29
+
30
+ if show:
31
+ dup = f' (DUPLICATE)' if gi.key in seen else ''
32
+ print(f' {BOLD}{gi.key}: {gi.doc}{NORM}{dup}')
33
+
34
+ if args.verbose:
35
+ block = Library.get_block(gi.key)
36
+ print(block_doc_text(block))
37
+ print()
38
+
39
+ seen.add(gi.key)
40
+
41
+ def dags_cmd(args):
42
+ """Display the dags found via plugin entry points."""
43
+
44
+ seen = set()
45
+ curr_ep = None
46
+ for entry_point, gi in _find_dags():
47
+ show = not args.dag or gi.key.endswith(args.dag)
48
+ if curr_ep is None or entry_point!=curr_ep:
49
+ if show:
50
+ pkg = _pkg(entry_point.module)
51
+ s = f'In {pkg} v{version(pkg)}'
52
+ u = '' # '\n' + '#' * len(s)
53
+ print(f'\n{BOLD}{s}{u}{NORM}')
54
+ curr_ep = entry_point
55
+
56
+ if show:
57
+ dup = f' (DUPLICATE)' if gi.key in seen else ''
58
+ print(f' {BOLD}{gi.key}: {gi.doc}{NORM}{dup}')
59
+
60
+ if args.verbose:
61
+ # We have to instantiate the dag to get the documentation.
62
+ #
63
+ dag = Library.get_dag(gi.key)
64
+ print(dag_doc_text(dag))
65
+
66
+ seen.add(gi.key)
67
+
68
+ def run_cmd(args):
69
+ run_dag(args.dag)
70
+
71
+ def main():
72
+ parser = argparse.ArgumentParser()
73
+ subparsers = parser.add_subparsers(help='sub-command help')
74
+
75
+ run = subparsers.add_parser('run', help='Run a dag')
76
+ run.add_argument('dag', type=str, help='A dag to run')
77
+ run.set_defaults(func=run_cmd)
78
+
79
+ blocks = subparsers.add_parser('blocks', help='Show available blocks')
80
+ blocks.add_argument('-v', '--verbose', action='store_true', help='Show help')
81
+ blocks.add_argument('block', nargs='?', help='Show all blocks ending with this string')
82
+ blocks.set_defaults(func=blocks_cmd)
83
+
84
+ dags = subparsers.add_parser('dags', help='Show available dags')
85
+ dags.add_argument('-v', '--verbose', action='store_true', help='Show help')
86
+ dags.add_argument('dag', nargs='?', help='Show all dags ending with this string')
87
+ dags.set_defaults(func=dags_cmd)
88
+
89
+ args = parser.parse_args()
90
+ if 'func' in args:
91
+ args.func(args)
92
+ else:
93
+ parser.print_help()
94
+
95
+ if __name__=='__main__':
96
+ main()
sier2/_block.py ADDED
@@ -0,0 +1,148 @@
1
+ from enum import StrEnum
2
+ import inspect
3
+ import param
4
+ from typing import Any, Callable
5
+ from collections import defaultdict
6
+
7
+ from . import _logger
8
+
9
+ class BlockError(Exception):
10
+ """Raised if a Block configuration is invalid."""
11
+
12
+ pass
13
+
14
+ class BlockState(StrEnum):
15
+ """The current state of a block; also used for logging."""
16
+
17
+ DAG = 'DAG' # Dag logging.
18
+ BLOCK = 'BLOCK' # Block logging.
19
+ INPUT = 'INPUT'
20
+ READY = 'READY'
21
+ EXECUTING = 'EXECUTING'
22
+ WAITING = 'WAITING'
23
+ SUCCESSFUL = 'SUCCESSFUL'
24
+ INTERRUPTED = 'INTERRUPTED'
25
+ ERROR = 'ERROR'
26
+
27
+ class Block(param.Parameterized):
28
+ """The base class for blocks.
29
+
30
+ A block is implemented as:
31
+
32
+ .. code-block:: python
33
+
34
+ class MyBlock(Block):
35
+ ...
36
+
37
+ A typical block will have at least one input parameter, and an ``execute()``
38
+ method that is called when an input parameter value changes.
39
+
40
+ .. code-block:: python
41
+
42
+ class MyBlock(Block):
43
+ value_in = param.String(label='Input Value')
44
+
45
+ def execute(self):
46
+ print(f'New value is {self.value_in}')
47
+ """
48
+
49
+ _block_state = param.String(default=BlockState.READY)
50
+
51
+ SIER2_KEY = '_sier2__key'
52
+
53
+ def __init__(self, *args, user_input=False, **kwargs):
54
+ super().__init__(*args, **kwargs)
55
+
56
+ if not self.__doc__:
57
+ raise BlockError(f'Class {self.__class__} must have a docstring')
58
+
59
+ self.user_input = user_input
60
+ self._block_state = BlockState.INPUT if user_input else BlockState.READY
61
+ self.logger = _logger.get_logger(self.name)
62
+
63
+ # Maintain a map of "block+output parameter being watched" -> "input parameter".
64
+ # This is used by _block_event() to set the correct input parameter.
65
+ #
66
+ self._block_name_map: dict[tuple[str, str], str] = {}
67
+
68
+ # Record this block's output parameters.
69
+ # If this is a user_input block, we need to trigger
70
+ # the output values before executing the next block,
71
+ # in case the user didn't change anything.
72
+ #
73
+ self._block_out_params = []
74
+
75
+ # self._block_context = _EmptyContext()
76
+
77
+ self._progress = None
78
+
79
+ @classmethod
80
+ def block_key(cls):
81
+ """The unique key of this block class.
82
+
83
+ Blocks require a unique key so they can be identified in the block library.
84
+ The default implementation should be sufficient, but can be overridden
85
+ in case of refactoring or name clashes.
86
+ """
87
+
88
+ im = inspect.getmodule(cls)
89
+
90
+ if hasattr(cls, Block.SIER2_KEY):
91
+ return getattr(cls, Block.SIER2_KEY)
92
+
93
+ return f'{im.__name__}.{cls.__qualname__}'
94
+
95
+ def execute(self, *_, **__):
96
+ """This method is called when one or more of the input parameters causes an event.
97
+
98
+ Override this method in a Block subclass.
99
+
100
+ The ``execute()`` method can have arguments. The arguments can be specified
101
+ in any order. It is not necessary to specify all, or any, arguments.
102
+ Arguments will not be passed via ``*args`` or ``**kwargs``.
103
+
104
+ * ``stopper`` - an indicator that the dag has been stopped. This may be
105
+ set while the block is executing, in which case the block should
106
+ stop executing as soon as possible.
107
+ * ``events`` - the param events that caused execute() to be called.
108
+ """
109
+
110
+ # print(f'** EXECUTE {self.__class__=}')
111
+ pass
112
+
113
+ def __panel__(self):
114
+ """A default Panel component.
115
+
116
+ When run in a Panel context, a block will typically implement
117
+ its own __panel__() method. If it doesn't, this method will be
118
+ used as a default. When a block without a __panel__() is wrapped
119
+ in a Card, self.progress will be assigned a pn.indicators.Progress()
120
+ widget which is returned here. The Panel context will make it active
121
+ before executing the block, and non-active after executing the block.
122
+ (Why not have a default Progress()? Because we don't want any
123
+ Panel-related code in the core implementation.)
124
+
125
+ If the block implements __panel__(), this will obviously be overridden.
126
+
127
+ When run in non-Panel context, this will remain unused.
128
+ """
129
+
130
+ return self._progress
131
+
132
+ def __call__(self, **kwargs) -> dict[str, Any]:
133
+ """Allow a block to be called directly."""
134
+
135
+ in_names = [name for name in self.__class__.param if name.startswith('in_')]
136
+ if len(kwargs)!=len(in_names) or any(name not in in_names for name in kwargs):
137
+ names = ', '.join(in_names)
138
+ raise BlockError(f'All input params must be specified: {names}')
139
+
140
+ for name, value in kwargs.items():
141
+ setattr(self, name, value)
142
+
143
+ self.execute()
144
+
145
+ out_names = [name for name in self.__class__.param if name.startswith('out_')]
146
+ result = {name: getattr(self, name) for name in out_names}
147
+
148
+ return result