requiresthat 2025.6.19.3__py3-none-any.whl → 2025.6.20.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.
- requiresthat/_exceptions.py +1 -1
- requiresthat/_requires.py +55 -30
- {requiresthat-2025.6.19.3.dist-info → requiresthat-2025.6.20.0.dist-info}/METADATA +1 -1
- requiresthat-2025.6.20.0.dist-info/RECORD +8 -0
- requiresthat-2025.6.19.3.dist-info/RECORD +0 -8
- {requiresthat-2025.6.19.3.dist-info → requiresthat-2025.6.20.0.dist-info}/WHEEL +0 -0
- {requiresthat-2025.6.19.3.dist-info → requiresthat-2025.6.20.0.dist-info}/top_level.txt +0 -0
requiresthat/_exceptions.py
CHANGED
@@ -3,7 +3,7 @@ import textwrap
|
|
3
3
|
class RequirementNotFulfilledError(Exception):
|
4
4
|
"""Raise this when a requirement is found wanting"""
|
5
5
|
|
6
|
-
def __init__(self, that, when, subwhen=str(), msg=None):
|
6
|
+
def __init__(self, that, when, subwhen: str = str(), msg=None):
|
7
7
|
"""Show a default or a user-provided message indicating that some condition is unmet"""
|
8
8
|
|
9
9
|
if subwhen:
|
requiresthat/_requires.py
CHANGED
@@ -8,50 +8,63 @@ from ._when import When, APRIORI, POSTMORTEM, BEFOREANDAFTER
|
|
8
8
|
from ._exceptions import RequirementNotFulfilledError, NoCallableConstructError
|
9
9
|
|
10
10
|
def requires(that, when: When = APRIORI) -> Optional[Callable]:
|
11
|
-
"""Require
|
11
|
+
"""Require ``that`` of the decoratee, and require it ``when``
|
12
12
|
|
13
|
-
|
13
|
+
Fail if the associated construct is not callable.
|
14
|
+
|
15
|
+
Fail if the condition is not met: do not invoke the callable or prevent the operation from being
|
16
|
+
considered a success.
|
17
|
+
|
18
|
+
Needs the callable to be an instance method of a class, like so:
|
19
|
+
|
20
|
+
class C:
|
21
|
+
|
22
|
+
def __init__(self, data=None):
|
23
|
+
self.data = data
|
24
|
+
|
25
|
+
@requires(that='self.data is not None')
|
26
|
+
@requires(that='self.data == "spam"', when=APRIORI)
|
27
|
+
@requires(that='True is not False')
|
28
|
+
@requires(that='self.data != "spam"', when=POSTMORTEM)
|
29
|
+
@requires(that='len(self.data) >= 3', when=BEFOREANDAFTER)
|
30
|
+
def method(self):
|
31
|
+
self.data = 'ham'
|
32
|
+
|
33
|
+
X = C(data='spam')
|
34
|
+
X.method()
|
35
|
+
|
36
|
+
Try adding
|
37
|
+
|
38
|
+
the_impossible = '1 / 0'
|
39
|
+
and
|
40
|
+
@requires(the_impossible)
|
41
|
+
|
42
|
+
to the list of decorators above and watch what happens.
|
43
|
+
"""
|
44
|
+
def func_wrapper(__func: Callable) -> Optional[Callable]:
|
14
45
|
"""First-level wrap the decoratee"""
|
15
46
|
|
16
|
-
@wraps(
|
47
|
+
@wraps(__func)
|
17
48
|
def inner_wrapper(self, *pargs, **kwargs) -> Optional[Callable]:
|
18
49
|
"""Wrap the first-level wrapper
|
19
50
|
|
20
51
|
The wrapping stops here...
|
21
52
|
"""
|
22
|
-
|
23
|
-
|
24
|
-
except AssertionError as exc:
|
25
|
-
raise NoCallableConstructError(func) from exc
|
53
|
+
if not callable(__func):
|
54
|
+
raise NoCallableConstructError(__func)
|
26
55
|
else:
|
27
|
-
# Since we want to give detailed sub-failure diax in case of BEFOREANDAFTER,
|
28
|
-
# economisng on the ifs below is tricky.
|
29
56
|
if when == APRIORI:
|
30
|
-
|
31
|
-
|
32
|
-
except AssertionError as exc:
|
33
|
-
raise RequirementNotFulfilledError(that, when) from exc
|
34
|
-
else:
|
35
|
-
func(self, *pargs, **kwargs)
|
57
|
+
__assert(self, that, when)
|
58
|
+
__func(self, *pargs, **kwargs)
|
36
59
|
|
37
60
|
elif when == POSTMORTEM:
|
38
|
-
|
39
|
-
|
40
|
-
assert eval(that)
|
41
|
-
except AssertionError as exc:
|
42
|
-
raise RequirementNotFulfilledError(that, when) from exc
|
61
|
+
__func(self, *pargs, **kwargs)
|
62
|
+
__assert(self, that, when)
|
43
63
|
|
44
64
|
elif when == BEFOREANDAFTER:
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
raise RequirementNotFulfilledError(that, when, APRIORI) from exc
|
49
|
-
else:
|
50
|
-
func(self, *pargs, **kwargs)
|
51
|
-
try:
|
52
|
-
assert eval(that)
|
53
|
-
except AssertionError as exc:
|
54
|
-
raise RequirementNotFulfilledError(that, when, POSTMORTEM) from exc
|
65
|
+
__assert(self, that, when, APRIORI)
|
66
|
+
__func(self, *pargs, **kwargs)
|
67
|
+
__assert(self, that, when, POSTMORTEM)
|
55
68
|
|
56
69
|
# We don't need an else clause; trying to enlist something that's not in the enum
|
57
70
|
# will be penalised with an AttributeError, and small typos will be healed with a
|
@@ -60,3 +73,15 @@ def requires(that, when: When = APRIORI) -> Optional[Callable]:
|
|
60
73
|
return inner_wrapper
|
61
74
|
|
62
75
|
return func_wrapper
|
76
|
+
|
77
|
+
def __assert(self, that, when: When, subwhen: str = str()):
|
78
|
+
"""Do the actual testing and raise the proper exceptions
|
79
|
+
|
80
|
+
The reason we don't use assert here is to avoid the Knuthian dilemma of premature optimisation;
|
81
|
+
namely, that it nukes this useful tool, :-[
|
82
|
+
"""
|
83
|
+
try:
|
84
|
+
if not eval(that):
|
85
|
+
raise RequirementNotFulfilledError(that, when, subwhen) from None
|
86
|
+
except:
|
87
|
+
raise RequirementNotFulfilledError(that, when, subwhen) from None
|
@@ -0,0 +1,8 @@
|
|
1
|
+
requiresthat/__init__.py,sha256=VTyJru4K2_e7UEa0od6kXU7M8ovfD176GXEXqtNf-nY,154
|
2
|
+
requiresthat/_exceptions.py,sha256=kzzbS5F-h4Duj84lx6AXWUBHv56VD9jX8og_gJIvN9c,1282
|
3
|
+
requiresthat/_requires.py,sha256=DzQkzMl12UCtPXmjkwsG1zy93oPSZWUGhB1T7o69Vt0,3066
|
4
|
+
requiresthat/_when.py,sha256=VoGuvoG9WDEMIPeKnxAsjMTaCMhgSszf4xxa1vLn5aU,299
|
5
|
+
requiresthat-2025.6.20.0.dist-info/METADATA,sha256=dI6DS-yxNlCu_BLsxd1A5JnId2wbiCWrHJjtYXVH45o,2072
|
6
|
+
requiresthat-2025.6.20.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
+
requiresthat-2025.6.20.0.dist-info/top_level.txt,sha256=mUgMTpAG75GYtt5_rVajUyWp-O_1VrrkqRo_hY9L9So,13
|
8
|
+
requiresthat-2025.6.20.0.dist-info/RECORD,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
requiresthat/__init__.py,sha256=VTyJru4K2_e7UEa0od6kXU7M8ovfD176GXEXqtNf-nY,154
|
2
|
-
requiresthat/_exceptions.py,sha256=-Q9lhvHnSapP_UGMAnlXxwDaGcxagULIe5oP5h8a2IU,1275
|
3
|
-
requiresthat/_requires.py,sha256=CYL7Toy2ttTFmbOKaU4oX8l9aGkLVyiEY3NkOYq6lbg,2530
|
4
|
-
requiresthat/_when.py,sha256=VoGuvoG9WDEMIPeKnxAsjMTaCMhgSszf4xxa1vLn5aU,299
|
5
|
-
requiresthat-2025.6.19.3.dist-info/METADATA,sha256=gtJ9ERcZMNjdMkjIolzWnFMt_C-lIOl_rcLSsDustLQ,2072
|
6
|
-
requiresthat-2025.6.19.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
-
requiresthat-2025.6.19.3.dist-info/top_level.txt,sha256=mUgMTpAG75GYtt5_rVajUyWp-O_1VrrkqRo_hY9L9So,13
|
8
|
-
requiresthat-2025.6.19.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|