assertpy2 2.1.1__tar.gz → 2.1.2__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 (77) hide show
  1. assertpy2-2.1.2/.github/dependabot.yml +15 -0
  2. {assertpy2-2.1.1 → assertpy2-2.1.2}/.github/workflows/ci.yml +10 -7
  3. assertpy2-2.1.2/.github/workflows/codeql.yml +24 -0
  4. assertpy2-2.1.2/.github/workflows/publish.yml +38 -0
  5. assertpy2-2.1.2/.github/workflows/scorecard.yml +33 -0
  6. {assertpy2-2.1.1 → assertpy2-2.1.2}/PKG-INFO +24 -19
  7. {assertpy2-2.1.1 → assertpy2-2.1.2}/README.md +23 -18
  8. assertpy2-2.1.2/SECURITY.md +16 -0
  9. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/assertpy.py +1 -1
  10. {assertpy2-2.1.1 → assertpy2-2.1.2}/pyproject.toml +1 -1
  11. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_matchers.py +0 -2
  12. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_overloads.py +0 -2
  13. assertpy2-2.1.1/.github/workflows/publish.yml +0 -29
  14. assertpy2-2.1.1/CLAUDE.md +0 -113
  15. {assertpy2-2.1.1 → assertpy2-2.1.2}/.codecov.yml +0 -0
  16. {assertpy2-2.1.1 → assertpy2-2.1.2}/.gitignore +0 -0
  17. {assertpy2-2.1.1 → assertpy2-2.1.2}/CONTRIBUTING.md +0 -0
  18. {assertpy2-2.1.1 → assertpy2-2.1.2}/LICENSE +0 -0
  19. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/__init__.py +0 -0
  20. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/_typing.py +0 -0
  21. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/async_assertions.py +0 -0
  22. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/base.py +0 -0
  23. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/collection.py +0 -0
  24. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/contains.py +0 -0
  25. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/date.py +0 -0
  26. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/dict.py +0 -0
  27. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/dynamic.py +0 -0
  28. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/errors.py +0 -0
  29. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/exception.py +0 -0
  30. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/extracting.py +0 -0
  31. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/file.py +0 -0
  32. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/helpers.py +0 -0
  33. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/matchers.py +0 -0
  34. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/numeric.py +0 -0
  35. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/py.typed +0 -0
  36. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/pytest_plugin.py +0 -0
  37. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/snapshot.py +0 -0
  38. {assertpy2-2.1.1 → assertpy2-2.1.2}/assertpy2/string.py +0 -0
  39. {assertpy2-2.1.1 → assertpy2-2.1.2}/docs/api.md +0 -0
  40. {assertpy2-2.1.1 → assertpy2-2.1.2}/docs/logo-dark.svg +0 -0
  41. {assertpy2-2.1.1 → assertpy2-2.1.2}/docs/logo.svg +0 -0
  42. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_async.py +0 -0
  43. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_bool.py +0 -0
  44. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_class.py +0 -0
  45. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_collection.py +0 -0
  46. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_core.py +0 -0
  47. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_custom_dict.py +0 -0
  48. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_custom_list.py +0 -0
  49. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_datetime.py +0 -0
  50. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_description.py +0 -0
  51. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_dict.py +0 -0
  52. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_dict_compare.py +0 -0
  53. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_dyn.py +0 -0
  54. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_equals.py +0 -0
  55. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_errors.py +0 -0
  56. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_expected_exception.py +0 -0
  57. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_extensions.py +0 -0
  58. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_extracting.py +0 -0
  59. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_fail.py +0 -0
  60. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_file.py +0 -0
  61. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_in.py +0 -0
  62. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_list.py +0 -0
  63. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_namedtuple.py +0 -0
  64. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_none.py +0 -0
  65. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_numbers.py +0 -0
  66. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_pytest_plugin.py +0 -0
  67. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_readme.py +0 -0
  68. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_same_as.py +0 -0
  69. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_snapshots.py +0 -0
  70. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_soft.py +0 -0
  71. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_soft_fail.py +0 -0
  72. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_string.py +0 -0
  73. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_structural.py +0 -0
  74. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_traceback.py +0 -0
  75. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_type.py +0 -0
  76. {assertpy2-2.1.1 → assertpy2-2.1.2}/tests/test_warn.py +0 -0
  77. {assertpy2-2.1.1 → assertpy2-2.1.2}/uv.lock +0 -0
@@ -0,0 +1,15 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ commit-message:
8
+ prefix: "chore"
9
+
10
+ - package-ecosystem: "github-actions"
11
+ directory: "/"
12
+ schedule:
13
+ interval: "weekly"
14
+ commit-message:
15
+ prefix: "ci"
@@ -6,6 +6,9 @@ on:
6
6
  pull_request:
7
7
  branches: [main]
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  jobs:
10
13
  test:
11
14
  runs-on: ubuntu-latest
@@ -13,13 +16,13 @@ jobs:
13
16
  matrix:
14
17
  python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
15
18
  steps:
16
- - uses: actions/checkout@v4
19
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
17
20
 
18
21
  - name: Install uv
19
- uses: astral-sh/setup-uv@v6
22
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
20
23
 
21
24
  - name: Set up Python ${{ matrix.python-version }}
22
- uses: actions/setup-python@v5
25
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
23
26
  with:
24
27
  python-version: ${{ matrix.python-version }}
25
28
  allow-prereleases: true
@@ -35,7 +38,7 @@ jobs:
35
38
 
36
39
  - name: Upload coverage to Codecov
37
40
  if: matrix.python-version == '3.14'
38
- uses: codecov/codecov-action@v6
41
+ uses: codecov/codecov-action@cddd853df119a48c5be31a973f8cd97e12e35e16 # v6.0.1
39
42
  with:
40
43
  token: ${{ secrets.CODECOV_TOKEN }}
41
44
  files: coverage.xml
@@ -43,13 +46,13 @@ jobs:
43
46
  lint:
44
47
  runs-on: ubuntu-latest
45
48
  steps:
46
- - uses: actions/checkout@v4
49
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
47
50
 
48
51
  - name: Install uv
49
- uses: astral-sh/setup-uv@v6
52
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
50
53
 
51
54
  - name: Set up Python
52
- uses: actions/setup-python@v5
55
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
53
56
  with:
54
57
  python-version: "3.14"
55
58
 
@@ -0,0 +1,24 @@
1
+ name: CodeQL
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ schedule:
9
+ - cron: "0 3 * * 1"
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ analyze:
16
+ runs-on: ubuntu-latest
17
+ permissions:
18
+ security-events: write
19
+ steps:
20
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
21
+ - uses: github/codeql-action/init@f52b05f4acaaa234e44466e66d29050e135ea9ef # v4.36.0
22
+ with:
23
+ languages: python
24
+ - uses: github/codeql-action/analyze@f52b05f4acaaa234e44466e66d29050e135ea9ef # v4.36.0
@@ -0,0 +1,38 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ permissions:
15
+ id-token: write
16
+ contents: write
17
+ steps:
18
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
25
+ with:
26
+ python-version: "3.15"
27
+ allow-prereleases: true
28
+
29
+ - name: Build
30
+ run: uv build
31
+
32
+ - name: Publish to PyPI
33
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
34
+
35
+ - name: Upload to GitHub Release
36
+ run: gh release upload "${{ github.event.release.tag_name }}" dist/*
37
+ env:
38
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,33 @@
1
+ name: Scorecard
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ schedule:
7
+ - cron: "30 1 * * 6"
8
+
9
+ permissions: read-all
10
+
11
+ jobs:
12
+ analysis:
13
+ runs-on: ubuntu-latest
14
+ permissions:
15
+ security-events: write
16
+ id-token: write
17
+ steps:
18
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19
+ with:
20
+ persist-credentials: false
21
+ - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
22
+ with:
23
+ results_file: results.sarif
24
+ results_format: sarif
25
+ publish_results: true
26
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
27
+ with:
28
+ name: SARIF file
29
+ path: results.sarif
30
+ retention-days: 5
31
+ - uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
32
+ with:
33
+ sarif_file: results.sarif
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: assertpy2
3
- Version: 2.1.1
3
+ Version: 2.1.2
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
@@ -50,6 +50,9 @@ Description-Content-Type: text/markdown
50
50
  <a href="https://codecov.io/gh/Solganis/assertpy2"><img src="https://codecov.io/gh/Solganis/assertpy2/graph/badge.svg" alt="Coverage"></a>
51
51
  <a href="https://github.com/Solganis/assertpy2/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Solganis/assertpy2" alt="License"></a>
52
52
  <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>
53
+ <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>
54
+ <a href="https://scorecard.dev/viewer/?uri=github.com/Solganis/assertpy2"><img src="https://api.scorecard.dev/projects/github.com/Solganis/assertpy2/badge" alt="OpenSSF Scorecard"></a>
55
+ <a href="https://www.bestpractices.dev/projects/12990"><img src="https://www.bestpractices.dev/projects/12990/badge" alt="OpenSSF Best Practices"></a>
53
56
  </p>
54
57
 
55
58
 
@@ -109,6 +112,25 @@ FAILED test_example.py::test_comparison
109
112
  ```
110
113
 
111
114
 
115
+ ## Comparison
116
+
117
+ <div align="center">
118
+
119
+ | | pytest assert | PyHamcrest | assertpy | **assertpy2** |
120
+ |---|:---:|:---:|:---:|:---:|
121
+ | **Type safety** | Partial (mypy plugin) | No | No | **py.typed, @overload, Self** |
122
+ | **IDE autocomplete** | Generic | Generic | Generic | **Type-specific per value** |
123
+ | **Fluent chaining** | No | No | Yes | **Yes** |
124
+ | **Composable matchers** | No | Yes (functions) | No | **Yes (`&` `\|` `~` operators)** |
125
+ | **Structural matching** | No | Flat (has_entries) | No | **Recursive with matchers** |
126
+ | **Async assertions** | No | No | No | **eventually() with polling** |
127
+ | **Soft assertions** | No | No | Yes (not thread-safe) | **Yes (thread-safe, async-safe)** |
128
+ | **Structured errors** | Rewrite only | Mismatch string | String only | **.actual .expected .diff** |
129
+ | **Maintained** | N/A | Minimal | 2020 | **Active (2026)** |
130
+
131
+ </div>
132
+
133
+
112
134
  ## Why fluent assertions?
113
135
 
114
136
  ```py
@@ -350,24 +372,7 @@ from assertpy import assert_that, soft_assertions
350
372
  from assertpy2 import assert_that, soft_assertions
351
373
  ```
352
374
 
353
- <div align="center">
354
-
355
- | | assertpy | assertpy2 |
356
- |---|---|---|
357
- | Maintained | Last release 2020 | Active |
358
- | Python | 2.7+ | 3.10-3.15 |
359
- | Type safety | No annotations | `Self` return types, `py.typed`, typed `@overload` on `assert_that()` |
360
- | IDE support | No type info | Full autocomplete, type-specific method suggestions |
361
- | Matchers | None | Composable matchers with `&` `\|` `~` operators |
362
- | Structural matching | None | `matches_structure()` with recursive dict validation |
363
- | Async | None | `eventually()` with polling/retry |
364
- | Error reporting | Flat strings | `AssertionFailure` with `.actual`, `.expected`, `.diff` |
365
- | Pytest integration | None | Rich diff sections in failure reports |
366
- | Soft assertions | Global state, not thread-safe | `contextvars`, thread-safe and async-safe |
367
- | Security | [CVE in snapshots](https://github.com/assertpy/assertpy/issues/156) | Fixed |
368
- | Open bugs | 15+ unresolved | All resolved |
369
-
370
- </div>
375
+ See the [comparison table](#comparison) above for feature differences with other libraries.
371
376
 
372
377
 
373
378
  ## Contributing
@@ -19,6 +19,9 @@
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
  <a href="https://github.com/Solganis/assertpy2/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Solganis/assertpy2" alt="License"></a>
21
21
  <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>
22
+ <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>
23
+ <a href="https://scorecard.dev/viewer/?uri=github.com/Solganis/assertpy2"><img src="https://api.scorecard.dev/projects/github.com/Solganis/assertpy2/badge" alt="OpenSSF Scorecard"></a>
24
+ <a href="https://www.bestpractices.dev/projects/12990"><img src="https://www.bestpractices.dev/projects/12990/badge" alt="OpenSSF Best Practices"></a>
22
25
  </p>
23
26
 
24
27
 
@@ -78,6 +81,25 @@ FAILED test_example.py::test_comparison
78
81
  ```
79
82
 
80
83
 
84
+ ## Comparison
85
+
86
+ <div align="center">
87
+
88
+ | | pytest assert | PyHamcrest | assertpy | **assertpy2** |
89
+ |---|:---:|:---:|:---:|:---:|
90
+ | **Type safety** | Partial (mypy plugin) | No | No | **py.typed, @overload, Self** |
91
+ | **IDE autocomplete** | Generic | Generic | Generic | **Type-specific per value** |
92
+ | **Fluent chaining** | No | No | Yes | **Yes** |
93
+ | **Composable matchers** | No | Yes (functions) | No | **Yes (`&` `\|` `~` operators)** |
94
+ | **Structural matching** | No | Flat (has_entries) | No | **Recursive with matchers** |
95
+ | **Async assertions** | No | No | No | **eventually() with polling** |
96
+ | **Soft assertions** | No | No | Yes (not thread-safe) | **Yes (thread-safe, async-safe)** |
97
+ | **Structured errors** | Rewrite only | Mismatch string | String only | **.actual .expected .diff** |
98
+ | **Maintained** | N/A | Minimal | 2020 | **Active (2026)** |
99
+
100
+ </div>
101
+
102
+
81
103
  ## Why fluent assertions?
82
104
 
83
105
  ```py
@@ -319,24 +341,7 @@ from assertpy import assert_that, soft_assertions
319
341
  from assertpy2 import assert_that, soft_assertions
320
342
  ```
321
343
 
322
- <div align="center">
323
-
324
- | | assertpy | assertpy2 |
325
- |---|---|---|
326
- | Maintained | Last release 2020 | Active |
327
- | Python | 2.7+ | 3.10-3.15 |
328
- | Type safety | No annotations | `Self` return types, `py.typed`, typed `@overload` on `assert_that()` |
329
- | IDE support | No type info | Full autocomplete, type-specific method suggestions |
330
- | Matchers | None | Composable matchers with `&` `\|` `~` operators |
331
- | Structural matching | None | `matches_structure()` with recursive dict validation |
332
- | Async | None | `eventually()` with polling/retry |
333
- | Error reporting | Flat strings | `AssertionFailure` with `.actual`, `.expected`, `.diff` |
334
- | Pytest integration | None | Rich diff sections in failure reports |
335
- | Soft assertions | Global state, not thread-safe | `contextvars`, thread-safe and async-safe |
336
- | Security | [CVE in snapshots](https://github.com/assertpy/assertpy/issues/156) | Fixed |
337
- | Open bugs | 15+ unresolved | All resolved |
338
-
339
- </div>
344
+ See the [comparison table](#comparison) above for feature differences with other libraries.
340
345
 
341
346
 
342
347
  ## Contributing
@@ -0,0 +1,16 @@
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
+ Please report security vulnerabilities by emailing solganis.dev@gmail.com.
13
+
14
+ Do not open a public issue for security vulnerabilities.
15
+
16
+ You should receive a response within 48 hours. If the vulnerability is confirmed, a fix will be released as soon as possible.
@@ -73,7 +73,7 @@ from .numeric import NumericMixin
73
73
  from .snapshot import SnapshotMixin
74
74
  from .string import StringMixin
75
75
 
76
- __version__ = "2.1.1"
76
+ __version__ = "2.1.2"
77
77
 
78
78
  __tracebackhide__ = True # clean tracebacks via py.test integration
79
79
  contextlib.__tracebackhide__ = True # monkey patch contextlib with clean py.test tracebacks
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "assertpy2"
3
- version = "2.1.1"
3
+ version = "2.1.2"
4
4
  description = "Fluent assertion library for Python with full type safety and soft assertions"
5
5
  readme = "README.md"
6
6
  license = "BSD-3-Clause"
@@ -490,8 +490,6 @@ class TestContainsWithMatcher:
490
490
 
491
491
 
492
492
  class TestDescribeCoverage:
493
- """Ensures all matcher describe() methods are exercised."""
494
-
495
493
  def test_greater_than_or_equal_to_describe(self):
496
494
  assert_that(match.greater_than_or_equal_to(5).describe()).contains("greater than or equal to")
497
495
 
@@ -6,8 +6,6 @@ from assertpy2.assertpy import AssertionBuilder
6
6
 
7
7
 
8
8
  class TestOverloadsRuntimeBehavior:
9
- """Verify that @overload decorators don't affect runtime behavior."""
10
-
11
9
  def test_str_returns_builder(self):
12
10
  result = assert_that("hello")
13
11
  assert isinstance(result, AssertionBuilder)
@@ -1,29 +0,0 @@
1
- name: Publish to PyPI
2
-
3
- on:
4
- release:
5
- types: [published]
6
-
7
- jobs:
8
- publish:
9
- runs-on: ubuntu-latest
10
- environment: pypi
11
- permissions:
12
- id-token: write
13
- steps:
14
- - uses: actions/checkout@v4
15
-
16
- - name: Install uv
17
- uses: astral-sh/setup-uv@v6
18
-
19
- - name: Set up Python
20
- uses: actions/setup-python@v5
21
- with:
22
- python-version: "3.15"
23
- allow-prereleases: true
24
-
25
- - name: Build
26
- run: uv build
27
-
28
- - name: Publish to PyPI
29
- uses: pypa/gh-action-pypi-publish@release/v1
assertpy2-2.1.1/CLAUDE.md DELETED
@@ -1,113 +0,0 @@
1
- # assertpy2
2
-
3
- Fluent assertion library for Python. Fork of assertpy с type safety, soft assertions, snapshot testing.
4
- Python 3.10+, единственная зависимость: typing_extensions>=4.0.
5
-
6
- ---
7
-
8
- ## Project layout
9
-
10
- ```
11
- assertpy2/
12
- ├── assertpy.py # assert_that(), AssertionBuilder, soft_assertions(), fail()
13
- ├── async_assertions.py # AsyncAssertionBuilder - eventually() polling
14
- ├── base.py # BaseMixin - is_equal_to, satisfies, each, matches_structure
15
- ├── string.py # StringMixin - starts_with, ends_with, matches, is_alpha, is_digit
16
- ├── numeric.py # NumericMixin - is_zero, is_positive, is_between, is_close_to
17
- ├── collection.py # CollectionMixin - is_iterable, is_subset_of, is_sorted
18
- ├── contains.py # ContainsMixin - contains, does_not_contain, is_empty
19
- ├── dict.py # DictMixin - contains_key, contains_value, contains_entry
20
- ├── date.py # DateMixin - is_before, is_after, is_equal_to_ignoring_time
21
- ├── file.py # FileMixin + contents_of() - exists, is_file, is_directory
22
- ├── exception.py # ExceptionMixin - raises, when_called_with
23
- ├── extracting.py # ExtractingMixin - extracting with filter/sort
24
- ├── dynamic.py # DynamicMixin - has_<attr>() via __getattr__
25
- ├── snapshot.py # SnapshotMixin - snapshot testing
26
- ├── helpers.py # HelpersMixin - _dict_not_equal, _fmt_items, validation
27
- ├── errors.py # AssertionFailure, DiffResult, DiffEntry - structured errors
28
- ├── matchers.py # Matcher protocol, composable matchers, match namespace
29
- ├── pytest_plugin.py # pytest entry point - rich diff output for AssertionFailure
30
- ├── _typing.py # TYPE_CHECKING-only Protocol classes for @overload return types
31
- ├── __init__.py # Public API exports
32
- └── py.typed # PEP 561 marker
33
-
34
- tests/ # 100% coverage required
35
- ```
36
-
37
- ---
38
-
39
- ## Architecture
40
-
41
- - AssertionBuilder наследует миксины. Каждый миксин - одна категория assertions.
42
- - Все assertion-методы возвращают Self для chaining.
43
- - error() в AssertionBuilder маршрутизирует: raise (default), log (warn), collect (soft).
44
- - __tracebackhide__ = True во всех миксинах для чистого pytest traceback.
45
- - Расширения через add_extension(func) - динамическая привязка через types.MethodType.
46
-
47
- Правило: новые assertions добавляются в существующий миксин по категории.
48
- Новый миксин создаётся только для принципиально новой категории (не для 1-2 методов).
49
-
50
- ---
51
-
52
- ## Tooling
53
-
54
- ```
55
- uv sync
56
- uv run pytest
57
- uv run pytest -v --cov=assertpy2 --cov-report=term-missing
58
- uv run ruff check .
59
- uv run ruff format .
60
- uv run ruff format --check .
61
- ```
62
-
63
- ---
64
-
65
- ## CI/CD
66
-
67
- GitHub Actions, два workflow:
68
-
69
- **CI** (.github/workflows/ci.yml):
70
- - Триггер: push/PR в main
71
- - Матрица: Python 3.10-3.15
72
- - Шаги: uv sync, ruff check, pytest с coverage (xml + term-missing)
73
- - Codecov upload: только с Python 3.14 (token в secrets)
74
- - Отдельный lint job: ruff check + ruff format --check
75
-
76
- **Publish** (.github/workflows/publish.yml):
77
- - Триггер: GitHub Release (published)
78
- - Trusted Publisher (id-token: write), без API-ключей
79
- - uv build, pypa/gh-action-pypi-publish
80
-
81
- Правила:
82
- - Версия только в pyproject.toml (одно место)
83
- - Релиз: обновить version в pyproject.toml, создать GitHub Release с тегом
84
- - Не мержить без зелёного CI
85
-
86
- ---
87
-
88
- ## Key dependencies
89
-
90
- | Package | Version | Role |
91
- |---|---|---|
92
- | typing_extensions | >=4.0 | Self type, runtime-free typing |
93
- | pytest | >=9.0.3 | test runner (dev) |
94
- | pytest-cov | >=6.1 | coverage (dev) |
95
- | ruff | >=0.15.14 | linter + formatter (dev) |
96
-
97
- ---
98
-
99
- ## Naming
100
-
101
- - Assertion-методы: is_*, has_*, does_not_*, contains_*, starts_with, ends_with
102
- - Новые assertions следуют существующему паттерну: глагол + предикат
103
- - Тестовые файлы: test_<feature>.py
104
- - Миксины: <Category>Mixin
105
-
106
- ---
107
-
108
- ## Testing
109
-
110
- - Coverage 100%. Каждый assertion-метод покрыт: happy path, error path, edge cases.
111
- - Тесты используют pytest.raises(AssertionError) для проверки сообщений об ошибках.
112
- - match= в pytest.raises для валидации текста ошибки.
113
- - Snapshot-тесты хранят данные в tests/__snapshots__/.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes