pipelime-python 2.2.0__tar.gz → 3.0.0__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-2.2.0 → pipelime_python-3.0.0}/PKG-INFO +9 -4
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/README.md +2 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/__init__.py +1 -1
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/ast/nodes.py +41 -3
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/utils/common.py +35 -1
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/visitors/decoder.py +5 -2
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/visitors/inspector.py +8 -5
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/visitors/processor.py +19 -8
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/xconfig.py +3 -3
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/main.py +16 -8
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/pretty_print.py +72 -77
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/tui/tui.py +3 -2
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/tui/utils.py +27 -24
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/utils.py +64 -37
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/commands/general.py +56 -53
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/commands/interfaces.py +92 -106
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/commands/piper.py +13 -19
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/commands/resume.py +9 -7
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/commands/shell.py +1 -3
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/commands/split_ops.py +15 -28
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/commands/tempman.py +2 -2
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/commands/toy_dataset.py +1 -3
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/__init__.py +1 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/checkpoint.py +5 -3
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/model.py +105 -80
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/parsers/choixe_parser.py +1 -1
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/receiver/zmq.py +1 -1
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/model.py +3 -3
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/tracker/zmq.py +1 -1
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/grabber.py +3 -2
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/pipes/base.py +2 -3
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/pipes/mapping.py +4 -3
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/pipes/operations.py +7 -14
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/pipes/validation.py +3 -5
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/pipes/writers.py +9 -7
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/sample.py +6 -2
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/samples_sequence.py +16 -17
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/sources/from_callable.py +1 -1
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/sources/raw.py +1 -1
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/sources/readers.py +22 -13
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/sources/toy_dataset.py +3 -2
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/utils.py +3 -8
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/stages/augmentations.py +30 -36
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/stages/base.py +33 -42
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/stages/entities.py +167 -99
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/stages/item_info.py +3 -2
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/stages/item_replacement.py +7 -8
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/stages/item_sources.py +1 -1
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/stages/key_transformations.py +3 -2
- pipelime_python-3.0.0/pipelime/utils/pydantic_compat.py +1064 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/utils/pydantic_types.py +186 -220
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pyproject.toml +5 -3
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/.gitignore +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/LICENSE +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/ast/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/ast/parser.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/utils/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/utils/imports.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/utils/io.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/utils/rand.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/visitors/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/visitors/processor_ui.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/visitors/unparser.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/choixe/visitors/walker.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/parser.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/subcommands.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/tui/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/cli/tui/tui.css +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/commands/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/items/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/items/base.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/items/binary_item.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/items/image_item.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/items/metadata_item.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/items/model3d_item.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/items/numpy_item.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/items/pickle_item.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/drawing/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/drawing/base.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/drawing/factory.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/drawing/graphviz.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/drawing/mermaid.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/executors/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/executors/base.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/executors/factory.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/executors/naive.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/graph.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/parsers/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/parsers/base.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/parsers/factory.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/estimator/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/estimator/base.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/estimator/factory.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/estimator/naive.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/base.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/callbacks/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/callbacks/file.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/callbacks/loguru.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/callbacks/rich_table.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/callbacks/tqdm_bars.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/factory.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/listener/receiver/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/tracker/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/tracker/base.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/tracker/direct.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/tracker/factory.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/piper/progress/tracker/loguru.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/direct_access.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/pipes/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/sources/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/sequences/torch.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/stages/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/utils/__init__.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/utils/context_managers.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/utils/deprecated.py +0 -0
- {pipelime_python-2.2.0 → pipelime_python-3.0.0}/pipelime/utils/inspection.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: pipelime-python
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
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
|
|
@@ -19,9 +19,10 @@ Classifier: Programming Language :: Python :: 3
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
23
|
Classifier: Topic :: Scientific/Engineering
|
|
23
24
|
Classifier: Topic :: Software Development
|
|
24
|
-
Requires-Python: <3.
|
|
25
|
+
Requires-Python: <3.14,>=3.10
|
|
25
26
|
Requires-Dist: albumentations>=1.0.0
|
|
26
27
|
Requires-Dist: astunparse
|
|
27
28
|
Requires-Dist: deepdiff
|
|
@@ -31,7 +32,8 @@ Requires-Dist: imageio[ffmpeg,pyav]>=2.17.0
|
|
|
31
32
|
Requires-Dist: loguru
|
|
32
33
|
Requires-Dist: networkx
|
|
33
34
|
Requires-Dist: numpy
|
|
34
|
-
Requires-Dist: pydantic
|
|
35
|
+
Requires-Dist: pydantic-extra-types
|
|
36
|
+
Requires-Dist: pydantic<3,>=2.10
|
|
35
37
|
Requires-Dist: pydash
|
|
36
38
|
Requires-Dist: python-box
|
|
37
39
|
Requires-Dist: pyyaml
|
|
@@ -63,6 +65,7 @@ Provides-Extra: tests
|
|
|
63
65
|
Requires-Dist: pytest; extra == 'tests'
|
|
64
66
|
Requires-Dist: pytest-asyncio; extra == 'tests'
|
|
65
67
|
Requires-Dist: pytest-cov; extra == 'tests'
|
|
68
|
+
Requires-Dist: pytest-xdist; extra == 'tests'
|
|
66
69
|
Requires-Dist: tox; extra == 'tests'
|
|
67
70
|
Description-Content-Type: text/markdown
|
|
68
71
|
|
|
@@ -100,6 +103,8 @@ Install `pipelime` using pip:
|
|
|
100
103
|
pip install pipelime-python
|
|
101
104
|
```
|
|
102
105
|
|
|
106
|
+
pipelime ≥ 3.0 requires pydantic v2 — see [`docs/migration/pydantic_v2.md`](docs/migration/pydantic_v2.md) if you upgrade from 2.x.
|
|
107
|
+
|
|
103
108
|
To be able to *draw* the dataflow graphs, you need the `draw` variant:
|
|
104
109
|
|
|
105
110
|
```
|
|
@@ -32,6 +32,8 @@ Install `pipelime` using pip:
|
|
|
32
32
|
pip install pipelime-python
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
pipelime ≥ 3.0 requires pydantic v2 — see [`docs/migration/pydantic_v2.md`](docs/migration/pydantic_v2.md) if you upgrade from 2.x.
|
|
36
|
+
|
|
35
37
|
To be able to *draw* the dataflow graphs, you need the `draw` variant:
|
|
36
38
|
|
|
37
39
|
```
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import functools
|
|
4
|
+
import types
|
|
5
|
+
import typing
|
|
3
6
|
from abc import ABC, abstractmethod
|
|
4
|
-
from dataclasses import field
|
|
7
|
+
from dataclasses import dataclass, field, fields
|
|
5
8
|
from typing import Any, Dict, Optional, Sequence, Tuple, Union
|
|
6
9
|
|
|
7
|
-
from pydantic.v1.dataclasses import dataclass
|
|
8
|
-
|
|
9
10
|
|
|
10
11
|
class NodeVisitor: # pragma: no cover
|
|
11
12
|
"""Base class for all Choixe visitors. A visitor can be seen as a special recursive
|
|
@@ -87,10 +88,47 @@ class NodeVisitor: # pragma: no cover
|
|
|
87
88
|
return self._ignore(node)
|
|
88
89
|
|
|
89
90
|
|
|
91
|
+
@functools.lru_cache(maxsize=None)
|
|
92
|
+
def _node_typed_fields(cls: type) -> Tuple[Tuple[str, Tuple[type, ...], bool], ...]:
|
|
93
|
+
"""For each dataclass field of `cls` whose resolved annotation is a `Node`
|
|
94
|
+
subclass, `Optional[<Node subclass>]`, or a `Union` of `Node` subclasses,
|
|
95
|
+
returns `(field_name, allowed_node_types, allow_none)`. Container-typed
|
|
96
|
+
fields (e.g. `Dict[...]`, `Sequence[...]`) and `Any` are left out. Cached
|
|
97
|
+
per class (outside any instance, since `HashNode.__hash__` hashes
|
|
98
|
+
`self.__dict__.values()`)."""
|
|
99
|
+
hints = typing.get_type_hints(cls)
|
|
100
|
+
specs = []
|
|
101
|
+
for f in fields(cls):
|
|
102
|
+
hint = hints.get(f.name)
|
|
103
|
+
is_union = typing.get_origin(hint) is Union or isinstance(hint, types.UnionType)
|
|
104
|
+
candidates = typing.get_args(hint) if is_union else (hint,)
|
|
105
|
+
node_types = tuple(
|
|
106
|
+
c for c in candidates if isinstance(c, type) and issubclass(c, Node)
|
|
107
|
+
)
|
|
108
|
+
if node_types:
|
|
109
|
+
specs.append((f.name, node_types, type(None) in candidates))
|
|
110
|
+
return tuple(specs)
|
|
111
|
+
|
|
112
|
+
|
|
90
113
|
@dataclass
|
|
91
114
|
class Node(ABC):
|
|
92
115
|
"""A generic element of the Choixe AST, all nodes must implement this interface."""
|
|
93
116
|
|
|
117
|
+
def __post_init__(self) -> None:
|
|
118
|
+
for name, node_types, allow_none in _node_typed_fields(type(self)):
|
|
119
|
+
value = getattr(self, name, None)
|
|
120
|
+
if value is None and allow_none:
|
|
121
|
+
continue
|
|
122
|
+
if isinstance(value, node_types):
|
|
123
|
+
continue
|
|
124
|
+
expected = " or ".join(t.__name__ for t in node_types)
|
|
125
|
+
if allow_none:
|
|
126
|
+
expected += " or None"
|
|
127
|
+
raise TypeError(
|
|
128
|
+
f"{type(self).__name__}.{name} expected {expected}, "
|
|
129
|
+
f"got {type(value).__name__}"
|
|
130
|
+
)
|
|
131
|
+
|
|
94
132
|
@abstractmethod
|
|
95
133
|
def accept(self, visitor: NodeVisitor) -> Any:
|
|
96
134
|
"""Accepts an incoming visitor. This method should simply call the respective
|
|
@@ -1,5 +1,39 @@
|
|
|
1
|
+
import re
|
|
1
2
|
import typing as t
|
|
2
3
|
|
|
4
|
+
# pydash's string path delimiter (the same in pydash 8.0 and 8.1): an unescaped `.`,
|
|
5
|
+
# or a `[<int>]` list index (captured, so that it is kept as a path part)
|
|
6
|
+
_PYDASH_PATH_DELIM = re.compile(r"(?<!\\)(?:\\\\)*\.|(\[-?\d+\])")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def pydash_path(key_path: t.Any) -> t.Any:
|
|
10
|
+
r"""A key path for pydash (``get``, ``set_``, ``has``, ``to_path``) addressing the
|
|
11
|
+
same value on every pydash version.
|
|
12
|
+
|
|
13
|
+
pydash 8.1 reads the empty key of a leading, trailing or doubled ``.`` in a string
|
|
14
|
+
path (``".a"`` is ``["", "a"]``, ``"."`` is ``["", ""]``), where earlier versions
|
|
15
|
+
dropped it (``["a"]``, ``[]``: the whole object). pipelime keeps the pre-8.1 meaning:
|
|
16
|
+
the empty keys of a dotted or indexed string path are dropped here, and the other
|
|
17
|
+
parts, escapes included, are joined back into a canonical path (``"a.b[0].c"``) that
|
|
18
|
+
every pydash version splits the same way. A path left with a single key is returned
|
|
19
|
+
unescaped (pydash takes a plain key as is), with no key as ``[]``. Any other path (a
|
|
20
|
+
plain key without ``.`` or ``[``, a key list, an int) is returned unchanged.
|
|
21
|
+
"""
|
|
22
|
+
if not (isinstance(key_path, str) and ("." in key_path or "[" in key_path)):
|
|
23
|
+
return key_path
|
|
24
|
+
# `re.split` alternates the text between two delimiters (a key, "" if empty) and
|
|
25
|
+
# the captured group (a `[<int>]` list index, None for a `.`)
|
|
26
|
+
parts = _PYDASH_PATH_DELIM.split(key_path)
|
|
27
|
+
path = ""
|
|
28
|
+
for i, part in enumerate(parts):
|
|
29
|
+
if part:
|
|
30
|
+
path += part if i % 2 or not path else "." + part
|
|
31
|
+
if not path:
|
|
32
|
+
return []
|
|
33
|
+
if "." in path or "[" in path:
|
|
34
|
+
return path
|
|
35
|
+
return path.replace("\\\\", "\\") # pydash's unescape; no `\.` is left here
|
|
36
|
+
|
|
3
37
|
|
|
4
38
|
class _RefItem:
|
|
5
39
|
def __init__(self, parent, idx):
|
|
@@ -73,7 +107,7 @@ def deep_set_( # noqa: C901
|
|
|
73
107
|
from pydash.utilities import to_path
|
|
74
108
|
|
|
75
109
|
if key_path is not None: # pragma: no branch
|
|
76
|
-
key_path_tokens = to_path(key_path)
|
|
110
|
+
key_path_tokens = to_path(pydash_path(key_path))
|
|
77
111
|
parent_node = _RefItem(obj, None)
|
|
78
112
|
for tk in key_path_tokens:
|
|
79
113
|
if isinstance(tk, int):
|
|
@@ -3,7 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
|
-
from pydantic
|
|
6
|
+
from pydantic import BaseModel
|
|
7
7
|
|
|
8
8
|
from pipelime.choixe.ast.nodes import LiteralNode, Node
|
|
9
9
|
from pipelime.choixe.visitors.unparser import Unparser
|
|
@@ -20,7 +20,10 @@ class Decoder(Unparser):
|
|
|
20
20
|
return data.item()
|
|
21
21
|
elif isinstance(data, BaseModel):
|
|
22
22
|
symbol = f"{data.__module__}.{data.__class__.__qualname__}"
|
|
23
|
-
return {
|
|
23
|
+
return {
|
|
24
|
+
"$model": symbol,
|
|
25
|
+
"$args": json.loads(data.model_dump_json(by_alias=True)),
|
|
26
|
+
}
|
|
24
27
|
elif isinstance(data, Path):
|
|
25
28
|
return str(data)
|
|
26
29
|
else:
|
|
@@ -10,6 +10,7 @@ import pydash as py_
|
|
|
10
10
|
|
|
11
11
|
import pipelime.choixe.ast.nodes as ast
|
|
12
12
|
from pipelime.choixe.ast.parser import parse
|
|
13
|
+
from pipelime.choixe.utils.common import pydash_path
|
|
13
14
|
from pipelime.choixe.utils.io import load
|
|
14
15
|
|
|
15
16
|
|
|
@@ -87,11 +88,11 @@ class Inspector(ast.NodeVisitor):
|
|
|
87
88
|
|
|
88
89
|
id_ = node.identifier.data # type: ignore
|
|
89
90
|
default = None if node.default is None else unparse(node.default)
|
|
90
|
-
variables = py_.set_({}, id_, default)
|
|
91
|
+
variables = py_.set_({}, pydash_path(id_), default)
|
|
91
92
|
|
|
92
93
|
help_strings = {}
|
|
93
94
|
if node.help is not None:
|
|
94
|
-
help_strings = py_.set_({}, id_, node.help.data)
|
|
95
|
+
help_strings = py_.set_({}, pydash_path(id_), node.help.data)
|
|
95
96
|
|
|
96
97
|
environ = {}
|
|
97
98
|
if node.env is not None and node.env.data: # type: ignore
|
|
@@ -150,7 +151,7 @@ class Inspector(ast.NodeVisitor):
|
|
|
150
151
|
if node.identifier is not None: # pragma: no branch
|
|
151
152
|
self._named_for_loops[node.identifier.data] = node.iterable.data
|
|
152
153
|
iterable_insp = (
|
|
153
|
-
Inspection(variables=py_.set_({}, node.iterable.data, None))
|
|
154
|
+
Inspection(variables=py_.set_({}, pydash_path(node.iterable.data), None))
|
|
154
155
|
if isinstance(node.iterable.data, str)
|
|
155
156
|
else Inspection(processed=True)
|
|
156
157
|
)
|
|
@@ -160,7 +161,9 @@ class Inspector(ast.NodeVisitor):
|
|
|
160
161
|
def visit_switch(self, node: ast.SwitchNode) -> Inspection:
|
|
161
162
|
insp = node.value.accept(self)
|
|
162
163
|
if insp.processed: # pragma: no branch
|
|
163
|
-
insp = Inspection(
|
|
164
|
+
insp = Inspection(
|
|
165
|
+
variables=py_.set_({}, pydash_path(node.value.data), None) # type: ignore
|
|
166
|
+
)
|
|
164
167
|
cases_insp = sum(
|
|
165
168
|
[x.accept(self) + y.accept(self) for x, y in node.cases],
|
|
166
169
|
start=Inspection(processed=True),
|
|
@@ -186,7 +189,7 @@ class Inspector(ast.NodeVisitor):
|
|
|
186
189
|
if key:
|
|
187
190
|
iterable_name = self._named_for_loops[loop_id]
|
|
188
191
|
full_path = f"{iterable_name}.{key}"
|
|
189
|
-
py_.set_(variables, full_path, None)
|
|
192
|
+
py_.set_(variables, pydash_path(full_path), None)
|
|
190
193
|
insp = insp + sub_insp + Inspection(variables=variables)
|
|
191
194
|
return insp
|
|
192
195
|
|
|
@@ -11,6 +11,7 @@ import pydash as py_
|
|
|
11
11
|
|
|
12
12
|
import pipelime.choixe.ast.nodes as ast
|
|
13
13
|
from pipelime.choixe.ast.parser import parse
|
|
14
|
+
from pipelime.choixe.utils.common import pydash_path
|
|
14
15
|
from pipelime.choixe.utils.imports import import_symbol
|
|
15
16
|
from pipelime.choixe.utils.io import PipelimeTmp, load
|
|
16
17
|
from pipelime.choixe.utils.rand import rand
|
|
@@ -148,8 +149,8 @@ class Processor(ast.NodeVisitor):
|
|
|
148
149
|
var_value = None
|
|
149
150
|
found = False
|
|
150
151
|
|
|
151
|
-
if py_.has(self._context, id_):
|
|
152
|
-
var_value = py_.get(self._context, id_)
|
|
152
|
+
if py_.has(self._context, pydash_path(id_)):
|
|
153
|
+
var_value = py_.get(self._context, pydash_path(id_))
|
|
153
154
|
found = True
|
|
154
155
|
if not found and env:
|
|
155
156
|
value = os.getenv(id_)
|
|
@@ -214,14 +215,24 @@ class Processor(ast.NodeVisitor):
|
|
|
214
215
|
symbol_branches = node.symbol.accept(self)
|
|
215
216
|
args_branches = node.args.accept(self)
|
|
216
217
|
branches = self._branches(symbol_branches, args_branches)
|
|
217
|
-
|
|
218
|
+
models = []
|
|
219
|
+
for s, a in branches:
|
|
220
|
+
model_cls = import_symbol(s, cwd=self._cwd)
|
|
221
|
+
if not hasattr(model_cls, "model_validate"):
|
|
222
|
+
raise TypeError(
|
|
223
|
+
f"`$model` needs a pydantic v2 model, got {model_cls!r}: "
|
|
224
|
+
"pipelime 3 no longer supports pydantic.v1 models "
|
|
225
|
+
"(see docs/migration/pydantic_v2.md)"
|
|
226
|
+
)
|
|
227
|
+
models.append(model_cls.model_validate(a))
|
|
228
|
+
return models
|
|
218
229
|
|
|
219
230
|
def visit_for(self, node: ast.ForNode) -> List[Any]:
|
|
220
231
|
if isinstance(node.iterable.data, str):
|
|
221
|
-
if not py_.has(self._context, node.iterable.data):
|
|
232
|
+
if not py_.has(self._context, pydash_path(node.iterable.data)):
|
|
222
233
|
iterable = self._handle_missing_for(node.iterable.data)
|
|
223
234
|
else:
|
|
224
|
-
iterable = py_.get(self._context, node.iterable.data)
|
|
235
|
+
iterable = py_.get(self._context, pydash_path(node.iterable.data))
|
|
225
236
|
else:
|
|
226
237
|
iterable = node.iterable.data
|
|
227
238
|
|
|
@@ -278,10 +289,10 @@ class Processor(ast.NodeVisitor):
|
|
|
278
289
|
for branch in all_branches:
|
|
279
290
|
varname = branch[0]
|
|
280
291
|
|
|
281
|
-
if not py_.has(self._context, varname):
|
|
292
|
+
if not py_.has(self._context, pydash_path(varname)):
|
|
282
293
|
value = self._handle_missing_switch(varname)
|
|
283
294
|
else:
|
|
284
|
-
value = py_.get(self._context, varname)
|
|
295
|
+
value = py_.get(self._context, pydash_path(varname))
|
|
285
296
|
|
|
286
297
|
# Match the value to the correct case
|
|
287
298
|
for i in range(len(node.cases)):
|
|
@@ -321,7 +332,7 @@ class Processor(ast.NodeVisitor):
|
|
|
321
332
|
items = []
|
|
322
333
|
for branch in branches:
|
|
323
334
|
loop_id, _, key = str(branch).partition(sep) # type: ignore
|
|
324
|
-
item = py_.get(self._loop_data[loop_id].item, f"{sep}{key}")
|
|
335
|
+
item = py_.get(self._loop_data[loop_id].item, pydash_path(f"{sep}{key}"))
|
|
325
336
|
items.append(item)
|
|
326
337
|
return items
|
|
327
338
|
|
|
@@ -9,7 +9,7 @@ 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.common import deep_set_
|
|
12
|
+
from pipelime.choixe.utils.common import deep_set_, pydash_path
|
|
13
13
|
from pipelime.choixe.utils.io import dump, load
|
|
14
14
|
from pipelime.choixe.visitors import Inspection, decode, inspect, process, walk
|
|
15
15
|
|
|
@@ -139,7 +139,7 @@ class XConfig(Box):
|
|
|
139
139
|
Returns:
|
|
140
140
|
Any: The value at the specified path.
|
|
141
141
|
"""
|
|
142
|
-
return py_.get(self, full_key, default=default)
|
|
142
|
+
return py_.get(self, pydash_path(full_key), default=default)
|
|
143
143
|
|
|
144
144
|
def deep_set(
|
|
145
145
|
self,
|
|
@@ -159,7 +159,7 @@ class XConfig(Box):
|
|
|
159
159
|
Defaults to False.
|
|
160
160
|
"""
|
|
161
161
|
|
|
162
|
-
if not only_valid_keys or py_.has(self, full_key):
|
|
162
|
+
if not only_valid_keys or py_.has(self, pydash_path(full_key)):
|
|
163
163
|
deep_set_(self, key_path=full_key, value=value, append=append_values)
|
|
164
164
|
|
|
165
165
|
def deep_update(
|
|
@@ -6,7 +6,6 @@ import typing as t
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
|
-
from pydantic.v1 import BaseModel
|
|
10
9
|
|
|
11
10
|
from pipelime.choixe import XConfig
|
|
12
11
|
from pipelime.choixe.visitors.processor_ui import ProcessorUi
|
|
@@ -18,12 +17,15 @@ from pipelime.cli.utils import (
|
|
|
18
17
|
print_command_op_stage_info,
|
|
19
18
|
print_commands_ops_stages_list,
|
|
20
19
|
)
|
|
20
|
+
from pipelime.utils.pydantic_compat import PipelimeModel
|
|
21
21
|
|
|
22
22
|
if t.TYPE_CHECKING:
|
|
23
23
|
from pipelime.piper.checkpoint import Checkpoint
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
class PlCliOptions(
|
|
26
|
+
class PlCliOptions(PipelimeModel):
|
|
27
|
+
# a `PipelimeModel` for the v1 Optional rule: the options saved in a checkpoint
|
|
28
|
+
# may lack an `Optional` key
|
|
27
29
|
_namespace: t.ClassVar[str] = "__plmain"
|
|
28
30
|
|
|
29
31
|
config: t.List[Path]
|
|
@@ -43,7 +45,7 @@ class PlCliOptions(BaseModel):
|
|
|
43
45
|
pipelime_tmp: t.Optional[str]
|
|
44
46
|
|
|
45
47
|
def purged_dict(self):
|
|
46
|
-
return self._purge(self.
|
|
48
|
+
return self._purge(self.model_dump())
|
|
47
49
|
|
|
48
50
|
def _purge(self, data):
|
|
49
51
|
if isinstance(data, Path):
|
|
@@ -918,14 +920,19 @@ def run_command(
|
|
|
918
920
|
|
|
919
921
|
import time
|
|
920
922
|
|
|
921
|
-
from pydantic
|
|
923
|
+
from pydantic import ValidationError
|
|
924
|
+
from rich.markup import escape
|
|
922
925
|
|
|
923
926
|
from pipelime.choixe.utils.io import PipelimeTmp, dump
|
|
924
|
-
from pipelime.cli.pretty_print import
|
|
927
|
+
from pipelime.cli.pretty_print import (
|
|
928
|
+
print_command_outputs,
|
|
929
|
+
print_error,
|
|
930
|
+
print_info,
|
|
931
|
+
)
|
|
925
932
|
from pipelime.cli.tui import TuiApp, is_tui_needed
|
|
926
933
|
from pipelime.cli.utils import (
|
|
934
|
+
format_validation_error,
|
|
927
935
|
get_pipelime_command_cls,
|
|
928
|
-
show_field_alias_valerr,
|
|
929
936
|
time_to_str,
|
|
930
937
|
)
|
|
931
938
|
from pipelime.commands import TempCommand
|
|
@@ -972,12 +979,13 @@ def run_command(
|
|
|
972
979
|
# so let's show the tui again if it was needed in the first place
|
|
973
980
|
|
|
974
981
|
except ValidationError as e:
|
|
975
|
-
|
|
982
|
+
# plain text, not Rich markup: `[type=...]` and bracketed values must survive
|
|
983
|
+
print_error(escape(format_validation_error(e, cmd_cls)))
|
|
976
984
|
raise e
|
|
977
985
|
|
|
978
986
|
if verbose > 0:
|
|
979
987
|
print_info(f"\nCreated command `{command}`:")
|
|
980
|
-
print_info(cmd_obj.
|
|
988
|
+
print_info(cmd_obj.model_dump(), pretty=True)
|
|
981
989
|
|
|
982
990
|
if dry_run or verbose > 0:
|
|
983
991
|
print_info(f"\nRunning `{command}`...")
|