assertpy 2.35.0__py3-none-any.whl

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 (63) hide show
  1. assertpy/__init__.py +87 -0
  2. assertpy/_clustering.py +511 -0
  3. assertpy/_dangling.py +505 -0
  4. assertpy/_engine/__init__.py +1 -0
  5. assertpy/_engine/_builder_check_typing.py +1365 -0
  6. assertpy/_engine/_capable_typing.py +772 -0
  7. assertpy/_engine/_check_typing.py +753 -0
  8. assertpy/_engine/_compare.py +983 -0
  9. assertpy/_engine/_compat.py +38 -0
  10. assertpy/_engine/_contract.py +1926 -0
  11. assertpy/_engine/_diff.py +1058 -0
  12. assertpy/_engine/_equality.py +843 -0
  13. assertpy/_engine/_introspection.py +729 -0
  14. assertpy/_engine/_membership.py +522 -0
  15. assertpy/_engine/_mixin_base.py +258 -0
  16. assertpy/_engine/_negated_typing.py +2176 -0
  17. assertpy/_engine/_operations.py +75 -0
  18. assertpy/_engine/_ordering.py +884 -0
  19. assertpy/_engine/_pairing.py +70 -0
  20. assertpy/_engine/_path.py +158 -0
  21. assertpy/_engine/_poll_typing.py +6039 -0
  22. assertpy/_engine/_require.py +312 -0
  23. assertpy/_engine/_size.py +31 -0
  24. assertpy/_engine/_text.py +39 -0
  25. assertpy/_engine/_typing.py +1226 -0
  26. assertpy/_hints.py +1213 -0
  27. assertpy/_inline.py +484 -0
  28. assertpy/_matcher_impls.py +1876 -0
  29. assertpy/_passes.py +249 -0
  30. assertpy/_renamed.py +64 -0
  31. assertpy/_satisfies.py +835 -0
  32. assertpy/_snapshot_codec.py +290 -0
  33. assertpy/assertpy.py +2640 -0
  34. assertpy/async_assertions.py +864 -0
  35. assertpy/base.py +862 -0
  36. assertpy/behave_matchers.py +96 -0
  37. assertpy/bytes_mixin.py +158 -0
  38. assertpy/collection.py +528 -0
  39. assertpy/contains.py +1068 -0
  40. assertpy/dataframe.py +200 -0
  41. assertpy/date.py +327 -0
  42. assertpy/dict.py +249 -0
  43. assertpy/dynamic.py +222 -0
  44. assertpy/errors.py +782 -0
  45. assertpy/exception.py +838 -0
  46. assertpy/extracting.py +266 -0
  47. assertpy/file.py +324 -0
  48. assertpy/helpers.py +747 -0
  49. assertpy/http_mixin.py +218 -0
  50. assertpy/json_mixin.py +1265 -0
  51. assertpy/matchers.py +639 -0
  52. assertpy/numeric.py +696 -0
  53. assertpy/outcome.py +163 -0
  54. assertpy/py.typed +0 -0
  55. assertpy/pytest_plugin.py +1650 -0
  56. assertpy/snapshot.py +1031 -0
  57. assertpy/string.py +803 -0
  58. assertpy/warning.py +137 -0
  59. assertpy-2.35.0.dist-info/METADATA +324 -0
  60. assertpy-2.35.0.dist-info/RECORD +63 -0
  61. assertpy-2.35.0.dist-info/WHEEL +4 -0
  62. assertpy-2.35.0.dist-info/entry_points.txt +2 -0
  63. assertpy-2.35.0.dist-info/licenses/LICENSE +28 -0
assertpy/outcome.py ADDED
@@ -0,0 +1,163 @@
1
+ """The record of one failed assertion, between composing it and doing something about it.
2
+
3
+ `AssertionBuilder.error()` used to do both jobs in one body: it built the message, and then it decided
4
+ whether to raise, collect or log. Only the string survived that function, which is why the soft
5
+ collector can hold nothing else, why there is nowhere to hand a caller a result instead of an
6
+ exception, and why `eventually()` has to bypass `error()` entirely to attach its poll trace.
7
+
8
+ Splitting the two leaves a record in the middle. This is that record. It carries what was composed,
9
+ not what should happen to it: the decision belongs to the delivery half, which reads the builder's
10
+ mode.
11
+
12
+ `check()` is the first caller outside this package to read one, so the type is exported and documented
13
+ from here on. What it does **not** promise yet is that no field will be added: the soft collector is
14
+ next to hold these, and holding them is what will tell whether a failure's location belongs on the
15
+ record too.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass, field
21
+ from typing import TYPE_CHECKING, Final
22
+
23
+ if TYPE_CHECKING:
24
+ from collections.abc import Mapping
25
+
26
+ from .errors import DiffResult, PollTrace
27
+
28
+
29
+ class _Missing:
30
+ """The type of `MISSING`, so it reprs as itself in a dataclass and in a debugger."""
31
+
32
+ __slots__ = ()
33
+
34
+ def __repr__(self) -> str:
35
+ return "MISSING"
36
+
37
+
38
+ MISSING: Final = _Missing()
39
+ """Stands in for an argument the caller did not pass.
40
+
41
+ ``None`` cannot do that job: an assertion comparing a value against ``None`` passes ``None`` as a real
42
+ expected value, and the two readings are then indistinguishable. The same ambiguity in `DiffEntry`
43
+ used to render a key whose value is ``None`` as a key that is not there at all.
44
+ """
45
+
46
+
47
+ @dataclass(frozen=True, slots=True)
48
+ class Requirement:
49
+ """What was asked of the value, as data rather than as a sentence.
50
+
51
+ A consumer reading `AssertionOutcome` could tell what failed only by parsing the message: the
52
+ fields beside it answer what the values were, not what was asked of them. `expected` carries five
53
+ different things depending on the family, the operand for `is_equal_to(2)`, a tuple for
54
+ `contains(2)`, a rendered description for `satisfies(...)`, a type for `is_instance_of(str)`, and
55
+ nothing at all for `is_empty()`, so grouping failures across a suite meant reading English.
56
+ """
57
+
58
+ operation: str
59
+ """The name the caller wrote, as it appears in the API.
60
+
61
+ A delegating assertion answers its own name rather than the one it delegates to: `is_positive()`
62
+ asks `is_greater_than(0)` underneath, and the reader wants the assertion in the test.
63
+ """
64
+
65
+ parameters: Mapping[str, object] = field(default_factory=dict)
66
+ """Every declared parameter with the value the assertion ran with, by name.
67
+
68
+ Bound values rather than the call as written, which means a parameter left out appears with its
69
+ default. Two spellings of one requirement then group as one: `is_close_to(9, 0.1)` and
70
+ `is_close_to(other=9, tolerance=0.1)` both read ``{"other": 9, "tolerance": 0.1}``, where a
71
+ positional tuple would also be indistinguishable from a two-operand `contains`.
72
+
73
+ The values are the objects themselves, as `actual` and `expected` already are, so a consumer that
74
+ serialises has the same work to do here as there.
75
+ """
76
+
77
+ negated: bool = False
78
+ """Whether the assertion ran through `not_`, where holding is the failure."""
79
+
80
+
81
+ @dataclass(frozen=True, slots=True, kw_only=True)
82
+ class AssertionOutcome:
83
+ """What one assertion decided, as a value rather than as a raised exception.
84
+
85
+ Returned by [`check()`][assertpy.assertpy.AssertionBuilder.check], which runs an assertion for its
86
+ verdict instead of for its failure. Truthy when the assertion passed, so it reads as the answer to
87
+ the question it was asked.
88
+ """
89
+
90
+ passed: bool = False
91
+ """Whether the assertion held.
92
+
93
+ ``False`` on every record the failure path builds, which is all of them until something asks for a
94
+ verdict: a failure is composed only when there is one.
95
+ """
96
+
97
+ message: str = ""
98
+ """The full failure text, description prefix and all, exactly as it reaches the reader.
99
+
100
+ Empty on a passing outcome. There is no message for an assertion that held, and inventing one
101
+ would put words in the report of anything that logs whatever it is handed.
102
+ """
103
+
104
+ actual: object = None
105
+ """The value under test. Filled from the builder when the assertion did not name one itself, so
106
+ every failure carries it, and `actual_provided` says which of the two happened."""
107
+
108
+ actual_provided: bool = False
109
+ """Whether the assertion passed ``actual`` itself rather than having it filled in.
110
+
111
+ Read by anything that renders: a value the assertion chose to name is worth showing, one filled in
112
+ from the builder is usually already in the message.
113
+ """
114
+
115
+ expected: object = MISSING
116
+ diff: DiffResult | None = None
117
+
118
+ requirement: Requirement | None = None
119
+ """Which operation was asked, with which parameters, and whether it was negated.
120
+
121
+ ``None`` where no operation was asked, and that is a limit of the contract rather than a gap to be
122
+ closed later. Three shapes have no operation to name: `fail()`, a bare `error()` carrying a
123
+ message of the caller's own, and a precondition of one of the few members `_operations.py` records
124
+ as asserting nothing, where the failure is about the value's shape and not about a requirement.
125
+ """
126
+
127
+ trace: PollTrace | None = None
128
+ """The convergence telemetry of a poll that timed out.
129
+
130
+ Here rather than only on the exception because a polling assertion under a soft block or in warn
131
+ mode never builds one: it goes through the same delivery as everything else, and the trace used to
132
+ stop at that boundary.
133
+ """
134
+
135
+ group: str | None = None
136
+ """The label a soft block was grouping under when this was collected.
137
+
138
+ Only a soft block groups, so this is ``None`` everywhere else, including on a failure that was
139
+ raised. Kept on the record rather than beside it so a collected failure stays one thing.
140
+ """
141
+
142
+ location: tuple[str, int] | None = None
143
+ """The ``(file, line)`` of the caller, on a failure that was collected rather than raised.
144
+
145
+ ``None`` on a raised failure, whose traceback is the better answer, and where finding this costs a
146
+ walk of the whole stack that nothing would read.
147
+ """
148
+
149
+ hint: str | None = None
150
+ """The diagnostic line, kept apart from ``message`` as well as glued into it.
151
+
152
+ It is glued in because that is where a reader needs it, and kept apart because once it is part of
153
+ the string nothing downstream can tell it from the assertion's own words.
154
+ """
155
+
156
+ @property
157
+ def has_expected(self) -> bool:
158
+ """Whether an expected value was named at all, which ``expected is not None`` cannot answer."""
159
+ return self.expected is not MISSING
160
+
161
+ def __bool__(self) -> bool:
162
+ """The verdict, so ``if assert_that(x).check().is_positive():`` reads as the question it asks."""
163
+ return self.passed
assertpy/py.typed ADDED
File without changes