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.
Files changed (176) hide show
  1. _crosshair_tracers.cpython-312-darwin.so +0 -0
  2. crosshair/__init__.py +42 -0
  3. crosshair/__main__.py +8 -0
  4. crosshair/_mark_stacks.h +790 -0
  5. crosshair/_preliminaries_test.py +18 -0
  6. crosshair/_tracers.h +94 -0
  7. crosshair/_tracers_pycompat.h +522 -0
  8. crosshair/_tracers_test.py +138 -0
  9. crosshair/abcstring.py +245 -0
  10. crosshair/auditwall.py +190 -0
  11. crosshair/auditwall_test.py +77 -0
  12. crosshair/codeconfig.py +113 -0
  13. crosshair/codeconfig_test.py +117 -0
  14. crosshair/condition_parser.py +1237 -0
  15. crosshair/condition_parser_test.py +497 -0
  16. crosshair/conftest.py +30 -0
  17. crosshair/copyext.py +155 -0
  18. crosshair/copyext_test.py +84 -0
  19. crosshair/core.py +1763 -0
  20. crosshair/core_and_libs.py +149 -0
  21. crosshair/core_regestered_types_test.py +82 -0
  22. crosshair/core_test.py +1316 -0
  23. crosshair/diff_behavior.py +314 -0
  24. crosshair/diff_behavior_test.py +261 -0
  25. crosshair/dynamic_typing.py +346 -0
  26. crosshair/dynamic_typing_test.py +210 -0
  27. crosshair/enforce.py +282 -0
  28. crosshair/enforce_test.py +182 -0
  29. crosshair/examples/PEP316/__init__.py +1 -0
  30. crosshair/examples/PEP316/bugs_detected/__init__.py +0 -0
  31. crosshair/examples/PEP316/bugs_detected/getattr_magic.py +16 -0
  32. crosshair/examples/PEP316/bugs_detected/hash_consistent_with_equals.py +31 -0
  33. crosshair/examples/PEP316/bugs_detected/shopping_cart.py +24 -0
  34. crosshair/examples/PEP316/bugs_detected/showcase.py +39 -0
  35. crosshair/examples/PEP316/correct_code/__init__.py +0 -0
  36. crosshair/examples/PEP316/correct_code/arith.py +60 -0
  37. crosshair/examples/PEP316/correct_code/chess.py +77 -0
  38. crosshair/examples/PEP316/correct_code/nesting_inference.py +17 -0
  39. crosshair/examples/PEP316/correct_code/numpy_examples.py +132 -0
  40. crosshair/examples/PEP316/correct_code/rolling_average.py +35 -0
  41. crosshair/examples/PEP316/correct_code/showcase.py +104 -0
  42. crosshair/examples/__init__.py +0 -0
  43. crosshair/examples/check_examples_test.py +146 -0
  44. crosshair/examples/deal/__init__.py +1 -0
  45. crosshair/examples/icontract/__init__.py +1 -0
  46. crosshair/examples/icontract/bugs_detected/__init__.py +0 -0
  47. crosshair/examples/icontract/bugs_detected/showcase.py +41 -0
  48. crosshair/examples/icontract/bugs_detected/wrong_sign.py +8 -0
  49. crosshair/examples/icontract/correct_code/__init__.py +0 -0
  50. crosshair/examples/icontract/correct_code/arith.py +51 -0
  51. crosshair/examples/icontract/correct_code/showcase.py +94 -0
  52. crosshair/fnutil.py +391 -0
  53. crosshair/fnutil_test.py +75 -0
  54. crosshair/fuzz_core_test.py +516 -0
  55. crosshair/libimpl/__init__.py +0 -0
  56. crosshair/libimpl/arraylib.py +161 -0
  57. crosshair/libimpl/binascii_ch_test.py +30 -0
  58. crosshair/libimpl/binascii_test.py +67 -0
  59. crosshair/libimpl/binasciilib.py +150 -0
  60. crosshair/libimpl/bisectlib_test.py +23 -0
  61. crosshair/libimpl/builtinslib.py +5228 -0
  62. crosshair/libimpl/builtinslib_ch_test.py +1191 -0
  63. crosshair/libimpl/builtinslib_test.py +3735 -0
  64. crosshair/libimpl/codecslib.py +86 -0
  65. crosshair/libimpl/codecslib_test.py +86 -0
  66. crosshair/libimpl/collectionslib.py +264 -0
  67. crosshair/libimpl/collectionslib_ch_test.py +252 -0
  68. crosshair/libimpl/collectionslib_test.py +332 -0
  69. crosshair/libimpl/copylib.py +23 -0
  70. crosshair/libimpl/copylib_test.py +18 -0
  71. crosshair/libimpl/datetimelib.py +2559 -0
  72. crosshair/libimpl/datetimelib_ch_test.py +354 -0
  73. crosshair/libimpl/datetimelib_test.py +112 -0
  74. crosshair/libimpl/decimallib.py +5257 -0
  75. crosshair/libimpl/decimallib_ch_test.py +78 -0
  76. crosshair/libimpl/decimallib_test.py +76 -0
  77. crosshair/libimpl/encodings/__init__.py +23 -0
  78. crosshair/libimpl/encodings/_encutil.py +187 -0
  79. crosshair/libimpl/encodings/ascii.py +44 -0
  80. crosshair/libimpl/encodings/latin_1.py +40 -0
  81. crosshair/libimpl/encodings/utf_8.py +93 -0
  82. crosshair/libimpl/encodings_ch_test.py +83 -0
  83. crosshair/libimpl/fractionlib.py +16 -0
  84. crosshair/libimpl/fractionlib_test.py +80 -0
  85. crosshair/libimpl/functoolslib.py +34 -0
  86. crosshair/libimpl/functoolslib_test.py +56 -0
  87. crosshair/libimpl/hashliblib.py +30 -0
  88. crosshair/libimpl/hashliblib_test.py +18 -0
  89. crosshair/libimpl/heapqlib.py +47 -0
  90. crosshair/libimpl/heapqlib_test.py +21 -0
  91. crosshair/libimpl/importliblib.py +18 -0
  92. crosshair/libimpl/importliblib_test.py +38 -0
  93. crosshair/libimpl/iolib.py +216 -0
  94. crosshair/libimpl/iolib_ch_test.py +128 -0
  95. crosshair/libimpl/iolib_test.py +19 -0
  96. crosshair/libimpl/ipaddresslib.py +8 -0
  97. crosshair/libimpl/itertoolslib.py +44 -0
  98. crosshair/libimpl/itertoolslib_test.py +44 -0
  99. crosshair/libimpl/jsonlib.py +984 -0
  100. crosshair/libimpl/jsonlib_ch_test.py +42 -0
  101. crosshair/libimpl/jsonlib_test.py +51 -0
  102. crosshair/libimpl/mathlib.py +179 -0
  103. crosshair/libimpl/mathlib_ch_test.py +44 -0
  104. crosshair/libimpl/mathlib_test.py +67 -0
  105. crosshair/libimpl/oslib.py +7 -0
  106. crosshair/libimpl/pathliblib_test.py +10 -0
  107. crosshair/libimpl/randomlib.py +178 -0
  108. crosshair/libimpl/randomlib_test.py +120 -0
  109. crosshair/libimpl/relib.py +846 -0
  110. crosshair/libimpl/relib_ch_test.py +169 -0
  111. crosshair/libimpl/relib_test.py +493 -0
  112. crosshair/libimpl/timelib.py +72 -0
  113. crosshair/libimpl/timelib_test.py +82 -0
  114. crosshair/libimpl/typeslib.py +15 -0
  115. crosshair/libimpl/typeslib_test.py +36 -0
  116. crosshair/libimpl/unicodedatalib.py +75 -0
  117. crosshair/libimpl/unicodedatalib_test.py +42 -0
  118. crosshair/libimpl/urlliblib.py +23 -0
  119. crosshair/libimpl/urlliblib_test.py +19 -0
  120. crosshair/libimpl/weakreflib.py +13 -0
  121. crosshair/libimpl/weakreflib_test.py +69 -0
  122. crosshair/libimpl/zliblib.py +15 -0
  123. crosshair/libimpl/zliblib_test.py +13 -0
  124. crosshair/lsp_server.py +261 -0
  125. crosshair/lsp_server_test.py +30 -0
  126. crosshair/main.py +973 -0
  127. crosshair/main_test.py +543 -0
  128. crosshair/objectproxy.py +376 -0
  129. crosshair/objectproxy_test.py +41 -0
  130. crosshair/opcode_intercept.py +601 -0
  131. crosshair/opcode_intercept_test.py +304 -0
  132. crosshair/options.py +218 -0
  133. crosshair/options_test.py +10 -0
  134. crosshair/patch_equivalence_test.py +75 -0
  135. crosshair/path_cover.py +209 -0
  136. crosshair/path_cover_test.py +138 -0
  137. crosshair/path_search.py +161 -0
  138. crosshair/path_search_test.py +52 -0
  139. crosshair/pathing_oracle.py +271 -0
  140. crosshair/pathing_oracle_test.py +21 -0
  141. crosshair/pure_importer.py +27 -0
  142. crosshair/pure_importer_test.py +16 -0
  143. crosshair/py.typed +0 -0
  144. crosshair/register_contract.py +273 -0
  145. crosshair/register_contract_test.py +190 -0
  146. crosshair/simplestructs.py +1165 -0
  147. crosshair/simplestructs_test.py +283 -0
  148. crosshair/smtlib.py +24 -0
  149. crosshair/smtlib_test.py +14 -0
  150. crosshair/statespace.py +1199 -0
  151. crosshair/statespace_test.py +108 -0
  152. crosshair/stubs_parser.py +352 -0
  153. crosshair/stubs_parser_test.py +43 -0
  154. crosshair/test_util.py +329 -0
  155. crosshair/test_util_test.py +26 -0
  156. crosshair/tools/__init__.py +0 -0
  157. crosshair/tools/check_help_in_doc.py +264 -0
  158. crosshair/tools/check_init_and_setup_coincide.py +119 -0
  159. crosshair/tools/generate_demo_table.py +127 -0
  160. crosshair/tracers.py +544 -0
  161. crosshair/tracers_test.py +154 -0
  162. crosshair/type_repo.py +151 -0
  163. crosshair/unicode_categories.py +589 -0
  164. crosshair/unicode_categories_test.py +27 -0
  165. crosshair/util.py +741 -0
  166. crosshair/util_test.py +173 -0
  167. crosshair/watcher.py +307 -0
  168. crosshair/watcher_test.py +107 -0
  169. crosshair/z3util.py +76 -0
  170. crosshair/z3util_test.py +11 -0
  171. crosshair_tool-0.0.99.dist-info/METADATA +144 -0
  172. crosshair_tool-0.0.99.dist-info/RECORD +176 -0
  173. crosshair_tool-0.0.99.dist-info/WHEEL +6 -0
  174. crosshair_tool-0.0.99.dist-info/entry_points.txt +3 -0
  175. crosshair_tool-0.0.99.dist-info/licenses/LICENSE +93 -0
  176. crosshair_tool-0.0.99.dist-info/top_level.txt +2 -0
@@ -0,0 +1,78 @@
1
+ import operator
2
+ import sys
3
+ from decimal import BasicContext, Decimal, ExtendedContext, localcontext
4
+ from typing import Union
5
+
6
+ import pytest # type: ignore
7
+
8
+ from crosshair.core_and_libs import MessageType, analyze_function, run_checkables
9
+ from crosshair.test_util import ResultComparison, compare_results, compare_returns
10
+
11
+
12
+ def _binary_op_under_context(ctx, op):
13
+ def run_op(d1, d2):
14
+ with localcontext(ctx):
15
+ return op(d1, d2)
16
+
17
+ return run_op
18
+
19
+
20
+ def check_division(
21
+ decimal1: Decimal, decimal2: Union[Decimal, int, float]
22
+ ) -> ResultComparison:
23
+ """post: _"""
24
+ return compare_returns(operator.truediv, decimal1, decimal2)
25
+
26
+
27
+ def check_pow(decimal1: Decimal, decimal2: Decimal) -> ResultComparison:
28
+ """post: _"""
29
+ return compare_results(operator.pow, decimal1, decimal2)
30
+
31
+
32
+ def check_extended_context(
33
+ decimal1: Decimal, decimal2: Union[Decimal, int, float]
34
+ ) -> ResultComparison:
35
+ """post: _"""
36
+ return compare_results(
37
+ _binary_op_under_context(ExtendedContext, operator.truediv), decimal1, decimal2
38
+ )
39
+
40
+
41
+ def check_basic_context(
42
+ decimal1: Decimal, decimal2: Union[Decimal, int, float]
43
+ ) -> ResultComparison:
44
+ """post: _"""
45
+ return compare_results(
46
+ _binary_op_under_context(BasicContext, operator.truediv), decimal1, decimal2
47
+ )
48
+
49
+
50
+ def check_div_using_context_method(
51
+ decimal1: Decimal, decimal2: Union[Decimal, int, float]
52
+ ) -> ResultComparison:
53
+ """post: _"""
54
+ return compare_returns(BasicContext.divide, decimal1, decimal2)
55
+
56
+
57
+ def check_div_using_context_parameter(
58
+ decimal1: Decimal, decimal2: Union[Decimal, int, float]
59
+ ) -> ResultComparison:
60
+ """post: _"""
61
+ return compare_returns(
62
+ lambda d1, d2: d1.divide(d2, context=BasicContext), decimal1, decimal2
63
+ )
64
+
65
+
66
+ def check_create_decimal_from_float(float_number: float):
67
+ """post: _"""
68
+ return compare_results(BasicContext.create_decimal_from_float, float_number)
69
+
70
+
71
+ # This is the only real test definition.
72
+ # It runs crosshair on each of the "check" functions defined above.
73
+ @pytest.mark.parametrize("fn_name", [fn for fn in dir() if fn.startswith("check_")])
74
+ def test_builtin(fn_name: str) -> None:
75
+ this_module = sys.modules[__name__]
76
+ messages = run_checkables(analyze_function(getattr(this_module, fn_name)))
77
+ errors = [m for m in messages if m.state > MessageType.PRE_UNSAT]
78
+ assert errors == []
@@ -0,0 +1,76 @@
1
+ from decimal import (
2
+ Decimal,
3
+ DivisionByZero,
4
+ ExtendedContext,
5
+ InvalidOperation,
6
+ localcontext,
7
+ )
8
+
9
+ import pytest
10
+
11
+ from crosshair.core import proxy_for_type, standalone_statespace
12
+ from crosshair.libimpl.decimallib import Decimal as PyDecimal
13
+ from crosshair.tracers import NoTracing
14
+ from crosshair.util import debug
15
+
16
+
17
+ def test_mixed_decimal_addition() -> None:
18
+ d1 = Decimal("1.05")
19
+ with standalone_statespace:
20
+ d2 = proxy_for_type(Decimal, "d2")
21
+ debug("type(d2)", type(d2))
22
+ d1 + d2
23
+
24
+
25
+ def test_external_decimal_context() -> None:
26
+ with localcontext(ExtendedContext):
27
+ Decimal("43.4") / 0 # does not raise
28
+ with pytest.raises(DivisionByZero):
29
+ Decimal("43.4") / 0
30
+ with pytest.raises(InvalidOperation):
31
+ Decimal("0") / 0
32
+ with pytest.raises(DivisionByZero):
33
+ PyDecimal("43.4") / 0
34
+ with standalone_statespace as space:
35
+ with NoTracing():
36
+ d1 = proxy_for_type(Decimal, "d1")
37
+ if d1 == 0:
38
+ d1 += 1
39
+ with pytest.raises(DivisionByZero):
40
+ d1 / 0
41
+ with pytest.raises(InvalidOperation):
42
+ (d1 - d1) / 0
43
+
44
+
45
+ def test_context_method_on_symbolic():
46
+ with standalone_statespace:
47
+ ExtendedContext.exp(proxy_for_type(Decimal, "d"))
48
+ ExtendedContext.divide_int(Decimal(12), proxy_for_type(Decimal, "d"))
49
+ ExtendedContext.divide_int(Decimal(12), Decimal(2))
50
+
51
+
52
+ def test_precision():
53
+ """post: _"""
54
+ d1, d2 = Decimal("3.4445"), Decimal("1.0023")
55
+ expected = Decimal("4.45")
56
+ assert d1 + d2 != expected
57
+ with standalone_statespace:
58
+ with localcontext() as ctx:
59
+ ctx.prec = 3
60
+ assert d1 + d2 == expected
61
+ with localcontext() as ctx:
62
+ ctx.prec = 1
63
+ assert +expected == Decimal("4")
64
+
65
+
66
+ # Still working on this! (rn, issue with z3 int exponent vars becoming reals)
67
+ # def test_decimal_end_to_end():
68
+ # def add_tax(price: Decimal):
69
+ # """post: _ != Decimal('1.05')"""
70
+ # ctx = ExtendedContext.copy()
71
+ # ctx.prec = 3
72
+ # with localcontext(ctx):
73
+ # return price + Decimal("0.05")
74
+ # # return price * Decimal("1.05")
75
+
76
+ # check_states(add_tax, POST_FAIL)
@@ -0,0 +1,23 @@
1
+ import codecs
2
+ import importlib
3
+ from encodings import normalize_encoding
4
+ from encodings.aliases import aliases
5
+ from typing import Optional
6
+
7
+
8
+ def codec_search(encoding: str) -> Optional[codecs.CodecInfo]:
9
+ enc_prefix = "crosshair_"
10
+ if not encoding.startswith(enc_prefix):
11
+ return None
12
+
13
+ encoding = normalize_encoding(encoding[len(enc_prefix) :])
14
+ encoding = aliases.get(encoding, encoding)
15
+
16
+ try:
17
+ module = importlib.import_module(f"crosshair.libimpl.encodings.{encoding}")
18
+ except ImportError:
19
+ pass
20
+ else:
21
+ if callable(getattr(module, "getregentry", None)):
22
+ return module.getregentry() # type: ignore
23
+ return None
@@ -0,0 +1,187 @@
1
+ import codecs
2
+ import sys
3
+ from dataclasses import dataclass
4
+ from typing import List, Optional, Tuple, Type, Union
5
+
6
+ if sys.version_info >= (3, 12):
7
+ from collections.abc import Buffer
8
+ else:
9
+ from collections.abc import ByteString as Buffer
10
+
11
+ from crosshair.core import realize
12
+ from crosshair.libimpl.builtinslib import AnySymbolicStr, SymbolicBytes
13
+
14
+
15
+ class ChunkError:
16
+ def reason(self) -> str:
17
+ raise NotImplementedError
18
+
19
+
20
+ class UnexpectedEndError(ChunkError):
21
+ def reason(self) -> str:
22
+ return "unexpected end of data"
23
+
24
+
25
+ @dataclass
26
+ class MidChunkError(ChunkError):
27
+ _reason: str
28
+
29
+ # _errlen: int = 1
30
+ def reason(self) -> str:
31
+ return self._reason
32
+
33
+
34
+ class _UnicodeDecodeError(UnicodeDecodeError):
35
+ def __init__(self, enc, byts, start, end, reason):
36
+ UnicodeDecodeError.__init__(self, enc, b"", start, end, reason)
37
+ self.object = byts
38
+
39
+ def __ch_deep_realize__(self, memo) -> object:
40
+ enc, obj, reason = self.encoding, self.object, self.reason
41
+ start, end = self.start, self.end
42
+ return UnicodeDecodeError(
43
+ realize(enc), realize(obj), realize(start), realize(end), realize(reason)
44
+ )
45
+
46
+ def __repr__(self):
47
+ enc, obj, reason = self.encoding, self.object, self.reason
48
+ start, end = self.start, self.end
49
+ return f"UnicodeDecodeError({enc!r}, {obj!r}, {start!r}, {end!r}, {reason!r})"
50
+
51
+
52
+ class StemEncoder:
53
+
54
+ encoding_name: str
55
+
56
+ @classmethod
57
+ def _encode_chunk(
58
+ cls, intput: str, start: int
59
+ ) -> Tuple[Union[bytes, SymbolicBytes], int, Optional[ChunkError]]:
60
+ raise NotImplementedError
61
+
62
+ @classmethod
63
+ def _decode_chunk(
64
+ cls, intput: bytes, start: int
65
+ ) -> Tuple[Union[str, AnySymbolicStr], int, Optional[ChunkError]]:
66
+ raise NotImplementedError
67
+
68
+ @classmethod
69
+ def encode(
70
+ cls, input: str, errors: str = "strict"
71
+ ) -> Tuple[Union[bytes, SymbolicBytes], int]:
72
+ if not (isinstance(input, str) and isinstance(errors, str)):
73
+ raise TypeError
74
+ parts: List[bytes] = []
75
+ idx = 0
76
+ inputlen = len(input)
77
+ while idx < inputlen:
78
+ out, idx, err = cls._encode_chunk(input, idx)
79
+ parts.append(out) # type: ignore
80
+ if err is not None:
81
+ realized_input = realize(input) # TODO: avoid realization here.
82
+ # (which possibly requires implementing the error handlers in python)
83
+ exc = UnicodeEncodeError(
84
+ cls.encoding_name, realized_input, idx, idx + 1, err.reason()
85
+ )
86
+ replacement, idx = codecs.lookup_error(errors)(exc)
87
+ if isinstance(replacement, str):
88
+ replacement = codecs.encode(replacement, cls.encoding_name)
89
+ parts.append(replacement)
90
+ return b"".join(parts), idx
91
+
92
+ @classmethod
93
+ def decode(
94
+ cls, input: bytes, errors: str = "strict"
95
+ ) -> Tuple[Union[str, AnySymbolicStr], int]:
96
+ if not (isinstance(input, Buffer) and isinstance(errors, str)):
97
+ raise TypeError
98
+ parts: List[Union[str, AnySymbolicStr]] = []
99
+ idx = 0
100
+ inputlen = len(input)
101
+ while idx < inputlen:
102
+ out, idx, err = cls._decode_chunk(input, idx)
103
+ parts.append(out)
104
+ if err is not None:
105
+ # 1. Handle some well-known error modes directly:
106
+ if errors == "strict":
107
+ raise _UnicodeDecodeError(
108
+ cls.encoding_name, input, idx, idx + 1, err.reason()
109
+ )
110
+ # TODO: continuation after erros seems poorly tested right now
111
+ if errors == "ignore":
112
+ idx += 1
113
+ continue
114
+ if errors == "replace":
115
+ idx += 1
116
+ parts.append("\ufffd")
117
+ continue
118
+
119
+ # 2. Then fall back to native implementations if necessary:
120
+ exc = UnicodeDecodeError(
121
+ cls.encoding_name, realize(input), idx, idx + 1, err.reason()
122
+ )
123
+ replacement, idx = codecs.lookup_error(errors)(exc)
124
+ if isinstance(replacement, bytes):
125
+ replacement = codecs.decode(replacement, cls.encoding_name)
126
+ parts.append(replacement)
127
+ return "".join(parts), idx # type: ignore
128
+
129
+ @classmethod
130
+ def getregentry(cls) -> codecs.CodecInfo:
131
+ return _getregentry(cls)
132
+
133
+
134
+ def _getregentry(stem_encoder: Type[StemEncoder]):
135
+ class StemIncrementalEncoder(codecs.BufferedIncrementalEncoder):
136
+ def _buffer_encode(
137
+ self, input: str, errors: str, final: bool
138
+ ) -> Tuple[bytes, int]:
139
+ enc_name = stem_encoder.encoding_name
140
+ out, idx, err = stem_encoder._encode_chunk(input, 0)
141
+ assert isinstance(out, bytes)
142
+ if not err:
143
+ return (out, idx)
144
+ if isinstance(err, UnexpectedEndError) or not final:
145
+ return (out, idx)
146
+ exc = UnicodeEncodeError(enc_name, input, idx, idx + 1, err.reason())
147
+ replacement, idx = codecs.lookup_error(errors)(exc)
148
+ if isinstance(replacement, str):
149
+ replacement = codecs.encode(replacement, enc_name)
150
+ return (out + replacement, idx)
151
+
152
+ class StemIncrementalDecoder(codecs.BufferedIncrementalDecoder):
153
+ def _buffer_decode(
154
+ self, input: Buffer, errors: str, final: bool
155
+ ) -> Tuple[str, int]:
156
+ if not isinstance(input, bytes):
157
+ input = memoryview(input).tobytes()
158
+ enc_name = stem_encoder.encoding_name
159
+ out, idx, err = stem_encoder._decode_chunk(input, 0)
160
+ assert isinstance(out, str)
161
+ if not err:
162
+ return out, idx
163
+ if isinstance(err, UnexpectedEndError) or not final:
164
+ return out, idx
165
+ exc = UnicodeDecodeError(enc_name, input, idx, idx + 1, err.reason())
166
+ replacement, idx = codecs.lookup_error(errors)(exc)
167
+ if isinstance(replacement, bytes):
168
+ replacement = codecs.decode(replacement, enc_name)
169
+ return (out + replacement, idx)
170
+
171
+ class StemStreamWriter(codecs.StreamWriter):
172
+ def encode(self, input: str, errors: str = "strict") -> Tuple[bytes, int]:
173
+ raise Exception # TODO implement
174
+
175
+ class StemStreamReader(codecs.StreamReader):
176
+ def decode(self, input: bytes, errors: str = "strict") -> Tuple[str, int]:
177
+ raise Exception
178
+
179
+ return codecs.CodecInfo(
180
+ name=stem_encoder.encoding_name,
181
+ encode=stem_encoder.encode, # type: ignore
182
+ decode=stem_encoder.decode, # type: ignore
183
+ incrementalencoder=StemIncrementalEncoder,
184
+ incrementaldecoder=StemIncrementalDecoder,
185
+ streamreader=StemStreamReader,
186
+ streamwriter=StemStreamWriter,
187
+ )
@@ -0,0 +1,44 @@
1
+ import codecs
2
+ from typing import List, Optional, Tuple, Union
3
+
4
+ from crosshair.libimpl.builtinslib import SymbolicBytes
5
+ from crosshair.libimpl.encodings._encutil import ChunkError, MidChunkError, StemEncoder
6
+
7
+
8
+ class AsciiStemEncoder(StemEncoder):
9
+ encoding_name = "ascii"
10
+
11
+ @classmethod
12
+ def _encode_chunk(
13
+ cls, string: str, start: int
14
+ ) -> Tuple[Union[bytes, SymbolicBytes], int, Optional[ChunkError]]:
15
+ byte_ints: List[int] = []
16
+ for idx in range(start, len(string)):
17
+ ch = string[idx]
18
+ cp = ord(ch)
19
+ if cp >= 0x80:
20
+ return (
21
+ SymbolicBytes(byte_ints),
22
+ idx,
23
+ MidChunkError("ordinal not in range"),
24
+ )
25
+ else:
26
+ byte_ints.append(cp)
27
+ return (SymbolicBytes(byte_ints), len(string), None)
28
+
29
+ @classmethod
30
+ def _decode_chunk(
31
+ cls, byts: bytes, start: int
32
+ ) -> Tuple[str, int, Optional[ChunkError]]:
33
+ chars: List[str] = []
34
+ for idx in range(start, len(byts)):
35
+ cp = byts[idx]
36
+ if cp >= 0x80:
37
+ return ("".join(chars), idx, MidChunkError("ordinal not in range"))
38
+ else:
39
+ chars.append(chr(cp))
40
+ return ("".join(chars), len(byts), None) # type: ignore
41
+
42
+
43
+ def getregentry() -> codecs.CodecInfo:
44
+ return AsciiStemEncoder.getregentry()
@@ -0,0 +1,40 @@
1
+ import codecs
2
+ from typing import List, Optional, Tuple, Union
3
+
4
+ from crosshair.libimpl.builtinslib import SymbolicBytes
5
+ from crosshair.libimpl.encodings._encutil import ChunkError, MidChunkError, StemEncoder
6
+
7
+
8
+ class Latin1StemEncoder(StemEncoder):
9
+ encoding_name = "iso8859-1"
10
+
11
+ @classmethod
12
+ def _encode_chunk(
13
+ cls, string: str, start: int
14
+ ) -> Tuple[Union[bytes, SymbolicBytes], int, Optional[ChunkError]]:
15
+ byte_ints: List[int] = []
16
+ for idx in range(start, len(string)):
17
+ ch = string[idx]
18
+ cp = ord(ch)
19
+ if cp < 256:
20
+ byte_ints.append(cp)
21
+ else:
22
+ return (
23
+ SymbolicBytes(byte_ints),
24
+ idx,
25
+ MidChunkError("bytes must be in range(0, 256)"),
26
+ )
27
+ return (SymbolicBytes(byte_ints), len(string), None)
28
+
29
+ @classmethod
30
+ def _decode_chunk(
31
+ cls, byts: bytes, start: int
32
+ ) -> Tuple[str, int, Optional[ChunkError]]:
33
+ chars: List[str] = []
34
+ for cp in byts[start:]:
35
+ chars.append(chr(cp))
36
+ return ("".join(chars), len(byts), None)
37
+
38
+
39
+ def getregentry() -> codecs.CodecInfo:
40
+ return Latin1StemEncoder.getregentry()
@@ -0,0 +1,93 @@
1
+ import codecs
2
+ from typing import List, Optional, Tuple, Union
3
+
4
+ from crosshair.libimpl.builtinslib import SymbolicBytes
5
+ from crosshair.libimpl.encodings._encutil import (
6
+ ChunkError,
7
+ MidChunkError,
8
+ StemEncoder,
9
+ UnexpectedEndError,
10
+ )
11
+
12
+
13
+ def _encode_codepoint(codepoint: int) -> Tuple[int, ...]:
14
+ if codepoint <= 0b01111111:
15
+ return (codepoint,)
16
+ elif codepoint <= 0b111_11111111:
17
+ return (
18
+ (0b11000000 + ((codepoint >> 6) & 0b00011111)),
19
+ (0b10000000 + (codepoint & 0b00111111)),
20
+ )
21
+ elif codepoint <= 0b11111111_11111111:
22
+ return (
23
+ (0b11100000 + ((codepoint >> 12) & 0b00001111)),
24
+ (0b10000000 + ((codepoint >> 6) & 0b00111111)),
25
+ (0b10000000 + (codepoint & 0b00111111)),
26
+ )
27
+ else:
28
+ return (
29
+ (0b11110000 + ((codepoint >> 18) & 0b00000111)),
30
+ (0b10000000 + ((codepoint >> 12) & 0b00111111)),
31
+ (0b10000000 + ((codepoint >> 6) & 0b00111111)),
32
+ (0b10000000 + (codepoint & 0b00111111)),
33
+ )
34
+
35
+
36
+ class Utf8StemEncoder(StemEncoder):
37
+ encoding_name = "utf-8"
38
+
39
+ @classmethod
40
+ def _encode_chunk(
41
+ cls, string: str, start: int
42
+ ) -> Tuple[Union[bytes, SymbolicBytes], int, Optional[ChunkError]]:
43
+ byte_ints: List[int] = []
44
+ for ch in string[start:]:
45
+ byte_ints.extend(_encode_codepoint(ord(ch)))
46
+ return (SymbolicBytes(byte_ints), len(string), None)
47
+
48
+ @classmethod
49
+ def _decode_chunk(
50
+ cls, byts: bytes, start: int
51
+ ) -> Tuple[str, int, Optional[ChunkError]]:
52
+ num_bytes = len(byts)
53
+ byt = byts[start]
54
+ end = start + 1
55
+ if byt >= 0b11000000:
56
+ end += 1
57
+ if byt >= 0b11100000:
58
+ end += 1
59
+ if byt >= 0b11110000:
60
+ if byt > 0b11110111:
61
+ return ("", start, MidChunkError(f"can't decode byte"))
62
+ end += 1
63
+ cp = byt & 0b00000111
64
+ mincp, maxcp = 0x10000, 0x10FFFF
65
+ else:
66
+ if byt > 0b11101111:
67
+ return ("", start, MidChunkError(f"can't decode byte"))
68
+ cp = byt & 0b00001111
69
+ mincp, maxcp = 0x0800, 0xFFFF
70
+ else:
71
+ if byt > 0b11011111:
72
+ return ("", start, MidChunkError(f"can't decode byte"))
73
+ cp = byt & 0b00011111
74
+ mincp, maxcp = 0x0080, 0x07FF
75
+ else:
76
+ cp = byt
77
+ mincp, maxcp = 0, 0x007F
78
+ if end > num_bytes:
79
+ return ("", start, UnexpectedEndError())
80
+ for idx in range(start + 1, end):
81
+ byt = byts[idx]
82
+ if 0b10_000000 <= byt <= 0b10_111111:
83
+ cp = (cp * 64) + (byts[idx] - 0b10_000000)
84
+ else:
85
+ return ("", start, MidChunkError(f"can't decode byte"))
86
+ if mincp <= cp <= maxcp:
87
+ return (chr(cp), end, None)
88
+ else:
89
+ return ("", start, MidChunkError(f"invalid start byte"))
90
+
91
+
92
+ def getregentry() -> codecs.CodecInfo:
93
+ return Utf8StemEncoder.getregentry()
@@ -0,0 +1,83 @@
1
+ import codecs
2
+ import sys
3
+ from io import BytesIO
4
+
5
+ import pytest # type: ignore
6
+
7
+ from crosshair.core_and_libs import MessageType, analyze_function, run_checkables
8
+ from crosshair.options import AnalysisOptionSet
9
+ from crosshair.test_util import ResultComparison, compare_results
10
+
11
+ _ERROR_HANDLERS = ["strict", "replace", "ignore"]
12
+
13
+ # crosshair: max_iterations=20
14
+
15
+
16
+ def check_encode_ascii(string: str, errors: str) -> ResultComparison:
17
+ """
18
+ pre: errors in _ERROR_HANDLERS
19
+ post: _
20
+ """
21
+ return compare_results(lambda s, e: s.encode("ascii", e), string, errors)
22
+
23
+
24
+ def check_encode_latin1(string: str, errors: str) -> ResultComparison:
25
+ """
26
+ pre: errors in _ERROR_HANDLERS
27
+ post: _
28
+ """
29
+ return compare_results(lambda s, e: s.encode("latin1", e), string, errors)
30
+
31
+
32
+ def check_encode_utf8(string: str, errors: str) -> ResultComparison:
33
+ """
34
+ pre: errors in _ERROR_HANDLERS
35
+ post: _
36
+ """
37
+ return compare_results(lambda s, e: s.encode("utf8", e), string, errors)
38
+
39
+
40
+ def check_decode_ascii(bytestring: bytes, errors: str) -> ResultComparison:
41
+ """
42
+ pre: errors in _ERROR_HANDLERS
43
+ post: _
44
+ """
45
+ return compare_results(lambda b, e: b.decode("ascii", e), bytestring, errors)
46
+
47
+
48
+ def check_decode_latin1(bytestring: bytes, errors: str) -> ResultComparison:
49
+ """
50
+ pre: errors in _ERROR_HANDLERS
51
+ post: _
52
+ """
53
+ return compare_results(lambda b, e: b.decode("latin1", e), bytestring, errors)
54
+
55
+
56
+ def check_decode_utf8(bytestring: bytes, errors: str) -> ResultComparison:
57
+ """
58
+ pre: errors in _ERROR_HANDLERS
59
+ post: _
60
+ """
61
+ # crosshair: max_iterations=200
62
+ return compare_results(lambda b, e: b.decode("utf8", e), bytestring, errors)
63
+
64
+
65
+ # TODO add handling for BytesIO(SymbolicBytes())
66
+ # def check_stream_decode_utf8(bytestring: bytes, errors: str) -> ResultComparison:
67
+ # """
68
+ # pre: errors in _ERROR_HANDLERS
69
+ # post: _
70
+ # """
71
+ # def read_stream(b, e):
72
+ # return codecs.getreader("utf8")(BytesIO(b), e).read()
73
+ # return compare_results(read_stream, bytestring, errors)
74
+
75
+
76
+ # This is the only real test definition.
77
+ # It runs crosshair on each of the "check" functions defined above.
78
+ @pytest.mark.parametrize("fn_name", [fn for fn in dir() if fn.startswith("check_")])
79
+ def test_builtin(fn_name: str) -> None:
80
+ this_module = sys.modules[__name__]
81
+ messages = run_checkables(analyze_function(getattr(this_module, fn_name)))
82
+ errors = [m for m in messages if m.state > MessageType.PRE_UNSAT]
83
+ assert errors == []
@@ -0,0 +1,16 @@
1
+ from fractions import Fraction
2
+
3
+ from crosshair.core import SymbolicFactory, register_type
4
+ from crosshair.statespace import force_true
5
+ from crosshair.tracers import ResumedTracing
6
+
7
+
8
+ def _make_fraction(factory: SymbolicFactory):
9
+ n, d = factory(int, "_numerator"), factory(int, "_denominator")
10
+ with ResumedTracing():
11
+ force_true(d > 0)
12
+ return Fraction(n, d)
13
+
14
+
15
+ def make_registrations() -> None:
16
+ register_type(Fraction, _make_fraction)