pipelime-python 1.8.1__tar.gz → 1.9.1__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.
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/PKG-INFO +2 -2
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/README.md +1 -1
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/__init__.py +1 -1
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/utils/imports.py +49 -11
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/utils/io.py +4 -3
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/visitors/processor.py +45 -19
- pipelime_python-1.9.1/pipelime/choixe/visitors/processor_ui.py +64 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/xconfig.py +27 -8
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/main.py +148 -62
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/pretty_print.py +5 -3
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/subcommands.py +1 -5
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/tui/tui.py +3 -3
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/tui/utils.py +1 -35
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/utils.py +150 -9
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/commands/general.py +29 -13
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/commands/interfaces.py +150 -40
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/commands/piper.py +3 -1
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/commands/resume.py +28 -5
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/commands/shell.py +4 -4
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/commands/split_ops.py +3 -3
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/commands/tempman.py +2 -2
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/checkpoint.py +3 -2
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/drawing/mermaid.py +1 -1
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/executors/base.py +2 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/executors/factory.py +1 -1
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/model.py +24 -11
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/remotes/s3_remote.py +1 -1
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/pipes/operations.py +203 -10
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/samples_sequence.py +63 -51
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/torch.py +1 -1
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/stages/base.py +7 -1
- pipelime_python-1.9.1/pipelime/utils/deprecated.py +51 -0
- pipelime_python-1.8.1/pipelime/cli/wizard.py +0 -452
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/.gitignore +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/LICENSE +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/ast/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/ast/nodes.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/ast/parser.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/utils/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/utils/common.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/utils/rand.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/visitors/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/visitors/decoder.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/visitors/inspector.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/visitors/unparser.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/choixe/visitors/walker.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/parser.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/tui/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/cli/tui/tui.css +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/commands/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/commands/toy_dataset.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/items/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/items/base.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/items/binary_item.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/items/image_item.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/items/metadata_item.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/items/model3d_item.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/items/numpy_item.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/items/pickle_item.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/drawing/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/drawing/base.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/drawing/factory.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/drawing/graphviz.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/executors/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/executors/naive.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/graph.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/parsers/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/parsers/base.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/parsers/choixe_parser.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/parsers/factory.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/estimator/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/estimator/base.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/estimator/factory.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/estimator/naive.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/listener/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/listener/base.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/listener/callbacks/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/listener/callbacks/file.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/listener/callbacks/rich_table.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/listener/callbacks/tqdm_bars.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/listener/factory.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/listener/receiver/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/listener/receiver/zmq.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/model.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/tracker/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/tracker/base.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/tracker/direct.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/tracker/factory.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/tracker/loguru.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/piper/progress/tracker/zmq.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/remotes/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/remotes/base.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/remotes/shared_folder_remote.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/direct_access.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/grabber.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/pipes/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/pipes/base.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/pipes/mapping.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/pipes/validation.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/pipes/writers.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/sample.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/sources/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/sources/from_callable.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/sources/raw.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/sources/readers.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/sources/toy_dataset.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/sequences/utils.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/stages/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/stages/augmentations.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/stages/entities.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/stages/item_info.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/stages/item_replacement.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/stages/item_sources.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/stages/key_transformations.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/utils/__init__.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/utils/context_managers.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/utils/inspection.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pipelime/utils/pydantic_types.py +0 -0
- {pipelime_python-1.8.1 → pipelime_python-1.9.1}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pipelime-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.1
|
|
4
4
|
Summary: Data workflows, cli and dataflow automation.
|
|
5
5
|
Project-URL: Source, https://github.com/eyecan-ai/pipelime-python
|
|
6
6
|
Project-URL: Issues, https://github.com/eyecan-ai/pipelime-python/issues
|
|
@@ -88,7 +88,7 @@ Maybe too much for you? No worries, `pipelime` is **modular** and you can just t
|
|
|
88
88
|
- **easy dataflow**: `Piper` can manage and execute directed acyclic graphs (DAGs), giving back feedback on the progress through sockets or custom callbacks.
|
|
89
89
|
- **configuration management**: `Choixe` is a simple and intuitive mini scripting language designed to ease the creation of configuration files with the help of variables, symbol importing, for loops, switch statements, parameter sweeps and more.
|
|
90
90
|
- **command line interface**: `pipelime` can remove all the boilerplate code needed to create a beautiful CLI for you scripts and packages. You focus on *what matters* and we provide input parsing, advanced interfaces for complex arguments, automatic help generation, configuration management. Also, any `PipelimeCommand` can be used as a node in a dataflow for free!
|
|
91
|
-
- **pydantic tools**: most of the classes in `pipelime` derive from [`pydantic.BaseModel`](https://docs.pydantic.dev/), so we have built some useful tools to, e.g., inspect their structure, auto-generate human-friendly documentation and more (including a
|
|
91
|
+
- **pydantic tools**: most of the classes in `pipelime` derive from [`pydantic.BaseModel`](https://docs.pydantic.dev/), so we have built some useful tools to, e.g., inspect their structure, auto-generate human-friendly documentation and more (including a TUI to help you writing input data to [deserialize](https://docs.pydantic.dev/usage/models/#helper-functions) any pydantic model).
|
|
92
92
|
|
|
93
93
|
---
|
|
94
94
|
|
|
@@ -20,7 +20,7 @@ Maybe too much for you? No worries, `pipelime` is **modular** and you can just t
|
|
|
20
20
|
- **easy dataflow**: `Piper` can manage and execute directed acyclic graphs (DAGs), giving back feedback on the progress through sockets or custom callbacks.
|
|
21
21
|
- **configuration management**: `Choixe` is a simple and intuitive mini scripting language designed to ease the creation of configuration files with the help of variables, symbol importing, for loops, switch statements, parameter sweeps and more.
|
|
22
22
|
- **command line interface**: `pipelime` can remove all the boilerplate code needed to create a beautiful CLI for you scripts and packages. You focus on *what matters* and we provide input parsing, advanced interfaces for complex arguments, automatic help generation, configuration management. Also, any `PipelimeCommand` can be used as a node in a dataflow for free!
|
|
23
|
-
- **pydantic tools**: most of the classes in `pipelime` derive from [`pydantic.BaseModel`](https://docs.pydantic.dev/), so we have built some useful tools to, e.g., inspect their structure, auto-generate human-friendly documentation and more (including a
|
|
23
|
+
- **pydantic tools**: most of the classes in `pipelime` derive from [`pydantic.BaseModel`](https://docs.pydantic.dev/), so we have built some useful tools to, e.g., inspect their structure, auto-generate human-friendly documentation and more (including a TUI to help you writing input data to [deserialize](https://docs.pydantic.dev/usage/models/#helper-functions) any pydantic model).
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
@@ -103,13 +103,15 @@ def import_module_from_file(
|
|
|
103
103
|
Args:
|
|
104
104
|
module_file_path (Union[str, Path]): the path to the `.py` module file.
|
|
105
105
|
cwd (Optional[Path], optional): the folder to use for relative module import.
|
|
106
|
-
|
|
106
|
+
Defaults to None.
|
|
107
|
+
register_pl_module (bool, optional): whether to register the module with
|
|
108
|
+
PipelimeSymbolsHelper. Defaults to True.
|
|
107
109
|
|
|
108
110
|
Raises:
|
|
109
|
-
ImportError:
|
|
111
|
+
ImportError: if the module cannot be imported.
|
|
110
112
|
|
|
111
113
|
Returns:
|
|
112
|
-
ModuleType:
|
|
114
|
+
ModuleType: the imported module.
|
|
113
115
|
"""
|
|
114
116
|
from pipelime.cli.utils import PipelimeSymbolsHelper
|
|
115
117
|
|
|
@@ -130,6 +132,16 @@ def import_module_from_file(
|
|
|
130
132
|
def import_module_from_class_path(
|
|
131
133
|
module_class_path: str, register_pl_module: bool = True
|
|
132
134
|
) -> ModuleType:
|
|
135
|
+
"""Import a python module from a python class path.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
module_class_path (str): the dot-separated path to the module class.
|
|
139
|
+
register_pl_module (bool, optional): whether to register the module with
|
|
140
|
+
PipelimeSymbolsHelper. Defaults to True.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
ModuleType: the imported module.
|
|
144
|
+
"""
|
|
133
145
|
from pipelime.cli.utils import PipelimeSymbolsHelper
|
|
134
146
|
|
|
135
147
|
m = importlib.import_module(module_class_path)
|
|
@@ -141,6 +153,19 @@ def import_module_from_class_path(
|
|
|
141
153
|
def import_module_from_code(
|
|
142
154
|
module_code: str, register_pl_module: bool = True
|
|
143
155
|
) -> ModuleType:
|
|
156
|
+
"""Dynamically imports a Python module from its source code.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
module_code (str): the python source code of the module to import.
|
|
160
|
+
register_pl_module (bool, optional): whether to register the module with
|
|
161
|
+
PipelimeSymbolsHelper. Defaults to True.
|
|
162
|
+
|
|
163
|
+
Raises:
|
|
164
|
+
ImportError: if the module cannot be imported.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
ModuleType: the imported module.
|
|
168
|
+
"""
|
|
144
169
|
import hashlib
|
|
145
170
|
|
|
146
171
|
from pipelime.cli.utils import PipelimeSymbolsHelper
|
|
@@ -181,7 +206,23 @@ def import_module(
|
|
|
181
206
|
cwd: Optional[Path] = None,
|
|
182
207
|
register_pl_module: bool = True,
|
|
183
208
|
) -> ModuleType:
|
|
184
|
-
|
|
209
|
+
"""Import a module from a file, a class path, or its source code.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
module_file_or_class_path_or_code (str): the path to the module file, the
|
|
213
|
+
python class path, or its python code.
|
|
214
|
+
cwd (Optional[Path], optional): the current working directory for relative
|
|
215
|
+
imports. Defaults to None.
|
|
216
|
+
register_pl_module (bool, optional): whether to register the module with
|
|
217
|
+
PipelimeSymbolsHelper. Defaults to True.
|
|
218
|
+
|
|
219
|
+
Raises:
|
|
220
|
+
ImportError: If the module cannot be imported.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
ModuleType: The imported module.
|
|
224
|
+
"""
|
|
225
|
+
err_msgs = [""]
|
|
185
226
|
|
|
186
227
|
if module_file_or_class_path_or_code.endswith(".py"):
|
|
187
228
|
try:
|
|
@@ -189,28 +230,25 @@ def import_module(
|
|
|
189
230
|
module_file_or_class_path_or_code, cwd, register_pl_module
|
|
190
231
|
)
|
|
191
232
|
except Exception as e:
|
|
192
|
-
err_msgs.append(
|
|
233
|
+
err_msgs.append(f"from file: {e}")
|
|
193
234
|
try:
|
|
194
235
|
return import_module_from_class_path(
|
|
195
236
|
module_file_or_class_path_or_code, register_pl_module
|
|
196
237
|
)
|
|
197
238
|
except Exception as e:
|
|
198
|
-
err_msgs.append(
|
|
239
|
+
err_msgs.append(f"from classpath: {e}")
|
|
199
240
|
|
|
200
241
|
try:
|
|
201
242
|
return import_module_from_code(
|
|
202
243
|
module_file_or_class_path_or_code, register_pl_module
|
|
203
244
|
)
|
|
204
245
|
except Exception as e:
|
|
205
|
-
err_msgs.append(
|
|
246
|
+
err_msgs.append(f"from code: {e}")
|
|
206
247
|
|
|
207
248
|
raise ImportError(
|
|
208
249
|
"Cannot import:\n"
|
|
209
250
|
f" `{module_file_or_class_path_or_code}`\n"
|
|
210
|
-
"
|
|
211
|
-
f" from file: {err_msgs[0]}\n"
|
|
212
|
-
f" from classpath: {err_msgs[1]}\n"
|
|
213
|
-
f" from code: {err_msgs[2]}"
|
|
251
|
+
"Possible causes:" + "\n ".join(err_msgs)
|
|
214
252
|
)
|
|
215
253
|
|
|
216
254
|
|
|
@@ -111,9 +111,9 @@ class PipelimeTmp:
|
|
|
111
111
|
|
|
112
112
|
user_dirs: Dict[str, List[str]] = {}
|
|
113
113
|
for d in iglob(str(PipelimeTmp.base_dir() / (PipelimeTmp.prefix() + "*"))):
|
|
114
|
-
if os.path.isdir(d):
|
|
114
|
+
if os.path.isdir(d): # pragma: no branch
|
|
115
115
|
names = os.path.basename(d).split("-")
|
|
116
|
-
if len(names) >= 3:
|
|
116
|
+
if len(names) >= 3: # pragma: no branch
|
|
117
117
|
user_dirs.setdefault(names[2], []).append(d)
|
|
118
118
|
return user_dirs
|
|
119
119
|
|
|
@@ -135,7 +135,8 @@ class PipelimeTemporaryDirectory:
|
|
|
135
135
|
import shutil
|
|
136
136
|
|
|
137
137
|
def onerror(func, path, exc_info):
|
|
138
|
-
|
|
138
|
+
# this code comes from TemporaryDirectory
|
|
139
|
+
if issubclass(exc_info[0], PermissionError): # pragma: no cover
|
|
139
140
|
|
|
140
141
|
def resetperms(path):
|
|
141
142
|
try:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import uuid
|
|
3
|
-
from copy import
|
|
3
|
+
from copy import copy
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from itertools import product
|
|
@@ -12,7 +12,7 @@ import pydash as py_
|
|
|
12
12
|
import pipelime.choixe.ast.nodes as ast
|
|
13
13
|
from pipelime.choixe.ast.parser import parse
|
|
14
14
|
from pipelime.choixe.utils.imports import import_symbol
|
|
15
|
-
from pipelime.choixe.utils.io import
|
|
15
|
+
from pipelime.choixe.utils.io import PipelimeTmp, load
|
|
16
16
|
from pipelime.choixe.utils.rand import rand
|
|
17
17
|
from pipelime.choixe.visitors.unparser import unparse
|
|
18
18
|
|
|
@@ -62,11 +62,37 @@ class Processor(ast.NodeVisitor):
|
|
|
62
62
|
return list(product(*branches))
|
|
63
63
|
|
|
64
64
|
def _repeat(self, data: Any, n: int) -> List[Any]:
|
|
65
|
+
# Avoid repeating if n is 1, skipping all the copy/deepcopy logic
|
|
66
|
+
if n == 1:
|
|
67
|
+
return data
|
|
68
|
+
|
|
65
69
|
new_data = []
|
|
66
70
|
for _ in range(n):
|
|
67
|
-
|
|
71
|
+
#! This used to work, but it fails when some data in the tree is not
|
|
72
|
+
#! serializable. The proposed workaround might break stuff in the future,
|
|
73
|
+
#! So I will leave this here for now.
|
|
74
|
+
# new_data.extend(deepcopy(data))
|
|
75
|
+
|
|
76
|
+
#! Simply copying the list e.g. `new_data.extend(copy(data))`
|
|
77
|
+
#! does not work because we need a true copy of the inner containers as
|
|
78
|
+
#! well, otherwise, any modification to the inner containers will be
|
|
79
|
+
#! reflected in all the branches.
|
|
80
|
+
|
|
81
|
+
#! The following code is a workaround that works for now, but it might
|
|
82
|
+
#! break in the future. It duplicates the data only for the first level,
|
|
83
|
+
#! switching to shallow copy for the inner containers.
|
|
84
|
+
new_data.extend([copy(x) for x in data])
|
|
68
85
|
return new_data
|
|
69
86
|
|
|
87
|
+
def _handle_missing_var(self, varname: str) -> Any:
|
|
88
|
+
raise ChoixeProcessingError(f"Variable `{varname}` not found")
|
|
89
|
+
|
|
90
|
+
def _handle_missing_for(self, varname: str) -> Any:
|
|
91
|
+
raise ChoixeProcessingError(f"Loop variable `{varname}` not found")
|
|
92
|
+
|
|
93
|
+
def _handle_missing_switch(self, varname: str) -> Any:
|
|
94
|
+
raise ChoixeProcessingError(f"Switch variable `{varname}` not found")
|
|
95
|
+
|
|
70
96
|
def visit_dict(self, node: ast.DictNode) -> List[Dict]:
|
|
71
97
|
data = [{}]
|
|
72
98
|
for k, v in node.nodes.items():
|
|
@@ -135,7 +161,7 @@ class Processor(ast.NodeVisitor):
|
|
|
135
161
|
found = True
|
|
136
162
|
|
|
137
163
|
if not found:
|
|
138
|
-
|
|
164
|
+
var_value = self._handle_missing_var(id_)
|
|
139
165
|
|
|
140
166
|
# Recursively process the variable value
|
|
141
167
|
re_parsed = parse(var_value)
|
|
@@ -192,7 +218,10 @@ class Processor(ast.NodeVisitor):
|
|
|
192
218
|
|
|
193
219
|
def visit_for(self, node: ast.ForNode) -> List[Any]:
|
|
194
220
|
if isinstance(node.iterable.data, str):
|
|
195
|
-
|
|
221
|
+
if not py_.has(self._context, node.iterable.data):
|
|
222
|
+
iterable = self._handle_missing_for(node.iterable.data)
|
|
223
|
+
else:
|
|
224
|
+
iterable = py_.get(self._context, node.iterable.data)
|
|
196
225
|
else:
|
|
197
226
|
iterable = node.iterable.data
|
|
198
227
|
|
|
@@ -200,15 +229,10 @@ class Processor(ast.NodeVisitor):
|
|
|
200
229
|
iterable = list(range(iterable))
|
|
201
230
|
|
|
202
231
|
if not isinstance(iterable, Iterable):
|
|
203
|
-
if isinstance(node.iterable.data, str) and not py_.has(
|
|
204
|
-
self._context, node.iterable.data
|
|
205
|
-
):
|
|
206
|
-
raise ChoixeProcessingError(
|
|
207
|
-
f"Loop variable `{node.iterable.data}` not found in context"
|
|
208
|
-
)
|
|
209
232
|
raise ChoixeProcessingError(
|
|
210
233
|
f"Loop variable `{node.iterable.data}` is not iterable"
|
|
211
234
|
)
|
|
235
|
+
|
|
212
236
|
id_ = uuid.uuid1().hex if node.identifier is None else str(node.identifier.data)
|
|
213
237
|
prev_loop = self._current_loop
|
|
214
238
|
self._current_loop = id_
|
|
@@ -254,13 +278,10 @@ class Processor(ast.NodeVisitor):
|
|
|
254
278
|
for branch in all_branches:
|
|
255
279
|
varname = branch[0]
|
|
256
280
|
|
|
257
|
-
# If the variable is not in the context, raise an error
|
|
258
281
|
if not py_.has(self._context, varname):
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
# Get the value of the variable
|
|
263
|
-
value = py_.get(self._context, varname)
|
|
282
|
+
value = self._handle_missing_switch(varname)
|
|
283
|
+
else:
|
|
284
|
+
value = py_.get(self._context, varname)
|
|
264
285
|
|
|
265
286
|
# Match the value to the correct case
|
|
266
287
|
for i in range(len(node.cases)):
|
|
@@ -275,7 +296,8 @@ class Processor(ast.NodeVisitor):
|
|
|
275
296
|
branches.append(branch[i + 1 + len(node.cases)])
|
|
276
297
|
break
|
|
277
298
|
|
|
278
|
-
# If no case matched, use the default if available
|
|
299
|
+
# If no case matched, use the default if available
|
|
300
|
+
# otherwise raise an error
|
|
279
301
|
else:
|
|
280
302
|
if node.default is not None:
|
|
281
303
|
branches.append(branch[-1])
|
|
@@ -372,5 +394,9 @@ def process(
|
|
|
372
394
|
Any: The list of all possible outcomes. If branching is disabled, the list will
|
|
373
395
|
have length 1.
|
|
374
396
|
"""
|
|
375
|
-
processor = Processor(
|
|
397
|
+
processor = Processor(
|
|
398
|
+
context=context,
|
|
399
|
+
cwd=cwd,
|
|
400
|
+
allow_branching=allow_branching,
|
|
401
|
+
)
|
|
376
402
|
return node.accept(processor)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
|
|
4
|
+
from pipelime.choixe.visitors.processor import ChoixeProcessingError, Processor
|
|
5
|
+
from pipelime.cli.utils import get_user_input
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ProcessorUi(Processor):
|
|
9
|
+
"""Processor that asks the user to fill the variables missing in the context."""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
context: Optional[Dict[str, Any]] = None,
|
|
14
|
+
cwd: Optional[Path] = None,
|
|
15
|
+
allow_branching: bool = True,
|
|
16
|
+
ask_missing_vars: bool = False,
|
|
17
|
+
) -> None:
|
|
18
|
+
"""Constructor for `ProcessorUi`
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
context (Optional[Dict[str, Any]], optional): A data structure containing
|
|
22
|
+
the values that will replace the variable nodes. Defaults to None.
|
|
23
|
+
cwd (Optional[Path], optional): current working directory used for relative
|
|
24
|
+
imports. If set to None, the `os.getcwd()` will be used.
|
|
25
|
+
Defaults to None.
|
|
26
|
+
allow_branching (bool, optional): Set to False to disable processing on
|
|
27
|
+
branching nodes, like sweeps. All branching nodes will be simply
|
|
28
|
+
unparsed. Defaults to True.
|
|
29
|
+
ask_missing_vars (bool, optional): Set to True to allow the user to fill
|
|
30
|
+
the variables missing in the context.
|
|
31
|
+
"""
|
|
32
|
+
super().__init__(context, cwd, allow_branching)
|
|
33
|
+
self._ask_missing_vars = ask_missing_vars
|
|
34
|
+
self._user_defined_vars: Dict[str, Any] = {}
|
|
35
|
+
|
|
36
|
+
def _handle_missing_var(self, varname: str) -> Any:
|
|
37
|
+
if self._ask_missing_vars:
|
|
38
|
+
msg = f"Enter value for [yellow]{varname}[/yellow]"
|
|
39
|
+
value = get_user_input(msg)
|
|
40
|
+
self._user_defined_vars[varname] = value
|
|
41
|
+
self._context[varname] = value
|
|
42
|
+
return value
|
|
43
|
+
else:
|
|
44
|
+
raise ChoixeProcessingError(f"Variable not found: `{varname}`")
|
|
45
|
+
|
|
46
|
+
def _handle_missing_for(self, varname: str) -> Any:
|
|
47
|
+
if self._ask_missing_vars:
|
|
48
|
+
msg = f"Enter value for [yellow]{varname}[/yellow]"
|
|
49
|
+
iterable = get_user_input(msg)
|
|
50
|
+
self._user_defined_vars[varname] = iterable
|
|
51
|
+
self._context[varname] = iterable
|
|
52
|
+
return iterable
|
|
53
|
+
else:
|
|
54
|
+
raise ChoixeProcessingError(f"Loop variable `{varname}` not found")
|
|
55
|
+
|
|
56
|
+
def _handle_missing_switch(self, varname: str) -> Any:
|
|
57
|
+
if self._ask_missing_vars:
|
|
58
|
+
msg = f"Enter value for [yellow]{varname}[/yellow]"
|
|
59
|
+
value = get_user_input(msg)
|
|
60
|
+
self._user_defined_vars[varname] = value
|
|
61
|
+
self._context[varname] = value
|
|
62
|
+
return value
|
|
63
|
+
else:
|
|
64
|
+
raise ChoixeProcessingError(f"Switch variable `{varname}` not found")
|
|
@@ -9,8 +9,8 @@ from schema import Schema
|
|
|
9
9
|
|
|
10
10
|
from pipelime.choixe.ast.nodes import Node
|
|
11
11
|
from pipelime.choixe.ast.parser import parse
|
|
12
|
-
from pipelime.choixe.utils.io import dump, load
|
|
13
12
|
from pipelime.choixe.utils.common import deep_set_
|
|
13
|
+
from pipelime.choixe.utils.io import dump, load
|
|
14
14
|
from pipelime.choixe.visitors import Inspection, decode, inspect, process, walk
|
|
15
15
|
|
|
16
16
|
|
|
@@ -224,15 +224,22 @@ class XConfig(Box):
|
|
|
224
224
|
return walk(self.parse())
|
|
225
225
|
|
|
226
226
|
def _process(
|
|
227
|
-
self,
|
|
227
|
+
self,
|
|
228
|
+
context: Optional[Dict[str, Any]] = None,
|
|
229
|
+
allow_branching: bool = True,
|
|
228
230
|
) -> List[XConfig]:
|
|
229
|
-
data = self._unsafe_process(
|
|
231
|
+
data = self._unsafe_process(
|
|
232
|
+
context=context,
|
|
233
|
+
allow_branching=allow_branching,
|
|
234
|
+
)
|
|
230
235
|
return [
|
|
231
236
|
XConfig(data=x, cwd=self.get_cwd(), schema=self.get_schema()) for x in data
|
|
232
237
|
]
|
|
233
238
|
|
|
234
239
|
def _unsafe_process(
|
|
235
|
-
self,
|
|
240
|
+
self,
|
|
241
|
+
context: Optional[Dict[str, Any]] = None,
|
|
242
|
+
allow_branching: bool = True,
|
|
236
243
|
) -> List[Any]:
|
|
237
244
|
return process(
|
|
238
245
|
self.parse(),
|
|
@@ -251,7 +258,10 @@ class XConfig(Box):
|
|
|
251
258
|
Returns:
|
|
252
259
|
XConfig: The processed XConfig.
|
|
253
260
|
"""
|
|
254
|
-
return self._process(
|
|
261
|
+
return self._process(
|
|
262
|
+
context=context,
|
|
263
|
+
allow_branching=False,
|
|
264
|
+
)[0]
|
|
255
265
|
|
|
256
266
|
def unsafe_process(self, context: Optional[Dict[str, Any]] = None) -> Any:
|
|
257
267
|
"""Process this XConfig without branching and return the result, without
|
|
@@ -267,7 +277,10 @@ class XConfig(Box):
|
|
|
267
277
|
Returns:
|
|
268
278
|
Any: The processed result.
|
|
269
279
|
"""
|
|
270
|
-
return self._unsafe_process(
|
|
280
|
+
return self._unsafe_process(
|
|
281
|
+
context=context,
|
|
282
|
+
allow_branching=False,
|
|
283
|
+
)[0]
|
|
271
284
|
|
|
272
285
|
def process_all(self, context: Optional[Dict[str, Any]] = None) -> List[XConfig]:
|
|
273
286
|
"""Process this XConfig with branching.
|
|
@@ -279,7 +292,10 @@ class XConfig(Box):
|
|
|
279
292
|
Returns:
|
|
280
293
|
List[XConfig]: A list of all processing outcomes.
|
|
281
294
|
"""
|
|
282
|
-
return self._process(
|
|
295
|
+
return self._process(
|
|
296
|
+
context=context,
|
|
297
|
+
allow_branching=True,
|
|
298
|
+
)
|
|
283
299
|
|
|
284
300
|
def unsafe_process_all(self, context: Optional[Dict[str, Any]] = None) -> List[Any]:
|
|
285
301
|
"""Process this XConfig with branching and return the results, without
|
|
@@ -295,7 +311,10 @@ class XConfig(Box):
|
|
|
295
311
|
Returns:
|
|
296
312
|
List[Any]: A list of all processing outcomes.
|
|
297
313
|
"""
|
|
298
|
-
return self._unsafe_process(
|
|
314
|
+
return self._unsafe_process(
|
|
315
|
+
context=context,
|
|
316
|
+
allow_branching=True,
|
|
317
|
+
)
|
|
299
318
|
|
|
300
319
|
def inspect(self) -> Inspection:
|
|
301
320
|
return inspect(self.parse(), cwd=self.get_cwd())
|