crosshair-tool 0.0.99__cp312-cp312-macosx_10_13_x86_64.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-312-darwin.so +0 -0
- crosshair/__init__.py +42 -0
- crosshair/__main__.py +8 -0
- crosshair/_mark_stacks.h +790 -0
- crosshair/_preliminaries_test.py +18 -0
- crosshair/_tracers.h +94 -0
- crosshair/_tracers_pycompat.h +522 -0
- crosshair/_tracers_test.py +138 -0
- crosshair/abcstring.py +245 -0
- crosshair/auditwall.py +190 -0
- crosshair/auditwall_test.py +77 -0
- crosshair/codeconfig.py +113 -0
- crosshair/codeconfig_test.py +117 -0
- crosshair/condition_parser.py +1237 -0
- crosshair/condition_parser_test.py +497 -0
- crosshair/conftest.py +30 -0
- crosshair/copyext.py +155 -0
- crosshair/copyext_test.py +84 -0
- crosshair/core.py +1763 -0
- crosshair/core_and_libs.py +149 -0
- crosshair/core_regestered_types_test.py +82 -0
- crosshair/core_test.py +1316 -0
- crosshair/diff_behavior.py +314 -0
- crosshair/diff_behavior_test.py +261 -0
- crosshair/dynamic_typing.py +346 -0
- crosshair/dynamic_typing_test.py +210 -0
- crosshair/enforce.py +282 -0
- crosshair/enforce_test.py +182 -0
- crosshair/examples/PEP316/__init__.py +1 -0
- crosshair/examples/PEP316/bugs_detected/__init__.py +0 -0
- crosshair/examples/PEP316/bugs_detected/getattr_magic.py +16 -0
- crosshair/examples/PEP316/bugs_detected/hash_consistent_with_equals.py +31 -0
- crosshair/examples/PEP316/bugs_detected/shopping_cart.py +24 -0
- crosshair/examples/PEP316/bugs_detected/showcase.py +39 -0
- crosshair/examples/PEP316/correct_code/__init__.py +0 -0
- crosshair/examples/PEP316/correct_code/arith.py +60 -0
- crosshair/examples/PEP316/correct_code/chess.py +77 -0
- crosshair/examples/PEP316/correct_code/nesting_inference.py +17 -0
- crosshair/examples/PEP316/correct_code/numpy_examples.py +132 -0
- crosshair/examples/PEP316/correct_code/rolling_average.py +35 -0
- crosshair/examples/PEP316/correct_code/showcase.py +104 -0
- crosshair/examples/__init__.py +0 -0
- crosshair/examples/check_examples_test.py +146 -0
- crosshair/examples/deal/__init__.py +1 -0
- crosshair/examples/icontract/__init__.py +1 -0
- crosshair/examples/icontract/bugs_detected/__init__.py +0 -0
- crosshair/examples/icontract/bugs_detected/showcase.py +41 -0
- crosshair/examples/icontract/bugs_detected/wrong_sign.py +8 -0
- crosshair/examples/icontract/correct_code/__init__.py +0 -0
- crosshair/examples/icontract/correct_code/arith.py +51 -0
- crosshair/examples/icontract/correct_code/showcase.py +94 -0
- crosshair/fnutil.py +391 -0
- crosshair/fnutil_test.py +75 -0
- crosshair/fuzz_core_test.py +516 -0
- crosshair/libimpl/__init__.py +0 -0
- crosshair/libimpl/arraylib.py +161 -0
- 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 +23 -0
- crosshair/libimpl/builtinslib.py +5228 -0
- crosshair/libimpl/builtinslib_ch_test.py +1191 -0
- crosshair/libimpl/builtinslib_test.py +3735 -0
- crosshair/libimpl/codecslib.py +86 -0
- crosshair/libimpl/codecslib_test.py +86 -0
- crosshair/libimpl/collectionslib.py +264 -0
- crosshair/libimpl/collectionslib_ch_test.py +252 -0
- crosshair/libimpl/collectionslib_test.py +332 -0
- crosshair/libimpl/copylib.py +23 -0
- crosshair/libimpl/copylib_test.py +18 -0
- crosshair/libimpl/datetimelib.py +2559 -0
- crosshair/libimpl/datetimelib_ch_test.py +354 -0
- crosshair/libimpl/datetimelib_test.py +112 -0
- crosshair/libimpl/decimallib.py +5257 -0
- crosshair/libimpl/decimallib_ch_test.py +78 -0
- crosshair/libimpl/decimallib_test.py +76 -0
- crosshair/libimpl/encodings/__init__.py +23 -0
- crosshair/libimpl/encodings/_encutil.py +187 -0
- crosshair/libimpl/encodings/ascii.py +44 -0
- crosshair/libimpl/encodings/latin_1.py +40 -0
- crosshair/libimpl/encodings/utf_8.py +93 -0
- crosshair/libimpl/encodings_ch_test.py +83 -0
- crosshair/libimpl/fractionlib.py +16 -0
- crosshair/libimpl/fractionlib_test.py +80 -0
- crosshair/libimpl/functoolslib.py +34 -0
- crosshair/libimpl/functoolslib_test.py +56 -0
- crosshair/libimpl/hashliblib.py +30 -0
- crosshair/libimpl/hashliblib_test.py +18 -0
- crosshair/libimpl/heapqlib.py +47 -0
- crosshair/libimpl/heapqlib_test.py +21 -0
- crosshair/libimpl/importliblib.py +18 -0
- crosshair/libimpl/importliblib_test.py +38 -0
- crosshair/libimpl/iolib.py +216 -0
- crosshair/libimpl/iolib_ch_test.py +128 -0
- crosshair/libimpl/iolib_test.py +19 -0
- crosshair/libimpl/ipaddresslib.py +8 -0
- crosshair/libimpl/itertoolslib.py +44 -0
- crosshair/libimpl/itertoolslib_test.py +44 -0
- crosshair/libimpl/jsonlib.py +984 -0
- crosshair/libimpl/jsonlib_ch_test.py +42 -0
- crosshair/libimpl/jsonlib_test.py +51 -0
- crosshair/libimpl/mathlib.py +179 -0
- crosshair/libimpl/mathlib_ch_test.py +44 -0
- crosshair/libimpl/mathlib_test.py +67 -0
- crosshair/libimpl/oslib.py +7 -0
- crosshair/libimpl/pathliblib_test.py +10 -0
- crosshair/libimpl/randomlib.py +178 -0
- crosshair/libimpl/randomlib_test.py +120 -0
- crosshair/libimpl/relib.py +846 -0
- crosshair/libimpl/relib_ch_test.py +169 -0
- crosshair/libimpl/relib_test.py +493 -0
- crosshair/libimpl/timelib.py +72 -0
- crosshair/libimpl/timelib_test.py +82 -0
- crosshair/libimpl/typeslib.py +15 -0
- crosshair/libimpl/typeslib_test.py +36 -0
- crosshair/libimpl/unicodedatalib.py +75 -0
- crosshair/libimpl/unicodedatalib_test.py +42 -0
- crosshair/libimpl/urlliblib.py +23 -0
- crosshair/libimpl/urlliblib_test.py +19 -0
- 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 +261 -0
- crosshair/lsp_server_test.py +30 -0
- crosshair/main.py +973 -0
- crosshair/main_test.py +543 -0
- crosshair/objectproxy.py +376 -0
- crosshair/objectproxy_test.py +41 -0
- crosshair/opcode_intercept.py +601 -0
- crosshair/opcode_intercept_test.py +304 -0
- crosshair/options.py +218 -0
- crosshair/options_test.py +10 -0
- crosshair/patch_equivalence_test.py +75 -0
- crosshair/path_cover.py +209 -0
- crosshair/path_cover_test.py +138 -0
- crosshair/path_search.py +161 -0
- crosshair/path_search_test.py +52 -0
- crosshair/pathing_oracle.py +271 -0
- crosshair/pathing_oracle_test.py +21 -0
- crosshair/pure_importer.py +27 -0
- crosshair/pure_importer_test.py +16 -0
- crosshair/py.typed +0 -0
- crosshair/register_contract.py +273 -0
- crosshair/register_contract_test.py +190 -0
- crosshair/simplestructs.py +1165 -0
- crosshair/simplestructs_test.py +283 -0
- crosshair/smtlib.py +24 -0
- crosshair/smtlib_test.py +14 -0
- crosshair/statespace.py +1199 -0
- crosshair/statespace_test.py +108 -0
- crosshair/stubs_parser.py +352 -0
- crosshair/stubs_parser_test.py +43 -0
- crosshair/test_util.py +329 -0
- crosshair/test_util_test.py +26 -0
- crosshair/tools/__init__.py +0 -0
- crosshair/tools/check_help_in_doc.py +264 -0
- crosshair/tools/check_init_and_setup_coincide.py +119 -0
- crosshair/tools/generate_demo_table.py +127 -0
- crosshair/tracers.py +544 -0
- crosshair/tracers_test.py +154 -0
- crosshair/type_repo.py +151 -0
- crosshair/unicode_categories.py +589 -0
- crosshair/unicode_categories_test.py +27 -0
- crosshair/util.py +741 -0
- crosshair/util_test.py +173 -0
- crosshair/watcher.py +307 -0
- crosshair/watcher_test.py +107 -0
- crosshair/z3util.py +76 -0
- crosshair/z3util_test.py +11 -0
- crosshair_tool-0.0.99.dist-info/METADATA +144 -0
- crosshair_tool-0.0.99.dist-info/RECORD +176 -0
- crosshair_tool-0.0.99.dist-info/WHEEL +6 -0
- crosshair_tool-0.0.99.dist-info/entry_points.txt +3 -0
- crosshair_tool-0.0.99.dist-info/licenses/LICENSE +93 -0
- crosshair_tool-0.0.99.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,332 @@
|
|
|
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
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
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
|
|
18
|
+
from crosshair.statespace import CANNOT_CONFIRM, CONFIRMED, POST_FAIL, MessageType
|
|
19
|
+
from crosshair.test_util import check_states
|
|
20
|
+
from crosshair.tracers import NoTracing, ResumedTracing
|
|
21
|
+
from crosshair.util import CrossHairValue
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture
|
|
25
|
+
def test_list():
|
|
26
|
+
return ListBasedDeque([1, 2, 3, 4, 5])
|
|
27
|
+
|
|
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
|
+
|
|
43
|
+
def test_deque_appendleft(test_list) -> None:
|
|
44
|
+
test_list.appendleft(0)
|
|
45
|
+
assert test_list.popleft() == 0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_deque_appendleft_with_full_deque(test_list) -> None:
|
|
49
|
+
temp_list = ListBasedDeque([1, 2, 3, 4, 5], maxlen=5)
|
|
50
|
+
temp_list.appendleft(42)
|
|
51
|
+
assert temp_list.popleft() == 42 and temp_list.pop() == 4
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_deque_appendleft_doesnt_increase_size_with_maxlen(test_list) -> None:
|
|
55
|
+
temp_list = ListBasedDeque([1, 2, 3, 4, 5], maxlen=5)
|
|
56
|
+
temp_list.appendleft(42)
|
|
57
|
+
assert len(temp_list) == temp_list.maxlen()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_deque_append(test_list) -> None:
|
|
61
|
+
test_list.append(0)
|
|
62
|
+
assert test_list.pop() == 0
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_deque_append_with_full_deque(test_list) -> None:
|
|
66
|
+
temp_list = ListBasedDeque([1, 2, 3, 4, 5], maxlen=5)
|
|
67
|
+
temp_list.append(42)
|
|
68
|
+
assert temp_list.pop() == 42 and temp_list.popleft() == 2
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_deque_append_doesnt_increase_size_with_maxlen(test_list) -> None:
|
|
72
|
+
temp_list = ListBasedDeque([1, 2, 3, 4, 5], maxlen=5)
|
|
73
|
+
temp_list.append(42)
|
|
74
|
+
assert len(temp_list) == temp_list.maxlen()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_deque_clear(test_list) -> None:
|
|
78
|
+
test_list.clear()
|
|
79
|
+
assert len(test_list) == 0
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_deque_count(test_list) -> None:
|
|
83
|
+
test_list.append(1)
|
|
84
|
+
count = test_list.count(1)
|
|
85
|
+
assert count == 2
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_deque_index(test_list) -> None:
|
|
89
|
+
i = test_list.index(5)
|
|
90
|
+
assert i == 4
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_deque_index_with_start_index(test_list) -> None:
|
|
94
|
+
i = test_list.index(5, 1)
|
|
95
|
+
assert i == 4
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_deque_index_with_start_index_throws_correct_exception(test_list) -> None:
|
|
99
|
+
with pytest.raises(ValueError) as context:
|
|
100
|
+
test_list.index(1, 2)
|
|
101
|
+
|
|
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")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_deque_index_with_start_and_end_index(test_list) -> None:
|
|
110
|
+
i = test_list.index(2, 0, 3)
|
|
111
|
+
assert i == 1
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_deque_index_with_start_and_end_index_throws_correct_exception(
|
|
115
|
+
test_list,
|
|
116
|
+
) -> None:
|
|
117
|
+
with pytest.raises(ValueError) as context:
|
|
118
|
+
test_list.index(6, 0, 1)
|
|
119
|
+
|
|
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")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_deque_insert(test_list) -> None:
|
|
127
|
+
test_list.insert(index=1, item=42)
|
|
128
|
+
assert test_list.index(42) == 1
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_deque_pop(test_list) -> None:
|
|
132
|
+
assert test_list.pop() == 5
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_deque_popleft(test_list) -> None:
|
|
136
|
+
assert test_list.popleft() == 1
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_deque_remove(test_list) -> None:
|
|
140
|
+
original_length = len(test_list)
|
|
141
|
+
test_list.remove(1)
|
|
142
|
+
assert len(test_list) == original_length - 1
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_deque_reverse(test_list) -> None:
|
|
146
|
+
test_list.reverse()
|
|
147
|
+
assert test_list.popleft() == 5
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_deque_rotate(test_list) -> None:
|
|
151
|
+
test_list.rotate(n=1)
|
|
152
|
+
assert test_list.popleft() == 5
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_deque_rotate_left(test_list) -> None:
|
|
156
|
+
test_list.rotate(n=-1)
|
|
157
|
+
assert test_list.popleft() == 2
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_deque_maxlen(test_list) -> None:
|
|
161
|
+
ls = ListBasedDeque([1, 2, 3], 5)
|
|
162
|
+
assert ls.maxlen() == 5
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_deque_len_ok() -> None:
|
|
166
|
+
def f(ls: Deque[int]) -> Deque[int]:
|
|
167
|
+
"""
|
|
168
|
+
post: len(_) == len(__old__.ls) + 1
|
|
169
|
+
"""
|
|
170
|
+
ls.append(42)
|
|
171
|
+
return ls
|
|
172
|
+
|
|
173
|
+
check_states(f, CONFIRMED)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@pytest.mark.demo
|
|
177
|
+
def test_deque___len___method() -> None:
|
|
178
|
+
def f(ls: Deque[int]) -> int:
|
|
179
|
+
"""
|
|
180
|
+
Can the length of a deque equal the value of its last element?
|
|
181
|
+
|
|
182
|
+
pre: len(ls) >= 2
|
|
183
|
+
post: _ != ls[-1]
|
|
184
|
+
"""
|
|
185
|
+
return len(ls)
|
|
186
|
+
|
|
187
|
+
check_states(f, POST_FAIL)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@pytest.mark.demo
|
|
191
|
+
def test_deque_extendleft_method() -> None:
|
|
192
|
+
def f(ls: Deque[int]) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Can any deque be extended by itself and form this palindrome?
|
|
195
|
+
|
|
196
|
+
post[ls]: ls != deque([1, 2, 3, 3, 2, 1])
|
|
197
|
+
"""
|
|
198
|
+
ls.extendleft(ls)
|
|
199
|
+
|
|
200
|
+
check_states(f, POST_FAIL)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def test_deque_add_symbolic_to_concrete():
|
|
204
|
+
with standalone_statespace as space:
|
|
205
|
+
d = ListBasedDeque([1, 2]) + deque([3, 4])
|
|
206
|
+
assert list(d) == [1, 2, 3, 4]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def test_deque_eq():
|
|
210
|
+
with standalone_statespace as space:
|
|
211
|
+
assert ListBasedDeque([1, 2]) == ListBasedDeque([1, 2])
|
|
212
|
+
assert deque([1, 2]) == ListBasedDeque([1, 2])
|
|
213
|
+
assert ListBasedDeque([1, 2]) != ListBasedDeque([1, 55])
|
|
214
|
+
assert deque([1, 2]) != ListBasedDeque([1, 55])
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def test_defaultdict_repr_equiv(test_list) -> None:
|
|
218
|
+
def f(symbolic: DefaultDict[int, int]) -> Tuple[dict, dict]:
|
|
219
|
+
"""post: _[0] == _[1]"""
|
|
220
|
+
concrete = defaultdict(symbolic.default_factory, symbolic.items())
|
|
221
|
+
return (symbolic, concrete)
|
|
222
|
+
|
|
223
|
+
check_states(f, CANNOT_CONFIRM)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_defaultdict_basic_fail() -> None:
|
|
227
|
+
def f(a: DefaultDict[int, int], k: int, v: int) -> None:
|
|
228
|
+
"""
|
|
229
|
+
post[a]: a[42] != 42
|
|
230
|
+
"""
|
|
231
|
+
a[k] = v
|
|
232
|
+
|
|
233
|
+
check_states(f, POST_FAIL)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def test_defaultdict_default_fail(test_list) -> None:
|
|
237
|
+
def f(a: DefaultDict[int, int], k: int) -> None:
|
|
238
|
+
"""
|
|
239
|
+
pre: a.default_factory is not None
|
|
240
|
+
post: a[k] <= 100
|
|
241
|
+
"""
|
|
242
|
+
if a[k] > 100:
|
|
243
|
+
del a[k]
|
|
244
|
+
|
|
245
|
+
check_states(f, POST_FAIL)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def test_defaultdict_default_ok(test_list) -> None:
|
|
249
|
+
def f(a: DefaultDict[int, int], k: int) -> DefaultDict[int, int]:
|
|
250
|
+
"""
|
|
251
|
+
pre: len(a) == 0 and a.default_factory is not None
|
|
252
|
+
post: _[k] == _[k]
|
|
253
|
+
"""
|
|
254
|
+
return a
|
|
255
|
+
|
|
256
|
+
check_states(f, CONFIRMED)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def test_defaultdict_realize():
|
|
260
|
+
with standalone_statespace:
|
|
261
|
+
with NoTracing():
|
|
262
|
+
d = proxy_for_type(DefaultDict[int, int], "d")
|
|
263
|
+
assert type(realize(d)) is defaultdict
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
#
|
|
267
|
+
# We don't patch namedtuple, but namedtuple performs magic dynamic type
|
|
268
|
+
# generation, which can interfere with CrossHair.
|
|
269
|
+
#
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def test_namedtuple_creation():
|
|
273
|
+
with standalone_statespace:
|
|
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)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from copy import copy, deepcopy
|
|
2
|
+
|
|
3
|
+
from crosshair import NoTracing, register_patch
|
|
4
|
+
from crosshair.util import CrossHairValue
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _copy(x):
|
|
8
|
+
with NoTracing():
|
|
9
|
+
if isinstance(x, CrossHairValue):
|
|
10
|
+
return copy(x)
|
|
11
|
+
return copy(x)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _deepcopy(x, memo=None, _nil=[]):
|
|
15
|
+
with NoTracing():
|
|
16
|
+
if isinstance(x, CrossHairValue):
|
|
17
|
+
return deepcopy(x, memo)
|
|
18
|
+
return deepcopy(x, memo)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def make_registrations() -> None:
|
|
22
|
+
register_patch(copy, _copy)
|
|
23
|
+
register_patch(deepcopy, _deepcopy)
|
|
@@ -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)
|