pytestflow 0.2.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.
Files changed (63) hide show
  1. bootstrap_templates/__init__.py +0 -0
  2. bootstrap_templates/config.yaml +13 -0
  3. bootstrap_templates/custom_step_types/__init__.py +0 -0
  4. bootstrap_templates/custom_step_types/custom_step_template.py +7 -0
  5. bootstrap_templates/process_models/__init__.py +0 -0
  6. bootstrap_templates/process_models/reporting/README.md +48 -0
  7. bootstrap_templates/process_models/reporting/__init__.py +0 -0
  8. bootstrap_templates/process_models/reporting/default_report.html.j2 +122 -0
  9. bootstrap_templates/process_models/reporting/html_report.py +331 -0
  10. bootstrap_templates/process_models/sequential_model.py +141 -0
  11. bootstrap_templates/test_sequences/__init__.py +0 -0
  12. bootstrap_templates/test_sequences/basic_sequence.py +62 -0
  13. bootstrap_templates/test_sequences/message_box_and_flow_control.py +169 -0
  14. bootstrap_templates/test_sequences/motherboard_test_sequence.py +125 -0
  15. bootstrap_templates/test_sequences/step_types_quickstart.py +168 -0
  16. pytestflow/README.md +13 -0
  17. pytestflow/__init__.py +19 -0
  18. pytestflow/backend/__init__.py +2 -0
  19. pytestflow/backend/event_bus.py +27 -0
  20. pytestflow/backend/frontend/assets/full_logo-D1DRTUt8.svg +21 -0
  21. pytestflow/backend/frontend/assets/index-480TOyh4.js +2 -0
  22. pytestflow/backend/frontend/assets/index-qEI3VAQU.css +1 -0
  23. pytestflow/backend/frontend/index.html +14 -0
  24. pytestflow/backend/frontend/logo.svg +21 -0
  25. pytestflow/backend/handlers.py +214 -0
  26. pytestflow/backend/report_manager.py +15 -0
  27. pytestflow/backend/sequences_info.py +130 -0
  28. pytestflow/backend/start_backend.py +118 -0
  29. pytestflow/backend/uuids_handler.py +67 -0
  30. pytestflow/backend/websocket_gateway.py +91 -0
  31. pytestflow/cli.py +183 -0
  32. pytestflow/config/__init__.py +0 -0
  33. pytestflow/config/config_manager.py +44 -0
  34. pytestflow/core/README.md +110 -0
  35. pytestflow/core/__init__.py +15 -0
  36. pytestflow/core/context.py +41 -0
  37. pytestflow/core/core.py +112 -0
  38. pytestflow/core/pytestflow_states.py +88 -0
  39. pytestflow/core/runtime_control.py +164 -0
  40. pytestflow/core/seq_file_runner.py +38 -0
  41. pytestflow/core/sequence.py +404 -0
  42. pytestflow/core/utils.py +81 -0
  43. pytestflow/flow_utils/README.md +6 -0
  44. pytestflow/flow_utils/__init__.py +0 -0
  45. pytestflow/flow_utils/conditions.py +0 -0
  46. pytestflow/flow_utils/transitions.py +0 -0
  47. pytestflow/starter_here.md +43 -0
  48. pytestflow/steps/README.md +43 -0
  49. pytestflow/steps/__init__.py +15 -0
  50. pytestflow/steps/action_step.py +94 -0
  51. pytestflow/steps/common.py +51 -0
  52. pytestflow/steps/df_numeric_limits.py +151 -0
  53. pytestflow/steps/flow_control.py +86 -0
  54. pytestflow/steps/message_pop_up.py +76 -0
  55. pytestflow/steps/numeric_limit.py +109 -0
  56. pytestflow/steps/pass_fail.py +49 -0
  57. pytestflow/steps/string_check.py +104 -0
  58. pytestflow/steps/waveform_limit.py +170 -0
  59. pytestflow-0.2.0.dist-info/METADATA +73 -0
  60. pytestflow-0.2.0.dist-info/RECORD +63 -0
  61. pytestflow-0.2.0.dist-info/WHEEL +5 -0
  62. pytestflow-0.2.0.dist-info/entry_points.txt +2 -0
  63. pytestflow-0.2.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,104 @@
1
+ from prefect.artifacts import create_markdown_artifact
2
+ from pytestflow.core.core import StepWrapper
3
+ from pytestflow.core.pytestflow_states import PyTestflowPassed, PyTestflowFailed
4
+ from pytestflow.core.utils import get_data_for_gui
5
+ from pytestflow.steps.common import get_metadata_from_prefect_context
6
+ from typing import Callable
7
+ from pytestflow.steps.common import get_runtime_value
8
+ import re
9
+
10
+
11
+ class StringCheckStep(StepWrapper):
12
+ def __init__(self, fn, *, expected, match="exact", case_sensitive=True,
13
+ name=None, autowire=True, **task_kwargs):
14
+ super().__init__(fn, name=name, autowire=autowire, **task_kwargs)
15
+ self.expected = expected
16
+ self.match = match
17
+ self.case_sensitive = case_sensitive
18
+ self.step_type = "string_check"
19
+
20
+ def _run(self, *args, **kwargs):
21
+ actual = super()._run(*args, **kwargs)
22
+
23
+ expected_value = get_runtime_value(self.expected)
24
+ match_mode = get_runtime_value(self.match)
25
+ is_case_sensitive = get_runtime_value(self.case_sensitive)
26
+
27
+ if not isinstance(actual, str):
28
+ raise TypeError(f"Expected string output, got {type(actual)}")
29
+
30
+ compare_actual = actual if is_case_sensitive else actual.lower()
31
+ compare_expected = expected_value if is_case_sensitive else expected_value.lower()
32
+
33
+ if match_mode == "exact":
34
+ passed = compare_actual == compare_expected
35
+ elif match_mode == "contains":
36
+ passed = compare_expected in compare_actual
37
+ else:
38
+ raise ValueError(f"Unsupported match mode: {match_mode}")
39
+
40
+ result_data = {
41
+ "step_status": "passed" if passed else "failed",
42
+ "output": actual,
43
+ "expected": expected_value,
44
+ "match": match_mode,
45
+ "case_sensitive": is_case_sensitive,
46
+ "step_type": self.step_type,
47
+ }
48
+
49
+ result_data.update(self.get_meta_info())
50
+ result_data.update(get_metadata_from_prefect_context())
51
+
52
+ # Send End data to GUI
53
+ get_data_for_gui(self, result_data.get("end_time"), result_data)
54
+
55
+ # Prefect UI artifact (best-effort)
56
+ try:
57
+ safe_step_name = re.sub(r"[^a-z0-9\-]", "-", self.name.lower())
58
+
59
+ artifact_md = (
60
+ f"### Step: `{self.name}`\n"
61
+ f"- **Actual:** `{actual}`\n"
62
+ f"- **Expected:** `{expected_value}`\n"
63
+ f"- **Match mode:** `{match_mode}`\n"
64
+ f"- **Case sensitive:** `{is_case_sensitive}`\n"
65
+ f"- **Status:** {'✅ PASSED' if passed else '❌ FAILED'}"
66
+ )
67
+
68
+ create_markdown_artifact(
69
+ key=f"result-{safe_step_name}",
70
+ markdown=artifact_md,
71
+ description=f"String check for `{self.name}`",
72
+ )
73
+
74
+ except Exception:
75
+ # Runtime might not support artifacts or not be in task context
76
+ pass
77
+
78
+ return (
79
+ PyTestflowPassed(ptf_result=result_data)
80
+ if passed
81
+ else PyTestflowFailed(
82
+ ptf_result=result_data,
83
+ message=f"{self.name} failed"
84
+ )
85
+ )
86
+
87
+
88
+ def string_check_step(*, expected, match="exact", case_sensitive=True,
89
+ name=None, autowire=True, **task_kwargs):
90
+ """
91
+ Decorator factory for string check steps.
92
+ """
93
+ def decorator(fn: Callable):
94
+ # IMPORTANT: already task-wrapped by StepWrapper
95
+ return StringCheckStep(
96
+ fn,
97
+ expected=expected,
98
+ match=match,
99
+ case_sensitive=case_sensitive,
100
+ name=name,
101
+ autowire=autowire,
102
+ **task_kwargs,
103
+ )
104
+ return decorator
@@ -0,0 +1,170 @@
1
+ from prefect.artifacts import create_markdown_artifact
2
+ from pytestflow.core.core import StepWrapper
3
+ from pytestflow.core.pytestflow_states import PyTestflowPassed, PyTestflowFailed
4
+ from pytestflow.core.utils import get_data_for_gui
5
+ from pytestflow.steps.common import get_metadata_from_prefect_context, get_runtime_value
6
+ from typing import Callable
7
+ import numpy as np
8
+ import re
9
+
10
+
11
+ class WaveformLimitStep(StepWrapper):
12
+ def __init__(
13
+ self,
14
+ fn,
15
+ *,
16
+ lower_mask=None,
17
+ upper_mask=None,
18
+ mode="between",
19
+ name=None,
20
+ autowire=True,
21
+ **task_kwargs
22
+ ):
23
+ super().__init__(fn, name=name, autowire=autowire, **task_kwargs)
24
+ self.lower_mask = lower_mask
25
+ self.upper_mask = upper_mask
26
+ self.mode = mode
27
+ self.step_type = "waveform_limit"
28
+
29
+ def _run(self, *args, **kwargs):
30
+ wvf = super()._run(*args, **kwargs)
31
+
32
+ lower_mask = get_runtime_value(self.lower_mask)
33
+ upper_mask = get_runtime_value(self.upper_mask)
34
+ mode = get_runtime_value(self.mode)
35
+
36
+ x, y = np.array(wvf["x"]), np.array(wvf["y"])
37
+
38
+ result_data = {
39
+ "step_status": "done",
40
+ "mode": mode,
41
+ "output": {"x": x.tolist(), "y": y.tolist()},
42
+ "violations": [],
43
+ "step_type": self.step_type,
44
+ }
45
+
46
+ if mode == "ge":
47
+ assert lower_mask is not None, "GE mode requires lower_mask"
48
+
49
+ lx, ly = zip(*lower_mask)
50
+ lower = np.interp(x, lx, ly)
51
+
52
+ if np.any(y < lower):
53
+ result_data["violations"].append("below_lower_mask")
54
+ result_data["step_status"] = "failed"
55
+ else:
56
+ result_data["step_status"] = "passed"
57
+
58
+ elif mode == "le":
59
+ assert upper_mask is not None, "LE mode requires upper_mask"
60
+
61
+ ux, uy = zip(*upper_mask)
62
+ upper = np.interp(x, ux, uy)
63
+
64
+ if np.any(y > upper):
65
+ result_data["violations"].append("above_upper_mask")
66
+ result_data["step_status"] = "failed"
67
+ else:
68
+ result_data["step_status"] = "passed"
69
+
70
+ elif mode == "between":
71
+ assert lower_mask and upper_mask, "BETWEEN requires both masks"
72
+
73
+ lx, ly = zip(*lower_mask)
74
+ ux, uy = zip(*upper_mask)
75
+
76
+ lower = np.interp(x, lx, ly)
77
+ upper = np.interp(x, ux, uy)
78
+
79
+ below = y < lower
80
+ above = y > upper
81
+
82
+ if np.any(below):
83
+ result_data["violations"].append("below_lower_mask")
84
+
85
+ if np.any(above):
86
+ result_data["violations"].append("above_upper_mask")
87
+
88
+ result_data["step_status"] = (
89
+ "passed" if not (np.any(below) or np.any(above)) else "failed"
90
+ )
91
+
92
+ elif mode == "outside":
93
+ assert lower_mask and upper_mask, "OUTSIDE requires both masks"
94
+
95
+ lx, ly = zip(*lower_mask)
96
+ ux, uy = zip(*upper_mask)
97
+
98
+ lower = np.interp(x, lx, ly)
99
+ upper = np.interp(x, ux, uy)
100
+
101
+ outside = (y < lower) | (y > upper)
102
+
103
+ if np.any(~outside):
104
+ result_data["violations"].append("inside_forbidden_zone")
105
+ result_data["step_status"] = "failed"
106
+ else:
107
+ result_data["step_status"] = "passed"
108
+
109
+ else:
110
+ raise ValueError(f"Unsupported mode: {mode}")
111
+
112
+ result_data.update(self.get_meta_info())
113
+ result_data.update(get_metadata_from_prefect_context())
114
+
115
+ get_data_for_gui(self, result_data.get("end_time"), result_data)
116
+
117
+ try:
118
+ safe_step_name = re.sub(r"[^a-z0-9\-]", "-", self.name.lower())
119
+
120
+ artifact_lines = [
121
+ f"### Step: `{self.name}`",
122
+ f"- **Mode:** `{mode}`",
123
+ f"- **Status:** {'✅ PASSED' if result_data['step_status'] == 'passed' else '❌ FAILED'}",
124
+ "",
125
+ ]
126
+
127
+ if result_data["step_status"] != "passed":
128
+ v_list = result_data["violations"]
129
+ artifact_lines.append(
130
+ f"- **Violations:** {', '.join(v_list[:10]) or 'none'}"
131
+ )
132
+
133
+ if len(v_list) > 10:
134
+ artifact_lines.append(
135
+ f"- ... and {len(v_list) - 10} more. See full logs for details."
136
+ )
137
+
138
+ create_markdown_artifact(
139
+ key=f"result-{safe_step_name}",
140
+ markdown="\n".join(artifact_lines),
141
+ description=f"Waveform limit check for `{self.name}`",
142
+ )
143
+
144
+ except Exception:
145
+ pass
146
+
147
+ return (
148
+ PyTestflowPassed(ptf_result=result_data)
149
+ if result_data["step_status"] == "passed"
150
+ else PyTestflowFailed(ptf_result=result_data, message=f"{self.name} failed")
151
+ )
152
+
153
+
154
+ def waveform_limit_step(*, lower_mask=None, upper_mask=None, mode="between",
155
+ name=None, autowire=True, **task_kwargs):
156
+ """
157
+ Decorator factory for waveform limit steps.
158
+ """
159
+ def decorator(fn: Callable):
160
+ # IMPORTANT: already task-wrapped by StepWrapper
161
+ return WaveformLimitStep(
162
+ fn,
163
+ lower_mask=lower_mask,
164
+ upper_mask=upper_mask,
165
+ mode=mode,
166
+ name=name,
167
+ autowire=autowire,
168
+ **task_kwargs,
169
+ )
170
+ return decorator
@@ -0,0 +1,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytestflow
3
+ Version: 0.2.0
4
+ Summary: Test automation framework inspired by NI TestStand, built on Prefect
5
+ Author-email: Alberto Manzoni <alb.manzoni@gmail.com>
6
+ Project-URL: Homepage, https://github.com/Alberto-Manzoni/PyTestFlow
7
+ Project-URL: Repository, https://github.com/Alberto-Manzoni/PyTestFlow
8
+ Project-URL: Issues, https://github.com/Alberto-Manzoni/PyTestFlow/issues
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Testing
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: prefect==3.6.24
21
+ Requires-Dist: pandas
22
+ Requires-Dist: numpy
23
+ Requires-Dist: rich
24
+ Requires-Dist: bottle
25
+ Requires-Dist: websockets
26
+ Requires-Dist: fakeredis==2.34.1
27
+ Requires-Dist: jinja2
28
+
29
+ # PyTestFlow
30
+
31
+ PyTestFlow is a Python test executive built on top of Prefect.
32
+ It turns decorated Python functions into traceable test steps and runs them in
33
+ ordered flows.
34
+
35
+ > Requires `prefect>=3.4` and Python 3.10+.
36
+
37
+ ## Repositories
38
+
39
+ - Engine: https://github.com/Alberto-Manzoni/PyTestFlow
40
+ - Frontend: https://github.com/Alberto-Manzoni/PyTestFlow-FrontEnd
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ python -m pip install pytestflow
46
+ ```
47
+
48
+ ### Init the workspace
49
+
50
+ ```bash
51
+ pytestflow init
52
+ ```
53
+
54
+ Set the environment variable as suggested from the CLI
55
+
56
+
57
+ ## Usage
58
+
59
+ ```bash
60
+ pytestflow start
61
+ ```
62
+ Open the web gui at the url indicated by the CLI.
63
+
64
+
65
+
66
+ ## Core concepts
67
+
68
+ - Steps are Prefect tasks defined using `@step` or specialized step decorators.
69
+ - A `TestSequence` is a Prefect flow that aggregates and executes multiple steps, tracking their states.
70
+ - `ptf_context` is a shared runtime context that provides access to `globals`, `locals`, `results`, and `current_step`.
71
+ - `SequentialProcessModel` orchestrates execution callbacks in a fixed lifecycle:
72
+ `pre_uut -> main_sequence -> post_uut -> report -> database_logging`.
73
+ - The main output of the process model is stored in both `main_results` and `main_result` for backward compatibility.
@@ -0,0 +1,63 @@
1
+ bootstrap_templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ bootstrap_templates/config.yaml,sha256=6FYQoyTfg_XCK-HDRpO6-A9lZjWEKlvLtv580Yt32fU,219
3
+ bootstrap_templates/custom_step_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ bootstrap_templates/custom_step_types/custom_step_template.py,sha256=fMcZdMhdOZ142AfBZx6cBM_3ODZRD3J-PmdH6DJlMMk,167
5
+ bootstrap_templates/process_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ bootstrap_templates/process_models/sequential_model.py,sha256=Y267Rj8tI3OEu_UFQJzl8BPjLw23oDIz3f_tFdBvKxI,5182
7
+ bootstrap_templates/process_models/reporting/README.md,sha256=MNyr1oQTmBHjHIDc3BtBHTrnUY6SS08aTVZfKJZDr1Y,1645
8
+ bootstrap_templates/process_models/reporting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ bootstrap_templates/process_models/reporting/default_report.html.j2,sha256=xleBHhZqB3CuM-zzE6DrxvSpW26frTrVVhGFQNgesHU,3674
10
+ bootstrap_templates/process_models/reporting/html_report.py,sha256=jeYnaJ7eRgJkVuuVAXGf2g_6g3_xn6KhL_BFV6sQ3yE,11314
11
+ bootstrap_templates/test_sequences/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ bootstrap_templates/test_sequences/basic_sequence.py,sha256=saWovmg58tWmLNIZjb6iqSRcfuhlDMeqSGUVOy8HW2g,2421
13
+ bootstrap_templates/test_sequences/message_box_and_flow_control.py,sha256=8kh0gE1rKXwzKPU6xBwFVkKUvU9ZiJoD7wLYI3-nGsM,5530
14
+ bootstrap_templates/test_sequences/motherboard_test_sequence.py,sha256=tNnCGbovb5DJfAHGbgNE4Umfh5rCwLwaNOHUlUwlxhs,4029
15
+ bootstrap_templates/test_sequences/step_types_quickstart.py,sha256=MfQ3iFkIC5WATO3i4x8BzCiicIWRnkrQhZFwbLI7UYU,6104
16
+ pytestflow/README.md,sha256=FMFBw82GgmzlUZI9l5a1jL2ouMgDDtx3kCFIX5bUNjw,619
17
+ pytestflow/__init__.py,sha256=SZoMtFvPEt84XVvd2BqNs0zDUd8I61jI5ZRgdhMzB4o,332
18
+ pytestflow/cli.py,sha256=OXhClJZQdX96udhVyVa686dAawZ6K29r1kpgIhKN5nk,5827
19
+ pytestflow/starter_here.md,sha256=toCXJwJvhZNfkLm9EdTSy4LoqJrTAt6ai6HHQMBFZzM,903
20
+ pytestflow/backend/__init__.py,sha256=pgOTXFCXcVcfwJjC0wqqVPs7jCjd7Q_hkuoL45Dzcdk,41
21
+ pytestflow/backend/event_bus.py,sha256=5etZwVeD2dTU6P2FIFuINzFht5qFTWCUI6LPgfwVKtA,719
22
+ pytestflow/backend/handlers.py,sha256=AZVRYX_bkW8CskbqaMZwkcTeMSAktcP3wIifSb8eLb0,7331
23
+ pytestflow/backend/report_manager.py,sha256=csAN-igKV80eQItoWa7uGZywEcCk8fuHXdTPynhq9xs,419
24
+ pytestflow/backend/sequences_info.py,sha256=dzCAYZkiWsAtZ5LDH1W70hEuaqidMWrsPnDaEk-EYq0,4911
25
+ pytestflow/backend/start_backend.py,sha256=9lt0pAQaMIMT2Eo7C34pgt6Jgj06RBA3jYg9fwWd8-s,3049
26
+ pytestflow/backend/uuids_handler.py,sha256=Vu4zcMgY1EXoAX7ea9-dIAz7XuQKtfbYi0ro76KnpCk,2337
27
+ pytestflow/backend/websocket_gateway.py,sha256=-9HIVIzmWDMwe0LHec7yCuPiVsOAlksbyJfgoZQysc4,2987
28
+ pytestflow/backend/frontend/index.html,sha256=I50KarjefeL7Y_NlT_NgTtebIN33TdQo12CBN9Rxz4E,473
29
+ pytestflow/backend/frontend/logo.svg,sha256=u_FtoFuvpXLoemlbRHm8MUaAnbkW3Km4NRDgW6x9eb8,16087
30
+ pytestflow/backend/frontend/assets/full_logo-D1DRTUt8.svg,sha256=u_FtoFuvpXLoemlbRHm8MUaAnbkW3Km4NRDgW6x9eb8,16087
31
+ pytestflow/backend/frontend/assets/index-480TOyh4.js,sha256=Y-jITV27EZzJtDyjcCasOekH_QxNPwg1PMpy8eqsOQU,88162
32
+ pytestflow/backend/frontend/assets/index-qEI3VAQU.css,sha256=QBsF7BUtM9q2dMujOODuJepTEc-guEBD0u6fpeyIEiU,7606
33
+ pytestflow/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ pytestflow/config/config_manager.py,sha256=Sf6SSC8HdkpCewCo7VX1k0tz6WK5Prq-RW9S9sGRsDY,1182
35
+ pytestflow/core/README.md,sha256=tBWqJ2jVksgEMDPesmhhyFkz6WEpTlIM4c9lEppFwYE,3707
36
+ pytestflow/core/__init__.py,sha256=zcsgFcLygAK1FNoDh41ptMmNxohAhIcyGdXoD4aX-Ww,387
37
+ pytestflow/core/context.py,sha256=0rct317NSPS_T6P-yO4QiOcJtq3Gxm4V9vYFkMIaiwk,1778
38
+ pytestflow/core/core.py,sha256=8h5GOagOcDTsnAfaTIX5Ojtij_aCCibmtdv7aDSAF58,3657
39
+ pytestflow/core/pytestflow_states.py,sha256=yAHoQbp3D0Wnn9gtioqwXUSZ6DB31E8AymnHP4aXsfU,2785
40
+ pytestflow/core/runtime_control.py,sha256=35PPx7QUT_qKu71fBuGCJi2yTMLgf3WC1MfBJPgutz4,5934
41
+ pytestflow/core/seq_file_runner.py,sha256=F1xcWEcJiRYgFyjMCIbdYQz8KF_RzikJoOWaKr0a9nI,1217
42
+ pytestflow/core/sequence.py,sha256=yjkRm3s0f8mxWqAXUiRAICHdP4wjjXEKdq7vRvHTJFU,15782
43
+ pytestflow/core/utils.py,sha256=5eBg4Tvt06Te2lhzNQP7lISv4oE70pE_A5QqrOQGBNA,3074
44
+ pytestflow/flow_utils/README.md,sha256=ms40HYRQBT8M1PH9yy7NzVOzackmjF90iDPo_P89UtQ,319
45
+ pytestflow/flow_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ pytestflow/flow_utils/conditions.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ pytestflow/flow_utils/transitions.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
+ pytestflow/steps/README.md,sha256=yOXmaEUaKaqukPlHtP1xEV8mt2u4hWBYJE0M3TQRc1E,1899
49
+ pytestflow/steps/__init__.py,sha256=OWnFhQxUPtqzz-j8lFSHZ5Wgq_CZSZvj-E337aEpojA,459
50
+ pytestflow/steps/action_step.py,sha256=MInrWUr7c6f_OUZcvJMcGoNQRLfuqQXiQONI3vqA89Y,3486
51
+ pytestflow/steps/common.py,sha256=WCW45lgRBCur1A7qr5t9sGMmFmYUOu_XtN0Momr7y10,1710
52
+ pytestflow/steps/df_numeric_limits.py,sha256=0GTg_ORkDTSjP7w8ETc-ENwr7Gy9fSMfcM-fkRR9eyc,5168
53
+ pytestflow/steps/flow_control.py,sha256=2OZj1d-KFP8ax61bpufVj2J9-QDTHxc9aOIM8uSh9iE,3180
54
+ pytestflow/steps/message_pop_up.py,sha256=NrwKg7OekHSFOCI9T9TRP8SJQrKWuCIg81QIM2NTKC4,2892
55
+ pytestflow/steps/numeric_limit.py,sha256=azVEBvy_1csb58DyCW_y74YxuQMjBq4MiwVfYn01zIc,3632
56
+ pytestflow/steps/pass_fail.py,sha256=4ZVXuBsG6-Eyy2VAwWR1QVNwg_fZigiuXdJ9q_ru9PE,1659
57
+ pytestflow/steps/string_check.py,sha256=HQzhGwFycNBNqHXweMDWgHIx6Mml6vPQMcJayLFdRLo,3838
58
+ pytestflow/steps/waveform_limit.py,sha256=cfZkxeu1f7ZzwhjuAZn0tAzjeD1n3ccjvvL5KOl-qIs,5650
59
+ pytestflow-0.2.0.dist-info/METADATA,sha256=8XDQeD-nxsTINpo9R2f213rpCk7G2WMq7f0KPxBTXaQ,2381
60
+ pytestflow-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
61
+ pytestflow-0.2.0.dist-info/entry_points.txt,sha256=zAevseDFTil6KPYtATMcV57s_HiBJ1UbwHcifCY4egc,51
62
+ pytestflow-0.2.0.dist-info/top_level.txt,sha256=fsTDlvcqnrA-YeF53nHbc4SdgT0zykleKm_LuBwgMfA,31
63
+ pytestflow-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pytestflow = pytestflow.cli:main
@@ -0,0 +1,2 @@
1
+ bootstrap_templates
2
+ pytestflow