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.
- assertpy2-2.6.0/.gitattributes +2 -0
- assertpy2-2.6.0/.github/dependabot.yml +20 -0
- assertpy2-2.6.0/.github/workflows/docs.yml +57 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/.gitignore +1 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/PKG-INFO +25 -25
- {assertpy2-2.5.0 → assertpy2-2.6.0}/README.md +23 -23
- assertpy2-2.6.0/SECURITY.md +24 -0
- assertpy2-2.6.0/assertpy2/_compat.py +15 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/_mixin_base.py +13 -3
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/_typing.py +4 -3
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/assertpy.py +6 -3
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/async_assertions.py +1 -1
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/base.py +97 -87
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/bytes_mixin.py +1 -1
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/collection.py +29 -29
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/contains.py +36 -31
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/date.py +1 -1
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/dict.py +23 -23
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/errors.py +3 -3
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/exception.py +16 -8
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/extracting.py +32 -32
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/file.py +1 -1
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/helpers.py +89 -69
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/json_mixin.py +1 -1
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/matchers.py +75 -7
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/numeric.py +1 -1
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/snapshot.py +2 -2
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/string.py +18 -18
- assertpy2-2.6.0/assertpy2/warning.py +123 -0
- assertpy2-2.6.0/docs/assertions.md +377 -0
- assertpy2-2.6.0/docs/comparison.md +170 -0
- assertpy2-2.6.0/docs/data.md +84 -0
- assertpy2-2.6.0/docs/errors.md +161 -0
- assertpy2-2.6.0/docs/extending.md +72 -0
- assertpy2-2.6.0/docs/fluent.md +104 -0
- assertpy2-2.6.0/docs/getting-started.md +55 -0
- assertpy2-2.6.0/docs/index.md +41 -0
- assertpy2-2.6.0/docs/integrations.md +120 -0
- assertpy2-2.6.0/docs/matchers.md +214 -0
- assertpy2-2.6.0/docs/migration.md +101 -0
- assertpy2-2.6.0/docs/testing.md +119 -0
- assertpy2-2.6.0/mkdocs.yml +81 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/pyproject.toml +12 -8
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_async.py +8 -8
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_bool.py +7 -9
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_class.py +7 -9
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_collection.py +40 -70
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_custom_dict.py +11 -21
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_custom_list.py +7 -9
- assertpy2-2.6.0/tests/test_datetime.py +440 -0
- assertpy2-2.6.0/tests/test_description.py +29 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_dict.py +95 -173
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_dict_compare.py +107 -163
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_dyn.py +17 -25
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_equals.py +15 -25
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_errors.py +1 -1
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_expected_exception.py +32 -46
- assertpy2-2.6.0/tests/test_expected_warning.py +177 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_extensions.py +29 -53
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_extracting.py +61 -105
- assertpy2-2.6.0/tests/test_fail.py +15 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_file.py +47 -91
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_in.py +11 -17
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_list.py +79 -141
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_namedtuple.py +11 -15
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_none.py +7 -9
- assertpy2-2.6.0/tests/test_numbers.py +643 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_phase2.py +83 -156
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_pytest_plugin.py +2 -2
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_readme.py +31 -41
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_rich_diff.py +68 -64
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_same_as.py +7 -9
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_snapshots.py +6 -12
- assertpy2-2.6.0/tests/test_soft.py +188 -0
- assertpy2-2.6.0/tests/test_soft_fail.py +61 -0
- assertpy2-2.6.0/tests/test_string.py +625 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_traceback.py +3 -1
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_type.py +14 -22
- {assertpy2-2.5.0 → assertpy2-2.6.0}/uv.lock +594 -51
- assertpy2-2.5.0/.github/dependabot.yml +0 -20
- assertpy2-2.5.0/SECURITY.md +0 -16
- assertpy2-2.5.0/docs/api.md +0 -2120
- assertpy2-2.5.0/tests/test_datetime.py +0 -526
- assertpy2-2.5.0/tests/test_description.py +0 -35
- assertpy2-2.5.0/tests/test_fail.py +0 -17
- assertpy2-2.5.0/tests/test_numbers.py +0 -791
- assertpy2-2.5.0/tests/test_soft.py +0 -219
- assertpy2-2.5.0/tests/test_soft_fail.py +0 -75
- assertpy2-2.5.0/tests/test_string.py +0 -773
- {assertpy2-2.5.0 → assertpy2-2.6.0}/.codecov.yml +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/ci.yml +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/codeql.yml +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/publish.yml +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/scorecard.yml +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/.github/workflows/zizmor.yml +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/CONTRIBUTING.md +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/LICENSE +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/__init__.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/behave_matchers.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/dynamic.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/py.typed +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/assertpy2/pytest_plugin.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/docs/logo-dark.svg +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/docs/logo.svg +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_behave_matchers.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_bytes.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_core.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_grouped_soft.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_json.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_matcher_registry.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_matchers.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_matchers_phase3.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_not.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_overloads.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_pipeline.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_recursive_compare.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_recursive_compare_attrs.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_recursive_compare_pydantic.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_regex_groups.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_structural.py +0 -0
- {assertpy2-2.5.0 → assertpy2-2.6.0}/tests/test_warn.py +0 -0
|
@@ -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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: assertpy2
|
|
3
|
-
Version: 2.
|
|
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.
|
|
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**](
|
|
127
|
-
- [**Structural matching**](
|
|
128
|
-
- [**Universal negation**](
|
|
129
|
-
- [**Collection pipeline**](
|
|
130
|
-
- [**Fluent chaining**](
|
|
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](
|
|
135
|
-
- [**Bytes assertions**](
|
|
136
|
-
- [**JSON assertions**](
|
|
137
|
-
- [**Dynamic assertions**](
|
|
138
|
-
- [**Dict comparison**](
|
|
139
|
-
- [**Extracting**](
|
|
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**](
|
|
144
|
-
- [**Async assertions**](
|
|
145
|
-
- [**Structured errors**](
|
|
146
|
-
- [**Rich pytest diffs**](
|
|
147
|
-
- [**Snapshot testing**](
|
|
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**](
|
|
157
|
-
- [**Regex group extraction**](
|
|
158
|
-
- [**Extensions**](
|
|
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**](
|
|
163
|
-
- [**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
|
|
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.
|
|
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**](
|
|
89
|
-
- [**Structural matching**](
|
|
90
|
-
- [**Universal negation**](
|
|
91
|
-
- [**Collection pipeline**](
|
|
92
|
-
- [**Fluent chaining**](
|
|
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](
|
|
97
|
-
- [**Bytes assertions**](
|
|
98
|
-
- [**JSON assertions**](
|
|
99
|
-
- [**Dynamic assertions**](
|
|
100
|
-
- [**Dict comparison**](
|
|
101
|
-
- [**Extracting**](
|
|
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**](
|
|
106
|
-
- [**Async assertions**](
|
|
107
|
-
- [**Structured errors**](
|
|
108
|
-
- [**Rich pytest diffs**](
|
|
109
|
-
- [**Snapshot testing**](
|
|
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**](
|
|
119
|
-
- [**Regex group extraction**](
|
|
120
|
-
- [**Extensions**](
|
|
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**](
|
|
125
|
-
- [**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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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.
|
|
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:
|