pipelime-python 2.0.0__py3-none-any.whl → 2.1.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 CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  __author__ = "Eyecan.ai"
4
4
  __email__ = "info@eyecan.ai"
5
- __version__ = "2.0.0"
5
+ __version__ = "2.1.0"
@@ -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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pipelime-python
3
- Version: 2.0.0
3
+ Version: 2.1.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
@@ -1,5 +1,5 @@
1
1
  LICENSE,sha256=Ufc-hKELjY574QxuUAosOjRD6RO0x_Y3jtnNbjRK5pc,1525
2
- pipelime/__init__.py,sha256=t0MCBd46vvOKdHwmMQDyXGhUuQtKpMh1MuZsrtF_MwU,115
2
+ pipelime/__init__.py,sha256=XdKxm5F9mco63-Q1cyLZzd3l0CN1F4ZAfgNnW27y11g,115
3
3
  pipelime/choixe/__init__.py,sha256=sFXmatlOorxsGTrB0q6W8RUA0KNqnC4qiRbDRbRZrBM,52
4
4
  pipelime/choixe/xconfig.py,sha256=b3ensxAIXgLNw_mVb6_yNnvkHEEGvuJmIN120ACnQlI,15124
5
5
  pipelime/choixe/ast/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -29,7 +29,7 @@ pipelime/cli/tui/tui.py,sha256=79iNTPe4TvJwkZVnnf-Iqk3rT_b2lntqZon7N5Yxy48,10163
29
29
  pipelime/cli/tui/utils.py,sha256=JaVAzfbdLVzrst6NzNCN5CVO0_gOREOy2142ysmmxkk,6447
30
30
  pipelime/commands/__init__.py,sha256=pk6Xr0MW7FDwtqJPOyzHhz6-vYksRzMw79HlhzhRDj4,741
31
31
  pipelime/commands/general.py,sha256=xPJYj5rnS4-aBY4B5AJt9RMZ4cP7JqyQeaG8EaQGGSs,37237
32
- pipelime/commands/interfaces.py,sha256=irjJNLKTLUFVnY-Av8O3NDWIzY4XeE4-jQC6kvY2BMM,30415
32
+ pipelime/commands/interfaces.py,sha256=aE42ysEy7YMwhzPMrTLoebeYKf4N1KSWKYWuUx_nY3Q,34348
33
33
  pipelime/commands/piper.py,sha256=luq7tk5Tr2i2k2KQX9h0DznF3c10yVptAY9Rtmne0YI,23771
34
34
  pipelime/commands/resume.py,sha256=AGcf8UFttxUD5_Ojq7G41NxsKRD_bjXzkqt6rgXURcg,2581
35
35
  pipelime/commands/shell.py,sha256=wrW6WGVP_05t28B3CDHQoRb812_wQr8EHOFPemAnXE0,2502
@@ -113,8 +113,8 @@ pipelime/utils/context_managers.py,sha256=C3ubzFVBPNG_wu1jqKc0o1Vy1sxsZ-UzFKry2g
113
113
  pipelime/utils/deprecated.py,sha256=rBlOk3cJPZw3jB7QuIY8U3LPWLuovpj8Mfarn65GrqU,1544
114
114
  pipelime/utils/inspection.py,sha256=mpfx-wnoDrocpZ5nlh--XtqEGQEEo9WIYIlF18WXXWg,1369
115
115
  pipelime/utils/pydantic_types.py,sha256=OGw1bNdWmIRTFkF-fPBHLzAVqaTtrsaS6-mUZ8zgHSg,24955
116
- pipelime_python-2.0.0.dist-info/METADATA,sha256=5awPuwE6IIL1xsiUc4H0BeJjf-R_hBXc6ndRGPNYYI4,8509
117
- pipelime_python-2.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
118
- pipelime_python-2.0.0.dist-info/entry_points.txt,sha256=hY61hWhOC6q8tq5Qj5oOOEjZo9dtOrztO7IXylYsoTo,61
119
- pipelime_python-2.0.0.dist-info/licenses/LICENSE,sha256=Ufc-hKELjY574QxuUAosOjRD6RO0x_Y3jtnNbjRK5pc,1525
120
- pipelime_python-2.0.0.dist-info/RECORD,,
116
+ pipelime_python-2.1.0.dist-info/METADATA,sha256=fFTCHqhJSlF9tolV9gHA6L3m2-U_R0f1ncUHXV3LkRg,8509
117
+ pipelime_python-2.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
118
+ pipelime_python-2.1.0.dist-info/entry_points.txt,sha256=hY61hWhOC6q8tq5Qj5oOOEjZo9dtOrztO7IXylYsoTo,61
119
+ pipelime_python-2.1.0.dist-info/licenses/LICENSE,sha256=Ufc-hKELjY574QxuUAosOjRD6RO0x_Y3jtnNbjRK5pc,1525
120
+ pipelime_python-2.1.0.dist-info/RECORD,,