crosshair-tool 0.0.83__cp39-cp39-macosx_10_9_universal2.whl → 0.0.85__cp39-cp39-macosx_10_9_universal2.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.

Potentially problematic release.


This version of crosshair-tool might be problematic. Click here for more details.

Files changed (46) hide show
  1. _crosshair_tracers.cpython-39-darwin.so +0 -0
  2. crosshair/__init__.py +1 -1
  3. crosshair/_mark_stacks.h +0 -25
  4. crosshair/_tracers.h +2 -0
  5. crosshair/_tracers_test.py +8 -2
  6. crosshair/auditwall.py +0 -1
  7. crosshair/auditwall_test.py +5 -0
  8. crosshair/condition_parser.py +5 -5
  9. crosshair/condition_parser_test.py +50 -63
  10. crosshair/copyext.py +23 -7
  11. crosshair/copyext_test.py +11 -1
  12. crosshair/core.py +23 -17
  13. crosshair/core_test.py +625 -584
  14. crosshair/diff_behavior_test.py +14 -21
  15. crosshair/dynamic_typing.py +90 -1
  16. crosshair/dynamic_typing_test.py +73 -1
  17. crosshair/enforce_test.py +15 -22
  18. crosshair/fnutil_test.py +4 -8
  19. crosshair/libimpl/arraylib.py +2 -5
  20. crosshair/libimpl/binasciilib.py +2 -3
  21. crosshair/libimpl/builtinslib.py +28 -21
  22. crosshair/libimpl/builtinslib_test.py +1 -8
  23. crosshair/libimpl/collectionslib.py +18 -3
  24. crosshair/libimpl/collectionslib_test.py +89 -15
  25. crosshair/libimpl/encodings/_encutil.py +8 -3
  26. crosshair/libimpl/mathlib_test.py +0 -7
  27. crosshair/libimpl/relib_ch_test.py +2 -2
  28. crosshair/libimpl/timelib.py +34 -15
  29. crosshair/libimpl/timelib_test.py +12 -2
  30. crosshair/lsp_server.py +1 -1
  31. crosshair/main.py +3 -1
  32. crosshair/objectproxy_test.py +7 -11
  33. crosshair/opcode_intercept.py +24 -8
  34. crosshair/opcode_intercept_test.py +13 -2
  35. crosshair/py.typed +0 -0
  36. crosshair/tracers.py +27 -9
  37. crosshair/type_repo.py +2 -2
  38. crosshair/unicode_categories.py +1 -0
  39. crosshair/util.py +45 -16
  40. crosshair/watcher.py +2 -2
  41. {crosshair_tool-0.0.83.dist-info → crosshair_tool-0.0.85.dist-info}/METADATA +4 -3
  42. {crosshair_tool-0.0.83.dist-info → crosshair_tool-0.0.85.dist-info}/RECORD +46 -45
  43. {crosshair_tool-0.0.83.dist-info → crosshair_tool-0.0.85.dist-info}/WHEEL +1 -1
  44. {crosshair_tool-0.0.83.dist-info → crosshair_tool-0.0.85.dist-info}/entry_points.txt +0 -0
  45. {crosshair_tool-0.0.83.dist-info → crosshair_tool-0.0.85.dist-info/licenses}/LICENSE +0 -0
  46. {crosshair_tool-0.0.83.dist-info → crosshair_tool-0.0.85.dist-info}/top_level.txt +0 -0
@@ -81,7 +81,7 @@ def _sum_list_rewrite_2(int_list):
81
81
  return count
82
82
 
83
83
 
84
- class BehaviorDiffTest(unittest.TestCase):
84
+ class TestBehaviorDiff:
85
85
  def test_diff_method(self):
86
86
  diffs = diff_behavior(
87
87
  walk_qualname(Base, "foo"),
@@ -89,10 +89,9 @@ class BehaviorDiffTest(unittest.TestCase):
89
89
  DEFAULT_OPTIONS.overlay(max_iterations=10),
90
90
  )
91
91
  assert isinstance(diffs, list)
92
- self.assertEqual(
93
- [(d.result1.return_repr, d.result2.return_repr) for d in diffs],
94
- [("10", "11")],
95
- )
92
+ assert [(d.result1.return_repr, d.result2.return_repr) for d in diffs] == [
93
+ ("10", "11")
94
+ ]
96
95
 
97
96
  def test_diff_staticmethod(self):
98
97
  diffs = diff_behavior(
@@ -100,20 +99,20 @@ class BehaviorDiffTest(unittest.TestCase):
100
99
  foo2,
101
100
  DEFAULT_OPTIONS.overlay(max_iterations=10),
102
101
  )
103
- self.assertEqual(diffs, [])
102
+ assert diffs == []
104
103
 
105
104
  def test_diff_behavior_same(self) -> None:
106
105
  diffs = diff_behavior(foo1, foo2, DEFAULT_OPTIONS.overlay(max_iterations=10))
107
- self.assertEqual(diffs, [])
106
+ assert diffs == []
108
107
 
109
108
  def test_diff_behavior_different(self) -> None:
110
109
  diffs = diff_behavior(foo1, foo3, DEFAULT_OPTIONS.overlay(max_iterations=10))
111
- self.assertEqual(len(diffs), 1)
110
+ assert len(diffs) == 1
112
111
  diff = diffs[0]
113
112
  assert isinstance(diff, BehaviorDiff)
114
- self.assertGreater(int(diff.args["x"]), 1000)
115
- self.assertEqual(diff.result1.return_repr, "100")
116
- self.assertEqual(diff.result2.return_repr, "1000")
113
+ assert int(diff.args["x"]) > 1000
114
+ assert diff.result1.return_repr == "100"
115
+ assert diff.result2.return_repr == "1000"
117
116
 
118
117
  def test_diff_behavior_mutation(self) -> None:
119
118
  def cut_out_item1(a: List[int], i: int):
@@ -130,10 +129,10 @@ class BehaviorDiffTest(unittest.TestCase):
130
129
  opts,
131
130
  )
132
131
  assert not isinstance(diffs, str)
133
- self.assertEqual(len(diffs), 1)
132
+ assert len(diffs) == 1
134
133
  diff = diffs[0]
135
- self.assertGreater(len(diff.args["a"]), 1)
136
- self.assertEqual(diff.args["i"], "-1")
134
+ assert len(diff.args["a"]) > 1
135
+ assert diff.args["i"] == "-1"
137
136
 
138
137
  def test_example_coverage(self) -> None:
139
138
  # Try to get examples that highlist the differences in the code.
@@ -159,7 +158,7 @@ class BehaviorDiffTest(unittest.TestCase):
159
158
  debug("diffs=", diffs)
160
159
  assert not isinstance(diffs, str)
161
160
  return_vals = set((d.result1.return_repr, d.result2.return_repr) for d in diffs)
162
- self.assertEqual(return_vals, {("False", "None"), ("False", "True")})
161
+ assert return_vals == {("False", "None"), ("False", "True")}
163
162
 
164
163
 
165
164
  def test_diff_behavior_lambda() -> None:
@@ -262,9 +261,3 @@ def test_diff_behavior_nan() -> None:
262
261
  DEFAULT_OPTIONS,
263
262
  )
264
263
  assert diffs == []
265
-
266
-
267
- if __name__ == "__main__":
268
- if ("-v" in sys.argv) or ("--verbose" in sys.argv):
269
- set_debug(True)
270
- unittest.main()
@@ -1,9 +1,13 @@
1
1
  import collections.abc
2
2
  import typing
3
- from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Type
3
+ from inspect import Parameter, Signature
4
+ from itertools import zip_longest
5
+ from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Type
4
6
 
5
7
  import typing_inspect # type: ignore
6
8
 
9
+ from crosshair.util import debug # type: ignore
10
+
7
11
  _EMPTYSET: frozenset = frozenset()
8
12
 
9
13
 
@@ -234,3 +238,88 @@ def realize(pytype: Type, bindings: Mapping[object, type]) -> object:
234
238
  if pytype_origin is Callable: # Callable args get flattened
235
239
  newargs = [newargs[:-1], newargs[-1]]
236
240
  return pytype_origin.__getitem__(tuple(newargs))
241
+
242
+
243
+ def isolate_var_params(
244
+ sig: Signature,
245
+ ) -> Tuple[
246
+ List[Parameter], Dict[str, Parameter], Optional[Parameter], Optional[Parameter]
247
+ ]:
248
+ pos_only_params: List[Parameter] = []
249
+ keyword_params: Dict[str, Parameter] = {}
250
+ var_positional: Optional[Parameter] = None
251
+ var_keyword: Optional[Parameter] = None
252
+ for name, param in sig.parameters.items():
253
+ if param.kind == Parameter.VAR_POSITIONAL:
254
+ var_positional = param
255
+ elif param.kind == Parameter.VAR_KEYWORD:
256
+ var_keyword = param
257
+ elif param.kind == Parameter.POSITIONAL_ONLY:
258
+ pos_only_params.append(param)
259
+ else:
260
+ keyword_params[name] = param
261
+ return pos_only_params, keyword_params, var_positional, var_keyword
262
+
263
+
264
+ def intersect_signatures(
265
+ sig1: Signature,
266
+ sig2: Signature,
267
+ ) -> Signature:
268
+ """
269
+ Approximate the intersection of two signatures.
270
+ The resulting signature may be overly loose
271
+ (matching some inputs that neither of the original signatures would match),
272
+ but it should cover all the inputs for each original signature.
273
+
274
+ One minor exception: All arguments that are allowed to be called as
275
+ keyword arguments will be converted to keyword-only arguments.
276
+ We do this to resolve the abiguity when position-or-keyword arguments
277
+ appear in the same position but with different names.
278
+ """
279
+ pos1, key1, var_pos1, var_key1 = isolate_var_params(sig1)
280
+ pos2, key2, var_pos2, var_key2 = isolate_var_params(sig2)
281
+ is_squishy1 = var_pos1 is not None or var_key1 is not None
282
+ is_squishy2 = var_pos2 is not None or var_key2 is not None
283
+ out_params: Dict[str, Parameter] = {}
284
+ for (p1, p2) in zip_longest(pos1, pos2):
285
+ if p1 is None:
286
+ if is_squishy1:
287
+ out_params[p2.name] = p2
288
+ elif p2 is None:
289
+ if is_squishy2:
290
+ out_params[p1.name] = p1
291
+ elif unify(p1.annotation, p2.annotation):
292
+ out_params[p1.name] = p1
293
+ else:
294
+ out_params[p2.name] = p2
295
+ for key in [
296
+ k
297
+ for pair in zip_longest(key1.keys(), key2.keys())
298
+ for k in pair
299
+ if k is not None
300
+ ]:
301
+ if key not in key2:
302
+ if is_squishy2:
303
+ out_params[key] = key1[key].replace(kind=Parameter.KEYWORD_ONLY)
304
+ continue
305
+ if key not in key1:
306
+ if is_squishy1:
307
+ out_params[key] = key2[key].replace(kind=Parameter.KEYWORD_ONLY)
308
+ continue
309
+ if unify(key1[key].annotation, key2[key].annotation):
310
+ out_params[key] = key1[key].replace(kind=Parameter.KEYWORD_ONLY)
311
+ else:
312
+ out_params[key] = key2[key].replace(kind=Parameter.KEYWORD_ONLY)
313
+ if var_pos1 and var_pos2:
314
+ out_params[var_pos1.name] = var_pos1
315
+ if var_key1 and var_key2:
316
+ out_params[var_key1.name] = var_key1
317
+ if unify(sig1.return_annotation, sig2.return_annotation):
318
+ out_return_annotation = sig1.return_annotation
319
+ else:
320
+ out_return_annotation = sig2.return_annotation
321
+ result = Signature(
322
+ parameters=list(out_params.values()), return_annotation=out_return_annotation
323
+ )
324
+ debug("Combined __init__ and __new__ signatures", sig1, "and", sig2, "into", result)
325
+ return result
@@ -1,4 +1,5 @@
1
1
  import collections
2
+ from inspect import Parameter, Signature, signature
2
3
  from typing import (
3
4
  Callable,
4
5
  Dict,
@@ -6,15 +7,25 @@ from typing import (
6
7
  Iterable,
7
8
  List,
8
9
  Mapping,
10
+ Optional,
9
11
  Sequence,
10
12
  Tuple,
11
13
  TypeVar,
12
14
  Union,
13
15
  )
14
16
 
17
+ import pytest
15
18
  from typing_extensions import TypedDict
16
19
 
17
- from crosshair.dynamic_typing import get_bindings_from_type_arguments, realize, unify
20
+ from crosshair.dynamic_typing import (
21
+ get_bindings_from_type_arguments,
22
+ intersect_signatures,
23
+ realize,
24
+ unify,
25
+ )
26
+ from crosshair.options import AnalysisOptionSet
27
+ from crosshair.statespace import CANNOT_CONFIRM
28
+ from crosshair.test_util import check_states
18
29
 
19
30
  _T = TypeVar("_T")
20
31
  _U = TypeVar("_U")
@@ -121,3 +132,64 @@ def test_bindings_from_type_arguments():
121
132
  var_mapping = get_bindings_from_type_arguments(Pair[int, str])
122
133
  assert var_mapping == {_U: int, _T: str}
123
134
  assert realize(List[_U], var_mapping) == List[int]
135
+
136
+
137
+ def test_intersect_signatures_basic():
138
+ def f1(x: int, y: str, **kw) -> List[bool]:
139
+ return []
140
+
141
+ def f2(x: bool, *extra: str, **kw) -> List[int]:
142
+ return []
143
+
144
+ intersection = intersect_signatures(signature(f1), signature(f2))
145
+ assert intersection is not None
146
+ assert intersection.parameters == {
147
+ "x": Parameter("x", kind=Parameter.KEYWORD_ONLY, annotation=bool),
148
+ "y": Parameter("y", kind=Parameter.KEYWORD_ONLY, annotation=str),
149
+ "kw": Parameter("kw", kind=Parameter.VAR_KEYWORD),
150
+ }
151
+ assert intersection.return_annotation == List[bool]
152
+
153
+
154
+ def test_intersect_signatures_typevars():
155
+ _T = TypeVar("_T")
156
+
157
+ def f1(cc, *args, **kwds):
158
+ pass
159
+
160
+ def f2(dd, left: Optional[_T], right: Optional[_T]):
161
+ pass
162
+
163
+ intersection = intersect_signatures(signature(f1), signature(f2))
164
+ assert intersection is not None
165
+ expected = {
166
+ "dd": Parameter("dd", kind=Parameter.KEYWORD_ONLY),
167
+ "left": Parameter("left", kind=Parameter.KEYWORD_ONLY, annotation=Optional[_T]),
168
+ "right": Parameter(
169
+ "right", kind=Parameter.KEYWORD_ONLY, annotation=Optional[_T]
170
+ ),
171
+ }
172
+ assert intersection.parameters == expected
173
+
174
+
175
+ @pytest.mark.skip(
176
+ reason="The inspect module doesn't expose runtime type information yet"
177
+ )
178
+ def test_intersect_signature_with_crosshair():
179
+ def check_intersect_signatures(
180
+ sig1: Signature, sig2: Signature, pos_args: List, kw_args: Mapping[str, object]
181
+ ) -> None:
182
+ """post: True"""
183
+
184
+ def _sig_bindable(sig: Signature) -> bool:
185
+ try:
186
+ sig.bind(*pos_args, **kw_args)
187
+ return True
188
+ except TypeError:
189
+ return False
190
+
191
+ if _sig_bindable(sig1) or _sig_bindable(sig2):
192
+ intersection = intersect_signatures(sig1, sig2)
193
+ assert _sig_bindable(intersection)
194
+
195
+ check_states(check_intersect_signatures, CANNOT_CONFIRM)
crosshair/enforce_test.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import abc
2
2
  import sys
3
- import unittest
4
3
  from contextlib import ExitStack
5
4
 
6
5
  import pytest
@@ -57,21 +56,21 @@ class Enforcement(ExitStack):
57
56
  COMPOSITE_TRACER.trace_caller()
58
57
 
59
58
 
60
- class CoreTest(unittest.TestCase):
59
+ class TestCore:
61
60
  def test_enforce_conditions(self) -> None:
62
- self.assertEqual(foo(-1), -2) # unchecked
61
+ assert foo(-1) == -2 # unchecked
63
62
  with Enforcement():
64
- self.assertEqual(foo(50), 100)
65
- with self.assertRaises(PreconditionFailed):
63
+ assert foo(50) == 100
64
+ with pytest.raises(PreconditionFailed):
66
65
  foo(-1)
67
- with self.assertRaises(PostconditionFailed):
66
+ with pytest.raises(PostconditionFailed):
68
67
  foo(0)
69
68
 
70
69
  def test_class_enforce(self) -> None:
71
70
  Pokeable().pokeby(-1) # no exception (yet!)
72
71
  with Enforcement():
73
72
  Pokeable().poke()
74
- with self.assertRaises(PreconditionFailed):
73
+ with pytest.raises(PreconditionFailed):
75
74
  Pokeable().pokeby(-1)
76
75
 
77
76
  def test_enforce_on_uncopyable_value(self) -> None:
@@ -81,7 +80,7 @@ class CoreTest(unittest.TestCase):
81
80
 
82
81
  not_copyable = NotCopyable()
83
82
  with Enforcement():
84
- with self.assertRaises(AttributeError):
83
+ with pytest.raises(AttributeError):
85
84
  same_thing(not_copyable)
86
85
 
87
86
 
@@ -114,19 +113,19 @@ class DerivedFooable(BaseFooable):
114
113
  """pre: x > 0"""
115
114
 
116
115
 
117
- class TrickyCasesTest(unittest.TestCase):
116
+ class TestTrickyCases:
118
117
  def test_attrs_restored_properly(self) -> None:
119
118
  orig_class_dict = DerivedFooable.__dict__.copy()
120
119
  with Enforcement():
121
120
  pass
122
121
  for k, v in orig_class_dict.items():
123
- self.assertIs(
124
- DerivedFooable.__dict__[k], v, f'member "{k}" changed afer encforcement'
125
- )
122
+ assert (
123
+ DerivedFooable.__dict__[k] is v
124
+ ), f'member "{k}" changed afer encforcement'
126
125
 
127
126
  def test_enforcement_of_class_methods(self) -> None:
128
127
  with Enforcement():
129
- with self.assertRaises(PreconditionFailed):
128
+ with pytest.raises(PreconditionFailed):
130
129
  BaseFooable.class_foo(50)
131
130
  with Enforcement():
132
131
  DerivedFooable.class_foo(50)
@@ -134,14 +133,14 @@ class TrickyCasesTest(unittest.TestCase):
134
133
  def test_enforcement_of_static_methods(self) -> None:
135
134
  with Enforcement():
136
135
  DerivedFooable.static_foo(50)
137
- with self.assertRaises(PreconditionFailed):
136
+ with pytest.raises(PreconditionFailed):
138
137
  BaseFooable.static_foo(50)
139
138
 
140
139
  def test_super_method_enforced(self) -> None:
141
140
  with Enforcement():
142
- with self.assertRaises(PreconditionFailed):
141
+ with pytest.raises(PreconditionFailed):
143
142
  DerivedFooable().foo_only_in_super(50)
144
- with self.assertRaises(PreconditionFailed):
143
+ with pytest.raises(PreconditionFailed):
145
144
  DerivedFooable().foo(-1)
146
145
  # Derived class has a weaker precondition, so this is OK:
147
146
  DerivedFooable().foo(50)
@@ -181,9 +180,3 @@ def test_enforcement_init_on_abcmeta() -> None:
181
180
  with pytest.raises(PostconditionFailed):
182
181
  WithMetaclass(55)
183
182
  WithMetaclass(99)
184
-
185
-
186
- if __name__ == "__main__":
187
- if ("-v" in sys.argv) or ("--verbose" in sys.argv):
188
- set_debug(True)
189
- unittest.main()
crosshair/fnutil_test.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import builtins
2
2
  import inspect
3
3
  import sys
4
- import unittest
5
4
  from dataclasses import dataclass
6
5
  from typing import Generic
7
6
 
@@ -27,7 +26,10 @@ def test_fn_globals_on_builtin() -> None:
27
26
 
28
27
  def test_resolve_signature_invalid_annotations() -> None:
29
28
  sig = resolve_signature(with_invalid_type_annotation)
30
- assert sig == "name 'TypeThatIsNotDefined' is not defined"
29
+ if sys.version_info >= (3, 14):
30
+ assert sig == "TypeThatIsNotDefined"
31
+ else:
32
+ assert sig == "name 'TypeThatIsNotDefined' is not defined"
31
33
 
32
34
 
33
35
  @pytest.mark.skipif(
@@ -74,9 +76,3 @@ def test_load_function_at_line():
74
76
 
75
77
  def test_FunctionInfo_get_callable_on_generic():
76
78
  assert FunctionInfo.from_class(Generic, "__class_getitem__").get_callable() is None
77
-
78
-
79
- if __name__ == "__main__":
80
- if ("-v" in sys.argv) or ("--verbose" in sys.argv):
81
- set_debug(True)
82
- unittest.main()
@@ -1,5 +1,6 @@
1
+ import sys
1
2
  from array import array
2
- from typing import BinaryIO, ByteString, Dict, Iterable, List, Sequence, Tuple
3
+ from typing import BinaryIO, Dict, Iterable, List, Sequence, Tuple
3
4
 
4
5
  import z3 # type: ignore
5
6
 
@@ -29,10 +30,6 @@ INT_TYPE_BOUNDS: Dict[str, Tuple[int, int]] = {
29
30
  INT_TYPE_SIZE = {c: array(c).itemsize for c in INT_TYPE_BOUNDS.keys()}
30
31
 
31
32
 
32
- def is_bytes_like(obj: object) -> bool:
33
- return isinstance(obj, (ByteString, array))
34
-
35
-
36
33
  def pick_code(space: StateSpace) -> Tuple[str, int, int]:
37
34
  last_idx = len(INT_TYPE_BOUNDS) - 1
38
35
  for (idx, (code, rng)) in enumerate(INT_TYPE_BOUNDS.items()):
@@ -1,7 +1,6 @@
1
1
  import binascii
2
- from collections.abc import ByteString
3
2
  from functools import partial
4
- from typing import Dict, Iterable, Tuple
3
+ from typing import Dict, Iterable, Tuple, Union
5
4
 
6
5
  from crosshair.core import register_patch
7
6
  from crosshair.libimpl.builtinslib import _ALL_BYTES_TYPES, SymbolicBytes
@@ -89,7 +88,7 @@ _DECODE_MAPPER_BASE64_STRICT = partial(
89
88
  _ENCODE_MAPPER_BASE64 = partial(_remap, _ENCODE_BASE64_MAP)
90
89
 
91
90
 
92
- def make_bytes(arg: object) -> ByteString:
91
+ def make_bytes(arg: object) -> Union[bytes, bytearray, memoryview]:
93
92
  if isinstance(arg, (bytes, bytearray, memoryview)):
94
93
  return arg
95
94
  if isinstance(arg, str):
@@ -17,11 +17,10 @@ from dataclasses import dataclass
17
17
  from itertools import zip_longest
18
18
  from math import inf, isfinite, isinf, isnan, nan
19
19
  from numbers import Integral, Number, Real
20
- from sys import maxunicode
20
+ from sys import maxunicode, version_info
21
21
  from typing import (
22
22
  Any,
23
23
  BinaryIO,
24
- ByteString,
25
24
  Callable,
26
25
  Dict,
27
26
  FrozenSet,
@@ -59,6 +58,8 @@ try:
59
58
  except ImportError:
60
59
  from z3 import FfpEQ as fpEQ
61
60
 
61
+ import sys
62
+
62
63
  from crosshair.abcstring import AbcString
63
64
  from crosshair.core import (
64
65
  SymbolicFactory,
@@ -117,6 +118,7 @@ from crosshair.util import (
117
118
  assert_tracing,
118
119
  ch_stack,
119
120
  debug,
121
+ is_bytes_like,
120
122
  is_hashable,
121
123
  is_iterable,
122
124
  memo,
@@ -127,9 +129,9 @@ from crosshair.util import (
127
129
  from crosshair.z3util import z3And, z3Eq, z3Ge, z3Gt, z3IntVal, z3Not, z3Or
128
130
 
129
131
  if sys.version_info >= (3, 12):
130
- from collections.abc import Buffer as BufferAbc
132
+ from collections.abc import Buffer
131
133
  else:
132
- from collections.abc import ByteString as BufferAbc
134
+ from collections.abc import ByteString as Buffer
133
135
 
134
136
 
135
137
  _T = TypeVar("_T")
@@ -1214,10 +1216,11 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
1214
1216
  cur_divisor = 10
1215
1217
  while True:
1216
1218
  leftover = self // cur_divisor
1217
- if leftover == 0:
1219
+ if leftover != 0:
1220
+ codepoints.append(48 + (leftover % 10))
1221
+ cur_divisor *= 10
1222
+ else:
1218
1223
  break
1219
- codepoints.append(48 + (leftover % 10))
1220
- cur_divisor *= 10
1221
1224
  with NoTracing():
1222
1225
  codepoints.reverse()
1223
1226
  return LazyIntSymbolicStr(codepoints)
@@ -1317,7 +1320,7 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
1317
1320
  z3.If(val < 128, 7, 8)))))))))
1318
1321
  # fmt: on
1319
1322
 
1320
- if sys.version_info >= (3, 12):
1323
+ if version_info >= (3, 12):
1321
1324
 
1322
1325
  def is_integer(self):
1323
1326
  return True
@@ -2935,7 +2938,7 @@ class AnySymbolicStr(AbcString):
2935
2938
  def capitalize(self):
2936
2939
  if self.__len__() == 0:
2937
2940
  return ""
2938
- if sys.version_info >= (3, 8):
2941
+ if version_info >= (3, 8):
2939
2942
  firstchar = self[0].title()
2940
2943
  else:
2941
2944
  firstchar = self[0].upper()
@@ -3173,7 +3176,7 @@ class AnySymbolicStr(AbcString):
3173
3176
  return ""
3174
3177
 
3175
3178
  def splitlines(self, keepends=False):
3176
- if sys.version_info < (3, 12):
3179
+ if version_info < (3, 12):
3177
3180
  if not isinstance(keepends, int):
3178
3181
  raise TypeError
3179
3182
  mylen = self.__len__()
@@ -3439,6 +3442,7 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
3439
3442
  SliceView,
3440
3443
  SequenceConcatenation,
3441
3444
  list, # TODO: are we sharing mutable state here?
3445
+ tuple,
3442
3446
  ),
3443
3447
  ):
3444
3448
  self._codepoints = smtvar
@@ -3968,7 +3972,7 @@ def is_ascii_space_ord(char_ord: int):
3968
3972
  )
3969
3973
 
3970
3974
 
3971
- class BytesLike(BufferAbc, AbcString, CrossHairValue):
3975
+ class BytesLike(Buffer, AbcString, CrossHairValue):
3972
3976
  def __eq__(self, other) -> bool:
3973
3977
  if not isinstance(other, _ALL_BYTES_TYPES):
3974
3978
  return False
@@ -3976,7 +3980,7 @@ class BytesLike(BufferAbc, AbcString, CrossHairValue):
3976
3980
  return False
3977
3981
  return list(self) == list(other)
3978
3982
 
3979
- if sys.version_info >= (3, 12):
3983
+ if version_info >= (3, 12):
3980
3984
 
3981
3985
  def __buffer__(self, flags: int):
3982
3986
  with NoTracing():
@@ -4136,7 +4140,10 @@ class SymbolicBytes(BytesLike):
4136
4140
  accumulated = []
4137
4141
  high = None
4138
4142
  if not isinstance(hexstr, str):
4139
- raise TypeError
4143
+ if is_bytes_like(hexstr) and version_info >= (3, 14):
4144
+ hexstr = LazyIntSymbolicStr(tuple(hexstr))
4145
+ else:
4146
+ raise TypeError
4140
4147
  for idx, ch in enumerate(hexstr):
4141
4148
  if not ch.isascii():
4142
4149
  raise ValueError(
@@ -4895,7 +4902,7 @@ def _int_from_bytes(
4895
4902
  ) -> int:
4896
4903
  if byteorder is _MISSING:
4897
4904
  # byteorder defaults to "big" as of 3.11
4898
- if sys.version_info >= (3, 11):
4905
+ if version_info >= (3, 11):
4899
4906
  byteorder = "big"
4900
4907
  else:
4901
4908
  raise TypeError
@@ -5038,11 +5045,11 @@ def _str_percent_format(self, other):
5038
5045
 
5039
5046
 
5040
5047
  def _bytes_join(self, itr) -> str:
5041
- return _join(self, itr, self_type=bytes, item_type=BufferAbc)
5048
+ return _join(self, itr, self_type=bytes, item_type=Buffer)
5042
5049
 
5043
5050
 
5044
5051
  def _bytearray_join(self, itr) -> str:
5045
- return _join(self, itr, self_type=bytearray, item_type=BufferAbc)
5052
+ return _join(self, itr, self_type=bytearray, item_type=Buffer)
5046
5053
 
5047
5054
 
5048
5055
  def _str_format(self, *a, **kw) -> Union[AnySymbolicStr, str]:
@@ -5109,7 +5116,7 @@ def make_registrations():
5109
5116
 
5110
5117
  register_type(Union, make_union_choice)
5111
5118
 
5112
- if sys.version_info >= (3, 8):
5119
+ if version_info >= (3, 8):
5113
5120
  from typing import Final
5114
5121
 
5115
5122
  register_type(Final, lambda p, t: p(t))
@@ -5178,7 +5185,7 @@ def make_registrations():
5178
5185
  register_type(SupportsFloat, lambda p: p(float))
5179
5186
  register_type(SupportsInt, lambda p: p(int))
5180
5187
  register_type(SupportsRound, lambda p: p(float))
5181
- register_type(SupportsBytes, lambda p: p(ByteString))
5188
+ register_type(SupportsBytes, lambda p: p(Buffer))
5182
5189
  register_type(SupportsComplex, lambda p: p(complex))
5183
5190
 
5184
5191
  # Patches
@@ -5267,7 +5274,7 @@ def make_registrations():
5267
5274
  "upper",
5268
5275
  "zfill",
5269
5276
  ]
5270
- if sys.version_info >= (3, 9):
5277
+ if version_info >= (3, 9):
5271
5278
  names_to_str_patch.append("removeprefix")
5272
5279
  names_to_str_patch.append("removesuffix")
5273
5280
  for name in names_to_str_patch:
@@ -5323,12 +5330,12 @@ def make_registrations():
5323
5330
  # Patches on int
5324
5331
  register_patch(int.__repr__, with_checked_self(int, "__repr__"))
5325
5332
  register_patch(int.as_integer_ratio, with_checked_self(int, "as_integer_ratio"))
5326
- if sys.version_info >= (3, 10):
5333
+ if version_info >= (3, 10):
5327
5334
  register_patch(int.bit_count, with_checked_self(int, "bit_count"))
5328
5335
  register_patch(int.bit_length, with_checked_self(int, "bit_length"))
5329
5336
  register_patch(int.conjugate, with_checked_self(int, "conjugate"))
5330
5337
  register_patch(int.from_bytes, _int_from_bytes)
5331
- if sys.version_info >= (3, 12):
5338
+ if version_info >= (3, 12):
5332
5339
  register_patch(int.is_integer, with_checked_self(int, "is_integer"))
5333
5340
  register_patch(int.to_bytes, with_checked_self(int, "to_bytes"))
5334
5341
 
@@ -8,7 +8,6 @@ import math
8
8
  import operator
9
9
  import re
10
10
  import sys
11
- import unittest
12
11
  from abc import ABC, abstractmethod
13
12
  from array import array
14
13
  from numbers import Integral
@@ -2882,7 +2881,7 @@ def test_frozenset___or__(space):
2882
2881
  assert len(s1 | s2) == 2
2883
2882
 
2884
2883
 
2885
- class ProtocolsTest(unittest.TestCase):
2884
+ class TestProtocols:
2886
2885
  # TODO: move most of this into a collectionslib_test.py file
2887
2886
  def test_hashable_values_fail(self) -> None:
2888
2887
  def f(b: bool, i: int, t: Tuple[str, ...]) -> int:
@@ -3657,9 +3656,3 @@ def TODO_test_deepcopy_independence():
3657
3656
  with NoTracing():
3658
3657
  assert ls[0] is not lscopy[0]
3659
3658
  # Next try mutation on one and test the other...
3660
-
3661
-
3662
- if __name__ == "__main__":
3663
- if ("-v" in sys.argv) or ("--verbose" in sys.argv):
3664
- set_debug(True)
3665
- unittest.main()