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.
- _crosshair_tracers.cpython-39-darwin.so +0 -0
- crosshair/__init__.py +1 -1
- crosshair/_mark_stacks.h +51 -24
- crosshair/_tracers.h +9 -5
- crosshair/_tracers_test.py +19 -9
- crosshair/auditwall.py +9 -8
- crosshair/auditwall_test.py +31 -19
- crosshair/codeconfig.py +3 -2
- crosshair/condition_parser.py +17 -133
- crosshair/condition_parser_test.py +54 -96
- crosshair/conftest.py +1 -1
- crosshair/copyext.py +91 -22
- crosshair/copyext_test.py +33 -0
- crosshair/core.py +259 -203
- crosshair/core_and_libs.py +20 -0
- crosshair/core_regestered_types_test.py +82 -0
- crosshair/core_test.py +693 -664
- crosshair/diff_behavior.py +76 -21
- crosshair/diff_behavior_test.py +132 -23
- crosshair/dynamic_typing.py +128 -18
- crosshair/dynamic_typing_test.py +91 -4
- crosshair/enforce.py +1 -6
- crosshair/enforce_test.py +15 -23
- crosshair/examples/check_examples_test.py +2 -1
- crosshair/fnutil.py +2 -3
- crosshair/fnutil_test.py +0 -7
- crosshair/fuzz_core_test.py +70 -83
- crosshair/libimpl/arraylib.py +10 -7
- crosshair/libimpl/binascii_ch_test.py +30 -0
- crosshair/libimpl/binascii_test.py +67 -0
- crosshair/libimpl/binasciilib.py +150 -0
- crosshair/libimpl/bisectlib_test.py +5 -5
- crosshair/libimpl/builtinslib.py +1002 -682
- crosshair/libimpl/builtinslib_ch_test.py +108 -30
- crosshair/libimpl/builtinslib_test.py +431 -143
- crosshair/libimpl/codecslib.py +22 -2
- crosshair/libimpl/codecslib_test.py +41 -9
- crosshair/libimpl/collectionslib.py +44 -8
- crosshair/libimpl/collectionslib_test.py +108 -20
- crosshair/libimpl/copylib.py +1 -1
- crosshair/libimpl/copylib_test.py +18 -0
- crosshair/libimpl/datetimelib.py +84 -67
- crosshair/libimpl/datetimelib_ch_test.py +12 -7
- crosshair/libimpl/datetimelib_test.py +5 -6
- crosshair/libimpl/decimallib.py +5257 -0
- crosshair/libimpl/decimallib_ch_test.py +78 -0
- crosshair/libimpl/decimallib_test.py +76 -0
- crosshair/libimpl/encodings/_encutil.py +21 -11
- crosshair/libimpl/fractionlib.py +16 -0
- crosshair/libimpl/fractionlib_test.py +80 -0
- crosshair/libimpl/functoolslib.py +19 -7
- crosshair/libimpl/functoolslib_test.py +22 -6
- crosshair/libimpl/hashliblib.py +30 -0
- crosshair/libimpl/hashliblib_test.py +18 -0
- crosshair/libimpl/heapqlib.py +32 -5
- crosshair/libimpl/heapqlib_test.py +15 -12
- crosshair/libimpl/iolib.py +7 -4
- crosshair/libimpl/ipaddresslib.py +8 -0
- crosshair/libimpl/itertoolslib_test.py +1 -1
- crosshair/libimpl/mathlib.py +165 -2
- crosshair/libimpl/mathlib_ch_test.py +44 -0
- crosshair/libimpl/mathlib_test.py +59 -16
- crosshair/libimpl/oslib.py +7 -0
- crosshair/libimpl/pathliblib_test.py +10 -0
- crosshair/libimpl/randomlib.py +1 -0
- crosshair/libimpl/randomlib_test.py +6 -4
- crosshair/libimpl/relib.py +180 -59
- crosshair/libimpl/relib_ch_test.py +26 -2
- crosshair/libimpl/relib_test.py +77 -14
- crosshair/libimpl/timelib.py +35 -13
- crosshair/libimpl/timelib_test.py +13 -3
- crosshair/libimpl/typeslib.py +15 -0
- crosshair/libimpl/typeslib_test.py +36 -0
- crosshair/libimpl/unicodedatalib_test.py +3 -3
- crosshair/libimpl/weakreflib.py +13 -0
- crosshair/libimpl/weakreflib_test.py +69 -0
- crosshair/libimpl/zliblib.py +15 -0
- crosshair/libimpl/zliblib_test.py +13 -0
- crosshair/lsp_server.py +21 -10
- crosshair/main.py +48 -28
- crosshair/main_test.py +59 -14
- crosshair/objectproxy.py +39 -14
- crosshair/objectproxy_test.py +27 -13
- crosshair/opcode_intercept.py +212 -24
- crosshair/opcode_intercept_test.py +172 -18
- crosshair/options.py +0 -1
- crosshair/patch_equivalence_test.py +5 -21
- crosshair/path_cover.py +7 -5
- crosshair/path_search.py +6 -4
- crosshair/path_search_test.py +1 -2
- crosshair/pathing_oracle.py +53 -10
- crosshair/pathing_oracle_test.py +21 -0
- crosshair/pure_importer_test.py +5 -21
- crosshair/register_contract.py +16 -6
- crosshair/register_contract_test.py +2 -14
- crosshair/simplestructs.py +154 -85
- crosshair/simplestructs_test.py +16 -2
- crosshair/smtlib.py +24 -0
- crosshair/smtlib_test.py +14 -0
- crosshair/statespace.py +319 -196
- crosshair/statespace_test.py +45 -0
- crosshair/stubs_parser.py +0 -2
- crosshair/test_util.py +87 -25
- crosshair/test_util_test.py +26 -0
- crosshair/tools/check_init_and_setup_coincide.py +0 -3
- crosshair/tools/generate_demo_table.py +2 -2
- crosshair/tracers.py +141 -49
- crosshair/type_repo.py +11 -4
- crosshair/unicode_categories.py +1 -0
- crosshair/util.py +158 -76
- crosshair/util_test.py +13 -20
- crosshair/watcher.py +4 -4
- crosshair/z3util.py +1 -1
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/METADATA +45 -36
- crosshair_tool-0.0.100.dist-info/RECORD +176 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/WHEEL +2 -1
- crosshair/examples/hypothesis/__init__.py +0 -2
- crosshair/examples/hypothesis/bugs_detected/simple_strategies.py +0 -74
- crosshair_tool-0.0.56.dist-info/RECORD +0 -152
- /crosshair/{examples/hypothesis/bugs_detected/__init__.py → py.typed} +0 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/entry_points.txt +0 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info/licenses}/LICENSE +0 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/top_level.txt +0 -0
crosshair/libimpl/codecslib.py
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import codecs
|
|
2
2
|
|
|
3
3
|
from crosshair import NoTracing, register_patch
|
|
4
|
-
from crosshair.core import realize
|
|
4
|
+
from crosshair.core import class_with_realized_methods, realize, with_realized_args
|
|
5
5
|
from crosshair.libimpl.encodings import codec_search
|
|
6
|
+
from crosshair.util import is_iterable
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RealizingCodecInfo(codecs.CodecInfo):
|
|
10
|
+
def __new__(cls, c: codecs.CodecInfo):
|
|
11
|
+
encode = with_realized_args(c.encode)
|
|
12
|
+
decode = with_realized_args(c.decode)
|
|
13
|
+
streamreader = class_with_realized_methods(c.streamreader)
|
|
14
|
+
streamwriter = class_with_realized_methods(c.streamwriter)
|
|
15
|
+
incrementaldecoder = class_with_realized_methods(c.incrementaldecoder)
|
|
16
|
+
incrementalencoder = class_with_realized_methods(c.incrementalencoder)
|
|
17
|
+
return codecs.CodecInfo(
|
|
18
|
+
encode,
|
|
19
|
+
decode,
|
|
20
|
+
streamreader=streamreader,
|
|
21
|
+
streamwriter=streamwriter,
|
|
22
|
+
incrementalencoder=incrementalencoder,
|
|
23
|
+
incrementaldecoder=incrementaldecoder,
|
|
24
|
+
name=c.name,
|
|
25
|
+
)
|
|
6
26
|
|
|
7
27
|
|
|
8
28
|
def _decode(obj, encoding="utf-8", errors="strict"):
|
|
@@ -50,7 +70,7 @@ def _lookup(encoding: str) -> codecs.CodecInfo:
|
|
|
50
70
|
try:
|
|
51
71
|
return codecs.lookup("crosshair_" + encoding)
|
|
52
72
|
except LookupError:
|
|
53
|
-
return codecs.lookup(encoding)
|
|
73
|
+
return RealizingCodecInfo(codecs.lookup(encoding))
|
|
54
74
|
|
|
55
75
|
|
|
56
76
|
def make_registrations() -> None:
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import codecs
|
|
2
|
+
import io
|
|
3
|
+
import sys
|
|
2
4
|
|
|
3
5
|
import pytest
|
|
4
6
|
|
|
5
|
-
from crosshair.
|
|
7
|
+
from crosshair.core import proxy_for_type
|
|
8
|
+
from crosshair.core_and_libs import ResumedTracing, standalone_statespace
|
|
6
9
|
from crosshair.libimpl.builtinslib import LazyIntSymbolicStr, SymbolicBytes, SymbolicInt
|
|
7
10
|
from crosshair.options import AnalysisOptionSet
|
|
8
11
|
from crosshair.statespace import POST_FAIL, MessageType
|
|
@@ -22,27 +25,56 @@ def test_encode_utf8_literal(space):
|
|
|
22
25
|
|
|
23
26
|
def test_encode_utf8_symbolic_char(space):
|
|
24
27
|
cp = SymbolicInt("cp")
|
|
25
|
-
space.add(cp.var >= ord("a"))
|
|
26
|
-
space.add(cp.var <= ord("z"))
|
|
27
28
|
with ResumedTracing():
|
|
29
|
+
space.add(cp >= ord("a"))
|
|
30
|
+
space.add(cp <= ord("z"))
|
|
28
31
|
encoded = codecs.encode(chr(cp), "utf-8")
|
|
29
32
|
assert isinstance(encoded, SymbolicBytes)
|
|
30
33
|
byte_value = encoded[0]
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
with ResumedTracing():
|
|
35
|
+
assert space.is_possible(byte_value == ord("a"))
|
|
36
|
+
assert space.is_possible(byte_value == ord("b"))
|
|
33
37
|
|
|
34
38
|
|
|
35
39
|
def test_decode_utf8_symbolic_char(space):
|
|
36
40
|
cp = SymbolicInt("cp")
|
|
37
|
-
space.add(cp.var >= ord("a"))
|
|
38
|
-
space.add(cp.var <= ord("z"))
|
|
39
41
|
with ResumedTracing():
|
|
42
|
+
space.add(cp >= ord("a"))
|
|
43
|
+
space.add(cp <= ord("z"))
|
|
40
44
|
decoded = codecs.decode(SymbolicBytes([cp]), "utf-8")
|
|
41
45
|
assert isinstance(decoded, LazyIntSymbolicStr)
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
with ResumedTracing():
|
|
47
|
+
assert space.is_possible(decoded._codepoints[0] == ord("a"))
|
|
48
|
+
assert space.is_possible(decoded._codepoints[0] == ord("b"))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_unsupported_codec_encode(space):
|
|
52
|
+
s = proxy_for_type(str, "s")
|
|
53
|
+
with ResumedTracing():
|
|
54
|
+
s.encode("cp858")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_unsupported_codec_streamwriter(space):
|
|
58
|
+
s = proxy_for_type(str, "s")
|
|
59
|
+
buf = bytearray()
|
|
60
|
+
with ResumedTracing():
|
|
61
|
+
codecs.getwriter("cp858")(io.BytesIO(buf)).write(s)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.mark.xfail(reason="not yet implemented")
|
|
65
|
+
def test_supported_codec_streamwriter(space):
|
|
66
|
+
s = proxy_for_type(str, "s")
|
|
67
|
+
with ResumedTracing():
|
|
68
|
+
space.add(len(s) == 1)
|
|
69
|
+
buf = bytearray()
|
|
70
|
+
codecs.getwriter("ascii")(io.BytesIO(buf)).write(s)
|
|
71
|
+
space.is_possible(buf[0] == ord("x"))
|
|
44
72
|
|
|
45
73
|
|
|
74
|
+
@pytest.mark.skipif(
|
|
75
|
+
sys.version_info >= (3, 13),
|
|
76
|
+
reason="Need to intercept UnicodeDecodeError.str in 3.13+",
|
|
77
|
+
)
|
|
46
78
|
def test_decode_e2e():
|
|
47
79
|
def f(byts: bytes) -> str:
|
|
48
80
|
"""
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import collections
|
|
2
|
+
import sys
|
|
2
3
|
from typing import (
|
|
3
4
|
Any,
|
|
4
5
|
Callable,
|
|
5
6
|
Dict,
|
|
6
7
|
Generic,
|
|
7
8
|
Iterable,
|
|
9
|
+
KeysView,
|
|
8
10
|
List,
|
|
9
11
|
Optional,
|
|
10
12
|
Set,
|
|
11
13
|
Tuple,
|
|
12
14
|
TypeVar,
|
|
15
|
+
Union,
|
|
16
|
+
ValuesView,
|
|
13
17
|
)
|
|
14
18
|
|
|
15
19
|
from crosshair import register_type
|
|
16
|
-
from crosshair.core import
|
|
20
|
+
from crosshair.core import realize
|
|
17
21
|
from crosshair.tracers import NoTracing, ResumedTracing
|
|
18
|
-
from crosshair.util import is_iterable
|
|
22
|
+
from crosshair.util import CrossHairValue, is_iterable
|
|
19
23
|
|
|
20
24
|
T = TypeVar("T")
|
|
21
25
|
|
|
@@ -122,8 +126,19 @@ class ListBasedDeque(collections.abc.MutableSequence, CrossHairValue, Generic[T]
|
|
|
122
126
|
prefix.reverse()
|
|
123
127
|
self._contents = prefix + self._contents
|
|
124
128
|
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
if sys.version_info >= (3, 14):
|
|
130
|
+
|
|
131
|
+
def index(self, item: T, *bounds) -> int:
|
|
132
|
+
try:
|
|
133
|
+
return self._contents.index(item, *bounds)
|
|
134
|
+
except ValueError as exc:
|
|
135
|
+
exc.args = ("deque.index(x): x not in deque",)
|
|
136
|
+
raise
|
|
137
|
+
|
|
138
|
+
else:
|
|
139
|
+
|
|
140
|
+
def index(self, item: T, *bounds) -> int:
|
|
141
|
+
return self._contents.index(item, *bounds)
|
|
127
142
|
|
|
128
143
|
def insert(self, index: int, item: T) -> None:
|
|
129
144
|
self._contents.insert(index, item)
|
|
@@ -203,11 +218,29 @@ def make_registrations():
|
|
|
203
218
|
register_type(collections.abc.Mapping, lambda p, kt=Any, vt=Any: p(Dict[kt, vt])) # type: ignore
|
|
204
219
|
register_type(collections.abc.MutableMapping, lambda p, kt=Any, vt=Any: p(Dict[kt, vt])) # type: ignore
|
|
205
220
|
register_type(collections.OrderedDict, lambda p, kt=Any, vt=Any: collections.OrderedDict(p(Dict[kt, vt]))) # type: ignore
|
|
221
|
+
# TODO: This implementation of Counter probably over-realizes the symbolic map it is given:
|
|
206
222
|
register_type(collections.Counter, lambda p, t=Any: collections.Counter(p(Dict[t, int]))) # type: ignore
|
|
207
223
|
# TODO: MappingView is missing
|
|
208
|
-
register_type(
|
|
209
|
-
|
|
210
|
-
|
|
224
|
+
register_type(
|
|
225
|
+
collections.abc.ItemsView,
|
|
226
|
+
lambda p, k=Any, v=Any: p(Dict.__getitem__((k, v))).items(),
|
|
227
|
+
)
|
|
228
|
+
register_type(
|
|
229
|
+
collections.abc.MappingView,
|
|
230
|
+
lambda p, t=Any: p(
|
|
231
|
+
Union[
|
|
232
|
+
KeysView.__getitem__((t,)),
|
|
233
|
+
ValuesView.__getitem__((t,)),
|
|
234
|
+
]
|
|
235
|
+
),
|
|
236
|
+
)
|
|
237
|
+
register_type(
|
|
238
|
+
collections.abc.KeysView, lambda p, t=Any: p(Dict.__getitem__((t, Any))).keys()
|
|
239
|
+
)
|
|
240
|
+
register_type(
|
|
241
|
+
collections.abc.ValuesView,
|
|
242
|
+
lambda p, t=Any: p(Dict.__getitem__((Any, t))).values(),
|
|
243
|
+
)
|
|
211
244
|
|
|
212
245
|
register_type(collections.abc.Container, lambda p, t=Any: p(Tuple[t, ...]))
|
|
213
246
|
register_type(collections.abc.Collection, lambda p, t=Any: p(Tuple[t, ...]))
|
|
@@ -224,5 +257,8 @@ def make_registrations():
|
|
|
224
257
|
|
|
225
258
|
register_type(collections.abc.MutableSet, lambda p, t=Any: p(Set[t])) # type: ignore
|
|
226
259
|
|
|
227
|
-
|
|
260
|
+
if sys.version_info < (3, 14):
|
|
261
|
+
register_type(collections.abc.ByteString, lambda p: p(bytes))
|
|
262
|
+
if sys.version_info >= (3, 12):
|
|
263
|
+
register_type(collections.abc.Buffer, lambda p: p(bytes))
|
|
228
264
|
register_type(collections.abc.Hashable, lambda p: p(int))
|
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
from collections import Counter, defaultdict, deque, namedtuple
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from inspect import Parameter, Signature
|
|
6
|
+
from typing import Callable, Counter, DefaultDict, Deque, Dict, NamedTuple, Tuple
|
|
3
7
|
|
|
4
8
|
import pytest
|
|
5
9
|
|
|
6
|
-
from crosshair.core import
|
|
7
|
-
|
|
10
|
+
from crosshair.core import (
|
|
11
|
+
deep_realize,
|
|
12
|
+
get_constructor_signature,
|
|
13
|
+
proxy_for_type,
|
|
14
|
+
realize,
|
|
15
|
+
standalone_statespace,
|
|
16
|
+
)
|
|
17
|
+
from crosshair.libimpl.collectionslib import ListBasedDeque, PureDefaultDict
|
|
8
18
|
from crosshair.statespace import CANNOT_CONFIRM, CONFIRMED, POST_FAIL, MessageType
|
|
9
19
|
from crosshair.test_util import check_states
|
|
10
|
-
from crosshair.tracers import NoTracing
|
|
11
|
-
from crosshair.util import
|
|
20
|
+
from crosshair.tracers import NoTracing, ResumedTracing
|
|
21
|
+
from crosshair.util import CrossHairValue
|
|
12
22
|
|
|
13
23
|
|
|
14
24
|
@pytest.fixture
|
|
@@ -16,6 +26,20 @@ def test_list():
|
|
|
16
26
|
return ListBasedDeque([1, 2, 3, 4, 5])
|
|
17
27
|
|
|
18
28
|
|
|
29
|
+
def test_counter_symbolic_deep(space):
|
|
30
|
+
d = proxy_for_type(Counter[int], "d")
|
|
31
|
+
with ResumedTracing():
|
|
32
|
+
deep_realize(d)
|
|
33
|
+
deepcopy(d)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_counter_deep(space):
|
|
37
|
+
d = Counter()
|
|
38
|
+
with ResumedTracing():
|
|
39
|
+
deep_realize(d)
|
|
40
|
+
deepcopy(d)
|
|
41
|
+
|
|
42
|
+
|
|
19
43
|
def test_deque_appendleft(test_list) -> None:
|
|
20
44
|
test_list.appendleft(0)
|
|
21
45
|
assert test_list.popleft() == 0
|
|
@@ -75,7 +99,11 @@ def test_deque_index_with_start_index_throws_correct_exception(test_list) -> Non
|
|
|
75
99
|
with pytest.raises(ValueError) as context:
|
|
76
100
|
test_list.index(1, 2)
|
|
77
101
|
|
|
78
|
-
|
|
102
|
+
if sys.version_info >= (3, 14):
|
|
103
|
+
# assert context.match(re.escape("list.index(x): x not in list"))
|
|
104
|
+
assert context.match(re.escape("deque.index(x): x not in deque"))
|
|
105
|
+
else:
|
|
106
|
+
assert context.match("1 is not in list")
|
|
79
107
|
|
|
80
108
|
|
|
81
109
|
def test_deque_index_with_start_and_end_index(test_list) -> None:
|
|
@@ -89,7 +117,10 @@ def test_deque_index_with_start_and_end_index_throws_correct_exception(
|
|
|
89
117
|
with pytest.raises(ValueError) as context:
|
|
90
118
|
test_list.index(6, 0, 1)
|
|
91
119
|
|
|
92
|
-
|
|
120
|
+
if sys.version_info >= (3, 14):
|
|
121
|
+
assert context.match(re.escape("deque.index(x): x not in deque"))
|
|
122
|
+
else:
|
|
123
|
+
assert context.match("6 is not in list")
|
|
93
124
|
|
|
94
125
|
|
|
95
126
|
def test_deque_insert(test_list) -> None:
|
|
@@ -162,7 +193,7 @@ def test_deque_extendleft_method() -> None:
|
|
|
162
193
|
"""
|
|
163
194
|
Can any deque be extended by itself and form this palindrome?
|
|
164
195
|
|
|
165
|
-
post[ls]: ls !=
|
|
196
|
+
post[ls]: ls != deque([1, 2, 3, 3, 2, 1])
|
|
166
197
|
"""
|
|
167
198
|
ls.extendleft(ls)
|
|
168
199
|
|
|
@@ -171,28 +202,28 @@ def test_deque_extendleft_method() -> None:
|
|
|
171
202
|
|
|
172
203
|
def test_deque_add_symbolic_to_concrete():
|
|
173
204
|
with standalone_statespace as space:
|
|
174
|
-
d = ListBasedDeque([1, 2]) +
|
|
205
|
+
d = ListBasedDeque([1, 2]) + deque([3, 4])
|
|
175
206
|
assert list(d) == [1, 2, 3, 4]
|
|
176
207
|
|
|
177
208
|
|
|
178
209
|
def test_deque_eq():
|
|
179
210
|
with standalone_statespace as space:
|
|
180
211
|
assert ListBasedDeque([1, 2]) == ListBasedDeque([1, 2])
|
|
181
|
-
assert
|
|
212
|
+
assert deque([1, 2]) == ListBasedDeque([1, 2])
|
|
182
213
|
assert ListBasedDeque([1, 2]) != ListBasedDeque([1, 55])
|
|
183
|
-
assert
|
|
214
|
+
assert deque([1, 2]) != ListBasedDeque([1, 55])
|
|
184
215
|
|
|
185
216
|
|
|
186
217
|
def test_defaultdict_repr_equiv(test_list) -> None:
|
|
187
218
|
def f(symbolic: DefaultDict[int, int]) -> Tuple[dict, dict]:
|
|
188
219
|
"""post: _[0] == _[1]"""
|
|
189
|
-
concrete =
|
|
220
|
+
concrete = defaultdict(symbolic.default_factory, symbolic.items())
|
|
190
221
|
return (symbolic, concrete)
|
|
191
222
|
|
|
192
223
|
check_states(f, CANNOT_CONFIRM)
|
|
193
224
|
|
|
194
225
|
|
|
195
|
-
def test_defaultdict_basic_fail(
|
|
226
|
+
def test_defaultdict_basic_fail() -> None:
|
|
196
227
|
def f(a: DefaultDict[int, int], k: int, v: int) -> None:
|
|
197
228
|
"""
|
|
198
229
|
post[a]: a[42] != 42
|
|
@@ -215,10 +246,10 @@ def test_defaultdict_default_fail(test_list) -> None:
|
|
|
215
246
|
|
|
216
247
|
|
|
217
248
|
def test_defaultdict_default_ok(test_list) -> None:
|
|
218
|
-
def f(a: DefaultDict[int, int],
|
|
249
|
+
def f(a: DefaultDict[int, int], k: int) -> DefaultDict[int, int]:
|
|
219
250
|
"""
|
|
220
251
|
pre: len(a) == 0 and a.default_factory is not None
|
|
221
|
-
post: _[
|
|
252
|
+
post: _[k] == _[k]
|
|
222
253
|
"""
|
|
223
254
|
return a
|
|
224
255
|
|
|
@@ -229,16 +260,73 @@ def test_defaultdict_realize():
|
|
|
229
260
|
with standalone_statespace:
|
|
230
261
|
with NoTracing():
|
|
231
262
|
d = proxy_for_type(DefaultDict[int, int], "d")
|
|
232
|
-
assert type(realize(d)) is
|
|
263
|
+
assert type(realize(d)) is defaultdict
|
|
233
264
|
|
|
234
265
|
|
|
235
266
|
#
|
|
236
|
-
# We don't patch namedtuple, but namedtuple performs magic
|
|
267
|
+
# We don't patch namedtuple, but namedtuple performs magic dynamic type
|
|
237
268
|
# generation, which can interfere with CrossHair.
|
|
238
269
|
#
|
|
239
270
|
|
|
240
271
|
|
|
241
272
|
def test_namedtuple_creation():
|
|
242
273
|
with standalone_statespace:
|
|
243
|
-
# Ensure type creation doesn't raise exception:
|
|
244
|
-
Color =
|
|
274
|
+
# Ensure type creation under trace doesn't raise exception:
|
|
275
|
+
Color = namedtuple("Color", ("name", "hex"))
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def test_namedtuple_argument_detection_untyped():
|
|
279
|
+
UntypedColor = namedtuple("UntypedColor", ("name", "hex"))
|
|
280
|
+
expected_signature = Signature(
|
|
281
|
+
parameters=[
|
|
282
|
+
Parameter("name", Parameter.POSITIONAL_OR_KEYWORD),
|
|
283
|
+
Parameter("hex", Parameter.POSITIONAL_OR_KEYWORD),
|
|
284
|
+
],
|
|
285
|
+
return_annotation=Signature.empty,
|
|
286
|
+
)
|
|
287
|
+
assert get_constructor_signature(UntypedColor) == expected_signature
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def test_namedtuple_argument_detection_typed_with_subclass():
|
|
291
|
+
class ClassTypedColor(NamedTuple):
|
|
292
|
+
name: str
|
|
293
|
+
hex: int
|
|
294
|
+
|
|
295
|
+
expected_parameters = {
|
|
296
|
+
"name": Parameter("name", Parameter.POSITIONAL_OR_KEYWORD, annotation=str),
|
|
297
|
+
"hex": Parameter("hex", Parameter.POSITIONAL_OR_KEYWORD, annotation=int),
|
|
298
|
+
}
|
|
299
|
+
assert get_constructor_signature(ClassTypedColor).parameters == expected_parameters
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
@pytest.mark.skipif(
|
|
303
|
+
sys.version_info < (3, 9),
|
|
304
|
+
reason="Functional namedtuple field types supported on Python >= 3.9",
|
|
305
|
+
)
|
|
306
|
+
def test_namedtuple_argument_detection_typed_functionally():
|
|
307
|
+
FunctionallyTypedColor = NamedTuple(
|
|
308
|
+
"FunctionallyTypedColor", [("name", str), ("hex", int)]
|
|
309
|
+
)
|
|
310
|
+
expected_parameters = {
|
|
311
|
+
"name": Parameter("name", Parameter.POSITIONAL_OR_KEYWORD, annotation=str),
|
|
312
|
+
"hex": Parameter("hex", Parameter.POSITIONAL_OR_KEYWORD, annotation=int),
|
|
313
|
+
}
|
|
314
|
+
assert (
|
|
315
|
+
get_constructor_signature(FunctionallyTypedColor).parameters
|
|
316
|
+
== expected_parameters
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
@pytest.mark.skipif(
|
|
321
|
+
sys.version_info < (3, 9),
|
|
322
|
+
reason="Functional namedtuple field types supported on Python >= 3.9",
|
|
323
|
+
)
|
|
324
|
+
def test_namedtuple_symbolic_creation(space):
|
|
325
|
+
UntypedColor = namedtuple("Color", "name hex")
|
|
326
|
+
Color = NamedTuple("Color", [("name", str), ("hex", int)])
|
|
327
|
+
untyped_color = proxy_for_type(UntypedColor, "color")
|
|
328
|
+
assert isinstance(untyped_color.hex, CrossHairValue)
|
|
329
|
+
color = proxy_for_type(Color, "color")
|
|
330
|
+
with ResumedTracing():
|
|
331
|
+
assert space.is_possible(color.hex == 5)
|
|
332
|
+
assert space.is_possible(color.hex == 10)
|
crosshair/libimpl/copylib.py
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import datetime
|
|
3
|
+
|
|
4
|
+
from crosshair.core import proxy_for_type
|
|
5
|
+
from crosshair.statespace import StateSpace
|
|
6
|
+
from crosshair.tracers import ResumedTracing
|
|
7
|
+
from crosshair.util import debug
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_date_copy(space: StateSpace) -> None:
|
|
11
|
+
concrete_date = datetime.date(2000, 2, 3)
|
|
12
|
+
symbolic_date = proxy_for_type(datetime.date, "d")
|
|
13
|
+
with ResumedTracing():
|
|
14
|
+
copied_symbolic = copy.deepcopy(symbolic_date)
|
|
15
|
+
copied_concrete = copy.deepcopy(concrete_date)
|
|
16
|
+
assert not space.is_possible(copied_concrete != concrete_date)
|
|
17
|
+
assert not space.is_possible(copied_concrete != datetime.date(2000, 2, 3))
|
|
18
|
+
assert not space.is_possible(copied_symbolic != symbolic_date)
|