Skip to content

Commit

Permalink
Merge pull request #12 from Jordan-Kowal/feat/keep-value-history
Browse files Browse the repository at this point in the history
feat: added pipe start history for debug mode to track values
  • Loading branch information
Jordan-Kowal authored Nov 22, 2024
2 parents f235995 + cef9465 commit 78c5fae
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
## TBD

-[Python] Keep same `PipeStart` object throughout the pipe for improved performances
-[Python] the `PipeStart.history` attribute to keep track of all its values (only in debug mode)

## 1.0.4 - 2024-11-22

Expand Down
17 changes: 12 additions & 5 deletions pipe_operator/python_flow/pipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Any,
Callable,
Generic,
List,
Optional,
TypeVar,
Union,
Expand Down Expand Up @@ -68,15 +69,18 @@ class PipeStart(Generic[TValue]):
153
"""

__slots__ = ("value", "debug", "result", "chained")
__slots__ = ("value", "debug", "result", "chained", "history")

def __init__(
self, value: TValue, debug: bool = False, chained: bool = False
) -> None:
self.value = value
self.debug = debug
self.history: List[Any] = []
self.result: Optional[Any] = None
self.chained = chained
if self.debug:
self.history.append(value)

def __rshift__(
self, other: Union["Pipe[TValue, FuncParams, TOutput]", "Then[TValue, TOutput]"]
Expand All @@ -99,23 +103,26 @@ def __rshift__(
if isinstance(other, PipeEnd):
return self.value # type: ignore
self.result = other.f(self.value, *other.args, **other.kwargs) # type: ignore
if self.debug:
self._print_data(other.tap)
self._handle_debug(other.tap)
if other.tap:
return self # type: ignore
# Performance: update self instead of creating a new PipeStart
self.value, self.result, self.chained = self.result, None, True # type: ignore
return self # type: ignore

def _print_data(self, is_tap: bool) -> None:
"""Will either its value, its result, or both."""
def _handle_debug(self, is_tap: bool) -> None:
"""Will either its value, its result, or both. Debug mode only."""
if not self.debug:
return
# Extra print if first call
if not self.chained:
print(self.value)
# Then print either the value or the result
if is_tap:
self.history.append(self.value)
print(self.value)
else:
self.history.append(self.result)
print(self.result)


Expand Down
18 changes: 16 additions & 2 deletions pipe_operator/python_flow/tests/test_pipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,30 @@ def test_with_tap(self) -> None:

def test_debug(self) -> None:
with patch("builtins.print") as mock_print:
op = (
instance = (
PipeStart(3, debug=True)
>> Pipe(double)
>> Tap(lambda x: mock_print(x))
>> Pipe(double)
>> PipeEnd()
)
op = instance >> PipeEnd()
self.assertEqual(op, 12)
self.assertListEqual(instance.history, [3, 6, 6, 12])
self.assertEqual(mock_print.call_count, 5)

def test_not_debug(self) -> None:
with patch("builtins.print") as mock_print:
instance = (
PipeStart(3)
>> Pipe(double)
>> Tap(lambda x: mock_print(x))
>> Pipe(double)
)
op = instance >> PipeEnd()
self.assertEqual(op, 12)
self.assertListEqual(instance.history, [])
self.assertEqual(mock_print.call_count, 1) # The one from `Tap`

def test_complex(self) -> None:
op = (
PipeStart("3") # start
Expand Down

0 comments on commit 78c5fae

Please sign in to comment.