crosshair-tool 0.0.56__cp39-cp39-macosx_11_0_arm64.whl → 0.0.100__cp39-cp39-macosx_11_0_arm64.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 (123) hide show
  1. _crosshair_tracers.cpython-39-darwin.so +0 -0
  2. crosshair/__init__.py +1 -1
  3. crosshair/_mark_stacks.h +51 -24
  4. crosshair/_tracers.h +9 -5
  5. crosshair/_tracers_test.py +19 -9
  6. crosshair/auditwall.py +9 -8
  7. crosshair/auditwall_test.py +31 -19
  8. crosshair/codeconfig.py +3 -2
  9. crosshair/condition_parser.py +17 -133
  10. crosshair/condition_parser_test.py +54 -96
  11. crosshair/conftest.py +1 -1
  12. crosshair/copyext.py +91 -22
  13. crosshair/copyext_test.py +33 -0
  14. crosshair/core.py +259 -203
  15. crosshair/core_and_libs.py +20 -0
  16. crosshair/core_regestered_types_test.py +82 -0
  17. crosshair/core_test.py +693 -664
  18. crosshair/diff_behavior.py +76 -21
  19. crosshair/diff_behavior_test.py +132 -23
  20. crosshair/dynamic_typing.py +128 -18
  21. crosshair/dynamic_typing_test.py +91 -4
  22. crosshair/enforce.py +1 -6
  23. crosshair/enforce_test.py +15 -23
  24. crosshair/examples/check_examples_test.py +2 -1
  25. crosshair/fnutil.py +2 -3
  26. crosshair/fnutil_test.py +0 -7
  27. crosshair/fuzz_core_test.py +70 -83
  28. crosshair/libimpl/arraylib.py +10 -7
  29. crosshair/libimpl/binascii_ch_test.py +30 -0
  30. crosshair/libimpl/binascii_test.py +67 -0
  31. crosshair/libimpl/binasciilib.py +150 -0
  32. crosshair/libimpl/bisectlib_test.py +5 -5
  33. crosshair/libimpl/builtinslib.py +1002 -682
  34. crosshair/libimpl/builtinslib_ch_test.py +108 -30
  35. crosshair/libimpl/builtinslib_test.py +431 -143
  36. crosshair/libimpl/codecslib.py +22 -2
  37. crosshair/libimpl/codecslib_test.py +41 -9
  38. crosshair/libimpl/collectionslib.py +44 -8
  39. crosshair/libimpl/collectionslib_test.py +108 -20
  40. crosshair/libimpl/copylib.py +1 -1
  41. crosshair/libimpl/copylib_test.py +18 -0
  42. crosshair/libimpl/datetimelib.py +84 -67
  43. crosshair/libimpl/datetimelib_ch_test.py +12 -7
  44. crosshair/libimpl/datetimelib_test.py +5 -6
  45. crosshair/libimpl/decimallib.py +5257 -0
  46. crosshair/libimpl/decimallib_ch_test.py +78 -0
  47. crosshair/libimpl/decimallib_test.py +76 -0
  48. crosshair/libimpl/encodings/_encutil.py +21 -11
  49. crosshair/libimpl/fractionlib.py +16 -0
  50. crosshair/libimpl/fractionlib_test.py +80 -0
  51. crosshair/libimpl/functoolslib.py +19 -7
  52. crosshair/libimpl/functoolslib_test.py +22 -6
  53. crosshair/libimpl/hashliblib.py +30 -0
  54. crosshair/libimpl/hashliblib_test.py +18 -0
  55. crosshair/libimpl/heapqlib.py +32 -5
  56. crosshair/libimpl/heapqlib_test.py +15 -12
  57. crosshair/libimpl/iolib.py +7 -4
  58. crosshair/libimpl/ipaddresslib.py +8 -0
  59. crosshair/libimpl/itertoolslib_test.py +1 -1
  60. crosshair/libimpl/mathlib.py +165 -2
  61. crosshair/libimpl/mathlib_ch_test.py +44 -0
  62. crosshair/libimpl/mathlib_test.py +59 -16
  63. crosshair/libimpl/oslib.py +7 -0
  64. crosshair/libimpl/pathliblib_test.py +10 -0
  65. crosshair/libimpl/randomlib.py +1 -0
  66. crosshair/libimpl/randomlib_test.py +6 -4
  67. crosshair/libimpl/relib.py +180 -59
  68. crosshair/libimpl/relib_ch_test.py +26 -2
  69. crosshair/libimpl/relib_test.py +77 -14
  70. crosshair/libimpl/timelib.py +35 -13
  71. crosshair/libimpl/timelib_test.py +13 -3
  72. crosshair/libimpl/typeslib.py +15 -0
  73. crosshair/libimpl/typeslib_test.py +36 -0
  74. crosshair/libimpl/unicodedatalib_test.py +3 -3
  75. crosshair/libimpl/weakreflib.py +13 -0
  76. crosshair/libimpl/weakreflib_test.py +69 -0
  77. crosshair/libimpl/zliblib.py +15 -0
  78. crosshair/libimpl/zliblib_test.py +13 -0
  79. crosshair/lsp_server.py +21 -10
  80. crosshair/main.py +48 -28
  81. crosshair/main_test.py +59 -14
  82. crosshair/objectproxy.py +39 -14
  83. crosshair/objectproxy_test.py +27 -13
  84. crosshair/opcode_intercept.py +212 -24
  85. crosshair/opcode_intercept_test.py +172 -18
  86. crosshair/options.py +0 -1
  87. crosshair/patch_equivalence_test.py +5 -21
  88. crosshair/path_cover.py +7 -5
  89. crosshair/path_search.py +6 -4
  90. crosshair/path_search_test.py +1 -2
  91. crosshair/pathing_oracle.py +53 -10
  92. crosshair/pathing_oracle_test.py +21 -0
  93. crosshair/pure_importer_test.py +5 -21
  94. crosshair/register_contract.py +16 -6
  95. crosshair/register_contract_test.py +2 -14
  96. crosshair/simplestructs.py +154 -85
  97. crosshair/simplestructs_test.py +16 -2
  98. crosshair/smtlib.py +24 -0
  99. crosshair/smtlib_test.py +14 -0
  100. crosshair/statespace.py +319 -196
  101. crosshair/statespace_test.py +45 -0
  102. crosshair/stubs_parser.py +0 -2
  103. crosshair/test_util.py +87 -25
  104. crosshair/test_util_test.py +26 -0
  105. crosshair/tools/check_init_and_setup_coincide.py +0 -3
  106. crosshair/tools/generate_demo_table.py +2 -2
  107. crosshair/tracers.py +141 -49
  108. crosshair/type_repo.py +11 -4
  109. crosshair/unicode_categories.py +1 -0
  110. crosshair/util.py +158 -76
  111. crosshair/util_test.py +13 -20
  112. crosshair/watcher.py +4 -4
  113. crosshair/z3util.py +1 -1
  114. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/METADATA +45 -36
  115. crosshair_tool-0.0.100.dist-info/RECORD +176 -0
  116. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/WHEEL +2 -1
  117. crosshair/examples/hypothesis/__init__.py +0 -2
  118. crosshair/examples/hypothesis/bugs_detected/simple_strategies.py +0 -74
  119. crosshair_tool-0.0.56.dist-info/RECORD +0 -152
  120. /crosshair/{examples/hypothesis/bugs_detected/__init__.py → py.typed} +0 -0
  121. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/entry_points.txt +0 -0
  122. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info/licenses}/LICENSE +0 -0
  123. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,7 @@ import traceback
10
10
  import types
11
11
  from dataclasses import dataclass, replace
12
12
  from functools import partial, wraps
13
- from inspect import BoundArguments, Parameter, Signature
13
+ from inspect import BoundArguments, Signature
14
14
  from itertools import chain
15
15
  from typing import (
16
16
  Any,
@@ -41,35 +41,22 @@ try:
41
41
  except ModuleNotFoundError:
42
42
  deal = None # type: ignore
43
43
 
44
-
45
- try:
46
- import hypothesis
47
- from hypothesis import strategies as st
48
- from hypothesis.control import BuildContext
49
- from hypothesis.database import ExampleDatabase
50
- from hypothesis.internal.conjecture.data import ConjectureData
51
- except ImportError:
52
- hypothesis = None # type: ignore
53
- ExampleDatabase = object # type: ignore
54
-
55
44
  from crosshair.auditwall import opened_auditwall
56
45
  from crosshair.fnutil import FunctionInfo, fn_globals, set_first_arg_type
57
46
  from crosshair.options import AnalysisKind
58
47
  from crosshair.register_contract import get_contract
59
48
  from crosshair.tracers import NoTracing
60
49
  from crosshair.util import (
50
+ CrossHairInternal,
61
51
  DynamicScopeVar,
62
52
  EvalFriendlyReprContext,
63
53
  IdKeyedDict,
64
- IgnoreAttempt,
65
- UnexploredPath,
66
54
  debug,
67
55
  eval_friendly_repr,
68
56
  format_boundargs,
69
57
  frame_summary_for_fn,
70
58
  is_pure_python,
71
59
  sourcelines,
72
- test_stack,
73
60
  )
74
61
 
75
62
 
@@ -128,7 +115,7 @@ def get_doc_lines(thing: object) -> Iterable[Tuple[int, str]]:
128
115
  if not isinstance(firstnode, ast.Expr):
129
116
  return
130
117
  strnode = firstnode.value
131
- if not isinstance(strnode, ast.Str):
118
+ if not (isinstance(strnode, ast.Constant) and isinstance(strnode.value, str)):
132
119
  return
133
120
  end_lineno = getattr(strnode, "end_lineno", None)
134
121
  if end_lineno is not None:
@@ -457,7 +444,7 @@ class ConditionParser:
457
444
  def get_class_conditions(self, cls: type) -> ClassConditions:
458
445
  raise NotImplementedError
459
446
 
460
- def class_can_have_conditions(sel, cls: type) -> bool:
447
+ def class_can_have_conditions(self, cls: type) -> bool:
461
448
  raise NotImplementedError
462
449
 
463
450
 
@@ -478,7 +465,7 @@ class ConcreteConditionParser(ConditionParser):
478
465
  """
479
466
  raise NotImplementedError
480
467
 
481
- def class_can_have_conditions(sel, cls: type) -> bool:
468
+ def class_can_have_conditions(self, cls: type) -> bool:
482
469
  # We can't get conditions/line numbers for classes written in C.
483
470
  return is_pure_python(cls)
484
471
 
@@ -499,6 +486,7 @@ class ConcreteConditionParser(ConditionParser):
499
486
  method = cls.__dict__.get(method_name, None)
500
487
  super_method_conditions = super_methods.get(method_name)
501
488
  if super_method_conditions is not None:
489
+ # Re-type the super's `self` argument to be this class:
502
490
  revised_sig = set_first_arg_type(super_method_conditions.sig, cls)
503
491
  super_method_conditions = replace(
504
492
  super_method_conditions, sig=revised_sig
@@ -524,13 +512,16 @@ class ConcreteConditionParser(ConditionParser):
524
512
  # Selectively add conditions inferred from invariants:
525
513
  final_pre = list(conditions.pre)
526
514
  final_post = list(conditions.post)
527
- if method_name in ("__new__", "__repr__"):
528
- # __new__ isn't passed a concrete instance.
529
- # __repr__ is itself required for reporting problems with invariants.
530
- pass
531
- elif method_name == "__replace__":
532
- # TODO: remove this case when fixed in 3.13
533
- # see https://github.com/python/cpython/issues/114198
515
+ if method_name in (
516
+ "__new__", # a staticmethod, but not isinstance(staticmethod)
517
+ "__repr__", # is itself required for reporting problems with invariants.
518
+ # [set/del]attr can do anything; we can't resonably enforce invariants:
519
+ "__setattr__",
520
+ "__delattr__",
521
+ "__replace__", # Will raise an exception with most arbitrary **kwargs.
522
+ "__annotate__", # a staticmethod, but not isinstance(staticmethod)
523
+ "__annotate_func__",
524
+ ):
534
525
  pass
535
526
  elif method_name == "__del__":
536
527
  final_pre.extend(inv)
@@ -1117,112 +1108,6 @@ class AssertsParser(ConcreteConditionParser):
1117
1108
  return []
1118
1109
 
1119
1110
 
1120
- class CrossHairDatabaseWrapper(ExampleDatabase):
1121
- """Save buffers to the underlying database but discard all other actions."""
1122
-
1123
- def __init__(self, db: ExampleDatabase) -> None:
1124
- super().__init__()
1125
- self._db = db
1126
- from crosshair.core import realize
1127
-
1128
- self._realize = realize
1129
-
1130
- def save(self, key: bytes, value: bytes) -> None:
1131
- with NoTracing(), opened_auditwall():
1132
- realkey = self._realize(key)
1133
- realvalue = self._realize(value)
1134
- self._db.save(realkey, realvalue)
1135
-
1136
- def fetch(self, key: bytes) -> Iterable[bytes]:
1137
- return ()
1138
-
1139
- def delete(self, key: bytes, value: bytes) -> None:
1140
- pass
1141
-
1142
- def move(self, src: bytes, dest: bytes, value: bytes) -> None:
1143
- pass
1144
-
1145
-
1146
- class HypothesisParser(ConcreteConditionParser):
1147
- def __init__(self, toplevel_parser: Optional[ConditionParser] = None):
1148
- super().__init__(toplevel_parser)
1149
-
1150
- def _generate_args(self, payload: bytes, decorated_fn: Callable):
1151
- given_kwargs = decorated_fn.hypothesis._given_kwargs # type: ignore
1152
- strategy = st.fixed_dictionaries(given_kwargs)
1153
- data = ConjectureData.for_buffer(payload)
1154
- with BuildContext(data):
1155
- return data.draw(strategy)
1156
-
1157
- def _format_counterexample(
1158
- self,
1159
- fn: Callable,
1160
- args: BoundArguments,
1161
- return_val: object,
1162
- repr_overrides: IdKeyedDict,
1163
- ) -> Tuple[str, str]:
1164
- payload_bytes = args.arguments["payload"]
1165
- kwargs = self._generate_args(payload_bytes, fn)
1166
- sig = inspect.signature(fn.hypothesis.inner_test) # type: ignore
1167
- real_args = sig.bind(**kwargs)
1168
- return default_counterexample(fn.__name__, real_args, None, repr_overrides)
1169
-
1170
- def get_fn_conditions(self, ctxfn: FunctionInfo) -> Optional[Conditions]:
1171
- fn_and_sig = ctxfn.get_callable()
1172
- if fn_and_sig is None:
1173
- return None
1174
- (fn, sig) = fn_and_sig
1175
- if not getattr(fn, "is_hypothesis_test", False):
1176
- return None
1177
- handle = getattr(fn, "hypothesis", None)
1178
- if handle is None:
1179
- return None
1180
-
1181
- # Mess with the settings to wrap whatever database we're using for CrossHair
1182
- if hasattr(fn, "_hypothesis_internal_use_settings"):
1183
- db = fn._hypothesis_internal_use_settings.database # type: ignore
1184
- fn._hypothesis_internal_use_settings = hypothesis.settings( # type: ignore
1185
- database=CrossHairDatabaseWrapper(db),
1186
- phases=[hypothesis.Phase.generate],
1187
- )
1188
- fuzz_one = getattr(handle, "fuzz_one_input", None)
1189
- if fuzz_one is None:
1190
- return None
1191
-
1192
- filename, first_line, _lines = sourcelines(fn)
1193
- post = [
1194
- ConditionExpr(
1195
- POSTCONDITION,
1196
- lambda _: True,
1197
- filename,
1198
- first_line,
1199
- "",
1200
- )
1201
- ]
1202
- sig = inspect.Signature(
1203
- parameters=[
1204
- inspect.Parameter(
1205
- "payload", inspect.Parameter.POSITIONAL_ONLY, annotation=bytes
1206
- )
1207
- ]
1208
- )
1209
-
1210
- return Conditions(
1211
- fuzz_one,
1212
- fn,
1213
- [], # (pre)
1214
- post,
1215
- raises=frozenset(),
1216
- sig=sig,
1217
- mutable_args=None,
1218
- fn_syntax_messages=[],
1219
- counterexample_description_maker=partial(self._format_counterexample, fn),
1220
- )
1221
-
1222
- def get_class_invariants(self, cls: type) -> List[ConditionExpr]:
1223
- return []
1224
-
1225
-
1226
1111
  class RegisteredContractsParser(ConcreteConditionParser):
1227
1112
  """Parser for manually registered contracts."""
1228
1113
 
@@ -1310,7 +1195,7 @@ class RegisteredContractsParser(ConcreteConditionParser):
1310
1195
  fn_syntax_messages=[],
1311
1196
  )
1312
1197
 
1313
- def class_can_have_conditions(sel, cls: type) -> bool:
1198
+ def class_can_have_conditions(self, cls: type) -> bool:
1314
1199
  # We might have registered contracts for classes written in C, so we don't want
1315
1200
  # to skip evaluating conditions on the class methods.
1316
1201
  return True
@@ -1325,7 +1210,6 @@ _PARSER_MAP = {
1325
1210
  AnalysisKind.PEP316: Pep316Parser,
1326
1211
  AnalysisKind.icontract: IcontractParser,
1327
1212
  AnalysisKind.deal: DealParser,
1328
- AnalysisKind.hypothesis: HypothesisParser,
1329
1213
  }
1330
1214
 
1331
1215
 
@@ -1,8 +1,6 @@
1
1
  import inspect
2
2
  import json
3
3
  import sys
4
- import textwrap
5
- import unittest
6
4
  from typing import List
7
5
 
8
6
  import pytest
@@ -11,7 +9,6 @@ from crosshair.condition_parser import (
11
9
  AssertsParser,
12
10
  CompositeConditionParser,
13
11
  DealParser,
14
- HypothesisParser,
15
12
  IcontractParser,
16
13
  Pep316Parser,
17
14
  parse_sections,
@@ -31,11 +28,6 @@ try:
31
28
  except ImportError:
32
29
  deal = None # type: ignore
33
30
 
34
- try:
35
- import hypothesis # type: ignore
36
- except ImportError:
37
- hypothesis = None # type: ignore
38
-
39
31
 
40
32
  class LocallyDefiendException(Exception):
41
33
  pass
@@ -139,41 +131,32 @@ def test_parse_sphinx_raises() -> None:
139
131
  assert parse_sphinx_raises(sphinx_raises) == {LocallyDefiendException}
140
132
 
141
133
 
142
- class Pep316ParserTest(unittest.TestCase):
134
+ class TestPep316Parser:
143
135
  def test_class_parse(self) -> None:
144
136
  class_conditions = Pep316Parser().get_class_conditions(Foo)
145
- self.assertEqual(
146
- set([c.expr_source for c in class_conditions.inv]),
147
- set(["self.x >= 0", "self.y >= 0"]),
148
- )
149
- self.assertEqual(
150
- set(class_conditions.methods.keys()), set(["isready", "__init__"])
151
- )
137
+ assert set([c.expr_source for c in class_conditions.inv]) == {
138
+ "self.x >= 0",
139
+ "self.y >= 0",
140
+ }
141
+ assert {"isready", "__init__"} <= set(class_conditions.methods.keys())
152
142
  method = class_conditions.methods["isready"]
153
- self.assertEqual(
154
- set([c.expr_source for c in method.pre]),
155
- set(["self.x >= 0", "self.y >= 0"]),
156
- )
143
+ assert set([c.expr_source for c in method.pre]) == {
144
+ "self.x >= 0",
145
+ "self.y >= 0",
146
+ }
157
147
  startlineno = inspect.getsourcelines(Foo)[1]
158
- self.assertEqual(
159
- set([(c.expr_source, c.line) for c in method.post]),
160
- set(
161
- [
162
- ("self.x >= 0", startlineno + 7),
163
- ("self.y >= 0", startlineno + 12),
164
- ("__return__ == (self.x == 0)", startlineno + 24),
165
- ]
166
- ),
167
- )
148
+ assert set([(c.expr_source, c.line) for c in method.post]) == {
149
+ ("self.x >= 0", startlineno + 7),
150
+ ("self.y >= 0", startlineno + 12),
151
+ ("__return__ == (self.x == 0)", startlineno + 24),
152
+ }
168
153
 
169
154
  def test_single_line_condition(self) -> None:
170
155
  conditions = Pep316Parser().get_fn_conditions(
171
156
  FunctionInfo.from_fn(single_line_condition)
172
157
  )
173
158
  assert conditions is not None
174
- self.assertEqual(
175
- set([c.expr_source for c in conditions.post]), set(["__return__ >= x"])
176
- )
159
+ assert set([c.expr_source for c in conditions.post]) == {"__return__ >= x"}
177
160
 
178
161
  def test_implies_condition(self):
179
162
  conditions = Pep316Parser().get_fn_conditions(
@@ -188,37 +171,35 @@ class Pep316ParserTest(unittest.TestCase):
188
171
  FunctionInfo.from_fn(locally_defined_raises_condition)
189
172
  )
190
173
  assert conditions is not None
191
- self.assertEqual([], list(conditions.syntax_messages()))
192
- self.assertEqual(set([LocallyDefiendException]), conditions.raises)
174
+ assert [] == list(conditions.syntax_messages())
175
+ assert set([LocallyDefiendException]) == conditions.raises
193
176
 
194
177
  def test_tricky_raises_condition(self) -> None:
195
178
  conditions = Pep316Parser().get_fn_conditions(
196
179
  FunctionInfo.from_fn(tricky_raises_condition)
197
180
  )
198
181
  assert conditions is not None
199
- self.assertEqual([], list(conditions.syntax_messages()))
200
- self.assertEqual(conditions.raises, set([KeyError, json.JSONDecodeError]))
182
+ assert [] == list(conditions.syntax_messages())
183
+ assert conditions.raises == {KeyError, json.JSONDecodeError}
201
184
 
202
185
  def test_invariant_is_inherited(self) -> None:
203
186
  class_conditions = Pep316Parser().get_class_conditions(SubClassExample)
204
- self.assertEqual(set(class_conditions.methods.keys()), set(["foo", "__init__"]))
187
+ assert set(class_conditions.methods.keys()) == {"foo", "__init__"}
205
188
  method = class_conditions.methods["foo"]
206
- self.assertEqual(len(method.pre), 1)
207
- self.assertEqual(set([c.expr_source for c in method.pre]), set(["True"]))
208
- self.assertEqual(len(method.post), 2)
209
- self.assertEqual(
210
- set([c.expr_source for c in method.post]), set(["True", "False"])
211
- )
189
+ assert len(method.pre) == 1
190
+ assert set([c.expr_source for c in method.pre]) == {"True"}
191
+ assert len(method.post) == 2
192
+ assert set([c.expr_source for c in method.post]) == {"True", "False"}
212
193
 
213
194
  def test_invariant_applies_to_init(self) -> None:
214
195
  class_conditions = Pep316Parser().get_class_conditions(BaseClassExample)
215
- self.assertEqual(set(class_conditions.methods.keys()), set(["__init__", "foo"]))
196
+ assert set(class_conditions.methods.keys()) == {"__init__", "foo"}
216
197
 
217
198
  @pytest.mark.skipif(
218
199
  sys.version_info >= (3, 13), reason="builtins have signatures in 3.13"
219
200
  )
220
201
  def test_builtin_conditions_are_null(self) -> None:
221
- self.assertIsNone(Pep316Parser().get_fn_conditions(FunctionInfo.from_fn(zip)))
202
+ assert Pep316Parser().get_fn_conditions(FunctionInfo.from_fn(zip)) is None
222
203
 
223
204
  def test_conditions_with_closure_references_and_string_type(self) -> None:
224
205
  # This is a function that refers to something in its closure.
@@ -234,7 +215,7 @@ class Pep316ParserTest(unittest.TestCase):
234
215
 
235
216
 
236
217
  @pytest.mark.skipif(not icontract, reason="icontract is not installed")
237
- class IcontractParserTest(unittest.TestCase):
218
+ class TestIcontractParser:
238
219
  def test_simple_parse(self):
239
220
  @icontract.require(lambda ls: len(ls) > 0)
240
221
  @icontract.ensure(lambda ls, result: min(ls) <= result <= max(ls))
@@ -243,17 +224,17 @@ class IcontractParserTest(unittest.TestCase):
243
224
 
244
225
  conditions = IcontractParser().get_fn_conditions(FunctionInfo.from_fn(avg))
245
226
  assert conditions is not None
246
- self.assertEqual(len(conditions.pre), 1)
247
- self.assertEqual(len(conditions.post), 1)
248
- self.assertEqual(conditions.pre[0].evaluate({"ls": []}), False)
227
+ assert len(conditions.pre) == 1
228
+ assert len(conditions.post) == 1
229
+ assert conditions.pre[0].evaluate({"ls": []}) is False
249
230
  post_args = {
250
231
  "ls": [42, 43],
251
232
  "__old__": AttributeHolder({}),
252
233
  "__return__": 40,
253
234
  "_": 40,
254
235
  }
255
- self.assertEqual(conditions.post[0].evaluate(post_args), False)
256
- self.assertEqual(len(post_args), 4) # (check args are unmodified)
236
+ assert conditions.post[0].evaluate(post_args) is False
237
+ assert len(post_args) == 4 # (check args are unmodified)
257
238
 
258
239
  def test_simple_class_parse(self):
259
240
  @icontract.invariant(lambda self: self.i >= 0)
@@ -274,14 +255,14 @@ class IcontractParserTest(unittest.TestCase):
274
255
  self.i -= 1
275
256
 
276
257
  conditions = IcontractParser().get_class_conditions(Counter)
277
- self.assertEqual(len(conditions.inv), 1)
258
+ assert len(conditions.inv) == 1
278
259
 
279
260
  decr_conditions = conditions.methods["decr"]
280
- self.assertEqual(len(decr_conditions.pre), 2)
261
+ assert len(decr_conditions.pre) == 2
281
262
  # decr() precondition: count > 0
282
- self.assertEqual(decr_conditions.pre[0].evaluate({"self": Counter()}), False)
263
+ assert decr_conditions.pre[0].evaluate({"self": Counter()}) is False
283
264
  # invariant: count >= 0
284
- self.assertEqual(decr_conditions.pre[1].evaluate({"self": Counter()}), True)
265
+ assert decr_conditions.pre[1].evaluate({"self": Counter()}) is True
285
266
 
286
267
  class TruncatedCounter(Counter):
287
268
  @icontract.require(
@@ -293,21 +274,19 @@ class IcontractParserTest(unittest.TestCase):
293
274
 
294
275
  conditions = IcontractParser().get_class_conditions(TruncatedCounter)
295
276
  decr_conditions = conditions.methods["decr"]
296
- self.assertEqual(
297
- decr_conditions.pre[0].evaluate({"self": TruncatedCounter()}), True
298
- )
277
+ assert decr_conditions.pre[0].evaluate({"self": TruncatedCounter()}) is True
299
278
 
300
279
  # check the weakened precondition
301
- self.assertEqual(
302
- len(decr_conditions.pre), 2
280
+ assert (
281
+ len(decr_conditions.pre) == 2
303
282
  ) # one for the invariant, one for the disjunction
304
283
  ctr = TruncatedCounter()
305
284
  ctr.i = 1
306
- self.assertEqual(decr_conditions.pre[1].evaluate({"self": ctr}), True)
307
- self.assertEqual(decr_conditions.pre[0].evaluate({"self": ctr}), True)
285
+ assert decr_conditions.pre[1].evaluate({"self": ctr}) is True
286
+ assert decr_conditions.pre[0].evaluate({"self": ctr}) is True
308
287
  ctr.i = 0
309
- self.assertEqual(decr_conditions.pre[1].evaluate({"self": ctr}), True)
310
- self.assertEqual(decr_conditions.pre[0].evaluate({"self": ctr}), True)
288
+ assert decr_conditions.pre[1].evaluate({"self": ctr}) is True
289
+ assert decr_conditions.pre[0].evaluate({"self": ctr}) is True
311
290
 
312
291
 
313
292
  @pytest.mark.skipif(not deal, reason="deal is not installed")
@@ -401,20 +380,20 @@ def fn_with_docstring_comments_and_assert(numbers: List[int]) -> None:
401
380
  assert min(numbers) > smallest
402
381
 
403
382
 
404
- class AssertsParserTest(unittest.TestCase):
383
+ class TestAssertsParser:
405
384
  def tests_simple_parse(self) -> None:
406
385
  conditions = AssertsParser().get_fn_conditions(
407
386
  FunctionInfo.from_fn(avg_with_asserts)
408
387
  )
409
388
  assert conditions is not None
410
389
  conditions.fn([])
411
- self.assertEqual(conditions.fn([2.2]), 2.2)
412
- with self.assertRaises(AssertionError):
390
+ assert conditions.fn([2.2]) == 2.2
391
+ with pytest.raises(AssertionError):
413
392
  conditions.fn([9.2, 17.8])
414
393
 
415
394
  def tests_empty_parse(self) -> None:
416
395
  conditions = AssertsParser().get_fn_conditions(FunctionInfo.from_fn(debug))
417
- self.assertEqual(conditions, None)
396
+ assert conditions is None
418
397
 
419
398
  def tests_extra_ast_nodes(self) -> None:
420
399
  conditions = AssertsParser().get_fn_conditions(
@@ -428,10 +407,10 @@ class AssertsParserTest(unittest.TestCase):
428
407
  # normal, passing case:
429
408
  nums = [3, 1, 2]
430
409
  conditions.fn(nums)
431
- self.assertEqual(nums, [3, 2])
410
+ assert nums == [3, 2]
432
411
 
433
412
  # Failing case (duplicate minimum values):
434
- with self.assertRaises(AssertionError):
413
+ with pytest.raises(AssertionError):
435
414
  nums = [3, 1, 1, 2]
436
415
  conditions.fn(nums)
437
416
 
@@ -492,18 +471,10 @@ def test_lines_with_trailing_comment():
492
471
 
493
472
  def test_format_counterexample_positional_only():
494
473
  if sys.version_info >= (3, 8):
495
- # Use exec() here because the "/" marker is a syntax error in Python 3.7
496
- ns = {}
497
- foo = exec(
498
- textwrap.dedent(
499
- '''
500
- def foo(a=10, /, b=20):
501
- """post: True"""
502
- '''
503
- ),
504
- ns,
505
- )
506
- foo = ns["foo"]
474
+
475
+ def foo(a=10, /, b=20):
476
+ """post: True"""
477
+
507
478
  args = inspect.BoundArguments(inspect.signature(foo), {"a": 1, "b": 2})
508
479
  conditions = Pep316Parser().get_fn_conditions(FunctionInfo.from_fn(foo))
509
480
  assert conditions.format_counterexample(args, None, {}) == (
@@ -524,16 +495,3 @@ def test_format_counterexample_keyword_only():
524
495
  "foo(1, b=2)",
525
496
  "None",
526
497
  )
527
-
528
-
529
- @pytest.mark.skipif(not hypothesis, reason="hypothesis is not installed")
530
- def test_hypothesis_arg_regen():
531
- @hypothesis.given(hypothesis.strategies.integers())
532
- def hypothesis_fn_int(x):
533
- pass
534
-
535
- parser = HypothesisParser(None)
536
- # NOTE: Enocding not stable across hypothesis versions.
537
- # Byte string may need to be updated when our hypothesis dev version changes.
538
- ret = parser._generate_args(b"\x01\x04T", hypothesis_fn_int)
539
- assert ret == {"x": 42}
crosshair/conftest.py CHANGED
@@ -11,7 +11,7 @@ def pytest_configure(config):
11
11
  set_debug(True)
12
12
 
13
13
 
14
- LEAK_LIMIT_KB = 200 * 1024
14
+ LEAK_LIMIT_KB = 400 * 1024
15
15
 
16
16
 
17
17
  @pytest.hookimpl(hookwrapper=True)