crosshair-tool 0.0.84__cp311-cp311-macosx_10_9_universal2.whl → 0.0.86__cp311-cp311-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 (33) hide show
  1. _crosshair_tracers.cpython-311-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/condition_parser.py +5 -5
  7. crosshair/condition_parser_test.py +1 -1
  8. crosshair/copyext.py +23 -7
  9. crosshair/copyext_test.py +11 -1
  10. crosshair/dynamic_typing.py +1 -1
  11. crosshair/fnutil_test.py +4 -1
  12. crosshair/libimpl/arraylib.py +0 -13
  13. crosshair/libimpl/builtinslib.py +26 -274
  14. crosshair/libimpl/builtinslib_test.py +1 -1
  15. crosshair/libimpl/collectionslib.py +13 -2
  16. crosshair/libimpl/collectionslib_test.py +10 -2
  17. crosshair/libimpl/timelib.py +34 -15
  18. crosshair/libimpl/timelib_test.py +12 -2
  19. crosshair/libimpl/typeslib_test.py +2 -1
  20. crosshair/lsp_server.py +1 -1
  21. crosshair/opcode_intercept.py +131 -47
  22. crosshair/opcode_intercept_test.py +97 -6
  23. crosshair/statespace.py +9 -4
  24. crosshair/tracers.py +27 -9
  25. crosshair/type_repo.py +2 -2
  26. crosshair/unicode_categories.py +1 -0
  27. crosshair/util.py +48 -17
  28. {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/METADATA +1 -1
  29. {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/RECORD +33 -33
  30. {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/WHEEL +2 -1
  31. {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/entry_points.txt +0 -0
  32. {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/licenses/LICENSE +0 -0
  33. {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/top_level.txt +0 -0
Binary file
crosshair/__init__.py CHANGED
@@ -15,7 +15,7 @@ from crosshair.statespace import StateSpace
15
15
  from crosshair.tracers import NoTracing, ResumedTracing
16
16
  from crosshair.util import IgnoreAttempt, debug
17
17
 
18
- __version__ = "0.0.84" # Do not forget to update in setup.py!
18
+ __version__ = "0.0.86" # Do not forget to update in setup.py!
19
19
  __author__ = "Phillip Schanely"
20
20
  __license__ = "MIT"
21
21
  __status__ = "Alpha"
crosshair/_mark_stacks.h CHANGED
@@ -538,31 +538,6 @@ static const uint8_t _ch_DE_INSTRUMENT[256] = {
538
538
  #endif
539
539
  #endif
540
540
 
541
- static const uint8_t _ch_TRACABLE_INSTRUCTIONS[256] = {
542
- // This must be manually kept in sync the the various
543
- // instructions that we care about on the python side.
544
- [MAP_ADD] = 1,
545
- [BINARY_SUBSCR] = 1,
546
- [BINARY_SLICE] = 1,
547
- [CONTAINS_OP] = 1,
548
- [BUILD_STRING] = 1,
549
- #if PY_VERSION_HEX < 0x030D0000
550
- // <= 3.12
551
- [FORMAT_VALUE] = 1,
552
- #elif PY_VERSION_HEX < 0x030E0000
553
- // 3.13
554
- [CALL_KW] = 1,
555
- [CONVERT_VALUE] = 1,
556
- #endif
557
- [UNARY_NOT] = 1,
558
- [SET_ADD] = 1,
559
- [IS_OP] = 1,
560
- [BINARY_OP] = 1,
561
- [CALL] = 1,
562
- [CALL_FUNCTION_EX] = 1,
563
- };
564
-
565
-
566
541
  /* Get the underlying opcode, stripping instrumentation */
567
542
  int _ch_Py_GetBaseOpcode(PyCodeObject *code, int i)
568
543
  {
crosshair/_tracers.h CHANGED
@@ -88,4 +88,6 @@ typedef struct TraceSwap {
88
88
 
89
89
  extern PyTypeObject TraceSwapType;
90
90
 
91
+ extern const uint8_t _ch_TRACABLE_INSTRUCTIONS[256];
92
+
91
93
  #endif /* _COVERAGE_TRACER_H */
@@ -89,7 +89,10 @@ def _log_execution_stacks(fn, *a, **kw):
89
89
  return stacks
90
90
 
91
91
 
92
- @pytest.mark.skipif(sys.version_info < (3, 12), reason="stack depth on 3.12+")
92
+ @pytest.mark.skipif(
93
+ sys.version_info < (3, 12) or sys.version_info >= (3, 14),
94
+ reason="stack depths only in 3.12 & 3.13",
95
+ )
93
96
  def test_one_function_stack_depth():
94
97
  _E = (TypeError, KeyboardInterrupt)
95
98
 
@@ -100,7 +103,10 @@ def test_one_function_stack_depth():
100
103
  _log_execution_stacks(a, 4)
101
104
 
102
105
 
103
- @pytest.mark.skipif(sys.version_info < (3, 12), reason="stack depth on 3.12+")
106
+ @pytest.mark.skipif(
107
+ sys.version_info < (3, 12) or sys.version_info >= (3, 14),
108
+ reason="stack depths only in 3.12 & 3.13",
109
+ )
104
110
  def test_stack_get():
105
111
  def to_be_traced(x):
106
112
  r = 8 - x
@@ -47,6 +47,7 @@ from crosshair.options import AnalysisKind
47
47
  from crosshair.register_contract import get_contract
48
48
  from crosshair.tracers import NoTracing
49
49
  from crosshair.util import (
50
+ CrossHairInternal,
50
51
  DynamicScopeVar,
51
52
  EvalFriendlyReprContext,
52
53
  IdKeyedDict,
@@ -485,6 +486,7 @@ class ConcreteConditionParser(ConditionParser):
485
486
  method = cls.__dict__.get(method_name, None)
486
487
  super_method_conditions = super_methods.get(method_name)
487
488
  if super_method_conditions is not None:
489
+ # Re-type the super's `self` argument to be this class:
488
490
  revised_sig = set_first_arg_type(super_method_conditions.sig, cls)
489
491
  super_method_conditions = replace(
490
492
  super_method_conditions, sig=revised_sig
@@ -511,17 +513,15 @@ class ConcreteConditionParser(ConditionParser):
511
513
  final_pre = list(conditions.pre)
512
514
  final_post = list(conditions.post)
513
515
  if method_name in (
514
- "__new__", # isn't passed a concrete instance.
516
+ "__new__", # a staticmethod, but not isinstance(staticmethod)
515
517
  "__repr__", # is itself required for reporting problems with invariants.
516
518
  # [set/del]attr can do anything; we can't resonably enforce invariants:
517
519
  "__setattr__",
518
520
  "__delattr__",
521
+ "__replace__", # Will raise an exception with most arbitrary **kwargs.
522
+ "__annotate__", # a staticmethod, but not isinstance(staticmethod)
519
523
  ):
520
524
  pass
521
- elif method_name == "__replace__":
522
- # TODO: remove this case when fixed in 3.13
523
- # see https://github.com/python/cpython/issues/114198
524
- pass
525
525
  elif method_name == "__del__":
526
526
  final_pre.extend(inv)
527
527
  elif method_name == "__init__":
@@ -140,7 +140,7 @@ class TestPep316Parser:
140
140
  "self.x >= 0",
141
141
  "self.y >= 0",
142
142
  }
143
- assert set(class_conditions.methods.keys()) == {"isready", "__init__"}
143
+ assert {"isready", "__init__"} <= set(class_conditions.methods.keys())
144
144
  method = class_conditions.methods["isready"]
145
145
  assert set([c.expr_source for c in method.pre]) == {
146
146
  "self.x >= 0",
crosshair/copyext.py CHANGED
@@ -1,4 +1,9 @@
1
- from copy import _deepcopy_atomic # type: ignore
1
+ import sys
2
+
3
+ if sys.version_info >= (3, 14):
4
+ from copy import _atomic_types
5
+ else:
6
+ from copy import _deepcopy_atomic # type: ignore
2
7
  from copy import _deepcopy_dict # type: ignore
3
8
  from copy import _deepcopy_dispatch # type: ignore
4
9
  from copy import _deepcopy_list # type: ignore
@@ -9,7 +14,7 @@ from copy import Error
9
14
  from copyreg import dispatch_table # type: ignore
10
15
  from enum import Enum
11
16
  from types import MappingProxyType
12
- from typing import Any, Dict, Tuple
17
+ from typing import Any, Callable, Dict, Tuple
13
18
 
14
19
  from crosshair.tracers import ResumedTracing
15
20
  from crosshair.util import (
@@ -85,17 +90,28 @@ def deepcopyext(obj: object, mode: CopyMode, memo: Dict) -> Any:
85
90
  return cpy
86
91
 
87
92
 
93
+ if sys.version_info >= (3, 14):
94
+
95
+ def lookup_dispatch(cls: type) -> Callable:
96
+ if cls in _atomic_types:
97
+ return lambda obj, memo: obj
98
+ return _deepcopy_dispatch.get(cls)
99
+
100
+ else:
101
+
102
+ def lookup_dispatch(cls: type) -> Callable:
103
+ return _deepcopy_dispatch.get(cls)
104
+
105
+
88
106
  def _deepconstruct(obj: object, mode: CopyMode, memo: Dict):
89
107
  cls = type(obj)
90
108
 
91
109
  def subdeepcopy(obj: object, memo: Dict):
92
110
  return deepcopyext(obj, mode, memo)
93
111
 
94
- if cls in _deepcopy_dispatch:
95
- creator = _deepcopy_dispatch[cls]
96
- if creator is _deepcopy_atomic:
97
- return obj
98
- elif creator in (_deepcopy_dict, _deepcopy_list, _deepcopy_tuple):
112
+ creator = lookup_dispatch(cls)
113
+ if creator is not None:
114
+ if creator in (_deepcopy_dict, _deepcopy_list, _deepcopy_tuple):
99
115
  return creator(obj, memo, deepcopy=subdeepcopy)
100
116
  else:
101
117
  # TODO: We loose subdeepcopy in this case - won't
crosshair/copyext_test.py CHANGED
@@ -7,7 +7,7 @@ import pytest
7
7
  from crosshair.copyext import CopyMode, deepcopyext
8
8
  from crosshair.core_and_libs import proxy_for_type, standalone_statespace
9
9
  from crosshair.libimpl.builtinslib import SymbolicInt
10
- from crosshair.tracers import NoTracing, ResumedTracing
10
+ from crosshair.tracers import NoTracing
11
11
 
12
12
 
13
13
  def test_deepcopyext_best_effort():
@@ -33,6 +33,16 @@ def test_deepcopyext_symbolic_set():
33
33
  deepcopyext(s, CopyMode.REALIZE, {})
34
34
 
35
35
 
36
+ def test_deepcopyext_realize_simple(space):
37
+ x = SymbolicInt("x")
38
+ input = (x,)
39
+ output = deepcopyext(input, CopyMode.REALIZE, {})
40
+ assert input is not output
41
+ assert input[0] is not output[0]
42
+ assert type(input[0]) is SymbolicInt
43
+ assert type(output[0]) is int
44
+
45
+
36
46
  def test_deepcopyext_realize(space):
37
47
  x = SymbolicInt("x")
38
48
  lock = RLock()
@@ -4,7 +4,7 @@ from inspect import Parameter, Signature
4
4
  from itertools import zip_longest
5
5
  from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Type
6
6
 
7
- import typing_inspect
7
+ import typing_inspect # type: ignore
8
8
 
9
9
  from crosshair.util import debug # type: ignore
10
10
 
crosshair/fnutil_test.py CHANGED
@@ -26,7 +26,10 @@ def test_fn_globals_on_builtin() -> None:
26
26
 
27
27
  def test_resolve_signature_invalid_annotations() -> None:
28
28
  sig = resolve_signature(with_invalid_type_annotation)
29
- 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"
30
33
 
31
34
 
32
35
  @pytest.mark.skipif(
@@ -30,19 +30,6 @@ INT_TYPE_BOUNDS: Dict[str, Tuple[int, int]] = {
30
30
  INT_TYPE_SIZE = {c: array(c).itemsize for c in INT_TYPE_BOUNDS.keys()}
31
31
 
32
32
 
33
- if sys.version_info >= (3, 12):
34
- from collections.abc import Buffer
35
-
36
- def is_bytes_like(obj: object) -> bool:
37
- return isinstance(obj, Buffer)
38
-
39
- else:
40
- from collections.abc import ByteString
41
-
42
- def is_bytes_like(obj: object) -> bool:
43
- return isinstance(obj, (ByteString, array))
44
-
45
-
46
33
  def pick_code(space: StateSpace) -> Tuple[str, int, int]:
47
34
  last_idx = len(INT_TYPE_BOUNDS) - 1
48
35
  for (idx, (code, rng)) in enumerate(INT_TYPE_BOUNDS.items()):
@@ -17,7 +17,7 @@ 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,
@@ -58,6 +58,8 @@ try:
58
58
  except ImportError:
59
59
  from z3 import FfpEQ as fpEQ
60
60
 
61
+ import sys
62
+
61
63
  from crosshair.abcstring import AbcString
62
64
  from crosshair.core import (
63
65
  SymbolicFactory,
@@ -116,6 +118,7 @@ from crosshair.util import (
116
118
  assert_tracing,
117
119
  ch_stack,
118
120
  debug,
121
+ is_bytes_like,
119
122
  is_hashable,
120
123
  is_iterable,
121
124
  memo,
@@ -399,6 +402,10 @@ def crosshair_types_for_python_type(
399
402
  return _PYTYPE_TO_WRAPPER_TYPE.get(origin, ())
400
403
 
401
404
 
405
+ def python_types_using_atomic_symbolics() -> Iterable[Type[AtomicSymbolicValue]]:
406
+ return _PYTYPE_TO_WRAPPER_TYPE.keys()
407
+
408
+
402
409
  class ModelingDirector:
403
410
  def __init__(self, *a) -> None:
404
411
  # Maps python type to the symbolic type we've chosen to represent it (on this iteration)
@@ -1213,10 +1220,11 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
1213
1220
  cur_divisor = 10
1214
1221
  while True:
1215
1222
  leftover = self // cur_divisor
1216
- if leftover == 0:
1223
+ if leftover != 0:
1224
+ codepoints.append(48 + (leftover % 10))
1225
+ cur_divisor *= 10
1226
+ else:
1217
1227
  break
1218
- codepoints.append(48 + (leftover % 10))
1219
- cur_divisor *= 10
1220
1228
  with NoTracing():
1221
1229
  codepoints.reverse()
1222
1230
  return LazyIntSymbolicStr(codepoints)
@@ -1316,7 +1324,7 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
1316
1324
  z3.If(val < 128, 7, 8)))))))))
1317
1325
  # fmt: on
1318
1326
 
1319
- if sys.version_info >= (3, 12):
1327
+ if version_info >= (3, 12):
1320
1328
 
1321
1329
  def is_integer(self):
1322
1330
  return True
@@ -2934,7 +2942,7 @@ class AnySymbolicStr(AbcString):
2934
2942
  def capitalize(self):
2935
2943
  if self.__len__() == 0:
2936
2944
  return ""
2937
- if sys.version_info >= (3, 8):
2945
+ if version_info >= (3, 8):
2938
2946
  firstchar = self[0].title()
2939
2947
  else:
2940
2948
  firstchar = self[0].upper()
@@ -3172,7 +3180,7 @@ class AnySymbolicStr(AbcString):
3172
3180
  return ""
3173
3181
 
3174
3182
  def splitlines(self, keepends=False):
3175
- if sys.version_info < (3, 12):
3183
+ if version_info < (3, 12):
3176
3184
  if not isinstance(keepends, int):
3177
3185
  raise TypeError
3178
3186
  mylen = self.__len__()
@@ -3438,6 +3446,7 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
3438
3446
  SliceView,
3439
3447
  SequenceConcatenation,
3440
3448
  list, # TODO: are we sharing mutable state here?
3449
+ tuple,
3441
3450
  ),
3442
3451
  ):
3443
3452
  self._codepoints = smtvar
@@ -3483,10 +3492,6 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
3483
3492
  otherpoints = [ord(ch) for ch in other]
3484
3493
  with ResumedTracing():
3485
3494
  return mypoints.__eq__(otherpoints)
3486
- elif isinstance(other, SeqBasedSymbolicStr):
3487
- with ResumedTracing():
3488
- otherpoints = [ord(ch) for ch in other]
3489
- return mypoints.__eq__(otherpoints)
3490
3495
  else:
3491
3496
  return NotImplemented
3492
3497
 
@@ -3655,257 +3660,6 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
3655
3660
  return self._find(substr, start, end, from_right=True)
3656
3661
 
3657
3662
 
3658
- class SeqBasedSymbolicStr(AtomicSymbolicValue, SymbolicSequence, AnySymbolicStr):
3659
- def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type = str):
3660
- assert typ == str
3661
- SymbolicValue.__init__(self, smtvar, typ)
3662
- self.item_pytype = str
3663
- if isinstance(smtvar, str):
3664
- # Constrain fresh strings to valid codepoints
3665
- space = context_statespace()
3666
- idxvar = z3.Int("idxvar" + space.uniq())
3667
- z3seq = self.var
3668
- space.add(
3669
- z3.ForAll(
3670
- [idxvar], z3.And(0 <= z3seq[idxvar], z3seq[idxvar] <= maxunicode)
3671
- )
3672
- )
3673
-
3674
- @classmethod
3675
- def _ch_smt_sort(cls) -> z3.SortRef:
3676
- return z3.SeqSort(z3.IntSort())
3677
-
3678
- @classmethod
3679
- def _pytype(cls) -> Type:
3680
- return str
3681
-
3682
- @classmethod
3683
- def _smt_promote_literal(cls, literal) -> Optional[z3.SortRef]:
3684
- if isinstance(literal, str):
3685
- if len(literal) <= 1:
3686
- if len(literal) == 0:
3687
- return z3.Empty(z3.SeqSort(z3.IntSort()))
3688
- return z3.Unit(z3IntVal(ord(literal)))
3689
- return z3.Concat([z3.Unit(z3IntVal(ord(ch))) for ch in literal])
3690
- return None
3691
-
3692
- def __ch_realize__(self) -> object:
3693
- codepoints = context_statespace().find_model_value(self.var)
3694
- return "".join(chr(x) for x in codepoints)
3695
-
3696
- def __copy__(self):
3697
- return SeqBasedSymbolicStr(self.var)
3698
-
3699
- def __hash__(self):
3700
- return hash(self.__str__())
3701
-
3702
- @staticmethod
3703
- def _concat_strings(
3704
- a: Union[str, "SeqBasedSymbolicStr"], b: Union[str, "SeqBasedSymbolicStr"]
3705
- ) -> Union[str, "SeqBasedSymbolicStr"]:
3706
- assert not is_tracing()
3707
- # Assumes at least one argument is symbolic and not tracing
3708
- if isinstance(a, SeqBasedSymbolicStr) and isinstance(b, SeqBasedSymbolicStr):
3709
- return SeqBasedSymbolicStr(a.var + b.var)
3710
- elif isinstance(a, str) and isinstance(b, SeqBasedSymbolicStr):
3711
- return SeqBasedSymbolicStr(
3712
- SeqBasedSymbolicStr._coerce_to_smt_sort(a) + b.var
3713
- )
3714
- else:
3715
- assert isinstance(a, SeqBasedSymbolicStr)
3716
- assert isinstance(b, str)
3717
- return SeqBasedSymbolicStr(
3718
- a.var + SeqBasedSymbolicStr._coerce_to_smt_sort(b)
3719
- )
3720
-
3721
- def __add__(self, other):
3722
- with NoTracing():
3723
- if isinstance(other, (SeqBasedSymbolicStr, str)):
3724
- return SeqBasedSymbolicStr._concat_strings(self, other)
3725
- if isinstance(other, AnySymbolicStr):
3726
- return NotImplemented
3727
- raise TypeError
3728
-
3729
- def __radd__(self, other):
3730
- with NoTracing():
3731
- if isinstance(other, (SeqBasedSymbolicStr, str)):
3732
- return SeqBasedSymbolicStr._concat_strings(other, self)
3733
- if isinstance(other, AnySymbolicStr):
3734
- return NotImplemented
3735
- raise TypeError
3736
-
3737
- def __mul__(self, other):
3738
- if isinstance(other, Integral):
3739
- if other <= 1:
3740
- return self if other == 1 else ""
3741
- # Note that in SymbolicInt, we attempt string multiplication via regex.
3742
- # Z3 cannot do much with a symbolic regex, so we case-split on
3743
- # the repetition count.
3744
- return SeqBasedSymbolicStr(z3.Concat(*[self.var for _ in range(other)]))
3745
- return NotImplemented
3746
-
3747
- __rmul__ = __mul__
3748
-
3749
- def __mod__(self, other):
3750
- return self.__str__() % realize(other)
3751
-
3752
- def __contains__(self, other):
3753
- with NoTracing():
3754
- forced = force_to_smt_sort(other, SeqBasedSymbolicStr)
3755
- return SymbolicBool(z3.Contains(self.var, forced))
3756
-
3757
- def __getitem__(self, i: Union[int, slice]):
3758
- with NoTracing():
3759
- idx_or_pair = process_slice_vs_symbolic_len(
3760
- context_statespace(), i, z3.Length(self.var)
3761
- )
3762
- if isinstance(idx_or_pair, tuple):
3763
- (start, stop) = idx_or_pair
3764
- smt_result = z3.Extract(self.var, start, stop - start)
3765
- else:
3766
- smt_result = z3.Unit(self.var[idx_or_pair])
3767
- return SeqBasedSymbolicStr(smt_result)
3768
-
3769
- def endswith(self, substr):
3770
- with NoTracing():
3771
- smt_substr = force_to_smt_sort(substr, SeqBasedSymbolicStr)
3772
- return SymbolicBool(z3.SuffixOf(smt_substr, self.var))
3773
-
3774
- def find(self, substr, start=None, end=None):
3775
- if not isinstance(substr, str):
3776
- raise TypeError
3777
- with NoTracing():
3778
- space = context_statespace()
3779
- smt_my_len = z3.Length(self.var)
3780
- if start is None and end is None:
3781
- smt_start = z3IntVal(0)
3782
- smt_end = smt_my_len
3783
- smt_str = self.var
3784
- if len(substr) == 0:
3785
- return 0
3786
- else:
3787
- (smt_start, smt_end) = flip_slice_vs_symbolic_len(
3788
- space, slice(start, end, None), smt_my_len
3789
- )
3790
- if len(substr) == 0:
3791
- # Add oddity of CPython. We can find the empty string when over-slicing
3792
- # off the left side of the string, but not off the right:
3793
- # ''.find('', 3, 4) == -1
3794
- # ''.find('', -4, -3) == 0
3795
- if space.smt_fork(smt_start > smt_my_len):
3796
- return -1
3797
- elif space.smt_fork(smt_start > 0):
3798
- return SymbolicInt(smt_start)
3799
- else:
3800
- return 0
3801
- (smt_start, smt_end) = clip_range_to_symbolic_len(
3802
- space, smt_start, smt_end, smt_my_len
3803
- )
3804
- smt_str = z3.SubString(self.var, smt_start, smt_end - smt_start)
3805
-
3806
- smt_sub = force_to_smt_sort(substr, SeqBasedSymbolicStr)
3807
- if space.smt_fork(z3.Contains(smt_str, smt_sub)):
3808
- return SymbolicInt(z3.IndexOf(smt_str, smt_sub, 0) + smt_start)
3809
- else:
3810
- return -1
3811
-
3812
- def partition(self, sep: str):
3813
- if not isinstance(sep, str):
3814
- raise TypeError
3815
- if len(sep) == 0:
3816
- raise ValueError
3817
- with NoTracing():
3818
- space = context_statespace()
3819
- smt_str = self.var
3820
- smt_sep = force_to_smt_sort(sep, SeqBasedSymbolicStr)
3821
- if space.smt_fork(z3.Contains(smt_str, smt_sep)):
3822
- uniq = space.uniq()
3823
- # Divide my contents into 4 concatenated parts:
3824
- prefix = SeqBasedSymbolicStr(f"prefix{uniq}")
3825
- match1 = SeqBasedSymbolicStr(
3826
- f"match1{uniq}"
3827
- ) # the first character of the match
3828
- match_tail = SeqBasedSymbolicStr(f"match_tail{uniq}")
3829
- suffix = SeqBasedSymbolicStr(f"suffix{uniq}")
3830
- space.add(z3.Length(match1.var) == 1)
3831
- space.add(smt_sep == z3.Concat(match1.var, match_tail.var))
3832
- space.add(smt_str == z3.Concat(prefix.var, smt_sep, suffix.var))
3833
- space.add(
3834
- z3.Not(z3.Contains(z3.Concat(match_tail.var, suffix.var), smt_sep))
3835
- )
3836
- return (prefix, sep, suffix)
3837
- else:
3838
- return (self, "", "")
3839
-
3840
- def rfind(self, substr, start=None, end=None) -> Union[int, SymbolicInt]:
3841
- if not isinstance(substr, str):
3842
- raise TypeError
3843
- with NoTracing():
3844
- space = context_statespace()
3845
- smt_my_len = z3.Length(self.var)
3846
- if start is None and end is None:
3847
- smt_start = z3IntVal(0)
3848
- smt_end = smt_my_len
3849
- smt_str = self.var
3850
- if len(substr) == 0:
3851
- return SymbolicInt(smt_my_len)
3852
- else:
3853
- (smt_start, smt_end) = flip_slice_vs_symbolic_len(
3854
- space, slice(start, end, None), smt_my_len
3855
- )
3856
- if len(substr) == 0:
3857
- # Add oddity of CPython. We can find the empty string when over-slicing
3858
- # off the left side of the string, but not off the right:
3859
- # ''.find('', 3, 4) == -1
3860
- # ''.find('', -4, -3) == 0
3861
- if space.smt_fork(smt_start > smt_my_len):
3862
- return -1
3863
- elif space.smt_fork(smt_end < 0):
3864
- return 0
3865
- elif space.smt_fork(smt_end < smt_my_len):
3866
- return SymbolicInt(smt_end)
3867
- else:
3868
- return SymbolicInt(smt_my_len)
3869
- (smt_start, smt_end) = clip_range_to_symbolic_len(
3870
- space, smt_start, smt_end, smt_my_len
3871
- )
3872
- smt_str = z3.SubString(self.var, smt_start, smt_end - smt_start)
3873
- smt_sub = force_to_smt_sort(substr, SeqBasedSymbolicStr)
3874
- if space.smt_fork(z3.Contains(smt_str, smt_sub)):
3875
- uniq = space.uniq()
3876
- # Divide my contents into 4 concatenated parts:
3877
- prefix = SeqBasedSymbolicStr(f"prefix{uniq}")
3878
- match1 = SeqBasedSymbolicStr(f"match1{uniq}")
3879
- match_tail = SeqBasedSymbolicStr(f"match_tail{uniq}")
3880
- suffix = SeqBasedSymbolicStr(f"suffix{uniq}")
3881
- space.add(z3.Length(match1.var) == 1)
3882
- space.add(smt_sub == z3.Concat(match1.var, match_tail.var))
3883
- space.add(smt_str == z3.Concat(prefix.var, smt_sub, suffix.var))
3884
- space.add(
3885
- z3.Not(z3.Contains(z3.Concat(match_tail.var, suffix.var), smt_sub))
3886
- )
3887
- return SymbolicInt(smt_start + z3.Length(prefix.var))
3888
- else:
3889
- return -1
3890
-
3891
- def rpartition(self, sep: str):
3892
- result = self.rsplit(sep, maxsplit=1)
3893
- if len(result) == 1:
3894
- return ("", "", self)
3895
- elif len(result) == 2:
3896
- return (result[0], sep, result[1])
3897
-
3898
- def startswith(self, substr, start=None, end=None):
3899
- if isinstance(substr, tuple):
3900
- return any(self.startswith(s, start, end) for s in substr)
3901
- smt_substr = force_to_smt_sort(substr, SeqBasedSymbolicStr)
3902
- if start is not None or end is not None:
3903
- # TODO: "".startswith("", 1) should be False, not True
3904
- return self[start:end].startswith(substr)
3905
- with NoTracing():
3906
- return SymbolicBool(z3.PrefixOf(smt_substr, self.var))
3907
-
3908
-
3909
3663
  def buffer_to_byte_seq(obj: object) -> Optional[Sequence[int]]:
3910
3664
  if isinstance(obj, (bytes, bytearray)):
3911
3665
  return list(obj)
@@ -3975,7 +3729,7 @@ class BytesLike(Buffer, AbcString, CrossHairValue):
3975
3729
  return False
3976
3730
  return list(self) == list(other)
3977
3731
 
3978
- if sys.version_info >= (3, 12):
3732
+ if version_info >= (3, 12):
3979
3733
 
3980
3734
  def __buffer__(self, flags: int):
3981
3735
  with NoTracing():
@@ -4135,7 +3889,10 @@ class SymbolicBytes(BytesLike):
4135
3889
  accumulated = []
4136
3890
  high = None
4137
3891
  if not isinstance(hexstr, str):
4138
- raise TypeError
3892
+ if is_bytes_like(hexstr) and version_info >= (3, 14):
3893
+ hexstr = LazyIntSymbolicStr(tuple(hexstr))
3894
+ else:
3895
+ raise TypeError
4139
3896
  for idx, ch in enumerate(hexstr):
4140
3897
  if not ch.isascii():
4141
3898
  raise ValueError(
@@ -4814,11 +4571,6 @@ def _ord(c: str) -> int:
4814
4571
  with NoTracing():
4815
4572
  if isinstance(c, LazyIntSymbolicStr):
4816
4573
  return c._codepoints[0]
4817
- elif isinstance(c, SeqBasedSymbolicStr):
4818
- space = context_statespace()
4819
- ret = SymbolicInt("ord" + space.uniq())
4820
- space.add(c.var == z3.Unit(ret.var))
4821
- return ret
4822
4574
  return ord(realize(c))
4823
4575
 
4824
4576
 
@@ -4894,7 +4646,7 @@ def _int_from_bytes(
4894
4646
  ) -> int:
4895
4647
  if byteorder is _MISSING:
4896
4648
  # byteorder defaults to "big" as of 3.11
4897
- if sys.version_info >= (3, 11):
4649
+ if version_info >= (3, 11):
4898
4650
  byteorder = "big"
4899
4651
  else:
4900
4652
  raise TypeError
@@ -5108,7 +4860,7 @@ def make_registrations():
5108
4860
 
5109
4861
  register_type(Union, make_union_choice)
5110
4862
 
5111
- if sys.version_info >= (3, 8):
4863
+ if version_info >= (3, 8):
5112
4864
  from typing import Final
5113
4865
 
5114
4866
  register_type(Final, lambda p, t: p(t))
@@ -5266,7 +5018,7 @@ def make_registrations():
5266
5018
  "upper",
5267
5019
  "zfill",
5268
5020
  ]
5269
- if sys.version_info >= (3, 9):
5021
+ if version_info >= (3, 9):
5270
5022
  names_to_str_patch.append("removeprefix")
5271
5023
  names_to_str_patch.append("removesuffix")
5272
5024
  for name in names_to_str_patch:
@@ -5322,12 +5074,12 @@ def make_registrations():
5322
5074
  # Patches on int
5323
5075
  register_patch(int.__repr__, with_checked_self(int, "__repr__"))
5324
5076
  register_patch(int.as_integer_ratio, with_checked_self(int, "as_integer_ratio"))
5325
- if sys.version_info >= (3, 10):
5077
+ if version_info >= (3, 10):
5326
5078
  register_patch(int.bit_count, with_checked_self(int, "bit_count"))
5327
5079
  register_patch(int.bit_length, with_checked_self(int, "bit_length"))
5328
5080
  register_patch(int.conjugate, with_checked_self(int, "conjugate"))
5329
5081
  register_patch(int.from_bytes, _int_from_bytes)
5330
- if sys.version_info >= (3, 12):
5082
+ if version_info >= (3, 12):
5331
5083
  register_patch(int.is_integer, with_checked_self(int, "is_integer"))
5332
5084
  register_patch(int.to_bytes, with_checked_self(int, "to_bytes"))
5333
5085
 
@@ -2117,7 +2117,7 @@ def test_dict___bool___ok() -> None:
2117
2117
  check_states(f, CONFIRMED)
2118
2118
 
2119
2119
 
2120
- def test_dict___iter__() -> None:
2120
+ def test_dict___iter___fail() -> None:
2121
2121
  def f(a: Dict[int, str]) -> List[int]:
2122
2122
  """
2123
2123
  post[a]: 5 in _