Skip to content

Commit f54ca09

Browse files
authored
Add step decorator usage (#1)
* Add .python-version to .gitignore * Make step function usable as a decorator with warning * Update pipeline step error message * Update tests * Update README
1 parent afdc77b commit f54ca09

File tree

5 files changed

+95
-31
lines changed

5 files changed

+95
-31
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ __pycache__/
66
*.DS_Store
77
*.egg-info
88

9-
*.env
9+
*.env
10+
11+
.python-version

README.md

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ $ pip install tinypipeline
1414

1515
`tinypipeline` exposes two main objects:
1616
- `pipeline`: a decorator for defining your pipeline. Returns a `Pipeline` instance.
17-
- `step`: a function that is used to define individual pipeline steps. Returns a `Step` instance.
17+
- `step`: a decorator that is used to define individual pipeline steps. Returns a `Step` instance.
1818

1919
Each object requires you provide a `name`, `version`, and `description` to explicitly define what pipeline you're creating.
2020

@@ -27,11 +27,12 @@ If you'd like to use this package, you can follow the `example.py` below:
2727
```python
2828
from tinypipeline import pipeline, step
2929

30-
31-
def step_fn_one():
30+
@step(name='step_one', version='0.0.1', description='first step')
31+
def step_one():
3232
print("Step function one")
3333

34-
def step_fn_two():
34+
@step(name='step_two', version='0.0.1', description='second step')
35+
def step_two():
3536
print("Step function two")
3637

3738
@pipeline(
@@ -40,18 +41,6 @@ def step_fn_two():
4041
description='a test tinypipeline',
4142
)
4243
def pipe():
43-
step_one = step(
44-
callable=step_fn_one,
45-
name='step_one',
46-
version='0.0.1',
47-
description='first step',
48-
)
49-
step_two = step(
50-
name='step_two',
51-
version='0.0.1',
52-
description='second step',
53-
callable=step_fn_two,
54-
)
5544
return [step_one, step_two]
5645

5746
pipe = pipe()

tests/test_pipeline.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,58 @@ def test_pipeline():
5454
mock_step_2.assert_called_once()
5555
mock_step_3.assert_called_once()
5656

57+
def test_pipeline_completion_using_step_decorator():
58+
"""
59+
Test that the pipeline runs all the steps in the correct order,
60+
using the step decorator instead of the step function.
61+
"""
62+
mock_step_1 = Mock()
63+
mock_step_2 = Mock()
64+
mock_step_3 = Mock()
65+
66+
mock_step_1.return_value = "step 1"
67+
mock_step_2.return_value = "step 2"
68+
mock_step_3.return_value = "step 3"
69+
70+
@step(
71+
name="step_one",
72+
description="Step one",
73+
version="1.0.0",
74+
)
75+
def step_one():
76+
mock_step_1()
77+
78+
@step(
79+
name="step_two",
80+
description="Step two",
81+
version="1.0.0",
82+
)
83+
def step_two():
84+
mock_step_2()
85+
86+
@step(
87+
name="step_three",
88+
description="Step three",
89+
version="1.0.0",
90+
)
91+
def step_three():
92+
mock_step_3()
93+
94+
@pipeline(
95+
name="test_pipeline",
96+
description="Test pipeline",
97+
version="1.0.0",
98+
)
99+
def test_pipeline():
100+
return [step_one, step_two, step_three]
101+
102+
pipe = test_pipeline()
103+
pipe.run()
104+
105+
assert len(pipe.steps) == 3
106+
mock_step_1.assert_called_once()
107+
mock_step_2.assert_called_once()
108+
mock_step_3.assert_called_once()
57109

58110
def test_pipeline_failure_no_function_passed():
59111
"""
@@ -97,7 +149,8 @@ def test_pipeline():
97149
pipe.run()
98150

99151
assert (
100-
"Not a valid step. Consider using the step() method to create steps for your pipeline."
152+
"Not a valid step. Consider using the step decorator to "
153+
"create steps for your pipeline."
101154
== str(context.value)
102155
)
103156

@@ -145,7 +198,8 @@ def test_pipeline():
145198

146199
def test_pipeline_failure_exception_in_step():
147200
"""
148-
Test that the pipeline fails with an Exception if there is an exception in one of the steps.
201+
Test that the pipeline fails with an Exception if there is an exception
202+
in one of the steps.
149203
150204
Also check that the error message is correct.
151205
"""

tinypipeline/pipeline.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ def _get_steps(self):
5555

5656
if not all(isinstance(s, Step) for s in _steps):
5757
raise TypeError(
58-
"Not a valid step. Consider using the step() method to create steps for your pipeline."
58+
"Not a valid step. Consider using the step decorator "
59+
"to create steps for your pipeline."
5960
)
6061

6162
return _steps
@@ -86,7 +87,7 @@ def run(self) -> None:
8687

8788
completion_time = (end - start).total_seconds()
8889
print(f"Step [{step.name}] completed in {completion_time} seconds\n")
89-
except Exception as e:
90+
except Exception:
9091
print(f"Pipeline failed due to an exception in step [{step.name}]")
9192
raise
9293
return None
@@ -119,7 +120,8 @@ def wrapper():
119120
"""
120121
if not isinstance(func, Callable):
121122
raise TypeError(
122-
f"The pipeline decorator only accepts functions. Passed {type(func)}"
123+
"The pipeline decorator only accepts functions. "
124+
f"Passed {type(func)}"
123125
)
124126

125127
_pipeline = Pipeline(

tinypipeline/step.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ def run(self):
3737

3838

3939
def step(
40-
callable: Callable,
4140
name: str,
4241
version: str,
4342
description: str,
43+
callable: Callable = None,
4444
):
4545
"""
46-
Create a step for a pipeline.
46+
Create a step for a pipeline. Can be used as a decorator or a function.
4747
4848
Params
4949
------
@@ -56,10 +56,27 @@ def step(
5656
description: str
5757
A description of the step.
5858
"""
59-
_step = Step(
60-
callable=callable,
61-
name=name,
62-
version=version,
63-
description=description,
64-
)
65-
return _step
59+
if callable is not None:
60+
print(
61+
f"WARNING: step() is being used as a function for {name}. "
62+
"This is deprecated and will be removed in a future version. "
63+
"Please use step() as a decorator instead."
64+
)
65+
66+
_step = Step(
67+
callable=callable,
68+
name=name,
69+
version=version,
70+
description=description,
71+
)
72+
return _step
73+
74+
def decorator(callable):
75+
_step = Step(
76+
callable=callable,
77+
name=name,
78+
version=version,
79+
description=description,
80+
)
81+
return _step
82+
return decorator

0 commit comments

Comments
 (0)