Skip to content

Commit 231bcfa

Browse files
committed
docs: state what the public contract covers and what enforces it
1 parent 2e633c4 commit 231bcfa

3 files changed

Lines changed: 66 additions & 1 deletion

File tree

‎docs/concepts/stability.md‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Stability
2+
3+
What you can build on, and what enforces it. Every row below is held by a test that fails CI, not by
4+
an intention.
5+
6+
## The contract
7+
8+
| You can depend on | Enforced by |
9+
|---|---|
10+
| The 36 names `assertpy2` exports, and the fields of every record it hands you | [`test_public_surface.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_public_surface.py) pins both against a hand-written list |
11+
| Every assertion the type checker offers you existing at runtime | [`test_protocol_parity.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_protocol_parity.py) walks all ten protocols |
12+
| The type your chain has after each step | [`test_typing.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_typing.py), 123 `assert_type` checks under ty, mypy `--strict` and Pyright, zero suppressions |
13+
| One relation keeping one name across the API | [`test_api_vocabulary.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_api_vocabulary.py) |
14+
| The three-method `Matcher` protocol your custom matchers implement | [`test_matcher_parity.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_matcher_parity.py) |
15+
| The Allure attachment schema | versioned in its own `format` field, so a consumer branches on a number rather than guessing |
16+
| Your assertions keeping their verdict | 4100 tests at 100 % branch coverage, plus mutation testing |
17+
18+
The records in the first row are `AssertionOutcome`, `MatchResult`, `DiffEntry`, `DiffResult`, `Step`,
19+
`PollSample` and `PollTrace`. Fields are added, never renamed or removed, inside a major version.
20+
21+
Documentation is held to the same bar: every example in these guides is executed and type-checked in
22+
CI, so a snippet you copy is one that ran.
23+
24+
## Reading a failure from code
25+
26+
Failures carry [structured data](../guides/errors.md#structured-errors) for exactly this. Reach for it
27+
rather than for the rendered text:
28+
29+
| You want | Use |
30+
|---|---|
31+
| the values that were compared | `failure.actual`, `failure.expected` |
32+
| where they differ | `failure.diff.entries`, each with a `path` |
33+
| a location you can walk in code | [`entry.steps`](../guides/errors.md#paths-a-program-can-follow) |
34+
| every failure a soft block collected | `failure.failures` |
35+
| a verdict without an exception | [`check()`](../guides/errors.md#asking-instead-of-asserting) |
36+
37+
Those are covered by the table above. **Message wording is not**: it improves in minor releases, so a
38+
`pytest.raises(match=...)` written against our phrasing, or a snapshot of a failing run, is the one
39+
thing that predictably needs updating. Match on the exception type and read the fields.
40+
41+
## Upgrading
42+
43+
Semantic versioning, read strictly: a new assertion is a minor, a patch carries fixes only.
44+
45+
Two kinds of change ship in a minor, and the release notes list each one under **Behaviour changes**:
46+
47+
- **an input that was silently wrong starts raising**, such as an empty prefix that no value could fail
48+
- **a wrong verdict is corrected**, so an assertion that passed and should not have begins to fail
49+
50+
Both are the reason to read that section before upgrading. Nothing else in a minor is designed to
51+
change what your suite reports.
52+
53+
## Not part of the API
54+
55+
Anything whose name starts with `_`, and the module an assertion happens to live in. The reference
56+
lists `matches_structure()` because you can call it from `assert_that()`, and its anchor reads
57+
`assertpy2._satisfies.SatisfiesMixin.matches_structure` because that is where the code sits.
58+
Importing from there is not supported.
59+
60+
Python 3.10 and up. Dropping a version that upstream still supports would be a major.
61+
62+
Code written against [assertpy](../getting-started/migration.md) keeps working, and that holds for
63+
refactors: an internal change that would break a documented assertpy call is not internal.

‎docs/getting-started/migration.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,5 @@ New capabilities the original never had, ready whenever you want them:
106106
- [Async and blocking polling](../guides/testing.md#async-assertions) for eventual consistency.
107107
- [JSON Path / Schema](../guides/data.md), [regex group extraction](../guides/data.md#regex-group-extraction), and [bytes assertions](../guides/assertions.md#bytes--bytearray).
108108

109-
See the [comparison](comparison.md) for the full feature delta.
109+
See the [comparison](comparison.md) for the full feature delta, and [stability](../concepts/stability.md)
110+
for what is covered by a compatibility promise and what is not.

‎mkdocs.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ nav:
135135
- Patterns & recipes: recipes.md
136136
- Concepts:
137137
- Type safety: concepts/type-safety.md
138+
- Stability: concepts/stability.md
138139
- Extending:
139140
- Custom assertions: extending/custom-assertions.md
140141
- Integrations: extending/integrations.md

0 commit comments

Comments
 (0)