sier2 0.23__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 sier2 might be problematic. Click here for more details.
- sier2-0.23/LICENSE +21 -0
- sier2-0.23/PKG-INFO +73 -0
- sier2-0.23/README.rst +50 -0
- sier2-0.23/pyproject.toml +38 -0
- sier2-0.23/src/sier2/__init__.py +4 -0
- sier2-0.23/src/sier2/__main__.py +96 -0
- sier2-0.23/src/sier2/_block.py +194 -0
- sier2-0.23/src/sier2/_dag.py +620 -0
- sier2-0.23/src/sier2/_library.py +226 -0
- sier2-0.23/src/sier2/_logger.py +65 -0
- sier2-0.23/src/sier2/_util.py +65 -0
- sier2-0.23/src/sier2/_version.py +3 -0
- sier2-0.23/src/sier2/panel/__init__.py +1 -0
- sier2-0.23/src/sier2/panel/_feedlogger.py +153 -0
- sier2-0.23/src/sier2/panel/_panel.py +364 -0
- sier2-0.23/src/sier2/panel/_panel_util.py +83 -0
sier2-0.23/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Algol60
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
sier2-0.23/PKG-INFO
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: sier2
|
|
3
|
+
Version: 0.23
|
|
4
|
+
Summary: Blocks of code that are executed in dags
|
|
5
|
+
Author: algol60
|
|
6
|
+
Author-email: algol60@users.noreply.github.com
|
|
7
|
+
Requires-Python: >=3.11,<4.0
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Requires-Dist: holoviews (>=1.19.0)
|
|
19
|
+
Requires-Dist: panel (>=1.4.4)
|
|
20
|
+
Requires-Dist: param (>=2.1.0)
|
|
21
|
+
Description-Content-Type: text/x-rst
|
|
22
|
+
|
|
23
|
+
Sier2
|
|
24
|
+
======
|
|
25
|
+
|
|
26
|
+
Connect modular pieces of Python code ("blocks") into
|
|
27
|
+
a processing dag pipeline. Blocks are an improvement on libraries;
|
|
28
|
+
if you have a library, you still need to build an application.
|
|
29
|
+
Blocks are pieces of an application, you just have to connect them.
|
|
30
|
+
|
|
31
|
+
See the ``examples`` directory for examples.
|
|
32
|
+
|
|
33
|
+
Description
|
|
34
|
+
-----------
|
|
35
|
+
|
|
36
|
+
A ``block`` is a self-contained piece of code with input and output parameters.
|
|
37
|
+
Blocks can be connected to each other using a ``Dag`` to create
|
|
38
|
+
a dag of blocks.
|
|
39
|
+
|
|
40
|
+
More precisely, output parameters in one block can be connected to input parameters
|
|
41
|
+
in another block. The connections need not be one-to-one: parameters in multiple blocks
|
|
42
|
+
can be connected to parameters in a single block; conversely, parameters in a single block
|
|
43
|
+
can be connected to parameters in multiple blocks.
|
|
44
|
+
|
|
45
|
+
Block parameters use `param <https://param.holoviz.org/>`_, which not only implement
|
|
46
|
+
triggering and watching of events, but allow parameters to be named and documented.
|
|
47
|
+
|
|
48
|
+
A typical block implementation looks like this.
|
|
49
|
+
|
|
50
|
+
.. code-block:: python
|
|
51
|
+
|
|
52
|
+
from sier2 import Block
|
|
53
|
+
|
|
54
|
+
class Increment(Block):
|
|
55
|
+
"""A block that adds one to the input value."""
|
|
56
|
+
|
|
57
|
+
int_in = param.Integer(label='The input', doc='An integer')
|
|
58
|
+
int_out = param.Integer(label='The output', doc='The incremented value')
|
|
59
|
+
|
|
60
|
+
def execute(self):
|
|
61
|
+
self.int_out = self.int_in + 1
|
|
62
|
+
|
|
63
|
+
See the examples in ``examples`` (Python scripts) and ``examples-panel`` (scripts that use `Panel <https://panel.holoviz.org/>`_ as a UI).
|
|
64
|
+
|
|
65
|
+
Documentation
|
|
66
|
+
-------------
|
|
67
|
+
|
|
68
|
+
To build the documentation from the repository root directory:
|
|
69
|
+
|
|
70
|
+
.. code-block:: powershell
|
|
71
|
+
|
|
72
|
+
docs/make html
|
|
73
|
+
|
sier2-0.23/README.rst
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Sier2
|
|
2
|
+
======
|
|
3
|
+
|
|
4
|
+
Connect modular pieces of Python code ("blocks") into
|
|
5
|
+
a processing dag pipeline. Blocks are an improvement on libraries;
|
|
6
|
+
if you have a library, you still need to build an application.
|
|
7
|
+
Blocks are pieces of an application, you just have to connect them.
|
|
8
|
+
|
|
9
|
+
See the ``examples`` directory for examples.
|
|
10
|
+
|
|
11
|
+
Description
|
|
12
|
+
-----------
|
|
13
|
+
|
|
14
|
+
A ``block`` is a self-contained piece of code with input and output parameters.
|
|
15
|
+
Blocks can be connected to each other using a ``Dag`` to create
|
|
16
|
+
a dag of blocks.
|
|
17
|
+
|
|
18
|
+
More precisely, output parameters in one block can be connected to input parameters
|
|
19
|
+
in another block. The connections need not be one-to-one: parameters in multiple blocks
|
|
20
|
+
can be connected to parameters in a single block; conversely, parameters in a single block
|
|
21
|
+
can be connected to parameters in multiple blocks.
|
|
22
|
+
|
|
23
|
+
Block parameters use `param <https://param.holoviz.org/>`_, which not only implement
|
|
24
|
+
triggering and watching of events, but allow parameters to be named and documented.
|
|
25
|
+
|
|
26
|
+
A typical block implementation looks like this.
|
|
27
|
+
|
|
28
|
+
.. code-block:: python
|
|
29
|
+
|
|
30
|
+
from sier2 import Block
|
|
31
|
+
|
|
32
|
+
class Increment(Block):
|
|
33
|
+
"""A block that adds one to the input value."""
|
|
34
|
+
|
|
35
|
+
int_in = param.Integer(label='The input', doc='An integer')
|
|
36
|
+
int_out = param.Integer(label='The output', doc='The incremented value')
|
|
37
|
+
|
|
38
|
+
def execute(self):
|
|
39
|
+
self.int_out = self.int_in + 1
|
|
40
|
+
|
|
41
|
+
See the examples in ``examples`` (Python scripts) and ``examples-panel`` (scripts that use `Panel <https://panel.holoviz.org/>`_ as a UI).
|
|
42
|
+
|
|
43
|
+
Documentation
|
|
44
|
+
-------------
|
|
45
|
+
|
|
46
|
+
To build the documentation from the repository root directory:
|
|
47
|
+
|
|
48
|
+
.. code-block:: powershell
|
|
49
|
+
|
|
50
|
+
docs/make html
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "sier2"
|
|
3
|
+
version = "0.23"
|
|
4
|
+
description = "Blocks of code that are executed in dags"
|
|
5
|
+
authors = ["algol60 <algol60@users.noreply.github.com>"]
|
|
6
|
+
readme = "README.rst"
|
|
7
|
+
packages = [{include = "sier2", from = "src"}]
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Programming Language :: Python :: 3.11",
|
|
10
|
+
"Programming Language :: Python :: 3.12",
|
|
11
|
+
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Operating System :: OS Independent",
|
|
13
|
+
"Intended Audience :: Science/Research",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"Topic :: Scientific/Engineering",
|
|
16
|
+
"Topic :: Software Development :: Libraries"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[tool.poetry.dependencies]
|
|
20
|
+
python = "^3.11"
|
|
21
|
+
|
|
22
|
+
holoviews = ">=1.19.0"
|
|
23
|
+
panel = ">=1.4.4"
|
|
24
|
+
param = ">=2.1.0"
|
|
25
|
+
|
|
26
|
+
[[tool.mypy.overrides]]
|
|
27
|
+
module = [
|
|
28
|
+
"holoviews",
|
|
29
|
+
"param"
|
|
30
|
+
]
|
|
31
|
+
ignore_missing_imports = true
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/algol60/sier2"
|
|
35
|
+
|
|
36
|
+
[build-system]
|
|
37
|
+
requires = ["poetry-core"]
|
|
38
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -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()
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
import inspect
|
|
3
|
+
import param
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from . import _logger
|
|
7
|
+
|
|
8
|
+
class BlockError(Exception):
|
|
9
|
+
"""Raised if a Block configuration is invalid.
|
|
10
|
+
|
|
11
|
+
If this exception is raised, the executing dag sets its stop
|
|
12
|
+
flag (which must be manually reset), and displays a stacktrace.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
class BlockState(StrEnum):
|
|
18
|
+
"""The current state of a block; also used for logging."""
|
|
19
|
+
|
|
20
|
+
DAG = 'DAG' # Dag logging.
|
|
21
|
+
BLOCK = 'BLOCK' # Block logging.
|
|
22
|
+
INPUT = 'INPUT'
|
|
23
|
+
READY = 'READY'
|
|
24
|
+
EXECUTING = 'EXECUTING'
|
|
25
|
+
WAITING = 'WAITING'
|
|
26
|
+
SUCCESSFUL = 'SUCCESSFUL'
|
|
27
|
+
INTERRUPTED = 'INTERRUPTED'
|
|
28
|
+
ERROR = 'ERROR'
|
|
29
|
+
|
|
30
|
+
class Block(param.Parameterized):
|
|
31
|
+
"""The base class for blocks.
|
|
32
|
+
|
|
33
|
+
A block is implemented as:
|
|
34
|
+
|
|
35
|
+
.. code-block:: python
|
|
36
|
+
|
|
37
|
+
class MyBlock(Block):
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
A typical block will have at least one input parameter, and an ``execute()``
|
|
41
|
+
method that is called when an input parameter value changes.
|
|
42
|
+
|
|
43
|
+
.. code-block:: python
|
|
44
|
+
|
|
45
|
+
class MyBlock(Block):
|
|
46
|
+
value_in = param.String(label='Input Value')
|
|
47
|
+
|
|
48
|
+
def execute(self):
|
|
49
|
+
print(f'New value is {self.value_in}')
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
_block_state = param.String(default=BlockState.READY)
|
|
53
|
+
|
|
54
|
+
SIER2_KEY = '_sier2__key'
|
|
55
|
+
|
|
56
|
+
def __init__(self, *args, continue_label='Continue', **kwargs):
|
|
57
|
+
super().__init__(*args, **kwargs)
|
|
58
|
+
|
|
59
|
+
if not self.__doc__:
|
|
60
|
+
raise BlockError(f'Class {self.__class__} must have a docstring')
|
|
61
|
+
|
|
62
|
+
self.continue_label = continue_label
|
|
63
|
+
# self._block_state = BlockState.READY
|
|
64
|
+
self.logger = _logger.get_logger(self.name)
|
|
65
|
+
|
|
66
|
+
# Maintain a map of "block+output parameter being watched" -> "input parameter".
|
|
67
|
+
# This is used by _block_event() to set the correct input parameter.
|
|
68
|
+
#
|
|
69
|
+
self._block_name_map: dict[tuple[str, str], str] = {}
|
|
70
|
+
|
|
71
|
+
# Record this block's output parameters.
|
|
72
|
+
# If this is an input block, we need to trigger
|
|
73
|
+
# the output values before executing the next block,
|
|
74
|
+
# in case the user didn't change anything.
|
|
75
|
+
#
|
|
76
|
+
self._block_out_params = []
|
|
77
|
+
|
|
78
|
+
# self._block_context = _EmptyContext()
|
|
79
|
+
|
|
80
|
+
self._progress = None
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def block_key(cls):
|
|
84
|
+
"""The unique key of this block class.
|
|
85
|
+
|
|
86
|
+
Blocks require a unique key so they can be identified in the block library.
|
|
87
|
+
The default implementation should be sufficient, but can be overridden
|
|
88
|
+
in case of refactoring or name clashes.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
im = inspect.getmodule(cls)
|
|
92
|
+
|
|
93
|
+
if hasattr(cls, Block.SIER2_KEY):
|
|
94
|
+
return getattr(cls, Block.SIER2_KEY)
|
|
95
|
+
|
|
96
|
+
return f'{im.__name__}.{cls.__qualname__}'
|
|
97
|
+
|
|
98
|
+
def execute(self, *_, **__):
|
|
99
|
+
"""This method is called when one or more of the input parameters causes an event.
|
|
100
|
+
|
|
101
|
+
Override this method in a Block subclass.
|
|
102
|
+
|
|
103
|
+
The ``execute()`` method can have arguments. The arguments can be specified
|
|
104
|
+
in any order. It is not necessary to specify all, or any, arguments.
|
|
105
|
+
Arguments will not be passed via ``*args`` or ``**kwargs``.
|
|
106
|
+
|
|
107
|
+
* ``stopper`` - an indicator that the dag has been stopped. This may be
|
|
108
|
+
set while the block is executing, in which case the block should
|
|
109
|
+
stop executing as soon as possible.
|
|
110
|
+
* ``events`` - the param events that caused execute() to be called.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
# print(f'** EXECUTE {self.__class__=}')
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
def __panel__(self):
|
|
117
|
+
"""A default Panel component.
|
|
118
|
+
|
|
119
|
+
When run in a Panel context, a block will typically implement
|
|
120
|
+
its own __panel__() method. If it doesn't, this method will be
|
|
121
|
+
used as a default. When a block without a __panel__() is wrapped
|
|
122
|
+
in a Card, self.progress will be assigned a pn.indicators.Progress()
|
|
123
|
+
widget which is returned here. The Panel context will make it active
|
|
124
|
+
before executing the block, and non-active after executing the block.
|
|
125
|
+
(Why not have a default Progress()? Because we don't want any
|
|
126
|
+
Panel-related code in the core implementation.)
|
|
127
|
+
|
|
128
|
+
If the block implements __panel__(), this will obviously be overridden.
|
|
129
|
+
|
|
130
|
+
When run in non-Panel context, this will remain unused.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
return self._progress
|
|
134
|
+
|
|
135
|
+
def __call__(self, **kwargs) -> dict[str, Any]:
|
|
136
|
+
"""Allow a block to be called directly."""
|
|
137
|
+
|
|
138
|
+
in_names = [name for name in self.__class__.param if name.startswith('in_')]
|
|
139
|
+
if len(kwargs)!=len(in_names) or any(name not in in_names for name in kwargs):
|
|
140
|
+
names = ', '.join(in_names)
|
|
141
|
+
raise BlockError(f'All input params must be specified: {names}')
|
|
142
|
+
|
|
143
|
+
for name, value in kwargs.items():
|
|
144
|
+
setattr(self, name, value)
|
|
145
|
+
|
|
146
|
+
self.execute()
|
|
147
|
+
|
|
148
|
+
out_names = [name for name in self.__class__.param if name.startswith('out_')]
|
|
149
|
+
result = {name: getattr(self, name) for name in out_names}
|
|
150
|
+
|
|
151
|
+
return result
|
|
152
|
+
|
|
153
|
+
class InputBlock(Block):
|
|
154
|
+
"""A ``Block`` that accepts user input.
|
|
155
|
+
|
|
156
|
+
An ``InputBlock`` executes in two steps().
|
|
157
|
+
|
|
158
|
+
When the block is executed by a dag, the dag first sets the input
|
|
159
|
+
params, then calls ``prepare()``. Execution of the dag then stops.
|
|
160
|
+
|
|
161
|
+
The dag is then restarted using ``dag.execute_after_input(input_block)``.
|
|
162
|
+
(An input block must be specified because it is not required that the
|
|
163
|
+
same input block be used immediately.) This causes the block's
|
|
164
|
+
``execute()`` method to be called without resetting the input params.
|
|
165
|
+
|
|
166
|
+
Dag execution then continues as normal.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
def __init__(self, *args, continue_label='Continue', **kwargs):
|
|
170
|
+
super().__init__(*args, continue_label=continue_label, **kwargs)
|
|
171
|
+
self._block_state = BlockState.INPUT
|
|
172
|
+
|
|
173
|
+
def prepare(self):
|
|
174
|
+
"""Called by a dag before calling ``execute()```.
|
|
175
|
+
|
|
176
|
+
This gives the block author an opportunity to validate the
|
|
177
|
+
input params and set up a user inteface.
|
|
178
|
+
|
|
179
|
+
After the dag restarts on this block, ``execute()`` will be called.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
pass
|
|
183
|
+
|
|
184
|
+
class BlockValidateError(BlockError):
|
|
185
|
+
"""Raised if ``InputBlock.prepare()`` or ``Block.execute()`` determines that input data is invalid.
|
|
186
|
+
|
|
187
|
+
If this exception is raised, it will be caught by the executing dag.
|
|
188
|
+
The dag will not set its stop flag, no stacktrace will be displayed,
|
|
189
|
+
and the error message will be displayed.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
def __init__(self, block_name: str, error: str):
|
|
193
|
+
super().__init__(error)
|
|
194
|
+
self.block_name = block_name
|