assertpy2 2.5.0__tar.gz → 2.5.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.
Files changed (116) hide show
  1. assertpy2-2.5.1/.github/workflows/docs.yml +57 -0
  2. {assertpy2-2.5.0 → assertpy2-2.5.1}/.gitignore +1 -0
  3. {assertpy2-2.5.0 → assertpy2-2.5.1}/PKG-INFO +25 -25
  4. {assertpy2-2.5.0 → assertpy2-2.5.1}/README.md +23 -23
  5. assertpy2-2.5.1/assertpy2/_compat.py +15 -0
  6. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/_mixin_base.py +1 -2
  7. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/_typing.py +1 -2
  8. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/assertpy.py +2 -3
  9. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/async_assertions.py +1 -1
  10. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/base.py +1 -1
  11. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/bytes_mixin.py +1 -1
  12. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/collection.py +1 -1
  13. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/contains.py +1 -1
  14. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/date.py +1 -1
  15. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/dict.py +1 -1
  16. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/exception.py +1 -1
  17. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/extracting.py +1 -1
  18. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/file.py +1 -1
  19. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/json_mixin.py +1 -1
  20. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/matchers.py +68 -0
  21. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/numeric.py +1 -1
  22. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/snapshot.py +1 -1
  23. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/string.py +1 -1
  24. assertpy2-2.5.1/docs/assertions.md +377 -0
  25. assertpy2-2.5.1/docs/comparison.md +170 -0
  26. assertpy2-2.5.1/docs/data.md +84 -0
  27. assertpy2-2.5.1/docs/errors.md +133 -0
  28. assertpy2-2.5.1/docs/extending.md +72 -0
  29. assertpy2-2.5.1/docs/fluent.md +104 -0
  30. assertpy2-2.5.1/docs/getting-started.md +55 -0
  31. assertpy2-2.5.1/docs/index.md +41 -0
  32. assertpy2-2.5.1/docs/integrations.md +120 -0
  33. assertpy2-2.5.1/docs/matchers.md +214 -0
  34. assertpy2-2.5.1/docs/migration.md +101 -0
  35. assertpy2-2.5.1/docs/testing.md +119 -0
  36. assertpy2-2.5.1/mkdocs.yml +81 -0
  37. {assertpy2-2.5.0 → assertpy2-2.5.1}/pyproject.toml +10 -6
  38. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_bool.py +7 -9
  39. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_class.py +7 -9
  40. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_collection.py +40 -70
  41. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_custom_dict.py +11 -21
  42. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_custom_list.py +7 -9
  43. assertpy2-2.5.1/tests/test_datetime.py +440 -0
  44. assertpy2-2.5.1/tests/test_description.py +29 -0
  45. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_dict.py +95 -173
  46. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_dict_compare.py +107 -163
  47. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_dyn.py +17 -25
  48. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_equals.py +15 -25
  49. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_expected_exception.py +33 -45
  50. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_extensions.py +29 -53
  51. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_extracting.py +59 -103
  52. assertpy2-2.5.1/tests/test_fail.py +15 -0
  53. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_file.py +45 -89
  54. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_in.py +11 -17
  55. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_list.py +79 -141
  56. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_namedtuple.py +11 -15
  57. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_none.py +7 -9
  58. assertpy2-2.5.1/tests/test_numbers.py +643 -0
  59. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_phase2.py +83 -156
  60. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_readme.py +31 -41
  61. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_same_as.py +7 -9
  62. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_snapshots.py +6 -12
  63. assertpy2-2.5.1/tests/test_soft.py +188 -0
  64. assertpy2-2.5.1/tests/test_soft_fail.py +61 -0
  65. assertpy2-2.5.1/tests/test_string.py +625 -0
  66. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_type.py +14 -22
  67. {assertpy2-2.5.0 → assertpy2-2.5.1}/uv.lock +594 -51
  68. assertpy2-2.5.0/docs/api.md +0 -2120
  69. assertpy2-2.5.0/tests/test_datetime.py +0 -526
  70. assertpy2-2.5.0/tests/test_description.py +0 -35
  71. assertpy2-2.5.0/tests/test_fail.py +0 -17
  72. assertpy2-2.5.0/tests/test_numbers.py +0 -791
  73. assertpy2-2.5.0/tests/test_soft.py +0 -219
  74. assertpy2-2.5.0/tests/test_soft_fail.py +0 -75
  75. assertpy2-2.5.0/tests/test_string.py +0 -773
  76. {assertpy2-2.5.0 → assertpy2-2.5.1}/.codecov.yml +0 -0
  77. {assertpy2-2.5.0 → assertpy2-2.5.1}/.github/dependabot.yml +0 -0
  78. {assertpy2-2.5.0 → assertpy2-2.5.1}/.github/workflows/ci.yml +0 -0
  79. {assertpy2-2.5.0 → assertpy2-2.5.1}/.github/workflows/codeql.yml +0 -0
  80. {assertpy2-2.5.0 → assertpy2-2.5.1}/.github/workflows/publish.yml +0 -0
  81. {assertpy2-2.5.0 → assertpy2-2.5.1}/.github/workflows/scorecard.yml +0 -0
  82. {assertpy2-2.5.0 → assertpy2-2.5.1}/.github/workflows/zizmor.yml +0 -0
  83. {assertpy2-2.5.0 → assertpy2-2.5.1}/CONTRIBUTING.md +0 -0
  84. {assertpy2-2.5.0 → assertpy2-2.5.1}/LICENSE +0 -0
  85. {assertpy2-2.5.0 → assertpy2-2.5.1}/SECURITY.md +0 -0
  86. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/__init__.py +0 -0
  87. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/behave_matchers.py +0 -0
  88. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/dynamic.py +0 -0
  89. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/errors.py +0 -0
  90. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/helpers.py +0 -0
  91. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/py.typed +0 -0
  92. {assertpy2-2.5.0 → assertpy2-2.5.1}/assertpy2/pytest_plugin.py +0 -0
  93. {assertpy2-2.5.0 → assertpy2-2.5.1}/docs/logo-dark.svg +0 -0
  94. {assertpy2-2.5.0 → assertpy2-2.5.1}/docs/logo.svg +0 -0
  95. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_async.py +0 -0
  96. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_behave_matchers.py +0 -0
  97. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_bytes.py +0 -0
  98. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_core.py +0 -0
  99. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_errors.py +0 -0
  100. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_grouped_soft.py +0 -0
  101. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_json.py +0 -0
  102. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_matcher_registry.py +0 -0
  103. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_matchers.py +0 -0
  104. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_matchers_phase3.py +0 -0
  105. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_not.py +0 -0
  106. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_overloads.py +0 -0
  107. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_pipeline.py +0 -0
  108. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_pytest_plugin.py +0 -0
  109. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_recursive_compare.py +0 -0
  110. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_recursive_compare_attrs.py +0 -0
  111. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_recursive_compare_pydantic.py +0 -0
  112. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_regex_groups.py +0 -0
  113. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_rich_diff.py +0 -0
  114. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_structural.py +0 -0
  115. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_traceback.py +0 -0
  116. {assertpy2-2.5.0 → assertpy2-2.5.1}/tests/test_warn.py +0 -0
@@ -0,0 +1,57 @@
1
+ name: Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: pages
14
+ cancel-in-progress: false
15
+
16
+ jobs:
17
+ build:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
21
+ with:
22
+ persist-credentials: false
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
26
+
27
+ - name: Set up Python
28
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
29
+ with:
30
+ python-version: "3.14"
31
+
32
+ - name: Install docs dependencies
33
+ run: uv sync --only-group docs
34
+
35
+ - name: Build docs
36
+ run: uv run mkdocs build --strict
37
+
38
+ - name: Upload Pages artifact
39
+ if: github.ref == 'refs/heads/main'
40
+ uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
41
+ with:
42
+ path: site
43
+
44
+ deploy:
45
+ if: github.ref == 'refs/heads/main'
46
+ needs: build
47
+ runs-on: ubuntu-latest
48
+ permissions:
49
+ pages: write
50
+ id-token: write
51
+ environment:
52
+ name: github-pages
53
+ url: ${{ steps.deployment.outputs.page_url }}
54
+ steps:
55
+ - name: Deploy to GitHub Pages
56
+ id: deployment
57
+ uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
@@ -30,3 +30,4 @@ dist
30
30
  # docs output
31
31
  docs/build
32
32
  docs/out
33
+ /site/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: assertpy2
3
- Version: 2.5.0
3
+ Version: 2.5.1
4
4
  Summary: Fluent assertion library for Python with composable matchers, structural matching, and full type safety
5
5
  Project-URL: Homepage, https://github.com/Solganis/assertpy2
6
6
  Project-URL: Repository, https://github.com/Solganis/assertpy2
@@ -26,7 +26,7 @@ Classifier: Programming Language :: Python :: 3.15
26
26
  Classifier: Topic :: Software Development
27
27
  Classifier: Topic :: Software Development :: Testing
28
28
  Requires-Python: >=3.10
29
- Requires-Dist: typing-extensions>=4.0
29
+ Requires-Dist: typing-extensions>=4.0; python_version < '3.11'
30
30
  Provides-Extra: allure
31
31
  Requires-Dist: allure-pytest>=2.13; extra == 'allure'
32
32
  Provides-Extra: behave
@@ -56,7 +56,7 @@ Description-Content-Type: text/markdown
56
56
  <a href="https://pypi.org/project/assertpy2/"><img src="https://img.shields.io/pypi/pyversions/assertpy2" alt="Python"></a>
57
57
  <a href="https://codecov.io/gh/Solganis/assertpy2"><img src="https://codecov.io/gh/Solganis/assertpy2/graph/badge.svg" alt="Coverage"></a>
58
58
  <br>
59
- <a href="https://github.com/Solganis/assertpy2/blob/main/docs/api.md"><img src="https://img.shields.io/badge/Docs-Read%20The%20Docs-black" alt="Documentation"></a>
59
+ <a href="https://solganis.github.io/assertpy2/"><img src="https://img.shields.io/badge/Docs-online-black" alt="Documentation"></a>
60
60
  <a href="https://docs.astral.sh/ruff/"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff"></a>
61
61
  <a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="uv"></a>
62
62
  <a href="https://github.com/astral-sh/ty"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json" alt="ty"></a>
@@ -123,28 +123,28 @@ Your IDE shows only methods relevant to the value you're testing, not all 100+:
123
123
 
124
124
  **Fluent API**
125
125
 
126
- - [**Composable matchers**](docs/api.md#composable-matchers): `match.greater_than(5)`, `match.is_uuid()`, combine with `&`, `|`, `~`. Also work with plain `assert ==`.
127
- - [**Structural matching**](docs/api.md#structural-matching): `matches_structure()` for declarative dict/API response validation.
128
- - [**Universal negation**](docs/api.md#universal-negation): `.not_` inverts any assertion without dedicated `is_not_*` methods.
129
- - [**Collection pipeline**](docs/api.md#collection-pipeline): `filtered_on()`, `mapped()`, `flat_mapped()`, `first()`, `last()`, `element()`, `single()`.
130
- - [**Fluent chaining**](docs/api.md#chaining): write assertions as readable one-liners that chain naturally.
126
+ - [**Composable matchers**](https://solganis.github.io/assertpy2/matchers/): `match.greater_than(5)`, `match.is_uuid()`, combine with `&`, `|`, `~`. Also work with plain `assert ==`.
127
+ - [**Structural matching**](https://solganis.github.io/assertpy2/matchers/#structural-matching): `matches_structure()` for declarative dict/API response validation.
128
+ - [**Universal negation**](https://solganis.github.io/assertpy2/fluent/#universal-negation): `.not_` inverts any assertion without dedicated `is_not_*` methods.
129
+ - [**Collection pipeline**](https://solganis.github.io/assertpy2/fluent/#collection-pipeline): `filtered_on()`, `mapped()`, `flat_mapped()`, `first()`, `last()`, `element()`, `single()`.
130
+ - [**Fluent chaining**](https://solganis.github.io/assertpy2/fluent/#chaining): write assertions as readable one-liners that chain naturally.
131
131
 
132
132
  **Built-in types**
133
133
 
134
- - [Strings](docs/api.md#strings), [numbers](docs/api.md#numbers), [lists](docs/api.md#lists), [tuples](docs/api.md#tuples), [sets](docs/api.md#sets), [dicts](docs/api.md#dicts), [dates](docs/api.md#dates), [booleans](docs/api.md#booleans), [objects](docs/api.md#objects), [bytes](docs/api.md#bytes--bytearray-assertions), [files](docs/api.md#files), [exceptions](docs/api.md#failure).
135
- - [**Bytes assertions**](docs/api.md#bytes--bytearray-assertions): `is_valid_utf8()`, `starts_with_bytes()`, `is_hex_equal_to()`, `decoded_as()` for `bytes`/`bytearray`.
136
- - [**JSON assertions**](docs/api.md#json-path--schema-validation): JSONPath navigation and JSON Schema validation. `pip install assertpy2[json]`.
137
- - [**Dynamic assertions**](docs/api.md#objects): `has_<name>()` for any attribute, property, or zero-argument method.
138
- - [**Dict comparison**](docs/api.md#dicts): `is_equal_to()` with `ignore` and `include` for selective key/field matching (dicts, dataclasses, namedtuples, Pydantic models, attrs, plain objects).
139
- - [**Extracting**](docs/api.md#objects): flatten collections on attributes with `filter` and `sort` support.
134
+ - [Strings](https://solganis.github.io/assertpy2/assertions/#strings), [numbers](https://solganis.github.io/assertpy2/assertions/#numbers), [lists](https://solganis.github.io/assertpy2/assertions/#lists), [tuples](https://solganis.github.io/assertpy2/assertions/#tuples), [sets](https://solganis.github.io/assertpy2/assertions/#sets), [dicts](https://solganis.github.io/assertpy2/assertions/#dicts), [dates](https://solganis.github.io/assertpy2/assertions/#dates), [booleans](https://solganis.github.io/assertpy2/assertions/#booleans), [objects](https://solganis.github.io/assertpy2/assertions/#objects), [bytes](https://solganis.github.io/assertpy2/assertions/#bytes--bytearray), [files](https://solganis.github.io/assertpy2/assertions/#files), [exceptions](https://solganis.github.io/assertpy2/errors/#expected-exceptions).
135
+ - [**Bytes assertions**](https://solganis.github.io/assertpy2/assertions/#bytes--bytearray): `is_valid_utf8()`, `starts_with_bytes()`, `is_hex_equal_to()`, `decoded_as()` for `bytes`/`bytearray`.
136
+ - [**JSON assertions**](https://solganis.github.io/assertpy2/data/#json-path--schema): JSONPath navigation and JSON Schema validation. `pip install assertpy2[json]`.
137
+ - [**Dynamic assertions**](https://solganis.github.io/assertpy2/assertions/#dynamic-assertions-on-objects): `has_<name>()` for any attribute, property, or zero-argument method.
138
+ - [**Dict comparison**](https://solganis.github.io/assertpy2/assertions/#selective-comparison-ignore--include): `is_equal_to()` with `ignore` and `include` for selective key/field matching (dicts, dataclasses, namedtuples, Pydantic models, attrs, plain objects).
139
+ - [**Extracting**](https://solganis.github.io/assertpy2/assertions/#extracting-attributes-from-objects): flatten collections on attributes with `filter` and `sort` support.
140
140
 
141
141
  **Testing**
142
142
 
143
- - [**Soft assertions**](docs/api.md#soft-assertions): thread-safe, async-safe via `contextvars`. Group errors with `sa.group()`, or use `assert_all()`.
144
- - [**Async assertions**](docs/api.md#async-assertions): `eventually()` with polling/retry for eventual consistency.
145
- - [**Structured errors**](docs/api.md#structured-errors): `AssertionFailure` with `.actual`, `.expected`, `.diff` attributes.
146
- - [**Rich pytest diffs**](docs/api.md#rich-pytest-diffs): recursive structural diffs for lists, sets, strings, dicts, dataclasses, namedtuples, Pydantic models. Circular reference protection.
147
- - [**Snapshot testing**](docs/api.md#snapshot-testing): store and compare data structures in JSON format.
143
+ - [**Soft assertions**](https://solganis.github.io/assertpy2/testing/#soft-assertions): thread-safe, async-safe via `contextvars`. Group errors with `sa.group()`, or use `assert_all()`.
144
+ - [**Async assertions**](https://solganis.github.io/assertpy2/testing/#async-assertions): `eventually()` with polling/retry for eventual consistency.
145
+ - [**Structured errors**](https://solganis.github.io/assertpy2/errors/#structured-errors): `AssertionFailure` with `.actual`, `.expected`, `.diff` attributes.
146
+ - [**Rich pytest diffs**](https://solganis.github.io/assertpy2/errors/#rich-pytest-diffs): recursive structural diffs for lists, sets, strings, dicts, dataclasses, namedtuples, Pydantic models. Circular reference protection.
147
+ - [**Snapshot testing**](https://solganis.github.io/assertpy2/testing/#snapshot-testing): store and compare data structures in JSON format.
148
148
 
149
149
  **Type safety**
150
150
 
@@ -153,16 +153,16 @@ Your IDE shows only methods relevant to the value you're testing, not all 100+:
153
153
 
154
154
  **Extensibility**
155
155
 
156
- - [**Custom matchers**](docs/api.md#custom-matchers---registering-domain-matchers): `register_matcher()` for domain-specific matchers, composable with `&`, `|`, `~`.
157
- - [**Regex group extraction**](docs/api.md#regex-group-extraction): `extracting_group()` and `matches_with_groups()` for regex captures.
158
- - [**Extensions**](docs/api.md#extension-system---adding-custom-assertions): `add_extension()` for custom assertion methods.
156
+ - [**Custom matchers**](https://solganis.github.io/assertpy2/matchers/#custom-matchers): `register_matcher()` for domain-specific matchers, composable with `&`, `|`, `~`.
157
+ - [**Regex group extraction**](https://solganis.github.io/assertpy2/data/#regex-group-extraction): `extracting_group()` and `matches_with_groups()` for regex captures.
158
+ - [**Extensions**](https://solganis.github.io/assertpy2/extending/): `add_extension()` for custom assertion methods.
159
159
 
160
160
  **Integrations**
161
161
 
162
- - [**Allure**](docs/api.md#allure-integration): auto-attach structured diff and actual/expected data to reports. `pip install assertpy2[allure]`.
163
- - [**Behave**](docs/api.md#behave-step-matchers): ready-made parameter types for step definitions. `pip install assertpy2[behave]`.
162
+ - [**Allure**](https://solganis.github.io/assertpy2/integrations/#allure): auto-attach structured diff and actual/expected data to reports. `pip install assertpy2[allure]`.
163
+ - [**Behave**](https://solganis.github.io/assertpy2/integrations/#behave): ready-made parameter types for step definitions. `pip install assertpy2[behave]`.
164
164
 
165
- See the [full API reference](docs/api.md) for all assertion methods, examples, and advanced features.
165
+ See the [full documentation](https://solganis.github.io/assertpy2/) for all assertion methods, examples, and advanced features.
166
166
 
167
167
  ---
168
168
 
@@ -18,7 +18,7 @@
18
18
  <a href="https://pypi.org/project/assertpy2/"><img src="https://img.shields.io/pypi/pyversions/assertpy2" alt="Python"></a>
19
19
  <a href="https://codecov.io/gh/Solganis/assertpy2"><img src="https://codecov.io/gh/Solganis/assertpy2/graph/badge.svg" alt="Coverage"></a>
20
20
  <br>
21
- <a href="https://github.com/Solganis/assertpy2/blob/main/docs/api.md"><img src="https://img.shields.io/badge/Docs-Read%20The%20Docs-black" alt="Documentation"></a>
21
+ <a href="https://solganis.github.io/assertpy2/"><img src="https://img.shields.io/badge/Docs-online-black" alt="Documentation"></a>
22
22
  <a href="https://docs.astral.sh/ruff/"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff"></a>
23
23
  <a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="uv"></a>
24
24
  <a href="https://github.com/astral-sh/ty"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json" alt="ty"></a>
@@ -85,28 +85,28 @@ Your IDE shows only methods relevant to the value you're testing, not all 100+:
85
85
 
86
86
  **Fluent API**
87
87
 
88
- - [**Composable matchers**](docs/api.md#composable-matchers): `match.greater_than(5)`, `match.is_uuid()`, combine with `&`, `|`, `~`. Also work with plain `assert ==`.
89
- - [**Structural matching**](docs/api.md#structural-matching): `matches_structure()` for declarative dict/API response validation.
90
- - [**Universal negation**](docs/api.md#universal-negation): `.not_` inverts any assertion without dedicated `is_not_*` methods.
91
- - [**Collection pipeline**](docs/api.md#collection-pipeline): `filtered_on()`, `mapped()`, `flat_mapped()`, `first()`, `last()`, `element()`, `single()`.
92
- - [**Fluent chaining**](docs/api.md#chaining): write assertions as readable one-liners that chain naturally.
88
+ - [**Composable matchers**](https://solganis.github.io/assertpy2/matchers/): `match.greater_than(5)`, `match.is_uuid()`, combine with `&`, `|`, `~`. Also work with plain `assert ==`.
89
+ - [**Structural matching**](https://solganis.github.io/assertpy2/matchers/#structural-matching): `matches_structure()` for declarative dict/API response validation.
90
+ - [**Universal negation**](https://solganis.github.io/assertpy2/fluent/#universal-negation): `.not_` inverts any assertion without dedicated `is_not_*` methods.
91
+ - [**Collection pipeline**](https://solganis.github.io/assertpy2/fluent/#collection-pipeline): `filtered_on()`, `mapped()`, `flat_mapped()`, `first()`, `last()`, `element()`, `single()`.
92
+ - [**Fluent chaining**](https://solganis.github.io/assertpy2/fluent/#chaining): write assertions as readable one-liners that chain naturally.
93
93
 
94
94
  **Built-in types**
95
95
 
96
- - [Strings](docs/api.md#strings), [numbers](docs/api.md#numbers), [lists](docs/api.md#lists), [tuples](docs/api.md#tuples), [sets](docs/api.md#sets), [dicts](docs/api.md#dicts), [dates](docs/api.md#dates), [booleans](docs/api.md#booleans), [objects](docs/api.md#objects), [bytes](docs/api.md#bytes--bytearray-assertions), [files](docs/api.md#files), [exceptions](docs/api.md#failure).
97
- - [**Bytes assertions**](docs/api.md#bytes--bytearray-assertions): `is_valid_utf8()`, `starts_with_bytes()`, `is_hex_equal_to()`, `decoded_as()` for `bytes`/`bytearray`.
98
- - [**JSON assertions**](docs/api.md#json-path--schema-validation): JSONPath navigation and JSON Schema validation. `pip install assertpy2[json]`.
99
- - [**Dynamic assertions**](docs/api.md#objects): `has_<name>()` for any attribute, property, or zero-argument method.
100
- - [**Dict comparison**](docs/api.md#dicts): `is_equal_to()` with `ignore` and `include` for selective key/field matching (dicts, dataclasses, namedtuples, Pydantic models, attrs, plain objects).
101
- - [**Extracting**](docs/api.md#objects): flatten collections on attributes with `filter` and `sort` support.
96
+ - [Strings](https://solganis.github.io/assertpy2/assertions/#strings), [numbers](https://solganis.github.io/assertpy2/assertions/#numbers), [lists](https://solganis.github.io/assertpy2/assertions/#lists), [tuples](https://solganis.github.io/assertpy2/assertions/#tuples), [sets](https://solganis.github.io/assertpy2/assertions/#sets), [dicts](https://solganis.github.io/assertpy2/assertions/#dicts), [dates](https://solganis.github.io/assertpy2/assertions/#dates), [booleans](https://solganis.github.io/assertpy2/assertions/#booleans), [objects](https://solganis.github.io/assertpy2/assertions/#objects), [bytes](https://solganis.github.io/assertpy2/assertions/#bytes--bytearray), [files](https://solganis.github.io/assertpy2/assertions/#files), [exceptions](https://solganis.github.io/assertpy2/errors/#expected-exceptions).
97
+ - [**Bytes assertions**](https://solganis.github.io/assertpy2/assertions/#bytes--bytearray): `is_valid_utf8()`, `starts_with_bytes()`, `is_hex_equal_to()`, `decoded_as()` for `bytes`/`bytearray`.
98
+ - [**JSON assertions**](https://solganis.github.io/assertpy2/data/#json-path--schema): JSONPath navigation and JSON Schema validation. `pip install assertpy2[json]`.
99
+ - [**Dynamic assertions**](https://solganis.github.io/assertpy2/assertions/#dynamic-assertions-on-objects): `has_<name>()` for any attribute, property, or zero-argument method.
100
+ - [**Dict comparison**](https://solganis.github.io/assertpy2/assertions/#selective-comparison-ignore--include): `is_equal_to()` with `ignore` and `include` for selective key/field matching (dicts, dataclasses, namedtuples, Pydantic models, attrs, plain objects).
101
+ - [**Extracting**](https://solganis.github.io/assertpy2/assertions/#extracting-attributes-from-objects): flatten collections on attributes with `filter` and `sort` support.
102
102
 
103
103
  **Testing**
104
104
 
105
- - [**Soft assertions**](docs/api.md#soft-assertions): thread-safe, async-safe via `contextvars`. Group errors with `sa.group()`, or use `assert_all()`.
106
- - [**Async assertions**](docs/api.md#async-assertions): `eventually()` with polling/retry for eventual consistency.
107
- - [**Structured errors**](docs/api.md#structured-errors): `AssertionFailure` with `.actual`, `.expected`, `.diff` attributes.
108
- - [**Rich pytest diffs**](docs/api.md#rich-pytest-diffs): recursive structural diffs for lists, sets, strings, dicts, dataclasses, namedtuples, Pydantic models. Circular reference protection.
109
- - [**Snapshot testing**](docs/api.md#snapshot-testing): store and compare data structures in JSON format.
105
+ - [**Soft assertions**](https://solganis.github.io/assertpy2/testing/#soft-assertions): thread-safe, async-safe via `contextvars`. Group errors with `sa.group()`, or use `assert_all()`.
106
+ - [**Async assertions**](https://solganis.github.io/assertpy2/testing/#async-assertions): `eventually()` with polling/retry for eventual consistency.
107
+ - [**Structured errors**](https://solganis.github.io/assertpy2/errors/#structured-errors): `AssertionFailure` with `.actual`, `.expected`, `.diff` attributes.
108
+ - [**Rich pytest diffs**](https://solganis.github.io/assertpy2/errors/#rich-pytest-diffs): recursive structural diffs for lists, sets, strings, dicts, dataclasses, namedtuples, Pydantic models. Circular reference protection.
109
+ - [**Snapshot testing**](https://solganis.github.io/assertpy2/testing/#snapshot-testing): store and compare data structures in JSON format.
110
110
 
111
111
  **Type safety**
112
112
 
@@ -115,16 +115,16 @@ Your IDE shows only methods relevant to the value you're testing, not all 100+:
115
115
 
116
116
  **Extensibility**
117
117
 
118
- - [**Custom matchers**](docs/api.md#custom-matchers---registering-domain-matchers): `register_matcher()` for domain-specific matchers, composable with `&`, `|`, `~`.
119
- - [**Regex group extraction**](docs/api.md#regex-group-extraction): `extracting_group()` and `matches_with_groups()` for regex captures.
120
- - [**Extensions**](docs/api.md#extension-system---adding-custom-assertions): `add_extension()` for custom assertion methods.
118
+ - [**Custom matchers**](https://solganis.github.io/assertpy2/matchers/#custom-matchers): `register_matcher()` for domain-specific matchers, composable with `&`, `|`, `~`.
119
+ - [**Regex group extraction**](https://solganis.github.io/assertpy2/data/#regex-group-extraction): `extracting_group()` and `matches_with_groups()` for regex captures.
120
+ - [**Extensions**](https://solganis.github.io/assertpy2/extending/): `add_extension()` for custom assertion methods.
121
121
 
122
122
  **Integrations**
123
123
 
124
- - [**Allure**](docs/api.md#allure-integration): auto-attach structured diff and actual/expected data to reports. `pip install assertpy2[allure]`.
125
- - [**Behave**](docs/api.md#behave-step-matchers): ready-made parameter types for step definitions. `pip install assertpy2[behave]`.
124
+ - [**Allure**](https://solganis.github.io/assertpy2/integrations/#allure): auto-attach structured diff and actual/expected data to reports. `pip install assertpy2[allure]`.
125
+ - [**Behave**](https://solganis.github.io/assertpy2/integrations/#behave): ready-made parameter types for step definitions. `pip install assertpy2[behave]`.
126
126
 
127
- See the [full API reference](docs/api.md) for all assertion methods, examples, and advanced features.
127
+ See the [full documentation](https://solganis.github.io/assertpy2/) for all assertion methods, examples, and advanced features.
128
128
 
129
129
  ---
130
130
 
@@ -0,0 +1,15 @@
1
+ """Internal compatibility shims.
2
+
3
+ ``Self`` entered the standard library's :mod:`typing` in Python 3.11; on 3.10 it is provided by
4
+ ``typing_extensions``. Re-exporting it from one place lets every mixin import ``Self`` without repeating
5
+ the version gate, and lets ``typing_extensions`` be dropped as a runtime dependency on Python 3.11+.
6
+ """
7
+
8
+ import sys
9
+
10
+ if sys.version_info >= (3, 11):
11
+ from typing import Self
12
+ else:
13
+ from typing_extensions import Self
14
+
15
+ __all__ = ["Self"]
@@ -4,8 +4,7 @@ import logging
4
4
  from typing import TYPE_CHECKING, Any
5
5
 
6
6
  if TYPE_CHECKING:
7
- from typing_extensions import Self
8
-
7
+ from ._compat import Self
9
8
  from .errors import DiffResult
10
9
 
11
10
 
@@ -7,8 +7,7 @@ if TYPE_CHECKING:
7
7
  from pathlib import Path
8
8
  from typing import Any, Protocol
9
9
 
10
- from typing_extensions import Self
11
-
10
+ from ._compat import Self
12
11
  from .assertpy import NegatedBuilder
13
12
  from .async_assertions import AsyncAssertionBuilder
14
13
  from .matchers import Matcher
@@ -16,8 +16,7 @@ if TYPE_CHECKING:
16
16
  import datetime
17
17
  import pathlib
18
18
 
19
- from typing_extensions import Self
20
-
19
+ from ._compat import Self
21
20
  from ._typing import (
22
21
  _BytesAssertion,
23
22
  _CallableAssertion,
@@ -47,7 +46,7 @@ from .numeric import NumericMixin
47
46
  from .snapshot import SnapshotMixin
48
47
  from .string import StringMixin
49
48
 
50
- __version__ = "2.4.0"
49
+ __version__ = "2.5.1"
51
50
 
52
51
  __tracebackhide__ = True # clean tracebacks via py.test integration
53
52
  contextlib.__tracebackhide__ = True # ty: ignore[unresolved-attribute] # pytest monkey-patch
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
7
7
  if TYPE_CHECKING:
8
8
  from collections.abc import Callable
9
9
 
10
- from typing_extensions import Self
10
+ from ._compat import Self
11
11
 
12
12
  __tracebackhide__ = True
13
13
 
@@ -9,7 +9,7 @@ from .errors import DiffEntry, DiffResult
9
9
  from .matchers import Matcher, StructureMatcher
10
10
 
11
11
  if TYPE_CHECKING:
12
- from typing_extensions import Self
12
+ from ._compat import Self
13
13
 
14
14
  __tracebackhide__ = True
15
15
 
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
5
5
  from ._mixin_base import _MixinBase
6
6
 
7
7
  if TYPE_CHECKING:
8
- from typing_extensions import Self
8
+ from ._compat import Self
9
9
 
10
10
  __tracebackhide__ = True
11
11
 
@@ -8,7 +8,7 @@ from ._mixin_base import _MixinBase
8
8
  from .matchers import BaseMatcher
9
9
 
10
10
  if TYPE_CHECKING:
11
- from typing_extensions import Self
11
+ from ._compat import Self
12
12
 
13
13
  __tracebackhide__ = True
14
14
 
@@ -8,7 +8,7 @@ from .errors import DiffEntry, DiffResult
8
8
  from .matchers import Matcher
9
9
 
10
10
  if TYPE_CHECKING:
11
- from typing_extensions import Self
11
+ from ._compat import Self
12
12
 
13
13
  __tracebackhide__ = True
14
14
 
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
6
6
  from ._mixin_base import _MixinBase
7
7
 
8
8
  if TYPE_CHECKING:
9
- from typing_extensions import Self
9
+ from ._compat import Self
10
10
 
11
11
  __tracebackhide__ = True
12
12
 
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
5
5
  from ._mixin_base import _MixinBase
6
6
 
7
7
  if TYPE_CHECKING:
8
- from typing_extensions import Self
8
+ from ._compat import Self
9
9
 
10
10
  __tracebackhide__ = True
11
11
 
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, cast
5
5
  from ._mixin_base import _MixinBase
6
6
 
7
7
  if TYPE_CHECKING:
8
- from typing_extensions import Self
8
+ from ._compat import Self
9
9
 
10
10
  __tracebackhide__ = True
11
11
 
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
6
6
  from ._mixin_base import _MixinBase
7
7
 
8
8
  if TYPE_CHECKING:
9
- from typing_extensions import Self
9
+ from ._compat import Self
10
10
 
11
11
  __tracebackhide__ = True
12
12
 
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
6
6
  from ._mixin_base import _MixinBase
7
7
 
8
8
  if TYPE_CHECKING:
9
- from typing_extensions import Self
9
+ from ._compat import Self
10
10
 
11
11
  __tracebackhide__ = True
12
12
 
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any
7
7
  from ._mixin_base import _MixinBase
8
8
 
9
9
  if TYPE_CHECKING:
10
- from typing_extensions import Self
10
+ from ._compat import Self
11
11
 
12
12
  __tracebackhide__ = True
13
13
 
@@ -655,146 +655,214 @@ class _MatchNamespace:
655
655
 
656
656
  @staticmethod
657
657
  def equal_to(expected: object) -> EqualToMatcher:
658
+ """Matcher for a value equal to ``expected``."""
658
659
  return EqualToMatcher(expected)
659
660
 
660
661
  @staticmethod
661
662
  def greater_than(val: object) -> GreaterThanMatcher:
663
+ """Matcher for a value greater than ``val``."""
662
664
  return GreaterThanMatcher(val)
663
665
 
664
666
  @staticmethod
665
667
  def greater_than_or_equal_to(val: object) -> GreaterThanOrEqualToMatcher:
668
+ """Matcher for a value greater than or equal to ``val``."""
666
669
  return GreaterThanOrEqualToMatcher(val)
667
670
 
668
671
  @staticmethod
669
672
  def less_than(val: object) -> LessThanMatcher:
673
+ """Matcher for a value less than ``val``."""
670
674
  return LessThanMatcher(val)
671
675
 
672
676
  @staticmethod
673
677
  def less_than_or_equal_to(val: object) -> LessThanOrEqualToMatcher:
678
+ """Matcher for a value less than or equal to ``val``."""
674
679
  return LessThanOrEqualToMatcher(val)
675
680
 
676
681
  @staticmethod
677
682
  def between(low: object, high: object) -> BetweenMatcher:
683
+ """Matcher for a value in the inclusive range ``low`` to ``high``."""
678
684
  return BetweenMatcher(low, high)
679
685
 
680
686
  @staticmethod
681
687
  def close_to(expected: object, tolerance: object) -> CloseToMatcher:
688
+ """Matcher for a value within ``tolerance`` of ``expected`` (``abs(value - expected) <= tolerance``).
689
+
690
+ Args:
691
+ expected: the target value
692
+ tolerance: the maximum allowed absolute difference from ``expected``
693
+ """
682
694
  return CloseToMatcher(expected, tolerance)
683
695
 
684
696
  @staticmethod
685
697
  def is_none() -> IsNoneMatcher:
698
+ """Matcher for ``None``."""
686
699
  return IsNoneMatcher()
687
700
 
688
701
  @staticmethod
689
702
  def is_not_none() -> IsNotNoneMatcher:
703
+ """Matcher for any value that is not ``None``."""
690
704
  return IsNotNoneMatcher()
691
705
 
692
706
  @staticmethod
693
707
  def is_instance_of(expected_type: type) -> IsInstanceOfMatcher:
708
+ """Matcher for an instance of ``expected_type`` (via ``isinstance``)."""
694
709
  return IsInstanceOfMatcher(expected_type)
695
710
 
696
711
  @staticmethod
697
712
  def is_truthy() -> IsTruthyMatcher:
713
+ """Matcher for a truthy value."""
698
714
  return IsTruthyMatcher()
699
715
 
700
716
  @staticmethod
701
717
  def is_falsy() -> IsFalsyMatcher:
718
+ """Matcher for a falsy value."""
702
719
  return IsFalsyMatcher()
703
720
 
704
721
  @staticmethod
705
722
  def has_length(length: int) -> HasLengthMatcher:
723
+ """Matcher for a value whose ``len()`` equals ``length``."""
706
724
  return HasLengthMatcher(length)
707
725
 
708
726
  @staticmethod
709
727
  def is_empty() -> IsEmptyMatcher:
728
+ """Matcher for an empty value (``len() == 0``)."""
710
729
  return IsEmptyMatcher()
711
730
 
712
731
  @staticmethod
713
732
  def is_not_empty() -> IsNotEmptyMatcher:
733
+ """Matcher for a non-empty value (``len() > 0``)."""
714
734
  return IsNotEmptyMatcher()
715
735
 
716
736
  @staticmethod
717
737
  def is_positive() -> IsPositiveMatcher:
738
+ """Matcher for a value greater than zero."""
718
739
  return IsPositiveMatcher()
719
740
 
720
741
  @staticmethod
721
742
  def is_negative() -> IsNegativeMatcher:
743
+ """Matcher for a value less than zero."""
722
744
  return IsNegativeMatcher()
723
745
 
724
746
  @staticmethod
725
747
  def is_zero() -> IsZeroMatcher:
748
+ """Matcher for a value equal to zero."""
726
749
  return IsZeroMatcher()
727
750
 
728
751
  @staticmethod
729
752
  def is_even() -> IsEvenMatcher:
753
+ """Matcher for an even integer."""
730
754
  return IsEvenMatcher()
731
755
 
732
756
  @staticmethod
733
757
  def is_odd() -> IsOddMatcher:
758
+ """Matcher for an odd integer."""
734
759
  return IsOddMatcher()
735
760
 
736
761
  @staticmethod
737
762
  def is_divisible_by(divisor: int) -> IsDivisibleByMatcher:
763
+ """Matcher for an integer divisible by ``divisor``."""
738
764
  return IsDivisibleByMatcher(divisor)
739
765
 
740
766
  @staticmethod
741
767
  def is_callable() -> IsCallableMatcher:
768
+ """Matcher for a callable object."""
742
769
  return IsCallableMatcher()
743
770
 
744
771
  @staticmethod
745
772
  def is_in(*values: object) -> IsInMatcher:
773
+ """Matcher for a value present in ``values``.
774
+
775
+ Args:
776
+ *values: the candidate values; the matched value must equal one of them
777
+ """
746
778
  return IsInMatcher(*values)
747
779
 
748
780
  @staticmethod
749
781
  def has_property(name: str, matcher: Matcher | None = None) -> HasPropertyMatcher:
782
+ """Matcher for an object with attribute ``name``, optionally matching ``matcher``.
783
+
784
+ Args:
785
+ name: the attribute name the object must have
786
+ matcher: optional matcher the attribute value must satisfy; if ``None``,
787
+ only the presence of the attribute is checked
788
+ """
750
789
  return HasPropertyMatcher(name, matcher)
751
790
 
752
791
  @staticmethod
753
792
  def contains_string(substring: str) -> ContainsStringMatcher:
793
+ """Matcher for a string containing ``substring``."""
754
794
  return ContainsStringMatcher(substring)
755
795
 
756
796
  @staticmethod
757
797
  def matches_regex(pattern: str) -> MatchesRegexMatcher:
798
+ """Matcher for a string in which ``pattern`` is found (``re.search``)."""
758
799
  return MatchesRegexMatcher(pattern)
759
800
 
760
801
  @staticmethod
761
802
  def starts_with(prefix: str) -> StartsWithMatcher:
803
+ """Matcher for a string starting with ``prefix``."""
762
804
  return StartsWithMatcher(prefix)
763
805
 
764
806
  @staticmethod
765
807
  def ends_with(suffix: str) -> EndsWithMatcher:
808
+ """Matcher for a string ending with ``suffix``."""
766
809
  return EndsWithMatcher(suffix)
767
810
 
768
811
  @staticmethod
769
812
  def all_of(*matchers: Matcher) -> AllOfMatcher:
813
+ """Matcher that holds when every one of ``matchers`` matches (the ``&`` operator)."""
770
814
  return AllOfMatcher(*matchers)
771
815
 
772
816
  @staticmethod
773
817
  def any_of(*matchers: Matcher) -> AnyOfMatcher:
818
+ """Matcher that holds when at least one of ``matchers`` matches (the ``|`` operator)."""
774
819
  return AnyOfMatcher(*matchers)
775
820
 
776
821
  @staticmethod
777
822
  def not_(matcher: Matcher) -> NotMatcher:
823
+ """Matcher that inverts ``matcher`` (the ``~`` operator)."""
778
824
  return NotMatcher(matcher)
779
825
 
780
826
  @staticmethod
781
827
  def ignore() -> IgnoreMatcher:
828
+ """Matcher that accepts anything; useful as a placeholder in ``structure`` specs."""
782
829
  return IgnoreMatcher()
783
830
 
784
831
  @staticmethod
785
832
  def is_uuid() -> IsUuidMatcher:
833
+ """Matcher for a string parseable as a UUID."""
786
834
  return IsUuidMatcher()
787
835
 
788
836
  @staticmethod
789
837
  def is_non_empty_string() -> IsNonEmptyStringMatcher:
838
+ """Matcher for a non-empty string."""
790
839
  return IsNonEmptyStringMatcher()
791
840
 
792
841
  @staticmethod
793
842
  def each_item(matcher: Matcher) -> EachMatcher:
843
+ """Matcher for an iterable whose every item matches ``matcher``.
844
+
845
+ Args:
846
+ matcher: the matcher each item of the iterable must satisfy; a non-iterable
847
+ value never matches
848
+ """
794
849
  return EachMatcher(matcher)
795
850
 
796
851
  @staticmethod
797
852
  def structure(spec: dict) -> StructureMatcher:
853
+ """Matcher for a dict matching ``spec``.
854
+
855
+ Args:
856
+ spec: dict whose values are matchers, raw values (compared with ``==``),
857
+ or nested dict specs. Keys present in the value but absent from the spec are ignored.
858
+
859
+ Examples:
860
+ Usage::
861
+
862
+ assert_that(user).satisfies(
863
+ match.structure({"id": match.is_instance_of(int), "name": "Alice"})
864
+ )
865
+ """
798
866
  return StructureMatcher(spec)
799
867
 
800
868
  def __getattr__(self, name: str) -> Callable[..., BaseMatcher]: