assertpy2 2.5.0__tar.gz → 2.6.0__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 (121) hide show
  1. assertpy2-2.6.0/.gitattributes +2 -0
  2. assertpy2-2.6.0/.github/dependabot.yml +20 -0
  3. assertpy2-2.6.0/.github/workflows/docs.yml +57 -0
  4. {assertpy2-2.5.0 → assertpy2-2.6.0}/.gitignore +1 -0
  5. {assertpy2-2.5.0 → assertpy2-2.6.0}/PKG-INFO +25 -25
  6. {assertpy2-2.5.0 → assertpy2-2.6.0}/README.md +23 -23
  7. assertpy2-2.6.0/SECURITY.md +24 -0
  8. assertpy2-2.6.0/assertpy2/_compat.py +15 -0
  9. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/_mixin_base.py +13 -3
  10. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/_typing.py +4 -3
  11. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/assertpy.py +6 -3
  12. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/async_assertions.py +1 -1
  13. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/base.py +97 -87
  14. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/bytes_mixin.py +1 -1
  15. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/collection.py +29 -29
  16. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/contains.py +36 -31
  17. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/date.py +1 -1
  18. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/dict.py +23 -23
  19. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/errors.py +3 -3
  20. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/exception.py +16 -8
  21. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/extracting.py +32 -32
  22. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/file.py +1 -1
  23. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/helpers.py +89 -69
  24. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/json_mixin.py +1 -1
  25. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/matchers.py +75 -7
  26. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/numeric.py +1 -1
  27. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/snapshot.py +2 -2
  28. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/string.py +18 -18
  29. assertpy2-2.6.0/assertpy2/warning.py +123 -0
  30. assertpy2-2.6.0/docs/assertions.md +377 -0
  31. assertpy2-2.6.0/docs/comparison.md +170 -0
  32. assertpy2-2.6.0/docs/data.md +84 -0
  33. assertpy2-2.6.0/docs/errors.md +161 -0
  34. assertpy2-2.6.0/docs/extending.md +72 -0
  35. assertpy2-2.6.0/docs/fluent.md +104 -0
  36. assertpy2-2.6.0/docs/getting-started.md +55 -0
  37. assertpy2-2.6.0/docs/index.md +41 -0
  38. assertpy2-2.6.0/docs/integrations.md +120 -0
  39. assertpy2-2.6.0/docs/matchers.md +214 -0
  40. assertpy2-2.6.0/docs/migration.md +101 -0
  41. assertpy2-2.6.0/docs/testing.md +119 -0
  42. assertpy2-2.6.0/mkdocs.yml +81 -0
  43. {assertpy2-2.5.0 → assertpy2-2.6.0}/pyproject.toml +12 -8
  44. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_async.py +8 -8
  45. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_bool.py +7 -9
  46. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_class.py +7 -9
  47. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_collection.py +40 -70
  48. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_custom_dict.py +11 -21
  49. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_custom_list.py +7 -9
  50. assertpy2-2.6.0/tests/test_datetime.py +440 -0
  51. assertpy2-2.6.0/tests/test_description.py +29 -0
  52. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_dict.py +95 -173
  53. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_dict_compare.py +107 -163
  54. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_dyn.py +17 -25
  55. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_equals.py +15 -25
  56. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_errors.py +1 -1
  57. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_expected_exception.py +32 -46
  58. assertpy2-2.6.0/tests/test_expected_warning.py +177 -0
  59. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_extensions.py +29 -53
  60. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_extracting.py +61 -105
  61. assertpy2-2.6.0/tests/test_fail.py +15 -0
  62. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_file.py +47 -91
  63. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_in.py +11 -17
  64. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_list.py +79 -141
  65. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_namedtuple.py +11 -15
  66. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_none.py +7 -9
  67. assertpy2-2.6.0/tests/test_numbers.py +643 -0
  68. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_phase2.py +83 -156
  69. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_pytest_plugin.py +2 -2
  70. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_readme.py +31 -41
  71. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_rich_diff.py +68 -64
  72. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_same_as.py +7 -9
  73. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_snapshots.py +6 -12
  74. assertpy2-2.6.0/tests/test_soft.py +188 -0
  75. assertpy2-2.6.0/tests/test_soft_fail.py +61 -0
  76. assertpy2-2.6.0/tests/test_string.py +625 -0
  77. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_traceback.py +3 -1
  78. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_type.py +14 -22
  79. {assertpy2-2.5.0 → assertpy2-2.6.0}/uv.lock +594 -51
  80. assertpy2-2.5.0/.github/dependabot.yml +0 -20
  81. assertpy2-2.5.0/SECURITY.md +0 -16
  82. assertpy2-2.5.0/docs/api.md +0 -2120
  83. assertpy2-2.5.0/tests/test_datetime.py +0 -526
  84. assertpy2-2.5.0/tests/test_description.py +0 -35
  85. assertpy2-2.5.0/tests/test_fail.py +0 -17
  86. assertpy2-2.5.0/tests/test_numbers.py +0 -791
  87. assertpy2-2.5.0/tests/test_soft.py +0 -219
  88. assertpy2-2.5.0/tests/test_soft_fail.py +0 -75
  89. assertpy2-2.5.0/tests/test_string.py +0 -773
  90. {assertpy2-2.5.0 → assertpy2-2.6.0}/.codecov.yml +0 -0
  91. {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/ci.yml +0 -0
  92. {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/codeql.yml +0 -0
  93. {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/publish.yml +0 -0
  94. {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/scorecard.yml +0 -0
  95. {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/zizmor.yml +0 -0
  96. {assertpy2-2.5.0 → assertpy2-2.6.0}/CONTRIBUTING.md +0 -0
  97. {assertpy2-2.5.0 → assertpy2-2.6.0}/LICENSE +0 -0
  98. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/__init__.py +0 -0
  99. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/behave_matchers.py +0 -0
  100. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/dynamic.py +0 -0
  101. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/py.typed +0 -0
  102. {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/pytest_plugin.py +0 -0
  103. {assertpy2-2.5.0 → assertpy2-2.6.0}/docs/logo-dark.svg +0 -0
  104. {assertpy2-2.5.0 → assertpy2-2.6.0}/docs/logo.svg +0 -0
  105. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_behave_matchers.py +0 -0
  106. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_bytes.py +0 -0
  107. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_core.py +0 -0
  108. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_grouped_soft.py +0 -0
  109. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_json.py +0 -0
  110. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_matcher_registry.py +0 -0
  111. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_matchers.py +0 -0
  112. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_matchers_phase3.py +0 -0
  113. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_not.py +0 -0
  114. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_overloads.py +0 -0
  115. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_pipeline.py +0 -0
  116. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_recursive_compare.py +0 -0
  117. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_recursive_compare_attrs.py +0 -0
  118. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_recursive_compare_pydantic.py +0 -0
  119. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_regex_groups.py +0 -0
  120. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_structural.py +0 -0
  121. {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_warn.py +0 -0
@@ -0,0 +1,2 @@
1
+ * text=auto eol=lf
2
+ *.{png,ico,jpg,gif} binary
@@ -0,0 +1,20 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ cooldown:
8
+ default-days: 7
9
+ semver-major-days: 7
10
+ commit-message:
11
+ prefix: "chore(deps)"
12
+
13
+ - package-ecosystem: github-actions
14
+ directory: /
15
+ schedule:
16
+ interval: weekly
17
+ cooldown:
18
+ default-days: 7
19
+ commit-message:
20
+ prefix: "ci(deps)"
@@ -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.6.0
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,24 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ | Version | Supported |
6
+ |---------|-----------|
7
+ | 2.x | Yes |
8
+ | < 2.0 | No |
9
+
10
+ ## Reporting a Vulnerability
11
+
12
+ If you discover a security vulnerability, please report it responsibly:
13
+
14
+ 1. **Email**: [solganis.dev@gmail.com](mailto:solganis.dev@gmail.com)
15
+ 2. **Subject**: `[assertpy2] Security: <brief description>`
16
+ 3. Include steps to reproduce, affected versions, and potential impact
17
+
18
+ Please do **not** open a public GitHub issue for security vulnerabilities.
19
+
20
+ ## Response Timeline
21
+
22
+ - Acknowledgement: within 48 hours
23
+ - Initial assessment: within 7 days
24
+ - Fix or mitigation: depends on severity, typically within 30 days
@@ -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
 
@@ -17,6 +16,7 @@ class _MixinBase:
17
16
  expected: type[BaseException] | None
18
17
  logger: logging.LoggerAdapter
19
18
  _not_expected: bool
19
+ _expected_warning: type[Warning] | None
20
20
 
21
21
  def error(
22
22
  self,
@@ -36,9 +36,19 @@ class _MixinBase:
36
36
  logger: logging.LoggerAdapter | None = ...,
37
37
  ) -> Self: ...
38
38
 
39
+ # WarningMixin (terminals dispatched from ExceptionMixin.when_called_with)
40
+
41
+ def _when_called_with_warning(
42
+ self, expected: type[Warning], *some_args: object, **some_kwargs: object
43
+ ) -> Self: ...
44
+
45
+ def _when_called_with_not_warning(
46
+ self, expected: type[Warning], *some_args: object, **some_kwargs: object
47
+ ) -> Self: ...
48
+
39
49
  # HelpersMixin
40
50
 
41
- def _fmt_items(self, i: object) -> str: ...
51
+ def _fmt_items(self, items: object) -> str: ...
42
52
 
43
53
  def _fmt_args_kwargs(self, *some_args: object, **some_kwargs: object) -> str: ...
44
54
 
@@ -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
@@ -36,7 +35,7 @@ if TYPE_CHECKING:
36
35
  def is_in(self, *items: object) -> Self: ...
37
36
  def is_not_in(self, *items: object) -> Self: ...
38
37
  # SnapshotMixin
39
- def snapshot(self, id: str | None = ..., path: str = ...) -> Self: ...
38
+ def snapshot(self, id: str | None = ..., path: str = ...) -> Self: ... # noqa: A002 # mirrors public snapshot() parameter
40
39
  # NegatedBuilder
41
40
  @property
42
41
  def not_(self) -> NegatedBuilder: ...
@@ -221,5 +220,7 @@ if TYPE_CHECKING:
221
220
 
222
221
  def raises(self, ex: type) -> Self: ...
223
222
  def does_not_raise(self, ex: type) -> Self: ...
223
+ def warns(self, warning: type[Warning] = ...) -> Self: ...
224
+ def does_not_warn(self, warning: type[Warning] = ...) -> Self: ...
224
225
  def when_called_with(self, *some_args: object, **some_kwargs: object) -> Self: ...
225
226
  def eventually(self, *, timeout: float = ..., interval: float = ...) -> AsyncAssertionBuilder: ...
@@ -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,
@@ -46,8 +45,9 @@ from .json_mixin import JsonMixin
46
45
  from .numeric import NumericMixin
47
46
  from .snapshot import SnapshotMixin
48
47
  from .string import StringMixin
48
+ from .warning import WarningMixin
49
49
 
50
- __version__ = "2.4.0"
50
+ __version__ = "2.6.0"
51
51
 
52
52
  __tracebackhide__ = True # clean tracebacks via py.test integration
53
53
  contextlib.__tracebackhide__ = True # ty: ignore[unresolved-attribute] # pytest monkey-patch
@@ -76,6 +76,7 @@ ASSERTPY_FILES = [
76
76
  "pytest_plugin.py",
77
77
  "snapshot.py",
78
78
  "string.py",
79
+ "warning.py",
79
80
  ]
80
81
  ]
81
82
 
@@ -556,6 +557,7 @@ class AssertionBuilder(
556
557
  FileMixin,
557
558
  ExtractingMixin,
558
559
  ExceptionMixin,
560
+ WarningMixin,
559
561
  DynamicMixin,
560
562
  DictMixin,
561
563
  DateMixin,
@@ -586,6 +588,7 @@ class AssertionBuilder(
586
588
  self.expected = expected
587
589
  self.logger = logger if logger else _default_logger
588
590
  self._not_expected = False
591
+ self._expected_warning = None
589
592
 
590
593
  @property
591
594
  def not_(self) -> NegatedBuilder:
@@ -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