assertpy2 2.1.4__tar.gz → 2.3.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 (81) hide show
  1. {assertpy2-2.1.4 → assertpy2-2.3.0}/.github/workflows/ci.yml +5 -5
  2. {assertpy2-2.1.4 → assertpy2-2.3.0}/.github/workflows/codeql.yml +1 -1
  3. {assertpy2-2.1.4 → assertpy2-2.3.0}/.github/workflows/publish.yml +2 -2
  4. {assertpy2-2.1.4 → assertpy2-2.3.0}/.github/workflows/scorecard.yml +1 -1
  5. {assertpy2-2.1.4 → assertpy2-2.3.0}/PKG-INFO +62 -8
  6. {assertpy2-2.1.4 → assertpy2-2.3.0}/README.md +57 -7
  7. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/_typing.py +19 -0
  8. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/assertpy.py +5 -5
  9. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/async_assertions.py +2 -2
  10. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/base.py +157 -17
  11. assertpy2-2.3.0/assertpy2/behave_matchers.py +85 -0
  12. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/collection.py +9 -19
  13. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/contains.py +88 -27
  14. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/date.py +88 -20
  15. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/dict.py +8 -8
  16. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/dynamic.py +7 -7
  17. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/errors.py +2 -2
  18. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/exception.py +51 -15
  19. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/extracting.py +5 -5
  20. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/file.py +12 -12
  21. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/helpers.py +61 -42
  22. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/matchers.py +156 -41
  23. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/numeric.py +106 -41
  24. assertpy2-2.3.0/assertpy2/pytest_plugin.py +115 -0
  25. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/snapshot.py +1 -1
  26. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/string.py +123 -17
  27. {assertpy2-2.1.4 → assertpy2-2.3.0}/docs/api.md +190 -4
  28. {assertpy2-2.1.4 → assertpy2-2.3.0}/pyproject.toml +7 -3
  29. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_async.py +5 -5
  30. assertpy2-2.3.0/tests/test_behave_matchers.py +108 -0
  31. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_class.py +3 -3
  32. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_dyn.py +3 -3
  33. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_errors.py +29 -1
  34. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_expected_exception.py +2 -3
  35. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_extensions.py +5 -5
  36. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_extracting.py +2 -2
  37. assertpy2-2.3.0/tests/test_matchers_phase3.py +266 -0
  38. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_numbers.py +141 -1
  39. assertpy2-2.3.0/tests/test_phase2.py +422 -0
  40. assertpy2-2.3.0/tests/test_pytest_plugin.py +383 -0
  41. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_readme.py +4 -4
  42. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_string.py +180 -0
  43. {assertpy2-2.1.4 → assertpy2-2.3.0}/uv.lock +137 -22
  44. assertpy2-2.1.4/assertpy2/pytest_plugin.py +0 -42
  45. assertpy2-2.1.4/tests/test_pytest_plugin.py +0 -154
  46. {assertpy2-2.1.4 → assertpy2-2.3.0}/.codecov.yml +0 -0
  47. {assertpy2-2.1.4 → assertpy2-2.3.0}/.github/dependabot.yml +0 -0
  48. {assertpy2-2.1.4 → assertpy2-2.3.0}/.gitignore +0 -0
  49. {assertpy2-2.1.4 → assertpy2-2.3.0}/CONTRIBUTING.md +0 -0
  50. {assertpy2-2.1.4 → assertpy2-2.3.0}/LICENSE +0 -0
  51. {assertpy2-2.1.4 → assertpy2-2.3.0}/SECURITY.md +0 -0
  52. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/__init__.py +0 -0
  53. {assertpy2-2.1.4 → assertpy2-2.3.0}/assertpy2/py.typed +0 -0
  54. {assertpy2-2.1.4 → assertpy2-2.3.0}/docs/logo-dark.svg +0 -0
  55. {assertpy2-2.1.4 → assertpy2-2.3.0}/docs/logo.svg +0 -0
  56. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_bool.py +0 -0
  57. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_collection.py +0 -0
  58. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_core.py +0 -0
  59. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_custom_dict.py +0 -0
  60. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_custom_list.py +0 -0
  61. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_datetime.py +0 -0
  62. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_description.py +0 -0
  63. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_dict.py +0 -0
  64. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_dict_compare.py +0 -0
  65. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_equals.py +0 -0
  66. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_fail.py +0 -0
  67. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_file.py +0 -0
  68. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_in.py +0 -0
  69. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_list.py +0 -0
  70. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_matchers.py +0 -0
  71. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_namedtuple.py +0 -0
  72. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_none.py +0 -0
  73. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_overloads.py +0 -0
  74. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_same_as.py +0 -0
  75. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_snapshots.py +0 -0
  76. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_soft.py +0 -0
  77. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_soft_fail.py +0 -0
  78. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_structural.py +0 -0
  79. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_traceback.py +0 -0
  80. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_type.py +0 -0
  81. {assertpy2-2.1.4 → assertpy2-2.3.0}/tests/test_warn.py +0 -0
@@ -16,10 +16,10 @@ jobs:
16
16
  matrix:
17
17
  python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
18
18
  steps:
19
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
20
20
 
21
21
  - name: Install uv
22
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
22
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
23
23
 
24
24
  - name: Set up Python ${{ matrix.python-version }}
25
25
  uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@@ -38,7 +38,7 @@ jobs:
38
38
 
39
39
  - name: Upload coverage to Codecov
40
40
  if: matrix.python-version == '3.14'
41
- uses: codecov/codecov-action@cddd853df119a48c5be31a973f8cd97e12e35e16 # v6.0.1
41
+ uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
42
42
  with:
43
43
  token: ${{ secrets.CODECOV_TOKEN }}
44
44
  files: coverage.xml
@@ -46,10 +46,10 @@ jobs:
46
46
  lint:
47
47
  runs-on: ubuntu-latest
48
48
  steps:
49
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
49
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
50
50
 
51
51
  - name: Install uv
52
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
52
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
53
53
 
54
54
  - name: Set up Python
55
55
  uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@@ -17,7 +17,7 @@ jobs:
17
17
  permissions:
18
18
  security-events: write
19
19
  steps:
20
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
20
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
21
21
  - uses: github/codeql-action/init@f52b05f4acaaa234e44466e66d29050e135ea9ef # v4.36.0
22
22
  with:
23
23
  languages: python
@@ -16,10 +16,10 @@ jobs:
16
16
  contents: write
17
17
  attestations: write
18
18
  steps:
19
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
20
20
 
21
21
  - name: Install uv
22
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
22
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
23
23
 
24
24
  - name: Set up Python
25
25
  uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@@ -16,7 +16,7 @@ jobs:
16
16
  security-events: write
17
17
  id-token: write
18
18
  steps:
19
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
20
20
  with:
21
21
  persist-credentials: false
22
22
  - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: assertpy2
3
- Version: 2.1.4
3
+ Version: 2.3.0
4
4
  Summary: Fluent assertion library for Python with full type safety and soft assertions
5
5
  Project-URL: Homepage, https://github.com/Solganis/assertpy2
6
6
  Project-URL: Repository, https://github.com/Solganis/assertpy2
@@ -27,6 +27,10 @@ Classifier: Topic :: Software Development
27
27
  Classifier: Topic :: Software Development :: Testing
28
28
  Requires-Python: >=3.10
29
29
  Requires-Dist: typing-extensions>=4.0
30
+ Provides-Extra: allure
31
+ Requires-Dist: allure-pytest>=2.13; extra == 'allure'
32
+ Provides-Extra: behave
33
+ Requires-Dist: behave>=1.2.6; extra == 'behave'
30
34
  Description-Content-Type: text/markdown
31
35
 
32
36
  <p align="center">
@@ -126,7 +130,7 @@ FAILED test_example.py::test_comparison
126
130
  | **Async assertions** | No | No | No | **eventually() with polling** |
127
131
  | **Soft assertions** | No | No | Yes (not thread-safe) | **Yes (thread-safe, async-safe)** |
128
132
  | **Structured errors** | Rewrite only | Mismatch string | String only | **.actual .expected .diff** |
129
- | **Maintained** | N/A | Minimal | 2020 | **Active (2026)** |
133
+ | **Maintained** | N/A | Minimal | 2020 | **Active** |
130
134
 
131
135
  </div>
132
136
 
@@ -167,6 +171,8 @@ assert_that(items).is_type_of(list).is_length(3).contains("admin")
167
171
  - **Extracting**: flatten collections on attributes with `filter` and `sort` support.
168
172
  - **File assertions**: `exists()`, `is_file()`, `is_readable()`, `is_writable()`, `is_executable()` with `pathlib.Path` support.
169
173
  - **Snapshot testing**: store and compare data structures in JSON format, inspired by Jest.
174
+ - **Allure integration**: auto-attach structured diff and actual/expected data to Allure reports.
175
+ - **Behave step matchers**: ready-made parameter types (`PositiveInt`, `BoolLike`, etc.) for Behave step definitions.
170
176
  - **Extensions**: add custom assertions via `add_extension()`.
171
177
  - Strings, numbers, lists, tuples, sets, dicts, dates, booleans, objects, exceptions.
172
178
 
@@ -194,7 +200,7 @@ assert_that("hello").satisfies(~match.equal_to("world"))
194
200
  assert_that(150).satisfies(match.is_negative() | match.greater_than(100))
195
201
  ```
196
202
 
197
- Available matchers: `equal_to`, `greater_than`, `greater_than_or_equal_to`, `less_than`, `less_than_or_equal_to`, `between`, `close_to`, `is_none`, `is_not_none`, `is_instance_of`, `has_length`, `is_empty`, `is_not_empty`, `is_positive`, `is_negative`, `contains_string`, `matches_regex`, `is_uuid`, `is_non_empty_string`, `ignore`, `each_item`, `structure`.
203
+ Available matchers: `equal_to`, `greater_than`, `greater_than_or_equal_to`, `less_than`, `less_than_or_equal_to`, `between`, `close_to`, `is_none`, `is_not_none`, `is_instance_of`, `has_length`, `is_empty`, `is_not_empty`, `is_positive`, `is_negative`, `is_zero`, `is_even`, `is_odd`, `is_divisible_by`, `is_callable`, `is_in`, `has_property`, `contains_string`, `matches_regex`, `is_uuid`, `is_non_empty_string`, `ignore`, `each_item`, `structure`.
198
204
 
199
205
 
200
206
  ## Structural matching
@@ -347,19 +353,67 @@ assert_that({"a": 1, "b": 2, "c": 3}).snapshot()
347
353
  ```py
348
354
  from assertpy2 import add_extension
349
355
 
350
- def is_even(self):
351
- if self.val % 2 != 0:
352
- return self.error(f'{self.val} is not even!')
356
+ def is_5(self):
357
+ if self.val != 5:
358
+ return self.error(f'{self.val} is NOT 5!')
353
359
  return self
354
360
 
355
- add_extension(is_even)
361
+ add_extension(is_5)
356
362
 
357
- assert_that(4).is_even()
363
+ assert_that(5).is_5()
358
364
  ```
359
365
 
360
366
  See the [full API reference](docs/api.md) for all assertion methods, examples, and advanced features.
361
367
 
362
368
 
369
+ ## Allure integration
370
+
371
+ When `allure-pytest` is installed, the pytest plugin auto-attaches structured failure data to Allure reports as JSON attachments.
372
+
373
+ ```bash
374
+ pip install assertpy2[allure]
375
+ ```
376
+
377
+ Three modes controlled via `pytest.ini` (or `pyproject.toml`):
378
+
379
+ | Mode | What is attached |
380
+ |---|---|
381
+ | `diff` (default) | Structured Diff JSON (path-level breakdown) |
382
+ | `full` | Structured Diff + actual/expected JSON |
383
+ | `off` | Nothing |
384
+
385
+ ```toml
386
+ # pyproject.toml
387
+ [tool.pytest.ini_options]
388
+ assertpy2_allure = "full"
389
+ ```
390
+
391
+
392
+ ## Behave step matchers
393
+
394
+ Ready-made parameter types for Behave step definitions:
395
+
396
+ ```bash
397
+ pip install assertpy2[behave]
398
+ ```
399
+
400
+ ```py
401
+ # in environment.py or steps/conftest.py
402
+ from assertpy2.behave_matchers import register_assertpy_types
403
+ register_assertpy_types()
404
+ ```
405
+
406
+ Then use in step definitions:
407
+
408
+ ```py
409
+ @given('a user aged {age:PositiveInt}')
410
+ def step_impl(context, age):
411
+ context.age = age # already validated as int > 0
412
+ ```
413
+
414
+ Available types: `PositiveInt`, `NonNegativeInt`, `PositiveFloat`, `NonEmptyString`, `BoolLike`.
415
+
416
+
363
417
  ## Migration from assertpy
364
418
 
365
419
  assertpy2 is a drop-in replacement for Python 3.10+. Change the import, everything else works:
@@ -95,7 +95,7 @@ FAILED test_example.py::test_comparison
95
95
  | **Async assertions** | No | No | No | **eventually() with polling** |
96
96
  | **Soft assertions** | No | No | Yes (not thread-safe) | **Yes (thread-safe, async-safe)** |
97
97
  | **Structured errors** | Rewrite only | Mismatch string | String only | **.actual .expected .diff** |
98
- | **Maintained** | N/A | Minimal | 2020 | **Active (2026)** |
98
+ | **Maintained** | N/A | Minimal | 2020 | **Active** |
99
99
 
100
100
  </div>
101
101
 
@@ -136,6 +136,8 @@ assert_that(items).is_type_of(list).is_length(3).contains("admin")
136
136
  - **Extracting**: flatten collections on attributes with `filter` and `sort` support.
137
137
  - **File assertions**: `exists()`, `is_file()`, `is_readable()`, `is_writable()`, `is_executable()` with `pathlib.Path` support.
138
138
  - **Snapshot testing**: store and compare data structures in JSON format, inspired by Jest.
139
+ - **Allure integration**: auto-attach structured diff and actual/expected data to Allure reports.
140
+ - **Behave step matchers**: ready-made parameter types (`PositiveInt`, `BoolLike`, etc.) for Behave step definitions.
139
141
  - **Extensions**: add custom assertions via `add_extension()`.
140
142
  - Strings, numbers, lists, tuples, sets, dicts, dates, booleans, objects, exceptions.
141
143
 
@@ -163,7 +165,7 @@ assert_that("hello").satisfies(~match.equal_to("world"))
163
165
  assert_that(150).satisfies(match.is_negative() | match.greater_than(100))
164
166
  ```
165
167
 
166
- Available matchers: `equal_to`, `greater_than`, `greater_than_or_equal_to`, `less_than`, `less_than_or_equal_to`, `between`, `close_to`, `is_none`, `is_not_none`, `is_instance_of`, `has_length`, `is_empty`, `is_not_empty`, `is_positive`, `is_negative`, `contains_string`, `matches_regex`, `is_uuid`, `is_non_empty_string`, `ignore`, `each_item`, `structure`.
168
+ Available matchers: `equal_to`, `greater_than`, `greater_than_or_equal_to`, `less_than`, `less_than_or_equal_to`, `between`, `close_to`, `is_none`, `is_not_none`, `is_instance_of`, `has_length`, `is_empty`, `is_not_empty`, `is_positive`, `is_negative`, `is_zero`, `is_even`, `is_odd`, `is_divisible_by`, `is_callable`, `is_in`, `has_property`, `contains_string`, `matches_regex`, `is_uuid`, `is_non_empty_string`, `ignore`, `each_item`, `structure`.
167
169
 
168
170
 
169
171
  ## Structural matching
@@ -316,19 +318,67 @@ assert_that({"a": 1, "b": 2, "c": 3}).snapshot()
316
318
  ```py
317
319
  from assertpy2 import add_extension
318
320
 
319
- def is_even(self):
320
- if self.val % 2 != 0:
321
- return self.error(f'{self.val} is not even!')
321
+ def is_5(self):
322
+ if self.val != 5:
323
+ return self.error(f'{self.val} is NOT 5!')
322
324
  return self
323
325
 
324
- add_extension(is_even)
326
+ add_extension(is_5)
325
327
 
326
- assert_that(4).is_even()
328
+ assert_that(5).is_5()
327
329
  ```
328
330
 
329
331
  See the [full API reference](docs/api.md) for all assertion methods, examples, and advanced features.
330
332
 
331
333
 
334
+ ## Allure integration
335
+
336
+ When `allure-pytest` is installed, the pytest plugin auto-attaches structured failure data to Allure reports as JSON attachments.
337
+
338
+ ```bash
339
+ pip install assertpy2[allure]
340
+ ```
341
+
342
+ Three modes controlled via `pytest.ini` (or `pyproject.toml`):
343
+
344
+ | Mode | What is attached |
345
+ |---|---|
346
+ | `diff` (default) | Structured Diff JSON (path-level breakdown) |
347
+ | `full` | Structured Diff + actual/expected JSON |
348
+ | `off` | Nothing |
349
+
350
+ ```toml
351
+ # pyproject.toml
352
+ [tool.pytest.ini_options]
353
+ assertpy2_allure = "full"
354
+ ```
355
+
356
+
357
+ ## Behave step matchers
358
+
359
+ Ready-made parameter types for Behave step definitions:
360
+
361
+ ```bash
362
+ pip install assertpy2[behave]
363
+ ```
364
+
365
+ ```py
366
+ # in environment.py or steps/conftest.py
367
+ from assertpy2.behave_matchers import register_assertpy_types
368
+ register_assertpy_types()
369
+ ```
370
+
371
+ Then use in step definitions:
372
+
373
+ ```py
374
+ @given('a user aged {age:PositiveInt}')
375
+ def step_impl(context, age):
376
+ context.age = age # already validated as int > 0
377
+ ```
378
+
379
+ Available types: `PositiveInt`, `NonNegativeInt`, `PositiveFloat`, `NonEmptyString`, `BoolLike`.
380
+
381
+
332
382
  ## Migration from assertpy
333
383
 
334
384
  assertpy2 is a drop-in replacement for Python 3.10+. Change the import, everything else works:
@@ -27,6 +27,8 @@ if TYPE_CHECKING:
27
27
  def is_type_of(self, some_type: type) -> Self: ...
28
28
  def is_instance_of(self, some_class: type) -> Self: ...
29
29
  def is_length(self, length: int) -> Self: ...
30
+ def is_callable(self) -> Self: ...
31
+ def is_not_callable(self) -> Self: ...
30
32
  def satisfies(self, matcher: Matcher | Callable[..., bool]) -> Self: ...
31
33
  # ContainsMixin - universal
32
34
  def is_in(self, *items: object) -> Self: ...
@@ -48,6 +50,10 @@ if TYPE_CHECKING:
48
50
  def is_digit(self) -> Self: ...
49
51
  def is_lower(self) -> Self: ...
50
52
  def is_upper(self) -> Self: ...
53
+ def is_alphanumeric(self) -> Self: ...
54
+ def is_whitespace(self) -> Self: ...
55
+ def contains_any_of(self, *items: str) -> Self: ...
56
+ def contains_none_of(self, *items: str) -> Self: ...
51
57
  def is_unicode(self) -> Self: ...
52
58
  # ContainsMixin
53
59
  def contains(self, *items: object) -> Self: ...
@@ -56,6 +62,8 @@ if TYPE_CHECKING:
56
62
  def contains_sequence(self, *items: object) -> Self: ...
57
63
  def contains_duplicates(self) -> Self: ...
58
64
  def does_not_contain_duplicates(self) -> Self: ...
65
+ def contains_exactly(self, *items: object) -> Self: ...
66
+ def contains_in_order(self, *items: object) -> Self: ...
59
67
  def is_empty(self) -> Self: ...
60
68
  def is_not_empty(self) -> Self: ...
61
69
 
@@ -74,6 +82,9 @@ if TYPE_CHECKING:
74
82
  def is_less_than_or_equal_to(self, other: object) -> Self: ...
75
83
  def is_positive(self) -> Self: ...
76
84
  def is_negative(self) -> Self: ...
85
+ def is_even(self) -> Self: ...
86
+ def is_odd(self) -> Self: ...
87
+ def is_divisible_by(self, divisor: int) -> Self: ...
77
88
  def is_between(self, low: object, high: object) -> Self: ...
78
89
  def is_not_between(self, low: object, high: object) -> Self: ...
79
90
  def is_close_to(self, other: object, tolerance: object) -> Self: ...
@@ -89,6 +100,8 @@ if TYPE_CHECKING:
89
100
  def contains_sequence(self, *items: object) -> Self: ...
90
101
  def contains_duplicates(self) -> Self: ...
91
102
  def does_not_contain_duplicates(self) -> Self: ...
103
+ def contains_exactly(self, *items: object) -> Self: ...
104
+ def contains_in_order(self, *items: object) -> Self: ...
92
105
  def is_empty(self) -> Self: ...
93
106
  def is_not_empty(self) -> Self: ...
94
107
  # CollectionMixin
@@ -100,6 +113,9 @@ if TYPE_CHECKING:
100
113
  def extracting(self, *names: object, **kwargs: object) -> Self: ...
101
114
  # BaseMixin
102
115
  def each(self, matcher: Matcher | Callable[..., bool]) -> Self: ...
116
+ def any_satisfy(self, matcher: Matcher | Callable[..., bool]) -> Self: ...
117
+ def all_satisfy(self, matcher: Matcher | Callable[..., bool]) -> Self: ...
118
+ def none_satisfy(self, matcher: Matcher | Callable[..., bool]) -> Self: ...
103
119
 
104
120
  class _DictAssertion(_CoreAssertion, Protocol):
105
121
  """Assertions available for ``dict`` values."""
@@ -127,6 +143,8 @@ if TYPE_CHECKING:
127
143
 
128
144
  def is_before(self, other: object) -> Self: ...
129
145
  def is_after(self, other: object) -> Self: ...
146
+ def is_before_or_equal_to(self, other: object) -> Self: ...
147
+ def is_after_or_equal_to(self, other: object) -> Self: ...
130
148
  def is_equal_to_ignoring_milliseconds(self, other: object) -> Self: ...
131
149
  def is_equal_to_ignoring_seconds(self, other: object) -> Self: ...
132
150
  def is_equal_to_ignoring_time(self, other: object) -> Self: ...
@@ -148,5 +166,6 @@ if TYPE_CHECKING:
148
166
  """Assertions available for callable values."""
149
167
 
150
168
  def raises(self, ex: type) -> Self: ...
169
+ def does_not_raise(self, ex: type) -> Self: ...
151
170
  def when_called_with(self, *some_args: object, **some_kwargs: object) -> Self: ...
152
171
  def eventually(self, *, timeout: float = ..., interval: float = ...) -> AsyncAssertionBuilder: ...
@@ -160,7 +160,7 @@ def soft_assertions() -> Iterator[None]:
160
160
 
161
161
  errs = _soft_err.get([])
162
162
  if errs and _soft_ctx.get() == 0:
163
- out = "soft assertion failures:\n" + "\n".join("%d. %s" % (i + 1, msg) for i, msg in enumerate(errs))
163
+ out = "soft assertion failures:\n" + "\n".join(f"{i + 1}. {msg}" for i, msg in enumerate(errs))
164
164
  _soft_err.set([])
165
165
  raise AssertionError(out)
166
166
 
@@ -300,7 +300,7 @@ def fail(msg=""):
300
300
  except TypeError as e:
301
301
  assert_that(str(e)).contains('unsupported operand')
302
302
  """
303
- raise AssertionError("Fail: %s!" % msg if msg else "Fail!")
303
+ raise AssertionError(f"Fail: {msg}!" if msg else "Fail!")
304
304
 
305
305
 
306
306
  def soft_fail(msg=""):
@@ -332,7 +332,7 @@ def soft_fail(msg=""):
332
332
 
333
333
  """
334
334
  if _soft_ctx.get():
335
- _soft_err.get().append("Fail: %s!" % msg if msg else "Fail!")
335
+ _soft_err.get().append(f"Fail: {msg}!" if msg else "Fail!")
336
336
  return
337
337
  fail(msg)
338
338
 
@@ -427,7 +427,7 @@ class WarningLoggingAdapter(logging.LoggerAdapter):
427
427
  prev = frame
428
428
 
429
429
  filename, lineno = _unwind(inspect.currentframe())
430
- return "[%s:%d]: %s" % (os.path.basename(filename), lineno, msg), kwargs
430
+ return f"[{os.path.basename(filename)}:{lineno}]: {msg}", kwargs
431
431
 
432
432
 
433
433
  _logger = logging.getLogger("assertpy2")
@@ -512,7 +512,7 @@ class AssertionBuilder(
512
512
  AssertionBuilder: returns this instance to chain to the next assertion, but only when
513
513
  ``AssertionError`` is not raised, as is the case when ``kind`` is ``warn`` or ``soft``.
514
514
  """
515
- out = "%s%s" % ("[%s] " % self.description if len(self.description) > 0 else "", msg)
515
+ out = f"{f'[{self.description}] ' if len(self.description) > 0 else ''}{msg}"
516
516
  if self.kind == "warn":
517
517
  self.logger.warning(out)
518
518
  return self
@@ -73,8 +73,8 @@ class AsyncAssertionBuilder:
73
73
  last_error = exc
74
74
  if loop.time() >= deadline:
75
75
  raise AssertionError(
76
- "Expected condition not met after %.1f seconds. Last failure: %s"
77
- % (self._timeout, last_error)
76
+ f"Expected condition not met after {self._timeout:.1f} seconds."
77
+ f" Last failure: {last_error}"
78
78
  ) from last_error
79
79
  await asyncio.sleep(self._interval)
80
80