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
crosshair/codeconfig.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Configure analysis options at different levels."""
|
|
2
|
+
|
|
3
|
+
import importlib.resources
|
|
4
|
+
import inspect
|
|
5
|
+
import re
|
|
6
|
+
import sys
|
|
7
|
+
import tokenize
|
|
8
|
+
from io import StringIO
|
|
9
|
+
from typing import Any, Iterable, Tuple
|
|
10
|
+
|
|
11
|
+
from crosshair.options import AnalysisOptionSet
|
|
12
|
+
from crosshair.util import memo, sourcelines
|
|
13
|
+
|
|
14
|
+
_COMMENT_TOKEN_RE = re.compile(r"^\#\s*crosshair\s*\:\s*(.*?)\s*$")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_directives(source_text: str) -> Iterable[Tuple[int, int, str]]:
|
|
18
|
+
r"""
|
|
19
|
+
Extract directive text from source lines.
|
|
20
|
+
|
|
21
|
+
:returns: a list of (line number, column number, directive text) tuples
|
|
22
|
+
|
|
23
|
+
>>> get_directives("pass\n# crosshair: foo=bar")
|
|
24
|
+
[(2, 0, 'foo=bar')]
|
|
25
|
+
"""
|
|
26
|
+
ret = []
|
|
27
|
+
tokens = tokenize.generate_tokens(StringIO(source_text).readline)
|
|
28
|
+
# TODO catch tokenize.TokenError ... just in case?
|
|
29
|
+
for toktyp, tokval, begin, _, _ in tokens:
|
|
30
|
+
linenum, colnum = begin
|
|
31
|
+
if toktyp == tokenize.COMMENT:
|
|
32
|
+
directive = _COMMENT_TOKEN_RE.sub(r"\1", tokval)
|
|
33
|
+
if tokval != directive:
|
|
34
|
+
ret.append((linenum, colnum, directive))
|
|
35
|
+
return ret
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class InvalidDirective(Exception):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def parse_directives(
|
|
43
|
+
directive_lines: Iterable[Tuple[int, int, str]],
|
|
44
|
+
) -> AnalysisOptionSet:
|
|
45
|
+
"""
|
|
46
|
+
Parse options from directives in comments.
|
|
47
|
+
|
|
48
|
+
>>> parse_directives([(1, 0, "off")]).enabled
|
|
49
|
+
False
|
|
50
|
+
"""
|
|
51
|
+
result = AnalysisOptionSet()
|
|
52
|
+
for lineno, _colno, directive in directive_lines:
|
|
53
|
+
for part in directive.split():
|
|
54
|
+
if part == "on":
|
|
55
|
+
part = "enabled=yes"
|
|
56
|
+
if part == "off":
|
|
57
|
+
part = "enabled=no"
|
|
58
|
+
pair = part.split("=", 2)
|
|
59
|
+
if len(pair) != 2:
|
|
60
|
+
raise InvalidDirective(f'Malformed option: "{part}"', lineno)
|
|
61
|
+
key, strvalue = pair
|
|
62
|
+
if key not in AnalysisOptionSet.directive_fields:
|
|
63
|
+
raise InvalidDirective(f'Unknown option: "{key}"', lineno)
|
|
64
|
+
value = AnalysisOptionSet.parse_field(key, strvalue)
|
|
65
|
+
if value is None:
|
|
66
|
+
raise InvalidDirective(
|
|
67
|
+
f'"{strvalue}" is not a valid "{key}" value', lineno
|
|
68
|
+
)
|
|
69
|
+
if getattr(result, key) is not None:
|
|
70
|
+
raise InvalidDirective(
|
|
71
|
+
f'Option "{key}" is set multiple times at the same scope', lineno
|
|
72
|
+
)
|
|
73
|
+
result = result.overlay(AnalysisOptionSet(**{key: value}))
|
|
74
|
+
return result
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@memo
|
|
78
|
+
def collect_options(thing: Any) -> AnalysisOptionSet:
|
|
79
|
+
parent_opts = AnalysisOptionSet()
|
|
80
|
+
is_package = thing.__name__ == getattr(thing, "__package__", None)
|
|
81
|
+
if getattr(thing, "__module__", None):
|
|
82
|
+
parent_opts = collect_options(sys.modules[thing.__module__])
|
|
83
|
+
elif getattr(thing, "__package__", None):
|
|
84
|
+
if is_package:
|
|
85
|
+
parent_pkg, _, _ = thing.__package__.rpartition(".")
|
|
86
|
+
else:
|
|
87
|
+
parent_pkg = thing.__package__
|
|
88
|
+
if parent_pkg:
|
|
89
|
+
parent_opts = collect_options(sys.modules[parent_pkg])
|
|
90
|
+
|
|
91
|
+
source_text: str
|
|
92
|
+
if is_package:
|
|
93
|
+
try:
|
|
94
|
+
if sys.version_info >= (3, 10):
|
|
95
|
+
source_text = (
|
|
96
|
+
importlib.resources.files(thing).joinpath("__init__.py").read_text()
|
|
97
|
+
)
|
|
98
|
+
else:
|
|
99
|
+
source_text = importlib.resources.read_text(thing, "__init__.py")
|
|
100
|
+
except FileNotFoundError:
|
|
101
|
+
source_text = ""
|
|
102
|
+
else:
|
|
103
|
+
_file, _start, lines = sourcelines(thing)
|
|
104
|
+
source_text = "".join(lines)
|
|
105
|
+
directives = get_directives(source_text)
|
|
106
|
+
if inspect.ismodule(thing):
|
|
107
|
+
# Only look at toplevel comments in modules
|
|
108
|
+
# (we don't want to catch directives for functions inside it)
|
|
109
|
+
# TODO: detect directives at other levels like classes etc and warn that they
|
|
110
|
+
# will be ignored.
|
|
111
|
+
directives = [(l, c, t) for (l, c, t) in directives if c == 0]
|
|
112
|
+
my_opts = parse_directives(directives)
|
|
113
|
+
return parent_opts.overlay(my_opts)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import inspect
|
|
3
|
+
import sys
|
|
4
|
+
import textwrap
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest # type: ignore
|
|
8
|
+
|
|
9
|
+
from crosshair.codeconfig import (
|
|
10
|
+
AnalysisOptionSet,
|
|
11
|
+
InvalidDirective,
|
|
12
|
+
collect_options,
|
|
13
|
+
get_directives,
|
|
14
|
+
parse_directives,
|
|
15
|
+
)
|
|
16
|
+
from crosshair.test_util import simplefs
|
|
17
|
+
from crosshair.util import add_to_pypath
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# crosshair: off
|
|
21
|
+
def override_on():
|
|
22
|
+
# crosshair: on
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def timeout_of_10():
|
|
27
|
+
# crosshair: per_condition_timeout=10
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _example1():
|
|
32
|
+
# crosshair : First comment
|
|
33
|
+
# does not lead with crosshair: not present
|
|
34
|
+
print("# crosshair : this is a string, not a comment")
|
|
35
|
+
pass # crosshair:comment with trailing space
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_get_directives_example1() -> None:
|
|
39
|
+
lines, _ = inspect.getsourcelines(_example1)
|
|
40
|
+
assert get_directives("".join(lines)) == [
|
|
41
|
+
(2, 4, "First comment"),
|
|
42
|
+
(5, 10, "comment with trailing space"),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_parse_directives() -> None:
|
|
47
|
+
assert parse_directives([(1, 0, "per_condition_timeout=42")]) == AnalysisOptionSet(
|
|
48
|
+
per_condition_timeout=42
|
|
49
|
+
)
|
|
50
|
+
assert parse_directives([(1, 0, "on")]) == AnalysisOptionSet(enabled=True)
|
|
51
|
+
assert parse_directives([(1, 0, "off")]) == AnalysisOptionSet(enabled=False)
|
|
52
|
+
assert parse_directives([(1, 0, "on per_path_timeout=42")]) == AnalysisOptionSet(
|
|
53
|
+
enabled=True, per_path_timeout=42
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_parse_directive_errors() -> None:
|
|
58
|
+
with pytest.raises(InvalidDirective, match='Malformed option: "noequals"'):
|
|
59
|
+
parse_directives([(1, 0, "noequals")])
|
|
60
|
+
with pytest.raises(InvalidDirective, match='Unknown option: "notafield"'):
|
|
61
|
+
parse_directives([(1, 0, "notafield=42")])
|
|
62
|
+
with pytest.raises(
|
|
63
|
+
InvalidDirective,
|
|
64
|
+
match='"notanumber" is not a valid "per_condition_timeout" value',
|
|
65
|
+
):
|
|
66
|
+
parse_directives([(1, 0, "per_condition_timeout=notanumber")])
|
|
67
|
+
with pytest.raises(
|
|
68
|
+
InvalidDirective,
|
|
69
|
+
match='Option "enabled" is set multiple times at the same scope',
|
|
70
|
+
):
|
|
71
|
+
parse_directives([(1, 0, "on off")])
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_get_directives_multiline_string() -> None:
|
|
75
|
+
# This tests a regression where we'd previously thrown a "EOF in multi-line string"
|
|
76
|
+
# TokenError.
|
|
77
|
+
foo = '''"""
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
'''
|
|
81
|
+
assert list(get_directives(foo)) == []
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_collection_options() -> None:
|
|
85
|
+
this_module = sys.modules[__name__]
|
|
86
|
+
assert collect_options(this_module) == AnalysisOptionSet(enabled=False)
|
|
87
|
+
assert collect_options(override_on) == AnalysisOptionSet(enabled=True)
|
|
88
|
+
assert collect_options(timeout_of_10) == AnalysisOptionSet(
|
|
89
|
+
enabled=False, per_condition_timeout=10
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
DIRECTIVES_TREE = {
|
|
94
|
+
"pkg1": {
|
|
95
|
+
"__init__.py": textwrap.dedent(
|
|
96
|
+
"""\
|
|
97
|
+
# crosshair: off
|
|
98
|
+
# crosshair: per_condition_timeout=42
|
|
99
|
+
"""
|
|
100
|
+
),
|
|
101
|
+
"pkg2": {
|
|
102
|
+
"pkg3": {
|
|
103
|
+
"__init__.py": "# crosshair: max_iterations=5",
|
|
104
|
+
"mod.py": "# crosshair: on",
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_package_directives(tmp_path: Path):
|
|
112
|
+
simplefs(tmp_path, DIRECTIVES_TREE)
|
|
113
|
+
with add_to_pypath(tmp_path):
|
|
114
|
+
innermod = importlib.import_module("pkg1.pkg2.pkg3.mod")
|
|
115
|
+
assert collect_options(innermod) == AnalysisOptionSet(
|
|
116
|
+
enabled=True, max_iterations=5, per_condition_timeout=42
|
|
117
|
+
)
|