pyflowstep 0.6.0__tar.gz → 0.7.1__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.
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/PKG-INFO +36 -8
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/README.md +32 -5
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/pyproject.toml +11 -3
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/pyproject.toml.orig +9 -2
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/__init__.py +105 -98
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/compilers.py +160 -149
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/dependencies.py +327 -327
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/exceptions.py +137 -129
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/flow.py +163 -163
- pyflowstep-0.7.1/src/pyflowstep/json_schema.py +96 -0
- pyflowstep-0.7.1/src/pyflowstep/loaders.py +80 -0
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/registry.py +15 -17
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/validators.py +29 -29
- pyflowstep-0.6.0/src/pyflowstep/json_schema.py +0 -249
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/inputs.py +0 -0
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/py.typed +0 -0
- {pyflowstep-0.6.0 → pyflowstep-0.7.1}/src/pyflowstep/steps.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyflowstep
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: A lightweight, typed Python library for composing functions into readable, reusable flows that can be defined as JSON.
|
|
5
|
-
Keywords: flow,pipeline,composition,functional,fluent-interface,json,json-schema,workflow,dsl
|
|
3
|
+
Version: 0.7.1
|
|
4
|
+
Summary: A lightweight, typed Python library for composing functions into readable, reusable flows that can be defined as JSON or TOML.
|
|
5
|
+
Keywords: flow,pipeline,composition,functional,fluent-interface,json,json-schema,toml,workflow,dsl
|
|
6
6
|
Author: aaltatan
|
|
7
7
|
Author-email: aaltatan <a.altatan@gmail.com>
|
|
8
8
|
License-Expression: GPL-3.0-only
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
16
16
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
17
|
Classifier: Typing :: Typed
|
|
18
18
|
Requires-Dist: pyargprocessors>=0.1.0
|
|
19
|
+
Requires-Dist: pyvocabulary>=0.1.0
|
|
19
20
|
Requires-Python: >=3.12
|
|
20
21
|
Project-URL: Homepage, https://github.com/aaltatan/pyflowstep
|
|
21
22
|
Project-URL: Repository, https://github.com/aaltatan/pyflowstep
|
|
@@ -695,15 +696,42 @@ login = compiler.compile(
|
|
|
695
696
|
login(Page())
|
|
696
697
|
```
|
|
697
698
|
|
|
698
|
-
|
|
699
|
+
`compile` takes already-parsed data, so a database row or an API payload works too.
|
|
700
|
+
|
|
701
|
+
### Flow files
|
|
702
|
+
|
|
703
|
+
`compile_file` picks a parser by the file suffix. JSON and TOML are built in:
|
|
699
704
|
|
|
700
705
|
```python
|
|
701
|
-
|
|
702
|
-
|
|
706
|
+
login = compiler.compile_file("login.json") # a list of steps, as above
|
|
707
|
+
login = compiler.compile_file("login.toml")
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
A TOML flow lists its steps as `[[steps]]` tables:
|
|
711
|
+
|
|
712
|
+
```toml
|
|
713
|
+
[[steps]]
|
|
714
|
+
name = "navigate"
|
|
715
|
+
args = ["https://example.com/login"]
|
|
703
716
|
|
|
704
|
-
|
|
717
|
+
[[steps]]
|
|
718
|
+
name = "click"
|
|
719
|
+
kwargs = { selector = "button[type=submit]" }
|
|
705
720
|
```
|
|
706
721
|
|
|
722
|
+
Add any other format by registering a parser that turns the file text into a list of steps:
|
|
723
|
+
|
|
724
|
+
```python
|
|
725
|
+
import yaml
|
|
726
|
+
|
|
727
|
+
from pyflowstep import register_flow_format
|
|
728
|
+
|
|
729
|
+
register_flow_format(".yaml", yaml.safe_load)
|
|
730
|
+
register_flow_format(".yml", yaml.safe_load)
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
An unregistered suffix raises `UnsupportedFlowFormatError`; a file that does not parse raises `InvalidFlowDefinitionError`. Errors from a file carry an extra `in <path>` note. `load_flow_definition(path)` returns the parsed data without compiling it.
|
|
734
|
+
|
|
707
735
|
Each step dictionary has this shape — only `name` is required:
|
|
708
736
|
|
|
709
737
|
```json
|
|
@@ -719,7 +747,7 @@ at $[1]
|
|
|
719
747
|
|
|
720
748
|
| Problem | Exception |
|
|
721
749
|
| ------------------------------------------------ | ------------------------------------------------------ |
|
|
722
|
-
| Not a list / malformed step dict
|
|
750
|
+
| Not a list / malformed step dict / bad file | `InvalidFlowDefinitionError` |
|
|
723
751
|
| Unknown or hidden step name | `StepDoesNotExistError` (lists the available steps) |
|
|
724
752
|
| Missing / extra / duplicated arguments | `MissingArgumentError`, `TooManyArgumentsError`, ... |
|
|
725
753
|
| A processor rejects a value | `ProcessArgumentError` (from `pyargprocessors`) |
|
|
@@ -671,15 +671,42 @@ login = compiler.compile(
|
|
|
671
671
|
login(Page())
|
|
672
672
|
```
|
|
673
673
|
|
|
674
|
-
|
|
674
|
+
`compile` takes already-parsed data, so a database row or an API payload works too.
|
|
675
|
+
|
|
676
|
+
### Flow files
|
|
677
|
+
|
|
678
|
+
`compile_file` picks a parser by the file suffix. JSON and TOML are built in:
|
|
675
679
|
|
|
676
680
|
```python
|
|
677
|
-
|
|
678
|
-
|
|
681
|
+
login = compiler.compile_file("login.json") # a list of steps, as above
|
|
682
|
+
login = compiler.compile_file("login.toml")
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
A TOML flow lists its steps as `[[steps]]` tables:
|
|
686
|
+
|
|
687
|
+
```toml
|
|
688
|
+
[[steps]]
|
|
689
|
+
name = "navigate"
|
|
690
|
+
args = ["https://example.com/login"]
|
|
679
691
|
|
|
680
|
-
|
|
692
|
+
[[steps]]
|
|
693
|
+
name = "click"
|
|
694
|
+
kwargs = { selector = "button[type=submit]" }
|
|
681
695
|
```
|
|
682
696
|
|
|
697
|
+
Add any other format by registering a parser that turns the file text into a list of steps:
|
|
698
|
+
|
|
699
|
+
```python
|
|
700
|
+
import yaml
|
|
701
|
+
|
|
702
|
+
from pyflowstep import register_flow_format
|
|
703
|
+
|
|
704
|
+
register_flow_format(".yaml", yaml.safe_load)
|
|
705
|
+
register_flow_format(".yml", yaml.safe_load)
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
An unregistered suffix raises `UnsupportedFlowFormatError`; a file that does not parse raises `InvalidFlowDefinitionError`. Errors from a file carry an extra `in <path>` note. `load_flow_definition(path)` returns the parsed data without compiling it.
|
|
709
|
+
|
|
683
710
|
Each step dictionary has this shape — only `name` is required:
|
|
684
711
|
|
|
685
712
|
```json
|
|
@@ -695,7 +722,7 @@ at $[1]
|
|
|
695
722
|
|
|
696
723
|
| Problem | Exception |
|
|
697
724
|
| ------------------------------------------------ | ------------------------------------------------------ |
|
|
698
|
-
| Not a list / malformed step dict
|
|
725
|
+
| Not a list / malformed step dict / bad file | `InvalidFlowDefinitionError` |
|
|
699
726
|
| Unknown or hidden step name | `StepDoesNotExistError` (lists the available steps) |
|
|
700
727
|
| Missing / extra / duplicated arguments | `MissingArgumentError`, `TooManyArgumentsError`, ... |
|
|
701
728
|
| A processor rejects a value | `ProcessArgumentError` (from `pyargprocessors`) |
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pyflowstep"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "A lightweight, typed Python library for composing functions into readable, reusable flows that can be defined as JSON."
|
|
3
|
+
version = "0.7.1"
|
|
4
|
+
description = "A lightweight, typed Python library for composing functions into readable, reusable flows that can be defined as JSON or TOML."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "GPL-3.0-only"
|
|
7
7
|
requires-python = ">=3.12"
|
|
8
|
-
dependencies = [
|
|
8
|
+
dependencies = [
|
|
9
|
+
"pyargprocessors>=0.1.0",
|
|
10
|
+
"pyvocabulary>=0.1.0",
|
|
11
|
+
]
|
|
9
12
|
keywords = [
|
|
10
13
|
"flow",
|
|
11
14
|
"pipeline",
|
|
@@ -14,6 +17,7 @@ keywords = [
|
|
|
14
17
|
"fluent-interface",
|
|
15
18
|
"json",
|
|
16
19
|
"json-schema",
|
|
20
|
+
"toml",
|
|
17
21
|
"workflow",
|
|
18
22
|
"dsl",
|
|
19
23
|
]
|
|
@@ -118,3 +122,7 @@ extend-immutable-calls = [
|
|
|
118
122
|
"T201",
|
|
119
123
|
"INP001",
|
|
120
124
|
]
|
|
125
|
+
|
|
126
|
+
[tool.pyright]
|
|
127
|
+
venvPath = "."
|
|
128
|
+
venv = ".venv"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pyflowstep"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "A lightweight, typed Python library for composing functions into readable, reusable flows that can be defined as JSON."
|
|
3
|
+
version = "0.7.1"
|
|
4
|
+
description = "A lightweight, typed Python library for composing functions into readable, reusable flows that can be defined as JSON or TOML."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
7
7
|
{ name = "aaltatan", email = "a.altatan@gmail.com" }
|
|
@@ -10,6 +10,7 @@ license = "GPL-3.0-only"
|
|
|
10
10
|
requires-python = ">=3.12"
|
|
11
11
|
dependencies = [
|
|
12
12
|
"pyargprocessors>=0.1.0",
|
|
13
|
+
"pyvocabulary>=0.1.0",
|
|
13
14
|
]
|
|
14
15
|
keywords = [
|
|
15
16
|
"flow",
|
|
@@ -19,6 +20,7 @@ keywords = [
|
|
|
19
20
|
"fluent-interface",
|
|
20
21
|
"json",
|
|
21
22
|
"json-schema",
|
|
23
|
+
"toml",
|
|
22
24
|
"workflow",
|
|
23
25
|
"dsl",
|
|
24
26
|
]
|
|
@@ -93,3 +95,8 @@ extend-immutable-calls = [
|
|
|
93
95
|
[tool.ruff.lint.per-file-ignores]
|
|
94
96
|
"tests/**" = ["S101", "PLR2004", "ANN", "ARG", "INP001", "E501"]
|
|
95
97
|
"examples/**" = ["T201", "INP001"]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
[tool.pyright]
|
|
101
|
+
venvPath = "."
|
|
102
|
+
venv = ".venv"
|
|
@@ -1,98 +1,105 @@
|
|
|
1
|
-
"""Compose functions into readable, reusable, JSON-definable flows.
|
|
2
|
-
|
|
3
|
-
`pyflowstep` is a functional replacement for the fluent-interface (method chaining)
|
|
4
|
-
pattern. Instead of adding chainable methods to a class, you write small step
|
|
5
|
-
functions and compose them with `>>`:
|
|
6
|
-
|
|
7
|
-
```python
|
|
8
|
-
>>> from pyflowstep import step
|
|
9
|
-
>>> @step
|
|
10
|
-
... def add(total: int, amount: int) -> int:
|
|
11
|
-
... return total + amount
|
|
12
|
-
>>> @step
|
|
13
|
-
... def double(total: int) -> int:
|
|
14
|
-
... return total * 2
|
|
15
|
-
>>> (add(3) >> double() >> add(1))(0)
|
|
16
|
-
7
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Steps collected in a `StepsRegistry` can also be described as JSON
|
|
21
|
-
(`get_flow_json_schema`) and compiled from JSON (`FlowCompiler`)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
>>> from
|
|
29
|
-
>>>
|
|
30
|
-
|
|
31
|
-
...
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
from .
|
|
40
|
-
from .
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
from .
|
|
60
|
-
from .
|
|
61
|
-
from .
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
|
|
1
|
+
"""Compose functions into readable, reusable, JSON-definable flows.
|
|
2
|
+
|
|
3
|
+
`pyflowstep` is a functional replacement for the fluent-interface (method chaining)
|
|
4
|
+
pattern. Instead of adding chainable methods to a class, you write small step
|
|
5
|
+
functions and compose them with `>>`:
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
>>> from pyflowstep import step
|
|
9
|
+
>>> @step
|
|
10
|
+
... def add(total: int, amount: int) -> int:
|
|
11
|
+
... return total + amount
|
|
12
|
+
>>> @step
|
|
13
|
+
... def double(total: int) -> int:
|
|
14
|
+
... return total * 2
|
|
15
|
+
>>> (add(3) >> double() >> add(1))(0)
|
|
16
|
+
7
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Steps collected in a `StepsRegistry` can also be described as JSON
|
|
21
|
+
(`get_flow_json_schema`) and compiled from JSON or TOML files (`FlowCompiler`);
|
|
22
|
+
`register_flow_format` adds other file formats.
|
|
23
|
+
|
|
24
|
+
Raw JSON values become typed arguments with the `Process` marker of
|
|
25
|
+
`pyargprocessors`, written next to the parameter:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
>>> from typing import Annotated
|
|
29
|
+
>>> from pyargprocessors import Process
|
|
30
|
+
>>> @step
|
|
31
|
+
... def scale(total: int, factor: Annotated[int, Process(int)]) -> int:
|
|
32
|
+
... return total * factor
|
|
33
|
+
>>> scale("3")(2)
|
|
34
|
+
6
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from .compilers import FlowCompiler, FlowDefinition, StepDict, validate_step_dict
|
|
40
|
+
from .dependencies import Depends, override_dependencies
|
|
41
|
+
from .exceptions import (
|
|
42
|
+
ArgumentError,
|
|
43
|
+
InvalidDependencyError,
|
|
44
|
+
InvalidFlowDefinitionError,
|
|
45
|
+
InvalidInputError,
|
|
46
|
+
InvalidStepError,
|
|
47
|
+
InvalidStepNameError,
|
|
48
|
+
MissingArgumentError,
|
|
49
|
+
MissingInputError,
|
|
50
|
+
MultipleValuesArgumentError,
|
|
51
|
+
PositionalOnlyArgumentError,
|
|
52
|
+
PyflowstepError,
|
|
53
|
+
StepAlreadyRegisteredError,
|
|
54
|
+
StepDoesNotExistError,
|
|
55
|
+
TooManyArgumentsError,
|
|
56
|
+
UnexpectedKeywordArgumentError,
|
|
57
|
+
UnsupportedFlowFormatError,
|
|
58
|
+
)
|
|
59
|
+
from .flow import Action, Flow, compose
|
|
60
|
+
from .inputs import Input
|
|
61
|
+
from .json_schema import get_flow_json_schema, get_json_schema, get_step_json_schema
|
|
62
|
+
from .loaders import FlowParser, load_flow_definition, register_flow_format
|
|
63
|
+
from .registry import StepsRegistry
|
|
64
|
+
from .steps import StepFactory, StepFn, TapFn, step, tap
|
|
65
|
+
|
|
66
|
+
__all__ = [
|
|
67
|
+
"Action",
|
|
68
|
+
"ArgumentError",
|
|
69
|
+
"Depends",
|
|
70
|
+
"Flow",
|
|
71
|
+
"FlowCompiler",
|
|
72
|
+
"FlowDefinition",
|
|
73
|
+
"FlowParser",
|
|
74
|
+
"Input",
|
|
75
|
+
"InvalidDependencyError",
|
|
76
|
+
"InvalidFlowDefinitionError",
|
|
77
|
+
"InvalidInputError",
|
|
78
|
+
"InvalidStepError",
|
|
79
|
+
"InvalidStepNameError",
|
|
80
|
+
"MissingArgumentError",
|
|
81
|
+
"MissingInputError",
|
|
82
|
+
"MultipleValuesArgumentError",
|
|
83
|
+
"PositionalOnlyArgumentError",
|
|
84
|
+
"PyflowstepError",
|
|
85
|
+
"StepAlreadyRegisteredError",
|
|
86
|
+
"StepDict",
|
|
87
|
+
"StepDoesNotExistError",
|
|
88
|
+
"StepFactory",
|
|
89
|
+
"StepFn",
|
|
90
|
+
"StepsRegistry",
|
|
91
|
+
"TapFn",
|
|
92
|
+
"TooManyArgumentsError",
|
|
93
|
+
"UnexpectedKeywordArgumentError",
|
|
94
|
+
"UnsupportedFlowFormatError",
|
|
95
|
+
"compose",
|
|
96
|
+
"get_flow_json_schema",
|
|
97
|
+
"get_json_schema",
|
|
98
|
+
"get_step_json_schema",
|
|
99
|
+
"load_flow_definition",
|
|
100
|
+
"override_dependencies",
|
|
101
|
+
"register_flow_format",
|
|
102
|
+
"step",
|
|
103
|
+
"tap",
|
|
104
|
+
"validate_step_dict",
|
|
105
|
+
]
|