pipelime-python 2.0.0__tar.gz → 2.2.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.0.0 → pipelime_python-2.2.0}/PKG-INFO +1 -1
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/__init__.py +1 -1
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/utils.py +4 -2
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/commands/interfaces.py +127 -1
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/commands/piper.py +8 -3
- pipelime_python-2.2.0/pipelime/piper/progress/listener/callbacks/loguru.py +79 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/factory.py +5 -2
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/.gitignore +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/LICENSE +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/README.md +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/ast/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/ast/nodes.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/ast/parser.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/utils/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/utils/common.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/utils/imports.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/utils/io.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/utils/rand.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/visitors/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/visitors/decoder.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/visitors/inspector.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/visitors/processor.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/visitors/processor_ui.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/visitors/unparser.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/visitors/walker.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/choixe/xconfig.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/main.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/parser.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/pretty_print.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/subcommands.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/tui/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/tui/tui.css +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/tui/tui.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/cli/tui/utils.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/commands/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/commands/general.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/commands/resume.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/commands/shell.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/commands/split_ops.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/commands/tempman.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/commands/toy_dataset.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/items/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/items/base.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/items/binary_item.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/items/image_item.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/items/metadata_item.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/items/model3d_item.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/items/numpy_item.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/items/pickle_item.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/checkpoint.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/drawing/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/drawing/base.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/drawing/factory.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/drawing/graphviz.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/drawing/mermaid.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/executors/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/executors/base.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/executors/factory.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/executors/naive.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/graph.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/model.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/parsers/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/parsers/base.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/parsers/choixe_parser.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/parsers/factory.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/estimator/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/estimator/base.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/estimator/factory.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/estimator/naive.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/base.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/callbacks/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/callbacks/file.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/callbacks/rich_table.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/callbacks/tqdm_bars.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/receiver/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/receiver/zmq.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/model.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/tracker/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/tracker/base.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/tracker/direct.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/tracker/factory.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/tracker/loguru.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/tracker/zmq.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/direct_access.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/grabber.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/pipes/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/pipes/base.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/pipes/mapping.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/pipes/operations.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/pipes/validation.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/pipes/writers.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/sample.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/samples_sequence.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/sources/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/sources/from_callable.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/sources/raw.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/sources/readers.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/sources/toy_dataset.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/torch.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/sequences/utils.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/stages/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/stages/augmentations.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/stages/base.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/stages/entities.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/stages/item_info.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/stages/item_replacement.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/stages/item_sources.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/stages/key_transformations.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/utils/__init__.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/utils/context_managers.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/utils/deprecated.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/utils/inspection.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/utils/pydantic_types.py +0 -0
- {pipelime_python-2.0.0 → pipelime_python-2.2.0}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipelime-python
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.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
|
|
@@ -816,7 +816,9 @@ def get_user_input(text: str) -> t.Any:
|
|
|
816
816
|
Returns:
|
|
817
817
|
The parsed user input.
|
|
818
818
|
"""
|
|
819
|
-
live
|
|
819
|
+
# if there is only one live, pause it to avoid messing the prompt
|
|
820
|
+
live_stack = get_console()._live_stack
|
|
821
|
+
live = live_stack[0] if len(live_stack) == 1 else None
|
|
820
822
|
if live is not None:
|
|
821
823
|
live.stop()
|
|
822
824
|
|
|
@@ -878,7 +880,7 @@ class PipelimeUserAppDir:
|
|
|
878
880
|
for i in reversed(range(1, cls.MAX_CHECKPOINTS)):
|
|
879
881
|
ckpt = base_ckpt.with_name(base_ckpt.name + f"-{i}")
|
|
880
882
|
if ckpt.exists():
|
|
881
|
-
ckpt.rename(base_ckpt.with_name(base_ckpt.name + f"-{i+1}"))
|
|
883
|
+
ckpt.rename(base_ckpt.with_name(base_ckpt.name + f"-{i + 1}"))
|
|
882
884
|
|
|
883
885
|
ckpt = base_ckpt.with_name(base_ckpt.name + "-1")
|
|
884
886
|
ckpt.mkdir(parents=True, exist_ok=False)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import typing as t
|
|
4
5
|
import uuid
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from urllib.parse import ParseResult
|
|
7
7
|
|
|
8
8
|
import pydantic.v1 as pyd
|
|
9
|
+
from pydantic.v1.generics import GenericModel
|
|
9
10
|
|
|
10
11
|
from pipelime.piper import PiperPortType
|
|
11
12
|
from pipelime.utils.pydantic_types import ItemType, SampleValidationInterface, YamlInput
|
|
@@ -835,3 +836,128 @@ class ExtendedInterval(Interval):
|
|
|
835
836
|
raise ValueError(f"Invalid interval: {value}")
|
|
836
837
|
|
|
837
838
|
return cls(**data)
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
ValueType = t.TypeVar("ValueType")
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
class OutputValueInterface(
|
|
845
|
+
PydanticFieldNoDefaultMixin,
|
|
846
|
+
GenericModel,
|
|
847
|
+
t.Generic[ValueType],
|
|
848
|
+
extra="forbid",
|
|
849
|
+
copy_on_model_validation="none",
|
|
850
|
+
underscore_attrs_are_private=True,
|
|
851
|
+
):
|
|
852
|
+
"""Interface that allows to store a value in a file.
|
|
853
|
+
|
|
854
|
+
Examples:
|
|
855
|
+
How to use it in your command::
|
|
856
|
+
|
|
857
|
+
class EasyCommand(PipelimeCommand, title="easy"):
|
|
858
|
+
output_value: OutputValueInterface[int] = OutputValueInterface.pyd_field()
|
|
859
|
+
|
|
860
|
+
def run(self):
|
|
861
|
+
# this will write the value 42 to the given file
|
|
862
|
+
self.output_value.set(42)
|
|
863
|
+
"""
|
|
864
|
+
|
|
865
|
+
_default_type_description: t.ClassVar[t.Optional[str]] = "An output value."
|
|
866
|
+
_compact_form: t.ClassVar[t.Optional[str]] = "<file>[,<exists_ok>]"
|
|
867
|
+
_default_port_type: t.ClassVar[PiperPortType] = PiperPortType.OUTPUT
|
|
868
|
+
_data: ValueType
|
|
869
|
+
|
|
870
|
+
file: Path = pyd.Field(
|
|
871
|
+
description=(
|
|
872
|
+
"The output file that will store the value. If the file exists, it will"
|
|
873
|
+
"be overwritten if `exists_ok` is set to `True`."
|
|
874
|
+
),
|
|
875
|
+
)
|
|
876
|
+
exists_ok: bool = pyd.Field(
|
|
877
|
+
False,
|
|
878
|
+
description=(
|
|
879
|
+
"If `True`, the output file will be overwritten if it exists. "
|
|
880
|
+
"If `False`, an error will be raised."
|
|
881
|
+
),
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
@pyd.validator("file")
|
|
885
|
+
def resolve_file(cls, v: Path):
|
|
886
|
+
# see https://bugs.python.org/issue38671
|
|
887
|
+
return v.resolve().absolute()
|
|
888
|
+
|
|
889
|
+
@classmethod
|
|
890
|
+
def validate(cls, value):
|
|
891
|
+
if isinstance(value, OutputValueInterface):
|
|
892
|
+
return value
|
|
893
|
+
|
|
894
|
+
if isinstance(value, (str, bytes, Path)):
|
|
895
|
+
data = {}
|
|
896
|
+
raw_data = str(value).split(",")
|
|
897
|
+
data["file"] = raw_data[0]
|
|
898
|
+
if len(raw_data) > 1:
|
|
899
|
+
# NB: the value is left as-is when it is not True nor False
|
|
900
|
+
# to raise an error on validation
|
|
901
|
+
data["exists_ok"] = (
|
|
902
|
+
True
|
|
903
|
+
if raw_data[1].lower() == "true"
|
|
904
|
+
else (False if raw_data[1].lower() == "false" else raw_data[1])
|
|
905
|
+
)
|
|
906
|
+
value = data
|
|
907
|
+
|
|
908
|
+
if isinstance(value, t.Mapping):
|
|
909
|
+
return OutputValueInterface(**value)
|
|
910
|
+
|
|
911
|
+
raise ValueError("Invalid OutputValueInterface definition.")
|
|
912
|
+
|
|
913
|
+
@classmethod
|
|
914
|
+
def __get_validators__(cls):
|
|
915
|
+
yield cls.validate
|
|
916
|
+
|
|
917
|
+
@pyd.validator("exists_ok", always=True)
|
|
918
|
+
def _check_file_exists(cls, v: bool, values: t.Mapping[str, t.Any]) -> bool:
|
|
919
|
+
if "file" not in values:
|
|
920
|
+
# the "file" validator already failed
|
|
921
|
+
return v
|
|
922
|
+
|
|
923
|
+
if v is True:
|
|
924
|
+
if values["file"].is_dir():
|
|
925
|
+
raise ValueError(
|
|
926
|
+
f"Cannot overwrite the directory: `{values['file']}`. "
|
|
927
|
+
"Please provide a file path."
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
if v is False:
|
|
931
|
+
if values["file"].is_dir():
|
|
932
|
+
raise ValueError(
|
|
933
|
+
f"Trying to overwrite the directory: `{values['file']}`. "
|
|
934
|
+
"Please provide a file path and use `exists_ok=True` to overwrite."
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
if values["file"].exists():
|
|
938
|
+
raise ValueError(
|
|
939
|
+
f"Trying to overwrite an existing file: `{values['file']}`. "
|
|
940
|
+
"Please use `exists_ok=True` to overwrite."
|
|
941
|
+
)
|
|
942
|
+
|
|
943
|
+
return v
|
|
944
|
+
|
|
945
|
+
def get(self) -> ValueType:
|
|
946
|
+
"""Return the stored value."""
|
|
947
|
+
return self._data
|
|
948
|
+
|
|
949
|
+
def set(self, value: ValueType) -> None:
|
|
950
|
+
"""Writes the value to the output file.
|
|
951
|
+
|
|
952
|
+
Args:
|
|
953
|
+
value: The value to write.
|
|
954
|
+
"""
|
|
955
|
+
self._data = value
|
|
956
|
+
with open(self.file, "w") as f:
|
|
957
|
+
json.dump(self._data, f)
|
|
958
|
+
|
|
959
|
+
def __repr__(self) -> str:
|
|
960
|
+
return self.__piper_repr__()
|
|
961
|
+
|
|
962
|
+
def __piper_repr__(self) -> str:
|
|
963
|
+
return self.file.as_posix()
|
|
@@ -25,11 +25,13 @@ class WatcherBackend(Enum):
|
|
|
25
25
|
|
|
26
26
|
RICH = "rich"
|
|
27
27
|
TQDM = "tqdm"
|
|
28
|
+
LOGURU = "loguru"
|
|
28
29
|
|
|
29
30
|
def listener_key(self) -> str:
|
|
30
31
|
return {
|
|
31
32
|
WatcherBackend.RICH: "RICH_TABLE",
|
|
32
33
|
WatcherBackend.TQDM: "TQDM_BARS",
|
|
34
|
+
WatcherBackend.LOGURU: "LOGURU",
|
|
33
35
|
}[self]
|
|
34
36
|
|
|
35
37
|
|
|
@@ -254,8 +256,10 @@ class RunCommandBase(GraphPortForwardingCommand):
|
|
|
254
256
|
|
|
255
257
|
PipelimeCommand._track_callback = DirectTrackCallback(callback)
|
|
256
258
|
|
|
257
|
-
# disable annoying logging
|
|
258
|
-
|
|
259
|
+
# disable annoying logging (only if we don't need it for the watcher)
|
|
260
|
+
if watch != WatcherBackend.LOGURU:
|
|
261
|
+
logger.disable("pipelime")
|
|
262
|
+
|
|
259
263
|
exit_stack.callback(logger.enable, "pipelime")
|
|
260
264
|
exit_stack.callback(PipelimeCommand._track_callback.stop_callbacks)
|
|
261
265
|
|
|
@@ -456,7 +460,8 @@ class WatchCommand(PipelimeCommand, title="watch"):
|
|
|
456
460
|
from pipelime.utils.context_managers import CatchSignals
|
|
457
461
|
|
|
458
462
|
receiver = ProgressReceiverFactory.get_receiver(
|
|
459
|
-
self.token,
|
|
463
|
+
self.token,
|
|
464
|
+
**({"port": self.port} if self.port else {}), # type: ignore
|
|
460
465
|
)
|
|
461
466
|
if isinstance(self.watcher, WatcherBackend):
|
|
462
467
|
callback = ListenerCallbackFactory.get_callback(
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from typing import ClassVar
|
|
5
|
+
|
|
6
|
+
from loguru import logger
|
|
7
|
+
|
|
8
|
+
from pipelime.piper.progress.listener.base import ListenerCallback
|
|
9
|
+
from pipelime.piper.progress.model import ProgressUpdate
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LoguruListenerCallback(ListenerCallback):
|
|
13
|
+
"""Listener callback that sends progress updates to loguru.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
show_token: Whether to include the token in the logged data.
|
|
17
|
+
log_extras: Extra fields to bind to the logger. Can also be set via
|
|
18
|
+
environment variables with the prefix PIPELIME_PROGRESS_LOG_EXTRA_
|
|
19
|
+
(e.g., PIPELIME_PROGRESS_LOG_EXTRA_key=value). Environment variables
|
|
20
|
+
take precedence over this argument.
|
|
21
|
+
level: Logging level to use. Can also be set via the environment variable
|
|
22
|
+
PIPELIME_PROGRESS_LOG_LEVEL. Defaults to "DEBUG".
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
LOG_LEVEL_ENV_VAR: ClassVar[str] = "PIPELIME_PROGRESS_LOG_LEVEL"
|
|
26
|
+
LOG_EXTRA_ENV_VAR_PREFIX: ClassVar[str] = "PIPELIME_PROGRESS_LOG_EXTRA_"
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def update_data(cls, data: dict, prog: ProgressUpdate, show_token: bool) -> None:
|
|
30
|
+
"""Update the data dictionary with the progress update"""
|
|
31
|
+
root = data.setdefault(prog.op_info.token, dict()) if show_token else data
|
|
32
|
+
|
|
33
|
+
chunk_data = root.setdefault(prog.op_info.node, dict()).setdefault(
|
|
34
|
+
str(prog.op_info.chunk), dict()
|
|
35
|
+
)
|
|
36
|
+
chunk_data["message"] = prog.op_info.message
|
|
37
|
+
chunk_data["total"] = prog.op_info.total
|
|
38
|
+
chunk_data["progress"] = prog.progress
|
|
39
|
+
chunk_data["finished"] = prog.finished
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
show_token: bool = False,
|
|
44
|
+
log_extras: dict | None = None,
|
|
45
|
+
level: str = "DEBUG",
|
|
46
|
+
) -> None:
|
|
47
|
+
super().__init__(show_token)
|
|
48
|
+
|
|
49
|
+
self._data: dict = dict()
|
|
50
|
+
|
|
51
|
+
# collect log extras from environment variables
|
|
52
|
+
env_extras = {}
|
|
53
|
+
for key, value in os.environ.items():
|
|
54
|
+
if key.startswith(self.LOG_EXTRA_ENV_VAR_PREFIX):
|
|
55
|
+
extra_key = key[len(self.LOG_EXTRA_ENV_VAR_PREFIX) :].lower()
|
|
56
|
+
try:
|
|
57
|
+
env_extras[extra_key] = ast.literal_eval(value)
|
|
58
|
+
except (ValueError, SyntaxError):
|
|
59
|
+
env_extras[extra_key] = value
|
|
60
|
+
|
|
61
|
+
# merge env extras with provided extras (env vars take precedence)
|
|
62
|
+
log_extras = log_extras or {}
|
|
63
|
+
self._log_extras = {**log_extras, **env_extras}
|
|
64
|
+
|
|
65
|
+
# determine log level (env var takes precedence)
|
|
66
|
+
self._level = os.getenv(self.LOG_LEVEL_ENV_VAR, level).upper()
|
|
67
|
+
|
|
68
|
+
def _log_data(self) -> None:
|
|
69
|
+
if self._data:
|
|
70
|
+
logger.bind(**self._log_extras).log(self._level, json.dumps(self._data))
|
|
71
|
+
|
|
72
|
+
def on_update(self, prog: ProgressUpdate) -> None:
|
|
73
|
+
"""Called when a progress update is received"""
|
|
74
|
+
LoguruListenerCallback.update_data(self._data, prog, self.show_token)
|
|
75
|
+
self._log_data()
|
|
76
|
+
|
|
77
|
+
def on_stop(self) -> None:
|
|
78
|
+
"""Called when the listener stops - ensures final progress is sent"""
|
|
79
|
+
self._log_data()
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
|
+
|
|
2
3
|
from pipelime.piper.progress.listener.base import ListenerCallback, ProgressReceiver
|
|
3
|
-
from pipelime.piper.progress.listener.
|
|
4
|
+
from pipelime.piper.progress.listener.callbacks.file import FileListenerCallback
|
|
5
|
+
from pipelime.piper.progress.listener.callbacks.loguru import LoguruListenerCallback
|
|
4
6
|
from pipelime.piper.progress.listener.callbacks.rich_table import (
|
|
5
7
|
RichTableListenerCallback,
|
|
6
8
|
)
|
|
7
9
|
from pipelime.piper.progress.listener.callbacks.tqdm_bars import (
|
|
8
10
|
TqdmBarsListenerCallback,
|
|
9
11
|
)
|
|
10
|
-
from pipelime.piper.progress.listener.
|
|
12
|
+
from pipelime.piper.progress.listener.receiver.zmq import ZMQProgressReceiver
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
class ProgressReceiverFactory:
|
|
@@ -35,6 +37,7 @@ class ListenerCallbackFactory:
|
|
|
35
37
|
"RICH_TABLE": RichTableListenerCallback,
|
|
36
38
|
"TQDM_BARS": TqdmBarsListenerCallback,
|
|
37
39
|
"FILE": FileListenerCallback,
|
|
40
|
+
"LOGURU": LoguruListenerCallback,
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
@classmethod
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/estimator/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/estimator/factory.py
RENAMED
|
File without changes
|
|
File without changes
|
{pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/callbacks/file.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pipelime_python-2.0.0 → pipelime_python-2.2.0}/pipelime/piper/progress/listener/receiver/zmq.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|