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,86 @@
|
|
|
1
|
+
import codecs
|
|
2
|
+
|
|
3
|
+
from crosshair import NoTracing, register_patch
|
|
4
|
+
from crosshair.core import class_with_realized_methods, realize, with_realized_args
|
|
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
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _decode(obj, encoding="utf-8", errors="strict"):
|
|
29
|
+
errors = realize(errors)
|
|
30
|
+
(out, _len_consumed) = _getdecoder(encoding)(obj, errors)
|
|
31
|
+
return out
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _encode(obj, encoding="utf-8", errors="strict"):
|
|
35
|
+
with NoTracing():
|
|
36
|
+
errors = realize(errors)
|
|
37
|
+
if "\x00" in errors:
|
|
38
|
+
raise ValueError
|
|
39
|
+
(out, _len_consumed) = _getencoder(encoding)(obj, errors)
|
|
40
|
+
return out
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _getencoder(encoding):
|
|
44
|
+
return _lookup(encoding).encode
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _getdecoder(encoding):
|
|
48
|
+
return _lookup(encoding).decode
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _getincrementaldecoder(encoding):
|
|
52
|
+
return _lookup(encoding).incrementaldecoder
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _getincrementalencoder(encoding):
|
|
56
|
+
return _lookup(encoding).incrementalencoder
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _getreader(encoding):
|
|
60
|
+
return _lookup(encoding).streamreader
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _getwriter(encoding):
|
|
64
|
+
return _lookup(encoding).streamwriter
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _lookup(encoding: str) -> codecs.CodecInfo:
|
|
68
|
+
with NoTracing():
|
|
69
|
+
encoding = realize(encoding)
|
|
70
|
+
try:
|
|
71
|
+
return codecs.lookup("crosshair_" + encoding)
|
|
72
|
+
except LookupError:
|
|
73
|
+
return RealizingCodecInfo(codecs.lookup(encoding))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def make_registrations() -> None:
|
|
77
|
+
codecs.register(codec_search)
|
|
78
|
+
register_patch(codecs.decode, _decode)
|
|
79
|
+
register_patch(codecs.getencoder, _getencoder)
|
|
80
|
+
register_patch(codecs.getdecoder, _getdecoder)
|
|
81
|
+
register_patch(codecs.getincrementalencoder, _getincrementalencoder)
|
|
82
|
+
register_patch(codecs.getincrementaldecoder, _getincrementaldecoder)
|
|
83
|
+
register_patch(codecs.getreader, _getreader)
|
|
84
|
+
register_patch(codecs.getwriter, _getwriter)
|
|
85
|
+
register_patch(codecs.encode, _encode)
|
|
86
|
+
register_patch(codecs.lookup, _lookup)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import codecs
|
|
2
|
+
import io
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from crosshair.core import proxy_for_type
|
|
8
|
+
from crosshair.core_and_libs import ResumedTracing, standalone_statespace
|
|
9
|
+
from crosshair.libimpl.builtinslib import LazyIntSymbolicStr, SymbolicBytes, SymbolicInt
|
|
10
|
+
from crosshair.options import AnalysisOptionSet
|
|
11
|
+
from crosshair.statespace import POST_FAIL, MessageType
|
|
12
|
+
from crosshair.test_util import check_states
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_encode_strict(space):
|
|
16
|
+
with ResumedTracing():
|
|
17
|
+
with pytest.raises(UnicodeError):
|
|
18
|
+
codecs.encode("Â", "ascii")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_encode_utf8_literal(space):
|
|
22
|
+
with ResumedTracing():
|
|
23
|
+
assert codecs.encode("Â", "utf-8") == b"\xc3\x82"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_encode_utf8_symbolic_char(space):
|
|
27
|
+
cp = SymbolicInt("cp")
|
|
28
|
+
with ResumedTracing():
|
|
29
|
+
space.add(cp >= ord("a"))
|
|
30
|
+
space.add(cp <= ord("z"))
|
|
31
|
+
encoded = codecs.encode(chr(cp), "utf-8")
|
|
32
|
+
assert isinstance(encoded, SymbolicBytes)
|
|
33
|
+
byte_value = encoded[0]
|
|
34
|
+
with ResumedTracing():
|
|
35
|
+
assert space.is_possible(byte_value == ord("a"))
|
|
36
|
+
assert space.is_possible(byte_value == ord("b"))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_decode_utf8_symbolic_char(space):
|
|
40
|
+
cp = SymbolicInt("cp")
|
|
41
|
+
with ResumedTracing():
|
|
42
|
+
space.add(cp >= ord("a"))
|
|
43
|
+
space.add(cp <= ord("z"))
|
|
44
|
+
decoded = codecs.decode(SymbolicBytes([cp]), "utf-8")
|
|
45
|
+
assert isinstance(decoded, LazyIntSymbolicStr)
|
|
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"))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@pytest.mark.skipif(
|
|
75
|
+
sys.version_info >= (3, 13),
|
|
76
|
+
reason="Need to intercept UnicodeDecodeError.str in 3.13+",
|
|
77
|
+
)
|
|
78
|
+
def test_decode_e2e():
|
|
79
|
+
def f(byts: bytes) -> str:
|
|
80
|
+
"""
|
|
81
|
+
post: _ != 'Â'
|
|
82
|
+
raises: UnicodeDecodeError
|
|
83
|
+
"""
|
|
84
|
+
return byts.decode("utf-8", errors="strict")
|
|
85
|
+
|
|
86
|
+
check_states(f, POST_FAIL)
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import collections
|
|
2
|
+
import sys
|
|
3
|
+
from typing import (
|
|
4
|
+
Any,
|
|
5
|
+
Callable,
|
|
6
|
+
Dict,
|
|
7
|
+
Generic,
|
|
8
|
+
Iterable,
|
|
9
|
+
KeysView,
|
|
10
|
+
List,
|
|
11
|
+
Optional,
|
|
12
|
+
Set,
|
|
13
|
+
Tuple,
|
|
14
|
+
TypeVar,
|
|
15
|
+
Union,
|
|
16
|
+
ValuesView,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from crosshair import register_type
|
|
20
|
+
from crosshair.core import realize
|
|
21
|
+
from crosshair.tracers import NoTracing, ResumedTracing
|
|
22
|
+
from crosshair.util import CrossHairValue, is_iterable
|
|
23
|
+
|
|
24
|
+
T = TypeVar("T")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ListBasedDeque(collections.abc.MutableSequence, CrossHairValue, Generic[T]):
|
|
28
|
+
def __init__(self, contents: List[T], maxlen: Optional[int] = None):
|
|
29
|
+
self._contents = contents
|
|
30
|
+
self._maxlen = maxlen
|
|
31
|
+
|
|
32
|
+
def __ch_pytype__(self):
|
|
33
|
+
return collections.deque
|
|
34
|
+
|
|
35
|
+
def __ch_realize__(self):
|
|
36
|
+
with ResumedTracing():
|
|
37
|
+
return collections.deque(self._contents, maxlen=realize(self._maxlen))
|
|
38
|
+
|
|
39
|
+
def __add__(self, other):
|
|
40
|
+
if not isinstance(other, collections.deque):
|
|
41
|
+
raise TypeError
|
|
42
|
+
ret = self.copy()
|
|
43
|
+
ret.extend(other)
|
|
44
|
+
return ret
|
|
45
|
+
|
|
46
|
+
def __eq__(self, other: object) -> bool:
|
|
47
|
+
with NoTracing():
|
|
48
|
+
mycontents = self._contents
|
|
49
|
+
if isinstance(other, ListBasedDeque):
|
|
50
|
+
with ResumedTracing():
|
|
51
|
+
return mycontents == other._contents
|
|
52
|
+
elif isinstance(other, collections.deque):
|
|
53
|
+
with ResumedTracing():
|
|
54
|
+
return mycontents == list(other)
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
def __len__(self) -> int:
|
|
58
|
+
return len(self._contents)
|
|
59
|
+
|
|
60
|
+
def __mul__(self, count):
|
|
61
|
+
if not isinstance(count, int):
|
|
62
|
+
raise TypeError
|
|
63
|
+
ret = ListBasedDeque([], self._maxlen)
|
|
64
|
+
for _ in range(count):
|
|
65
|
+
ret.extend(self._contents)
|
|
66
|
+
return ret
|
|
67
|
+
|
|
68
|
+
def __repr__(self) -> str:
|
|
69
|
+
return repr(realize(self))
|
|
70
|
+
|
|
71
|
+
def __getitem__(self, k):
|
|
72
|
+
if isinstance(k, slice): # slicing isn't supported on deque
|
|
73
|
+
raise TypeError
|
|
74
|
+
return self._contents.__getitem__(k)
|
|
75
|
+
|
|
76
|
+
def __setitem__(self, k, v):
|
|
77
|
+
if isinstance(k, slice): # slicing isn't supported on deque
|
|
78
|
+
raise TypeError
|
|
79
|
+
return self._contents.__setitem__(k, v)
|
|
80
|
+
|
|
81
|
+
def __delitem__(self, k):
|
|
82
|
+
if isinstance(k, slice): # slicing isn't supported on deque
|
|
83
|
+
raise TypeError
|
|
84
|
+
return self._contents.__delitem__(k)
|
|
85
|
+
|
|
86
|
+
def _has_room(self) -> bool:
|
|
87
|
+
maxlen = self._maxlen
|
|
88
|
+
return maxlen is None or len(self._contents) < maxlen
|
|
89
|
+
|
|
90
|
+
def appendleft(self, item: T) -> None:
|
|
91
|
+
if self._has_room():
|
|
92
|
+
self._contents = [item] + self._contents
|
|
93
|
+
else:
|
|
94
|
+
del self._contents[-1]
|
|
95
|
+
self._contents = [item] + self._contents
|
|
96
|
+
|
|
97
|
+
def append(self, item: T) -> None:
|
|
98
|
+
if self._has_room():
|
|
99
|
+
self._contents = self._contents + [item]
|
|
100
|
+
else:
|
|
101
|
+
del self._contents[0]
|
|
102
|
+
self._contents = self._contents + [item]
|
|
103
|
+
|
|
104
|
+
def clear(self) -> None:
|
|
105
|
+
self._contents = []
|
|
106
|
+
|
|
107
|
+
def copy(self):
|
|
108
|
+
return ListBasedDeque(self._contents[:], self._maxlen)
|
|
109
|
+
|
|
110
|
+
def count(self, item: T) -> int:
|
|
111
|
+
c = 0
|
|
112
|
+
for i in self._contents:
|
|
113
|
+
if i == item:
|
|
114
|
+
c += 1
|
|
115
|
+
return c
|
|
116
|
+
|
|
117
|
+
def extend(self, items: Iterable[T]) -> None:
|
|
118
|
+
if not is_iterable(items):
|
|
119
|
+
raise TypeError
|
|
120
|
+
self._contents += list(items)
|
|
121
|
+
|
|
122
|
+
def extendleft(self, items: Iterable[T]) -> None:
|
|
123
|
+
if not is_iterable(items):
|
|
124
|
+
raise TypeError
|
|
125
|
+
prefix = list(items)
|
|
126
|
+
prefix.reverse()
|
|
127
|
+
self._contents = prefix + self._contents
|
|
128
|
+
|
|
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)
|
|
142
|
+
|
|
143
|
+
def insert(self, index: int, item: T) -> None:
|
|
144
|
+
self._contents.insert(index, item)
|
|
145
|
+
|
|
146
|
+
def pop(self) -> T: # type: ignore
|
|
147
|
+
x = self._contents[-1]
|
|
148
|
+
del self._contents[-1]
|
|
149
|
+
return x
|
|
150
|
+
|
|
151
|
+
def popleft(self) -> T:
|
|
152
|
+
x = self._contents[0]
|
|
153
|
+
del self._contents[0]
|
|
154
|
+
return x
|
|
155
|
+
|
|
156
|
+
def remove(self, item: T) -> None:
|
|
157
|
+
self._contents.remove(item)
|
|
158
|
+
|
|
159
|
+
def reverse(self) -> None:
|
|
160
|
+
self._contents.reverse()
|
|
161
|
+
|
|
162
|
+
def rotate(self, n: int = 1) -> None:
|
|
163
|
+
if not self._contents or n % len(self._contents) == 0:
|
|
164
|
+
return
|
|
165
|
+
self._contents = (
|
|
166
|
+
self._contents[-n % len(self._contents) :]
|
|
167
|
+
+ self._contents[: -n % len(self._contents)]
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def maxlen(self) -> Optional[int]:
|
|
171
|
+
return self._maxlen
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class PureDefaultDict(collections.abc.MutableMapping, CrossHairValue):
|
|
175
|
+
def __init__(self, factory, internal):
|
|
176
|
+
self.default_factory = factory
|
|
177
|
+
self._internal = internal
|
|
178
|
+
|
|
179
|
+
def __ch_pytype__(self):
|
|
180
|
+
return collections.defaultdict
|
|
181
|
+
|
|
182
|
+
def __ch_realize__(self):
|
|
183
|
+
with ResumedTracing():
|
|
184
|
+
return collections.defaultdict(self.default_factory, self._internal)
|
|
185
|
+
|
|
186
|
+
def __getitem__(self, k):
|
|
187
|
+
try:
|
|
188
|
+
return self._internal.__getitem__(k)
|
|
189
|
+
except KeyError:
|
|
190
|
+
return self.__missing__(k)
|
|
191
|
+
|
|
192
|
+
def __setitem__(self, k, v):
|
|
193
|
+
return self._internal.__setitem__(k, v)
|
|
194
|
+
|
|
195
|
+
def __delitem__(self, k):
|
|
196
|
+
return self._internal.__delitem__(k)
|
|
197
|
+
|
|
198
|
+
def __iter__(self):
|
|
199
|
+
return self._internal.__iter__()
|
|
200
|
+
|
|
201
|
+
def __len__(self):
|
|
202
|
+
return self._internal.__len__()
|
|
203
|
+
|
|
204
|
+
def __repr__(self):
|
|
205
|
+
return "defaultdict({!r}, {!r})".format(self.default_factory, self._internal)
|
|
206
|
+
|
|
207
|
+
def __missing__(self, k):
|
|
208
|
+
if self.default_factory is None:
|
|
209
|
+
raise KeyError(k)
|
|
210
|
+
value = self.default_factory()
|
|
211
|
+
self._internal[k] = value
|
|
212
|
+
return value
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def make_registrations():
|
|
216
|
+
register_type(collections.defaultdict, lambda p, kt=Any, vt=Any: PureDefaultDict(p(Optional[Callable[[], vt]], "_initalizer"), p(Dict[kt, vt]))) # type: ignore
|
|
217
|
+
register_type(collections.ChainMap, lambda p, kt=Any, vt=Any: collections.ChainMap(*p(Tuple[Dict[kt, vt], ...]))) # type: ignore
|
|
218
|
+
register_type(collections.abc.Mapping, lambda p, kt=Any, vt=Any: p(Dict[kt, vt])) # type: ignore
|
|
219
|
+
register_type(collections.abc.MutableMapping, lambda p, kt=Any, vt=Any: p(Dict[kt, vt])) # type: ignore
|
|
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:
|
|
222
|
+
register_type(collections.Counter, lambda p, t=Any: collections.Counter(p(Dict[t, int]))) # type: ignore
|
|
223
|
+
# TODO: MappingView is missing
|
|
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
|
+
)
|
|
244
|
+
|
|
245
|
+
register_type(collections.abc.Container, lambda p, t=Any: p(Tuple[t, ...]))
|
|
246
|
+
register_type(collections.abc.Collection, lambda p, t=Any: p(Tuple[t, ...]))
|
|
247
|
+
|
|
248
|
+
register_type(collections.deque, lambda p, t=Any: ListBasedDeque(p(List[t]))) # type: ignore
|
|
249
|
+
|
|
250
|
+
register_type(collections.abc.Iterable, lambda p, t=Any: p(Tuple[t, ...]))
|
|
251
|
+
register_type(collections.abc.Iterator, lambda p, t=Any: iter(p(Iterable[t]))) # type: ignore
|
|
252
|
+
|
|
253
|
+
register_type(collections.abc.MutableSequence, lambda p, t=Any: p(List[t])) # type: ignore
|
|
254
|
+
register_type(collections.abc.Reversible, lambda p, t=Any: p(Tuple[t, ...]))
|
|
255
|
+
register_type(collections.abc.Sequence, lambda p, t=Any: p(Tuple[t, ...]))
|
|
256
|
+
register_type(collections.abc.Sized, lambda p, t=Any: p(Tuple[t, ...]))
|
|
257
|
+
|
|
258
|
+
register_type(collections.abc.MutableSet, lambda p, t=Any: p(Set[t])) # type: ignore
|
|
259
|
+
|
|
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))
|
|
264
|
+
register_type(collections.abc.Hashable, lambda p: p(int))
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import DefaultDict, Deque, Optional, Sequence
|
|
3
|
+
|
|
4
|
+
import pytest # type: ignore
|
|
5
|
+
|
|
6
|
+
from crosshair.core_and_libs import MessageType, analyze_function, run_checkables
|
|
7
|
+
from crosshair.options import AnalysisOptionSet
|
|
8
|
+
from crosshair.test_util import ResultComparison, compare_results
|
|
9
|
+
|
|
10
|
+
# deque
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def check_deque_append(queue: Deque[int], item: int):
|
|
14
|
+
"""post: _"""
|
|
15
|
+
|
|
16
|
+
def checker(q, i):
|
|
17
|
+
q.append(i)
|
|
18
|
+
return q
|
|
19
|
+
|
|
20
|
+
return compare_results(checker, queue, item)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def check_deque_appendleft(queue: Deque[int], item: int):
|
|
24
|
+
"""post: _"""
|
|
25
|
+
|
|
26
|
+
def checker(q, i):
|
|
27
|
+
q.appendleft(i)
|
|
28
|
+
return q
|
|
29
|
+
|
|
30
|
+
return compare_results(checker, queue, item)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def check_deque_copy(queue: Deque[int]):
|
|
34
|
+
"""post: _"""
|
|
35
|
+
return compare_results(lambda d: d.copy(), queue)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def check_deque_count(queue: Deque[int], item: int):
|
|
39
|
+
"""post: _"""
|
|
40
|
+
return compare_results(lambda d, i: d.count(i), queue, item)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def check_deque_extend(queue: Deque[int], items: Sequence[int]):
|
|
44
|
+
"""post: _"""
|
|
45
|
+
|
|
46
|
+
def checker(q, i):
|
|
47
|
+
q.extend(i)
|
|
48
|
+
return q
|
|
49
|
+
|
|
50
|
+
return compare_results(checker, queue, items)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_deque_extendleft(queue: Deque[int], items: Sequence[int]):
|
|
54
|
+
"""post: _"""
|
|
55
|
+
|
|
56
|
+
def checker(q, i):
|
|
57
|
+
q.extendleft(i)
|
|
58
|
+
return q
|
|
59
|
+
|
|
60
|
+
return compare_results(checker, queue, items)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def check_deque_index(
|
|
64
|
+
queue: Deque[int], item: int, start: Optional[int], end: Optional[int]
|
|
65
|
+
):
|
|
66
|
+
"""post: _"""
|
|
67
|
+
return compare_results(lambda q, i, s, e: q.index(i, s, e), queue, item, start, end)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def check_deque_insert(queue: Deque[int], position: int, item: int):
|
|
71
|
+
"""post: _"""
|
|
72
|
+
|
|
73
|
+
def checker(q, p, i):
|
|
74
|
+
q.insert(p, i)
|
|
75
|
+
return q
|
|
76
|
+
|
|
77
|
+
return compare_results(checker, queue, position, item)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def check_deque_pop(queue: Deque[int]):
|
|
81
|
+
"""post: _"""
|
|
82
|
+
|
|
83
|
+
def checker(q):
|
|
84
|
+
item = q.pop()
|
|
85
|
+
return (item, q)
|
|
86
|
+
|
|
87
|
+
return compare_results(checker, queue)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def check_deque_popleft(queue: Deque[int]):
|
|
91
|
+
"""post: _"""
|
|
92
|
+
|
|
93
|
+
def checker(q):
|
|
94
|
+
item = q.popleft()
|
|
95
|
+
return item
|
|
96
|
+
|
|
97
|
+
return compare_results(checker, queue)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def check_deque_remove(queue: Deque[int], item: int):
|
|
101
|
+
"""post: _"""
|
|
102
|
+
|
|
103
|
+
def checker(q, n):
|
|
104
|
+
q.remove(n)
|
|
105
|
+
return q
|
|
106
|
+
|
|
107
|
+
return compare_results(checker, queue, item)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def check_deque_reverse(queue: Deque[int]):
|
|
111
|
+
"""post: _"""
|
|
112
|
+
|
|
113
|
+
def checker(q):
|
|
114
|
+
q.reverse()
|
|
115
|
+
return q
|
|
116
|
+
|
|
117
|
+
return compare_results(checker, queue)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def check_deque_rotate(queue: Deque[int], amount: int):
|
|
121
|
+
"""post: _"""
|
|
122
|
+
|
|
123
|
+
def checker(q, n):
|
|
124
|
+
q.rotate(n)
|
|
125
|
+
return q
|
|
126
|
+
|
|
127
|
+
return compare_results(checker, queue, amount)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def check_deque_maxlen(queue: Deque[int]):
|
|
131
|
+
return compare_results(lambda q: q.maxlen, queue)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def check_deque_eq(queue: Deque[int]):
|
|
135
|
+
return compare_results(lambda q: q, queue)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def check_deque_getitem(queue: Deque[int], idx: int):
|
|
139
|
+
"""post: _"""
|
|
140
|
+
return compare_results(lambda q, i: q[i], queue, idx)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def check_deque_contains(queue: Deque[int], item: int):
|
|
144
|
+
"""post: _"""
|
|
145
|
+
return compare_results(lambda q, i: i in q, queue, item)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def check_deque_add(queue: Deque[int], items: Deque[int]):
|
|
149
|
+
"""post: _"""
|
|
150
|
+
return compare_results(lambda q, i: q + i, queue, items)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def check_deque_mul(queue: Deque[int], count: int):
|
|
154
|
+
"""post: _"""
|
|
155
|
+
return compare_results(lambda q, i: q * i, queue, count)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# defaultdict
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def check_defaultdict_getitem(container: DefaultDict[int, int], key: int):
|
|
162
|
+
"""post: _"""
|
|
163
|
+
return compare_results(lambda d, k: d[k], container, key)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def check_defaultdict_delitem(container: DefaultDict[int, int], key: int):
|
|
167
|
+
"""post: _"""
|
|
168
|
+
|
|
169
|
+
def checker(d, k):
|
|
170
|
+
del d[k]
|
|
171
|
+
return d
|
|
172
|
+
|
|
173
|
+
return compare_results(checker, container, key)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def check_defaultdict_inplace_mutation(container: DefaultDict[int, int]):
|
|
177
|
+
"""post: _"""
|
|
178
|
+
|
|
179
|
+
def setter(c):
|
|
180
|
+
if c:
|
|
181
|
+
c[0] &= 42
|
|
182
|
+
return c
|
|
183
|
+
|
|
184
|
+
return compare_results(setter, container)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def check_defaultdict_iter(dictionary: DefaultDict[int, int]) -> ResultComparison:
|
|
188
|
+
"""post: _"""
|
|
189
|
+
return compare_results(lambda d: list(d), dictionary)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def check_defaultdict_clear(dictionary: DefaultDict[int, int]) -> ResultComparison:
|
|
193
|
+
"""post: _"""
|
|
194
|
+
|
|
195
|
+
def checker(d):
|
|
196
|
+
d.clear()
|
|
197
|
+
return d
|
|
198
|
+
|
|
199
|
+
return compare_results(checker, dictionary)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def check_defaultdict_pop(dictionary: DefaultDict[int, int]) -> ResultComparison:
|
|
203
|
+
"""post: _"""
|
|
204
|
+
|
|
205
|
+
def checker(d):
|
|
206
|
+
x = d.pop()
|
|
207
|
+
return (x, d)
|
|
208
|
+
|
|
209
|
+
return compare_results(checker, dictionary)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def check_defaultdict_popitem(
|
|
213
|
+
dictionary: DefaultDict[int, int], key: int
|
|
214
|
+
) -> ResultComparison:
|
|
215
|
+
"""post: _"""
|
|
216
|
+
|
|
217
|
+
def checker(d, k):
|
|
218
|
+
x = d.popitem(k)
|
|
219
|
+
return (x, d)
|
|
220
|
+
|
|
221
|
+
return compare_results(checker, dictionary)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def check_defaultdict_update(
|
|
225
|
+
left: DefaultDict[int, int], right: DefaultDict[int, int]
|
|
226
|
+
) -> ResultComparison:
|
|
227
|
+
"""post: _"""
|
|
228
|
+
|
|
229
|
+
def checker(d1, d2):
|
|
230
|
+
d1.update(d2)
|
|
231
|
+
return d1
|
|
232
|
+
|
|
233
|
+
return compare_results(checker, left, right)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def check_defaultdict_values(dictionary: DefaultDict[int, int]) -> ResultComparison:
|
|
237
|
+
"""post: _"""
|
|
238
|
+
# TODO: value views compare false even with new views from the same dict.
|
|
239
|
+
# Ensure we match this behavior.
|
|
240
|
+
return compare_results(lambda d: list(d.values()), dictionary)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# This is the only real test definition.
|
|
244
|
+
# It runs crosshair on each of the "check" functions defined above.
|
|
245
|
+
@pytest.mark.parametrize("fn_name", [fn for fn in dir() if fn.startswith("check_")])
|
|
246
|
+
def test_builtin(fn_name: str) -> None:
|
|
247
|
+
opts = AnalysisOptionSet(max_iterations=7)
|
|
248
|
+
this_module = sys.modules[__name__]
|
|
249
|
+
fn = getattr(this_module, fn_name)
|
|
250
|
+
messages = run_checkables(analyze_function(fn, opts))
|
|
251
|
+
errors = [m for m in messages if m.state > MessageType.PRE_UNSAT]
|
|
252
|
+
assert errors == []
|