behave-steplib 1.0.1.dev0__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 (44) hide show
  1. behave_steplib-1.0.1.dev0.dist-info/METADATA +303 -0
  2. behave_steplib-1.0.1.dev0.dist-info/RECORD +44 -0
  3. behave_steplib-1.0.1.dev0.dist-info/WHEEL +4 -0
  4. behave_steplib-1.0.1.dev0.dist-info/entry_points.txt +8 -0
  5. behave_steplib-1.0.1.dev0.dist-info/licenses/LICENSE +21 -0
  6. steplib/__init__.py +18 -0
  7. steplib/_version.py +24 -0
  8. steplib/behave.py +58 -0
  9. steplib/cli/__init__.py +1 -0
  10. steplib/cli/formatters.py +166 -0
  11. steplib/cli/main.py +136 -0
  12. steplib/core/__init__.py +29 -0
  13. steplib/core/decorators.py +92 -0
  14. steplib/core/discovery.py +111 -0
  15. steplib/core/ecosystem.py +98 -0
  16. steplib/core/exceptions.py +42 -0
  17. steplib/core/i18n.py +71 -0
  18. steplib/core/metadata.py +47 -0
  19. steplib/core/params.py +121 -0
  20. steplib/core/registry.py +162 -0
  21. steplib/core/state.py +68 -0
  22. steplib/core/validation.py +97 -0
  23. steplib/modules/__init__.py +1 -0
  24. steplib/modules/api/__init__.py +7 -0
  25. steplib/modules/api/actions.py +152 -0
  26. steplib/modules/api/client.py +183 -0
  27. steplib/modules/api/context.py +43 -0
  28. steplib/modules/api/steps.py +261 -0
  29. steplib/modules/api/transforms.py +161 -0
  30. steplib/modules/db/__init__.py +7 -0
  31. steplib/modules/db/actions.py +68 -0
  32. steplib/modules/db/client.py +51 -0
  33. steplib/modules/db/context.py +33 -0
  34. steplib/modules/db/steps.py +120 -0
  35. steplib/modules/kafka/__init__.py +7 -0
  36. steplib/modules/kafka/actions.py +113 -0
  37. steplib/modules/kafka/context.py +33 -0
  38. steplib/modules/kafka/steps.py +132 -0
  39. steplib/modules/web/__init__.py +7 -0
  40. steplib/modules/web/actions.py +66 -0
  41. steplib/modules/web/client.py +102 -0
  42. steplib/modules/web/context.py +30 -0
  43. steplib/modules/web/steps.py +138 -0
  44. steplib/py.typed +0 -0
@@ -0,0 +1,303 @@
1
+ Metadata-Version: 2.4
2
+ Name: behave-steplib
3
+ Version: 1.0.1.dev0
4
+ Summary: Librería de steps reusable para behave con soporte multitecnología e i18n.
5
+ Project-URL: Homepage, https://github.com/MathiasPaulenko/behave-steplib
6
+ Project-URL: Documentation, https://mathiaspaulenko.github.io/behave-steplib
7
+ Project-URL: Repository, https://github.com/MathiasPaulenko/behave-steplib
8
+ Project-URL: Issues, https://github.com/MathiasPaulenko/behave-steplib/issues
9
+ Project-URL: Changelog, https://github.com/MathiasPaulenko/behave-steplib/blob/main/CHANGELOG.md
10
+ Author: Mathias Paulenko
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: api,bdd,behave,db,i18n,kafka,steps,web
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Testing
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: behave>=1.3.0
26
+ Requires-Dist: parse>=1.19
27
+ Requires-Dist: typer>=0.12
28
+ Provides-Extra: all
29
+ Requires-Dist: behave-data>=1.0.2; extra == 'all'
30
+ Requires-Dist: behave-kit>=1.3.1; extra == 'all'
31
+ Requires-Dist: behave-tables>=1.3.1; extra == 'all'
32
+ Requires-Dist: httpx>=0.27; extra == 'all'
33
+ Requires-Dist: kafka-python-ng>=2.0; extra == 'all'
34
+ Requires-Dist: selenium>=4.0; extra == 'all'
35
+ Requires-Dist: sqlalchemy>=2.0; extra == 'all'
36
+ Provides-Extra: api
37
+ Requires-Dist: httpx>=0.27; extra == 'api'
38
+ Provides-Extra: data
39
+ Requires-Dist: behave-data>=1.0.2; extra == 'data'
40
+ Provides-Extra: db
41
+ Requires-Dist: sqlalchemy>=2.0; extra == 'db'
42
+ Provides-Extra: dev
43
+ Requires-Dist: build>=1.2; extra == 'dev'
44
+ Requires-Dist: mypy>=1.10; extra == 'dev'
45
+ Requires-Dist: pre-commit>=3.7; extra == 'dev'
46
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
47
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
48
+ Requires-Dist: pytest>=8.0; extra == 'dev'
49
+ Requires-Dist: responses>=0.25; extra == 'dev'
50
+ Requires-Dist: ruff>=0.6; extra == 'dev'
51
+ Requires-Dist: twine>=5.0; extra == 'dev'
52
+ Provides-Extra: docs
53
+ Requires-Dist: furo>=2024.0; extra == 'docs'
54
+ Requires-Dist: myst-parser>=3.0; extra == 'docs'
55
+ Requires-Dist: sphinx-autodoc-typehints>=2.0; extra == 'docs'
56
+ Requires-Dist: sphinx>=7.0; extra == 'docs'
57
+ Provides-Extra: kafka
58
+ Requires-Dist: kafka-python-ng>=2.0; extra == 'kafka'
59
+ Provides-Extra: kit
60
+ Requires-Dist: behave-kit>=1.3.1; extra == 'kit'
61
+ Provides-Extra: tables
62
+ Requires-Dist: behave-tables>=1.3.1; extra == 'tables'
63
+ Provides-Extra: web
64
+ Requires-Dist: selenium>=4.0; extra == 'web'
65
+ Description-Content-Type: text/markdown
66
+
67
+ # behave-steplib
68
+
69
+ Reusable step libraries for [Behave](https://github.com/behave/behave) BDD — share, discover and install step definitions across projects. Zero mandatory dependencies; each technology is an optional extra.
70
+
71
+ [![CI](https://github.com/MathiasPaulenko/behave-steplib/actions/workflows/ci.yml/badge.svg)](https://github.com/MathiasPaulenko/behave-steplib/actions/workflows/ci.yml)
72
+ [![Release](https://github.com/MathiasPaulenko/behave-steplib/actions/workflows/release.yml/badge.svg)](https://github.com/MathiasPaulenko/behave-steplib/actions/workflows/release.yml)
73
+ [![PyPI](https://img.shields.io/pypi/v/behave-steplib.svg)](https://pypi.org/project/behave-steplib/)
74
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
75
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
76
+
77
+ ## Why behave-steplib?
78
+
79
+ Writing BDD step definitions for HTTP APIs, web browsers, databases and Kafka is repetitive. Every project re-implements the same "send a request", "check the status code", "query the database" steps. behave-steplib provides a curated, typed, multilingual library of reusable steps that you install once and share across projects.
80
+
81
+ - **Modular** — `api`, `web`, `db`, `kafka` modules activated via extras and lazy imports. Install only what you need.
82
+ - **Auto-registered** — `autoload(context)` discovers every installed step via Python entry points and registers it with behave in one line.
83
+ - **Multilingual** — steps defined in English with `es` and `pt` translations; all patterns are registered with behave so matching works regardless of the language used in feature files.
84
+ - **Typed** — full type hints, `mypy --strict` clean, `py.typed` marker included.
85
+ - **CLI** — `steplib list / show / validate / init` powered by Typer for inspecting and validating your step library from the terminal.
86
+ - **Pluggable** — third-party packages can register steps via the `steplib.plugins` entry point group; `autoload` discovers them automatically.
87
+ - **Ecosystem** — integrates with `behave-kit` (soft assertions), `behave-tables` (table conversion) and `behave-data` (test data loading) when installed.
88
+ - **Backends** — each module supports multiple backends (e.g. stdlib/httpx/requests for API, selenium for web) selectable at autoload time.
89
+
90
+ ## Installation
91
+
92
+ ```bash
93
+ pip install behave-steplib # core only (behave, parse, typer)
94
+ pip install behave-steplib[api] # + httpx HTTP client
95
+ pip install "behave-steplib[api,web,db,kafka]" # + all technology extras
96
+ pip install "behave-steplib[all]" # + every technology extra
97
+ pip install behave-steplib[dev] # + pytest, ruff, mypy, build, twine
98
+ ```
99
+
100
+ | Extra | Packages | Description |
101
+ |-------|----------|-------------|
102
+ | `[api]` | `httpx` | HTTP API testing with httpx |
103
+ | `[web]` | `selenium` | Browser testing with Selenium |
104
+ | `[db]` | `sqlalchemy` | Database testing with SQLAlchemy |
105
+ | `[kafka]` | `kafka-python-ng` | Kafka producer/consumer testing |
106
+ | `[kit]` | `behave-kit` | Soft assertions, typed context, fixtures |
107
+ | `[data]` | `behave-data` | Test data loading (CSV, JSON, YAML, Excel) |
108
+ | `[tables]` | `behave-tables` | Table conversion helpers |
109
+ | `[dev]` | pytest, ruff, mypy, build, twine | Development tools |
110
+ | `[docs]` | sphinx, furo, myst-parser | Documentation tools |
111
+ | `[all]` | api, web, db, kafka, kit, data, tables | Everything except dev/docs |
112
+
113
+ ## Quickstart
114
+
115
+ ### Level 1 — Automatic wiring
116
+
117
+ Add three hooks to your `environment.py` and every installed step is wired automatically:
118
+
119
+ ```python
120
+ # features/environment.py
121
+ from steplib.behave import autoload
122
+
123
+ def before_all(context):
124
+ context.steplib = autoload(context)
125
+
126
+ def before_scenario(context, scenario):
127
+ context.steplib.reset()
128
+
129
+ def after_scenario(context, scenario):
130
+ context.steplib.cleanup()
131
+ ```
132
+
133
+ Or generate it with the CLI:
134
+
135
+ ```bash
136
+ steplib init
137
+ ```
138
+
139
+ ### Level 2 — Explicit load
140
+
141
+ Load only the modules you need by dotted path:
142
+
143
+ ```python
144
+ from steplib.behave import load
145
+
146
+ def before_all(context):
147
+ context.steplib = load(context, "steplib.modules.api.steps")
148
+ ```
149
+
150
+ ### Level 3 — Filtered autoload
151
+
152
+ When multiple extras are installed, narrow which steps are active:
153
+
154
+ ```python
155
+ from steplib.behave import autoload
156
+
157
+ def before_all(context):
158
+ context.steplib = autoload(
159
+ context,
160
+ categories=["api"],
161
+ backends={"api": "httpx"},
162
+ )
163
+ ```
164
+
165
+ ### Example feature
166
+
167
+ ```gherkin
168
+ Feature: API health check
169
+
170
+ Scenario: GET users returns 200
171
+ Given the API base url is "https://api.example.com"
172
+ When I send a GET request to "/users"
173
+ Then the response status is 200
174
+ And the response body is valid JSON
175
+ And the JSON path "$.users[0].name" equals "Ada"
176
+ ```
177
+
178
+ ### Multilingual features
179
+
180
+ Steps are defined in English and translated to Spanish and Portuguese. All patterns are registered with behave — no language switch needed:
181
+
182
+ ```gherkin
183
+ # es
184
+ Cuando envío una petición GET a "/users"
185
+ Entonces el estado de la respuesta es 200
186
+
187
+ # pt
188
+ Quando envio uma requisição GET para "/users"
189
+ Então o status da resposta é 200
190
+ ```
191
+
192
+ ## Modules
193
+
194
+ ### API
195
+
196
+ HTTP API testing with stdlib (urllib), httpx or requests backends.
197
+
198
+ ```gherkin
199
+ Given the API base url is "https://api.example.com"
200
+ When I send a GET request to "/users"
201
+ Then the response status is 200
202
+ And the JSON path "$.users[0].name" equals "Ada"
203
+ And the response header "Content-Type" is "application/json"
204
+ ```
205
+
206
+ ### Web
207
+
208
+ Browser testing with Selenium (Chrome, Firefox, headless).
209
+
210
+ ```gherkin
211
+ Given the web base url is "https://example.com"
212
+ When I navigate to "/login"
213
+ Then the page title is "Login"
214
+ And the element id "username" is present
215
+ And the page contains "Sign In"
216
+ ```
217
+
218
+ ### DB
219
+
220
+ Database testing with SQLAlchemy (SQLite, PostgreSQL, MySQL, ...).
221
+
222
+ ```gherkin
223
+ Given the database connection string is "sqlite:///test.db"
224
+ When I execute the SQL query "SELECT * FROM users"
225
+ Then the query returns 3 rows
226
+ And the column "name" in the first row equals "Ada"
227
+ ```
228
+
229
+ ### Kafka
230
+
231
+ Kafka producer and consumer testing with kafka-python-ng.
232
+
233
+ ```gherkin
234
+ Given the Kafka bootstrap servers are "localhost:9092"
235
+ When I produce a message to topic "events" with key "id" and value "hello"
236
+ And I consume messages from topic "events"
237
+ Then the consumed messages count is 1
238
+ And a consumed message contains "hello"
239
+ ```
240
+
241
+ ## CLI
242
+
243
+ ```bash
244
+ steplib list # list all registered steps
245
+ steplib list --category api # filter by category
246
+ steplib list --backend httpx # filter by backend
247
+ steplib list --json # output as JSON
248
+ steplib show "I send a {method} request to {url}"
249
+ steplib validate # validate step contracts
250
+ steplib init # generate features/environment.py
251
+ ```
252
+
253
+ ## Writing custom steps
254
+
255
+ Use the `@step` decorator to define your own steps with full metadata:
256
+
257
+ ```python
258
+ from steplib import Param, step
259
+
260
+ @step(
261
+ "the invoice total is {total:f}",
262
+ category="invoice",
263
+ description="Assert the invoice total matches.",
264
+ parameters=[Param("total", type=float, required=True)],
265
+ example='Then the invoice total is 19.99',
266
+ i18n={
267
+ "es": "el total de la factura es {total:f}",
268
+ "pt": "o total da fatura é {total:f}",
269
+ },
270
+ tags=["invoice"],
271
+ version="1.0.0",
272
+ )
273
+ def step_invoice_total(context, total):
274
+ assert context.invoice.total == total
275
+ ```
276
+
277
+ Register steps in a `register(registry)` function and declare an entry point:
278
+
279
+ ```toml
280
+ # pyproject.toml
281
+ [project.entry-points."steplib.plugins"]
282
+ mycompany = "mycompany.steps:register"
283
+ ```
284
+
285
+ Once installed, `autoload(context)` discovers your package automatically.
286
+
287
+ ## Development
288
+
289
+ ```bash
290
+ make dev # install with api, dev and docs extras
291
+ make lint # ruff + mypy --strict
292
+ make test-cov # pytest with >=80% coverage gate
293
+ make docs-build # build Sphinx documentation
294
+ make build # build sdist + wheel
295
+ ```
296
+
297
+ ## Documentation
298
+
299
+ Full documentation is available at <https://mathiaspaulenko.github.io/behave-steplib>.
300
+
301
+ ## License
302
+
303
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,44 @@
1
+ steplib/__init__.py,sha256=uHZuzdMz369B9Us648sLdwqo7qCR4S7fz9YXsafTfZ8,398
2
+ steplib/_version.py,sha256=Y_mQou6Ma9bKGw0wGkSUgyT333rlwHalgPFv_DCW8mY,533
3
+ steplib/behave.py,sha256=hyheeo7XjHbjWEXdud3UCAaQcV5HBK4kEC-9lNVELt0,1682
4
+ steplib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ steplib/cli/__init__.py,sha256=x-E6fqvwCyB59vrcFkgK1jbdwmE11-a7Hc2zvGwK_eM,47
6
+ steplib/cli/formatters.py,sha256=PMFP0NmTf1NsryEhcE7Lg5iU2C81R7rxt7l-jHltzCQ,4903
7
+ steplib/cli/main.py,sha256=2D1cTApiJ8Vo2YHU-hdIu3Mjp6AQ_86maCB13NR9rZ4,3920
8
+ steplib/core/__init__.py,sha256=Ipdf6bddOFExw6FzX0ndHU5Q2vpRAMGLNtDDwQLUF5k,716
9
+ steplib/core/decorators.py,sha256=3mWIWgO9HSsIY4_eHi4AFPVRUnWoBw0SSWdWRmYx5wc,3152
10
+ steplib/core/discovery.py,sha256=4mAlk-_RptfOJwX4BkWlICd1x8gelb8kRKE26SSYsDc,3675
11
+ steplib/core/ecosystem.py,sha256=P3Fo3sqFdrlmoxz-fuXuspH-45CHoBGt4_izb5pza0Q,2838
12
+ steplib/core/exceptions.py,sha256=3RA4cPnaa4ogYFaDCUamBv7metqwP-C7g592JUpC6Cc,1469
13
+ steplib/core/i18n.py,sha256=Qy8t8IYf0jrLel4TU5-uSjr6pNd4F6tQBxJoUC_dDTY,2356
14
+ steplib/core/metadata.py,sha256=LAzpV1sDlglymScIDktz3ScaMNB5sAelBjJfO-edA9E,1443
15
+ steplib/core/params.py,sha256=JGp9Ucrmc2smHCBsS0UHn_o5HYqX1Smn72Lkz_ok5tM,3554
16
+ steplib/core/registry.py,sha256=DycbyUj8_4UzJmWQTtvodIMUlawNbv4Jcbbk4YwiBrQ,5789
17
+ steplib/core/state.py,sha256=TVpq00E6uNyMbU69mV_F-eXzPnhNVY3DbtxNsbvCb-g,2014
18
+ steplib/core/validation.py,sha256=UwRCdMp50J3y2YYu0Gfr5SBGvGnxlmZySMpYxeMvRRE,3338
19
+ steplib/modules/__init__.py,sha256=Sfv7_PZXOAJnJFrODZ5XmbaAY3gx_7J6y5ytZNqxWjg,59
20
+ steplib/modules/api/__init__.py,sha256=qR4iUPbYCUyA9hs5R8yt2HvCvlOlHBqIF3EyAxwCEoM,146
21
+ steplib/modules/api/actions.py,sha256=4cdnG1_weBBQyAZV5o9UQp5eczQvRbTDtnJdjf1b5tI,5130
22
+ steplib/modules/api/client.py,sha256=81C3oTW3svQMHJnkL18vHA-b5Kh1JJxzHwBrp5kcauI,5226
23
+ steplib/modules/api/context.py,sha256=oebor5xzT4K03OLqEQCW_kj-IE89fQ9M2pp5ep_u0v0,1384
24
+ steplib/modules/api/steps.py,sha256=ldfHLBmG2VmB17TfQrOQMRG6fdD5m9nV1G8IeQPBykU,8353
25
+ steplib/modules/api/transforms.py,sha256=7DDp2S5iJ43UJa2S_57QM0TPhUQXLLVlvQcu6CPVR7g,4840
26
+ steplib/modules/db/__init__.py,sha256=OaUBBRuzdETaB0qLY_tP1p_tWoUXidfqtSYJjci3nho,164
27
+ steplib/modules/db/actions.py,sha256=W3Ax4O6LFTEf1AjX5LIp22_C8M3OQq7KJgYUrNyl-sA,2093
28
+ steplib/modules/db/client.py,sha256=hYhfuWX4Aci9PXmgLWNhLCEZ_dFrITjBV_lXvyrWbAc,1841
29
+ steplib/modules/db/context.py,sha256=0t92VVPCfcczdZlWv07YGimXlxwRWMqeRtpnq1w72Ug,1007
30
+ steplib/modules/db/steps.py,sha256=Lg9w5A4Nd9aRlKH22XmI_CUe0KiK3_msjFdCATKWdnc,3843
31
+ steplib/modules/kafka/__init__.py,sha256=PLsmP7fIXSGPKfEknjJZwjD-PrXcwxRYY2Lqj5EhLSQ,169
32
+ steplib/modules/kafka/actions.py,sha256=LrFgpZrrCVnQ4l8Fjali4j4SpntVWwVNDTcT3H-A2FI,3427
33
+ steplib/modules/kafka/context.py,sha256=R7PgMFFRF_J-Jq_BqXRmT_2xXkn3g9hof6P_Tvr_t0I,1031
34
+ steplib/modules/kafka/steps.py,sha256=hbvUkFiS_QliIVJ65BBMFvBrehJRr9ijLT-7nssYcvA,4223
35
+ steplib/modules/web/__init__.py,sha256=mGDM_4D2Xa04gBwiLkCPrtBzdCcgFf9y9w6qgMuDS4Y,163
36
+ steplib/modules/web/actions.py,sha256=a7bv96_2AOOau_hvCKUek2ByO72yw9BYldCdhmCVedI,2505
37
+ steplib/modules/web/client.py,sha256=Pu8ADRhvob5izdvkitIW-MFZCyMHNbAOGUamjf9o0qA,3276
38
+ steplib/modules/web/context.py,sha256=ySz-wz1jHyOzycybfodCfibDh7Je0MEzfyD9UZ-bGDc,843
39
+ steplib/modules/web/steps.py,sha256=h5VYIl31yKMmhPhWD1W0nYFP1r5fdzDC7cup4eyqjAI,3872
40
+ behave_steplib-1.0.1.dev0.dist-info/METADATA,sha256=TK3WfqUtUkJO1mpSJ-roOiXOwbGFE5izdcWlHrpLrA0,10890
41
+ behave_steplib-1.0.1.dev0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
42
+ behave_steplib-1.0.1.dev0.dist-info/entry_points.txt,sha256=tYJ2rN6gq8_reyWHDPBg8MklVdJnPfGNRZdZAH9QROk,235
43
+ behave_steplib-1.0.1.dev0.dist-info/licenses/LICENSE,sha256=LLr8AP65kT83seFEcknE3-76LL7oikrP87U33tZKYro,1073
44
+ behave_steplib-1.0.1.dev0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.31.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,8 @@
1
+ [console_scripts]
2
+ steplib = steplib.cli.main:main
3
+
4
+ [steplib.plugins]
5
+ api = steplib.modules.api.steps:register
6
+ db = steplib.modules.db.steps:register
7
+ kafka = steplib.modules.kafka.steps:register
8
+ web = steplib.modules.web.steps:register
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mathias Paulenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
steplib/__init__.py ADDED
@@ -0,0 +1,18 @@
1
+ """behave-steplib — reusable step libraries for Behave BDD."""
2
+
3
+ from __future__ import annotations
4
+
5
+ try:
6
+ from steplib._version import __version__ as __version__
7
+ except ImportError: # pragma: no cover
8
+ __version__ = "0.0.0+unknown"
9
+
10
+ from steplib.core import Param, StepInfo, StepRegistry, step
11
+
12
+ __all__ = [
13
+ "Param",
14
+ "StepInfo",
15
+ "StepRegistry",
16
+ "__version__",
17
+ "step",
18
+ ]
steplib/_version.py ADDED
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '1.0.1.dev0'
22
+ __version_tuple__ = version_tuple = (1, 0, 1, 'dev0')
23
+
24
+ __commit_id__ = commit_id = None
steplib/behave.py ADDED
@@ -0,0 +1,58 @@
1
+ """Integration helpers for behave's ``environment.py``.
2
+
3
+ ``before_all`` and ``after_scenario`` can be imported directly as behave
4
+ hooks. ``before_scenario`` must be written by the user (it calls
5
+ ``context.steplib.reset()``).
6
+
7
+ Usage::
8
+
9
+ # features/environment.py
10
+ from steplib.behave import after_scenario, before_all
11
+
12
+ def before_scenario(context, scenario):
13
+ context.steplib.reset()
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Any
19
+
20
+ from steplib.core.discovery import autoload as _autoload
21
+ from steplib.core.discovery import load as _load
22
+ from steplib.core.state import SteplibState
23
+
24
+ __all__ = ["SteplibState", "after_scenario", "autoload", "before_all", "load"]
25
+
26
+
27
+ def autoload(
28
+ context: Any,
29
+ categories: list[str] | None = None,
30
+ backends: dict[str, str] | None = None,
31
+ ) -> SteplibState:
32
+ """Load all installed steplib plugins and attach state to *context*.
33
+
34
+ See :func:`steplib.core.discovery.autoload` for details.
35
+ """
36
+ return _autoload(context, categories=categories, backends=backends)
37
+
38
+
39
+ def load(context: Any, *modules: str) -> SteplibState:
40
+ """Load specific step modules by dotted path.
41
+
42
+ See :func:`steplib.core.discovery.load` for details.
43
+ """
44
+ return _load(context, *modules)
45
+
46
+
47
+ def before_all(context: Any) -> SteplibState:
48
+ """Run autoload and attach steplib state to *context*."""
49
+ state = autoload(context)
50
+ context.steplib = state
51
+ return state
52
+
53
+
54
+ def after_scenario(context: Any, scenario: Any) -> None:
55
+ """Clean up steplib resources after a scenario."""
56
+ state = getattr(context, "steplib", None)
57
+ if state is not None and hasattr(state, "cleanup"):
58
+ state.cleanup()
@@ -0,0 +1 @@
1
+ """CLI package: steplib terminal interface."""
@@ -0,0 +1,166 @@
1
+ """Output formatters for the steplib CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Any
7
+
8
+ from steplib.core.metadata import StepInfo
9
+
10
+
11
+ def format_step_table(steps: list[StepInfo]) -> str:
12
+ """Format a list of steps as a simple aligned table.
13
+
14
+ Args:
15
+ steps: The step metadata entries to render.
16
+
17
+ Returns:
18
+ A multi-line string with columns: PATTERN, CATEGORY, BACKEND, DESCRIPTION.
19
+
20
+ """
21
+ if not steps:
22
+ return "No steps found."
23
+
24
+ headers = ("PATTERN", "CATEGORY", "BACKEND", "DESCRIPTION")
25
+ rows: list[tuple[str, str, str, str]] = []
26
+ for info in steps:
27
+ rows.append((
28
+ info.pattern,
29
+ info.category,
30
+ info.backend or "-",
31
+ (info.description or "").split("\n")[0][:60],
32
+ ))
33
+
34
+ col_widths = [
35
+ max(len(str(row[i])) for row in [headers, *rows])
36
+ for i in range(len(headers))
37
+ ]
38
+
39
+ lines: list[str] = []
40
+ header_line = " ".join(
41
+ headers[i].ljust(col_widths[i]) for i in range(len(headers))
42
+ )
43
+ lines.append(header_line)
44
+ lines.append(" ".join("-" * col_widths[i] for i in range(len(headers))))
45
+ for row in rows:
46
+ lines.append(" ".join(
47
+ str(row[i]).ljust(col_widths[i]) for i in range(len(headers))
48
+ ))
49
+ return "\n".join(lines)
50
+
51
+
52
+ def format_step_detail(info: StepInfo) -> str:
53
+ """Format a single step's metadata for detailed display.
54
+
55
+ Args:
56
+ info: The step metadata to render.
57
+
58
+ Returns:
59
+ A multi-line string with all metadata fields.
60
+
61
+ """
62
+ lines: list[str] = [
63
+ f"Pattern: {info.pattern}",
64
+ f"Category: {info.category}",
65
+ f"Backend: {info.backend or '-'}",
66
+ f"Module: {info.module}",
67
+ f"Function: {info.qualified_name}",
68
+ ]
69
+
70
+ if info.description:
71
+ lines.append(f"Description: {info.description}")
72
+ if info.example:
73
+ lines.append(f"Example: {info.example}")
74
+ if info.tags:
75
+ lines.append(f"Tags: {', '.join(info.tags)}")
76
+ if info.version:
77
+ lines.append(f"Version: {info.version}")
78
+ if info.deprecated:
79
+ lines.append(f"Deprecated: {info.deprecated}")
80
+ if info.requires:
81
+ lines.append(f"Requires: {', '.join(info.requires)}")
82
+
83
+ if info.parameters:
84
+ lines.append("Parameters:")
85
+ for param in info.parameters:
86
+ type_name = (
87
+ param.type.__name__ if isinstance(param.type, type)
88
+ else str(param.type)
89
+ )
90
+ default_hint = f", default {param.default!r}" if param.default is not None else ""
91
+ required_hint = " (required)" if param.required else ""
92
+ lines.append(
93
+ f" - {param.name}: {type_name}{required_hint}{default_hint}"
94
+ )
95
+
96
+ if info.i18n:
97
+ lines.append("Translations:")
98
+ for lang, translated in info.i18n.items():
99
+ lines.append(f" [{lang}] {translated}")
100
+
101
+ return "\n".join(lines)
102
+
103
+
104
+ def format_step_json(steps: list[StepInfo]) -> str:
105
+ """Format a list of steps as a JSON array.
106
+
107
+ Args:
108
+ steps: The step metadata entries to serialize.
109
+
110
+ Returns:
111
+ A JSON string with pattern, category, backend, and description per step.
112
+
113
+ """
114
+ data: list[dict[str, Any]] = []
115
+ for info in steps:
116
+ data.append({
117
+ "pattern": info.pattern,
118
+ "category": info.category,
119
+ "backend": info.backend,
120
+ "description": info.description,
121
+ "module": info.module,
122
+ "tags": info.tags,
123
+ "version": info.version,
124
+ "deprecated": info.deprecated,
125
+ "example": info.example,
126
+ "i18n": info.i18n,
127
+ })
128
+ return json.dumps(data, indent=2, ensure_ascii=False)
129
+
130
+
131
+ def format_step_detail_json(info: StepInfo) -> str:
132
+ """Format a single step's metadata as JSON.
133
+
134
+ Args:
135
+ info: The step metadata to serialize.
136
+
137
+ Returns:
138
+ A JSON string with all metadata fields.
139
+
140
+ """
141
+ data: dict[str, Any] = {
142
+ "pattern": info.pattern,
143
+ "category": info.category,
144
+ "backend": info.backend,
145
+ "description": info.description,
146
+ "module": info.module,
147
+ "function": info.qualified_name,
148
+ "example": info.example,
149
+ "tags": info.tags,
150
+ "version": info.version,
151
+ "deprecated": info.deprecated,
152
+ "requires": info.requires,
153
+ "i18n": info.i18n,
154
+ "parameters": [
155
+ {
156
+ "name": p.name,
157
+ "type": p.type.__name__ if isinstance(p.type, type) else str(p.type),
158
+ "required": p.required,
159
+ "default": p.default,
160
+ "description": p.description,
161
+ "choices": p.choices,
162
+ }
163
+ for p in info.parameters
164
+ ],
165
+ }
166
+ return json.dumps(data, indent=2, ensure_ascii=False)