pipelime-python 2.2.0__py3-none-any.whl → 3.0.0__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.
- pipelime/__init__.py +1 -1
- pipelime/choixe/ast/nodes.py +41 -3
- pipelime/choixe/utils/common.py +35 -1
- pipelime/choixe/visitors/decoder.py +5 -2
- pipelime/choixe/visitors/inspector.py +8 -5
- pipelime/choixe/visitors/processor.py +19 -8
- pipelime/choixe/xconfig.py +3 -3
- pipelime/cli/main.py +16 -8
- pipelime/cli/pretty_print.py +72 -77
- pipelime/cli/tui/tui.py +3 -2
- pipelime/cli/tui/utils.py +27 -24
- pipelime/cli/utils.py +64 -37
- pipelime/commands/general.py +56 -53
- pipelime/commands/interfaces.py +92 -106
- pipelime/commands/piper.py +13 -19
- pipelime/commands/resume.py +9 -7
- pipelime/commands/shell.py +1 -3
- pipelime/commands/split_ops.py +15 -28
- pipelime/commands/tempman.py +2 -2
- pipelime/commands/toy_dataset.py +1 -3
- pipelime/piper/__init__.py +1 -0
- pipelime/piper/checkpoint.py +5 -3
- pipelime/piper/model.py +105 -80
- pipelime/piper/parsers/choixe_parser.py +1 -1
- pipelime/piper/progress/listener/receiver/zmq.py +1 -1
- pipelime/piper/progress/model.py +3 -3
- pipelime/piper/progress/tracker/zmq.py +1 -1
- pipelime/sequences/grabber.py +3 -2
- pipelime/sequences/pipes/base.py +2 -3
- pipelime/sequences/pipes/mapping.py +4 -3
- pipelime/sequences/pipes/operations.py +7 -14
- pipelime/sequences/pipes/validation.py +3 -5
- pipelime/sequences/pipes/writers.py +9 -7
- pipelime/sequences/sample.py +6 -2
- pipelime/sequences/samples_sequence.py +16 -17
- pipelime/sequences/sources/from_callable.py +1 -1
- pipelime/sequences/sources/raw.py +1 -1
- pipelime/sequences/sources/readers.py +22 -13
- pipelime/sequences/sources/toy_dataset.py +3 -2
- pipelime/sequences/utils.py +3 -8
- pipelime/stages/augmentations.py +30 -36
- pipelime/stages/base.py +33 -42
- pipelime/stages/entities.py +167 -99
- pipelime/stages/item_info.py +3 -2
- pipelime/stages/item_replacement.py +7 -8
- pipelime/stages/item_sources.py +1 -1
- pipelime/stages/key_transformations.py +3 -2
- pipelime/utils/pydantic_compat.py +1064 -0
- pipelime/utils/pydantic_types.py +186 -220
- {pipelime_python-2.2.0.dist-info → pipelime_python-3.0.0.dist-info}/METADATA +9 -4
- {pipelime_python-2.2.0.dist-info → pipelime_python-3.0.0.dist-info}/RECORD +54 -53
- {pipelime_python-2.2.0.dist-info → pipelime_python-3.0.0.dist-info}/WHEEL +1 -1
- {pipelime_python-2.2.0.dist-info → pipelime_python-3.0.0.dist-info}/entry_points.txt +0 -0
- {pipelime_python-2.2.0.dist-info → pipelime_python-3.0.0.dist-info}/licenses/LICENSE +0 -0
pipelime/cli/tui/utils.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import inspect
|
|
1
2
|
from enum import Enum
|
|
2
3
|
from typing import List, Mapping, Tuple, Type, cast
|
|
3
4
|
|
|
4
|
-
from pydantic
|
|
5
|
-
from pydantic.v1.fields import ModelField
|
|
5
|
+
from pydantic import BaseModel
|
|
6
6
|
|
|
7
7
|
from pipelime.cli.utils import PipelimeSymbolsHelper
|
|
8
8
|
from pipelime.piper import PipelimeCommand
|
|
9
9
|
from pipelime.stages import SampleStage, StageInput
|
|
10
|
+
from pipelime.utils.pydantic_compat import FieldView, iter_fields, strip_annotated
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class TuiField(BaseModel):
|
|
@@ -31,9 +32,9 @@ def is_tui_needed(cmd_cls: Type[PipelimeCommand], cmd_args: Mapping) -> bool:
|
|
|
31
32
|
Returns:
|
|
32
33
|
True if the TUI is needed, False otherwise.
|
|
33
34
|
"""
|
|
34
|
-
for field in cmd_cls
|
|
35
|
+
for field in iter_fields(cmd_cls):
|
|
35
36
|
name = field.name
|
|
36
|
-
alias = field.
|
|
37
|
+
alias = field.effective_alias
|
|
37
38
|
required = field.required
|
|
38
39
|
|
|
39
40
|
if (name not in cmd_args) and (alias not in cmd_args) and required:
|
|
@@ -41,7 +42,7 @@ def is_tui_needed(cmd_cls: Type[PipelimeCommand], cmd_args: Mapping) -> bool:
|
|
|
41
42
|
return True
|
|
42
43
|
|
|
43
44
|
# if present, check if it's a StageInput
|
|
44
|
-
if field.
|
|
45
|
+
if field.inner_type is StageInput:
|
|
45
46
|
if name in cmd_args:
|
|
46
47
|
stage_input_args = cmd_args.get(name)
|
|
47
48
|
else:
|
|
@@ -81,9 +82,9 @@ def are_stageinput_args_present(
|
|
|
81
82
|
Returns:
|
|
82
83
|
True if the StageInput required args are present, False otherwise.
|
|
83
84
|
"""
|
|
84
|
-
for field in stage_cls
|
|
85
|
+
for field in iter_fields(stage_cls):
|
|
85
86
|
name = field.name
|
|
86
|
-
alias = field.
|
|
87
|
+
alias = field.effective_alias
|
|
87
88
|
required = field.required
|
|
88
89
|
|
|
89
90
|
if (name not in stage_args) and (alias not in stage_args) and required:
|
|
@@ -93,7 +94,7 @@ def are_stageinput_args_present(
|
|
|
93
94
|
return True
|
|
94
95
|
|
|
95
96
|
|
|
96
|
-
def init_tui_field(field:
|
|
97
|
+
def init_tui_field(field: FieldView, args: Mapping) -> TuiField:
|
|
97
98
|
"""Initialize a TuiField.
|
|
98
99
|
|
|
99
100
|
Args:
|
|
@@ -108,10 +109,10 @@ def init_tui_field(field: ModelField, args: Mapping) -> TuiField:
|
|
|
108
109
|
|
|
109
110
|
if field.name in args:
|
|
110
111
|
default = str(args[field.name])
|
|
111
|
-
elif field.
|
|
112
|
-
default = str(args[field.
|
|
112
|
+
elif field.effective_alias in args:
|
|
113
|
+
default = str(args[field.effective_alias])
|
|
113
114
|
else:
|
|
114
|
-
field_default = field.
|
|
115
|
+
field_default = None if field.required else field.default
|
|
115
116
|
|
|
116
117
|
if field_default is not None:
|
|
117
118
|
if isinstance(field_default, BaseModel):
|
|
@@ -124,7 +125,7 @@ def init_tui_field(field: ModelField, args: Mapping) -> TuiField:
|
|
|
124
125
|
tui_field = TuiField(
|
|
125
126
|
simple=True,
|
|
126
127
|
name=field.name,
|
|
127
|
-
description=str(field.
|
|
128
|
+
description=str(field.description),
|
|
128
129
|
hint=hint,
|
|
129
130
|
type_=get_field_type(field),
|
|
130
131
|
value=default,
|
|
@@ -132,7 +133,7 @@ def init_tui_field(field: ModelField, args: Mapping) -> TuiField:
|
|
|
132
133
|
return tui_field
|
|
133
134
|
|
|
134
135
|
|
|
135
|
-
def init_stageinput_tui_field(field:
|
|
136
|
+
def init_stageinput_tui_field(field: FieldView, cmd_args: Mapping) -> TuiField:
|
|
136
137
|
"""Initialize a TuiField for a StageInput.
|
|
137
138
|
|
|
138
139
|
Args:
|
|
@@ -142,11 +143,11 @@ def init_stageinput_tui_field(field: ModelField, cmd_args: Mapping) -> TuiField:
|
|
|
142
143
|
Returns:
|
|
143
144
|
The initialized TuiField.
|
|
144
145
|
"""
|
|
145
|
-
if (field.name not in cmd_args) and (field.
|
|
146
|
+
if (field.name not in cmd_args) and (field.effective_alias not in cmd_args):
|
|
146
147
|
tui_field = TuiField(
|
|
147
148
|
simple=True,
|
|
148
149
|
name=field.name,
|
|
149
|
-
description=str(field.
|
|
150
|
+
description=str(field.description),
|
|
150
151
|
type_=get_field_type(field),
|
|
151
152
|
)
|
|
152
153
|
return tui_field
|
|
@@ -154,7 +155,7 @@ def init_stageinput_tui_field(field: ModelField, cmd_args: Mapping) -> TuiField:
|
|
|
154
155
|
if field.name in cmd_args:
|
|
155
156
|
stage_input_args = cmd_args.get(field.name)
|
|
156
157
|
else:
|
|
157
|
-
stage_input_args = cmd_args.get(field.
|
|
158
|
+
stage_input_args = cmd_args.get(field.effective_alias)
|
|
158
159
|
|
|
159
160
|
stage_name = ""
|
|
160
161
|
stage_args = {}
|
|
@@ -170,8 +171,8 @@ def init_stageinput_tui_field(field: ModelField, cmd_args: Mapping) -> TuiField:
|
|
|
170
171
|
stage_cls = stage_info[-1]
|
|
171
172
|
|
|
172
173
|
tui_fields = []
|
|
173
|
-
for
|
|
174
|
-
tui_fields.append(init_tui_field(
|
|
174
|
+
for f in iter_fields(stage_cls):
|
|
175
|
+
tui_fields.append(init_tui_field(f, stage_args))
|
|
175
176
|
|
|
176
177
|
tui_field = TuiField(
|
|
177
178
|
simple=False,
|
|
@@ -182,7 +183,7 @@ def init_stageinput_tui_field(field: ModelField, cmd_args: Mapping) -> TuiField:
|
|
|
182
183
|
return tui_field
|
|
183
184
|
|
|
184
185
|
|
|
185
|
-
def get_field_type(field:
|
|
186
|
+
def get_field_type(field: FieldView) -> str:
|
|
186
187
|
"""Get the type of a field.
|
|
187
188
|
|
|
188
189
|
Args:
|
|
@@ -191,12 +192,14 @@ def get_field_type(field: ModelField) -> str:
|
|
|
191
192
|
Returns:
|
|
192
193
|
The type of the field.
|
|
193
194
|
"""
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if
|
|
197
|
-
type_ =
|
|
195
|
+
# constrained types (`Annotated[int, Gt(gt=0)]`) are shown as their base type
|
|
196
|
+
type_ = strip_annotated(field.annotation)
|
|
197
|
+
if inspect.isclass(type_) and not getattr(type_, "__args__", None):
|
|
198
|
+
type_ = type_.__name__
|
|
198
199
|
else:
|
|
199
|
-
|
|
200
|
+
# `typing` aliases print as `typing.List[int]`, modern hints as written
|
|
201
|
+
# (`list[int]`, `int | None`); never rely on `__name__` (UnionType has none)
|
|
202
|
+
type_ = str(type_).replace("typing.", "")
|
|
200
203
|
|
|
201
204
|
# replace common pipelime types
|
|
202
205
|
common_pipelime_types = {
|
pipelime/cli/utils.py
CHANGED
|
@@ -9,7 +9,7 @@ from types import ModuleType
|
|
|
9
9
|
|
|
10
10
|
import yaml
|
|
11
11
|
from loguru import logger
|
|
12
|
-
from pydantic
|
|
12
|
+
from pydantic import BaseModel, ValidationError
|
|
13
13
|
from rich import get_console
|
|
14
14
|
from rich.prompt import Prompt
|
|
15
15
|
from yaml.error import YAMLError
|
|
@@ -108,13 +108,15 @@ class PipelimeSymbolsHelper:
|
|
|
108
108
|
|
|
109
109
|
@classmethod
|
|
110
110
|
def _symbol_name(cls, symbol):
|
|
111
|
-
|
|
111
|
+
import inspect
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
)
|
|
113
|
+
from pydantic import BaseModel
|
|
114
|
+
|
|
115
|
+
from pipelime.utils.pydantic_compat import model_title
|
|
116
|
+
|
|
117
|
+
if inspect.isclass(symbol) and issubclass(symbol, BaseModel):
|
|
118
|
+
return model_title(symbol)
|
|
119
|
+
return symbol.__name__
|
|
118
120
|
|
|
119
121
|
@classmethod
|
|
120
122
|
def _warn_double_def(cls, type_, name, first, second):
|
|
@@ -148,22 +150,28 @@ class PipelimeSymbolsHelper:
|
|
|
148
150
|
)
|
|
149
151
|
|
|
150
152
|
# set module path when loading from file
|
|
153
|
+
# NB: only for the classes defined there, a class the file imports
|
|
154
|
+
# (eg, `from pipelime.stages import StageEntity`) keeps its own path
|
|
151
155
|
if module_name.endswith(".py"):
|
|
152
156
|
for _, sym_cls in module_symbols:
|
|
153
|
-
sym_cls.
|
|
157
|
+
if sym_cls.__module__ == module_.__name__:
|
|
158
|
+
sym_cls._classpath = f"{module_name}:{sym_cls.__name__}"
|
|
154
159
|
|
|
155
160
|
# check for double symbols in the same module
|
|
161
|
+
# NB: the same class re-exported under another name is not a duplicate
|
|
156
162
|
for idx, (sym_name, sym_cls) in enumerate(module_symbols):
|
|
157
163
|
for other_sym_name, other_sym_cls in module_symbols[idx + 1 :]: # noqa
|
|
158
|
-
if sym_name == other_sym_name:
|
|
164
|
+
if sym_name == other_sym_name and sym_cls is not other_sym_cls:
|
|
159
165
|
cls._warn_double_def(
|
|
160
166
|
symbol_type, sym_name, sym_cls, other_sym_cls
|
|
161
167
|
)
|
|
162
168
|
|
|
163
169
|
# check for double commands across modules
|
|
170
|
+
# NB: a module importing a symbol from another one (eg, a user file
|
|
171
|
+
# with `from pipelime.stages import StageEntity`) is not a duplicate
|
|
164
172
|
module_symbols = dict(module_symbols)
|
|
165
173
|
for sym_name, sym_cls in module_symbols.items():
|
|
166
|
-
if sym_name in all_syms:
|
|
174
|
+
if sym_name in all_syms and all_syms[sym_name] is not sym_cls:
|
|
167
175
|
cls._warn_double_def(
|
|
168
176
|
symbol_type, sym_name, sym_cls, all_syms[sym_name]
|
|
169
177
|
)
|
|
@@ -631,7 +639,7 @@ def pl_print(
|
|
|
631
639
|
"""
|
|
632
640
|
import inspect
|
|
633
641
|
|
|
634
|
-
from pydantic
|
|
642
|
+
from pydantic import BaseModel
|
|
635
643
|
from rich import print as rprint
|
|
636
644
|
|
|
637
645
|
from pipelime.cli.pretty_print import print_model_info
|
|
@@ -720,9 +728,11 @@ def get_pipelime_command_cls(
|
|
|
720
728
|
return cmd_cls[1]
|
|
721
729
|
|
|
722
730
|
|
|
723
|
-
def
|
|
731
|
+
def resolve_pipelime_command(
|
|
724
732
|
cmd: "T_DAG_NODE", checkpoint: t.Optional["CheckpointNamespace"] = None
|
|
725
|
-
)
|
|
733
|
+
):
|
|
734
|
+
"""Splits a DAG node definition into the command class (None when `cmd` is
|
|
735
|
+
already an instance) and a zero-arg builder returning the command."""
|
|
726
736
|
from pipelime.piper.model import LazyCommand, PipelimeCommand
|
|
727
737
|
|
|
728
738
|
cmd_cls, cmd_args = None, {}
|
|
@@ -734,19 +744,28 @@ def get_pipelime_command(
|
|
|
734
744
|
elif isinstance(cmd, LazyCommand):
|
|
735
745
|
cmd_cls = cmd
|
|
736
746
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
747
|
+
def _build() -> "PipelimeCommand":
|
|
748
|
+
obj = cmd
|
|
749
|
+
if cmd_cls is not None:
|
|
750
|
+
obj = (
|
|
751
|
+
cmd_cls(**cmd_args)
|
|
752
|
+
if checkpoint is None
|
|
753
|
+
else cmd_cls.init_from_checkpoint(checkpoint, **cmd_args)
|
|
754
|
+
)
|
|
755
|
+
if not isinstance(obj, PipelimeCommand):
|
|
756
|
+
raise ValueError(f"{obj} is not a pipelime command.")
|
|
757
|
+
if checkpoint is not None:
|
|
758
|
+
obj._checkpoint = checkpoint
|
|
759
|
+
return obj
|
|
760
|
+
|
|
761
|
+
real_cls = cmd_cls.command_class if isinstance(cmd_cls, LazyCommand) else cmd_cls
|
|
762
|
+
return real_cls, _build
|
|
743
763
|
|
|
744
|
-
if not isinstance(cmd, PipelimeCommand):
|
|
745
|
-
raise ValueError(f"{cmd} is not a pipelime command.")
|
|
746
764
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
765
|
+
def get_pipelime_command(
|
|
766
|
+
cmd: "T_DAG_NODE", checkpoint: t.Optional["CheckpointNamespace"] = None
|
|
767
|
+
) -> "PipelimeCommand":
|
|
768
|
+
return resolve_pipelime_command(cmd, checkpoint)[1]()
|
|
750
769
|
|
|
751
770
|
|
|
752
771
|
def time_to_str(nanosec: int) -> str:
|
|
@@ -761,21 +780,29 @@ def time_to_str(nanosec: int) -> str:
|
|
|
761
780
|
return str(timedelta(microseconds=nanosec / 1000))
|
|
762
781
|
|
|
763
782
|
|
|
764
|
-
def
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
783
|
+
def format_validation_error(e: ValidationError, model_cls=None) -> str:
|
|
784
|
+
"""The error text with `name / alias` locations, as pipelime 2.x printed them.
|
|
785
|
+
`model_cls` is the class that raised (v2 errors do not carry it)."""
|
|
786
|
+
from pipelime.utils.pydantic_compat import iter_fields
|
|
787
|
+
|
|
788
|
+
alias_to_name = {}
|
|
789
|
+
if model_cls is not None and model_cls.model_config.get("populate_by_name"):
|
|
790
|
+
alias_to_name = {
|
|
791
|
+
f.alias: f"{f.name} / {f.alias}"
|
|
792
|
+
for f in iter_fields(model_cls)
|
|
793
|
+
if f.has_alias
|
|
794
|
+
}
|
|
775
795
|
|
|
796
|
+
lines = [f"{e.error_count()} validation error(s) for {e.title}"]
|
|
776
797
|
for err in e.errors():
|
|
777
|
-
|
|
778
|
-
|
|
798
|
+
loc = " -> ".join(str(alias_to_name.get(p, p)) for p in err.get("loc", ()))
|
|
799
|
+
lines.append(f"{loc or '(root)'}\n {err['msg']} [type={err['type']}]")
|
|
800
|
+
return "\n".join(lines)
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
def show_field_alias_valerr(e: ValidationError, model_cls=None) -> str:
|
|
804
|
+
"""The pipelime 2.x name of `format_validation_error`."""
|
|
805
|
+
return format_validation_error(e, model_cls)
|
|
779
806
|
|
|
780
807
|
|
|
781
808
|
def parse_user_input(s: str) -> t.Any:
|