crosshair-tool 0.0.97__cp314-cp314-win_amd64.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.

Potentially problematic release.


This version of crosshair-tool might be problematic. Click here for more details.

Files changed (176) hide show
  1. _crosshair_tracers.cp314-win_amd64.pyd +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 +145 -0
  18. crosshair/copyext_test.py +74 -0
  19. crosshair/core.py +1759 -0
  20. crosshair/core_and_libs.py +149 -0
  21. crosshair/core_regestered_types_test.py +82 -0
  22. crosshair/core_test.py +1313 -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 +5133 -0
  62. crosshair/libimpl/builtinslib_ch_test.py +1191 -0
  63. crosshair/libimpl/builtinslib_test.py +3705 -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 +2546 -0
  72. crosshair/libimpl/datetimelib_ch_test.py +349 -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 +177 -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 +250 -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 +1161 -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 +1196 -0
  151. crosshair/statespace_test.py +99 -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 +525 -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 +736 -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.97.dist-info/METADATA +145 -0
  172. crosshair_tool-0.0.97.dist-info/RECORD +176 -0
  173. crosshair_tool-0.0.97.dist-info/WHEEL +5 -0
  174. crosshair_tool-0.0.97.dist-info/entry_points.txt +3 -0
  175. crosshair_tool-0.0.97.dist-info/licenses/LICENSE +93 -0
  176. crosshair_tool-0.0.97.dist-info/top_level.txt +2 -0
@@ -0,0 +1,250 @@
1
+ import logging
2
+ import os
3
+ import pathlib
4
+ import re
5
+ import sys
6
+ import time
7
+ from collections import defaultdict
8
+ from dataclasses import dataclass, field
9
+ from pathlib import Path
10
+ from threading import Thread
11
+ from typing import Any, Counter, Dict, List, Optional
12
+ from urllib.parse import unquote, urlparse
13
+
14
+ from lsprotocol.types import (
15
+ SHUTDOWN,
16
+ TEXT_DOCUMENT_DID_CHANGE,
17
+ TEXT_DOCUMENT_DID_CLOSE,
18
+ TEXT_DOCUMENT_DID_OPEN,
19
+ TEXT_DOCUMENT_DID_SAVE,
20
+ Diagnostic,
21
+ DiagnosticSeverity,
22
+ DidChangeTextDocumentParams,
23
+ DidCloseTextDocumentParams,
24
+ DidOpenTextDocumentParams,
25
+ Position,
26
+ Range,
27
+ )
28
+ from pygls.server import LanguageServer
29
+
30
+ from crosshair import __version__, env_info
31
+ from crosshair.options import DEFAULT_OPTIONS, AnalysisOptionSet
32
+ from crosshair.statespace import AnalysisMessage, MessageType
33
+ from crosshair.watcher import Watcher
34
+
35
+ # logging.basicConfig(
36
+ # filename="/Users/pschanely/pygls.log", filemode="w", level=logging.DEBUG
37
+ # )
38
+
39
+
40
+ class CrossHairLanguageServer(LanguageServer):
41
+ def __init__(self, options: AnalysisOptionSet):
42
+ self.options = options
43
+ super().__init__("CrossHairServer", __version__)
44
+
45
+ CMD_REGISTER_COMPLETIONS = "registerCompletions"
46
+ CMD_UNREGISTER_COMPLETIONS = "unregisterCompletions"
47
+
48
+
49
+ def get_diagnostic(message: AnalysisMessage, doclines: List[str]) -> Diagnostic:
50
+ line = message.line - 1
51
+ col = endcol = message.column
52
+ if col == 0:
53
+ if line < len(doclines):
54
+ line_text = doclines[line]
55
+ match = re.compile(r"^\s*(.*?)\s*$").fullmatch(line_text)
56
+ if match:
57
+ col, endcol = match.start(1), match.end(1)
58
+ if message.state < MessageType.PRE_UNSAT:
59
+ severity = DiagnosticSeverity.Information
60
+ elif message.state == MessageType.PRE_UNSAT:
61
+ severity = DiagnosticSeverity.Warning
62
+ else:
63
+ severity = DiagnosticSeverity.Error
64
+ return Diagnostic(
65
+ range=Range(
66
+ start=Position(line=line, character=col),
67
+ end=Position(line=line, character=endcol),
68
+ ),
69
+ severity=severity,
70
+ message=message.message,
71
+ source="CrossHair",
72
+ )
73
+
74
+
75
+ def publish_messages(
76
+ messages: Dict[str, Optional[Dict[int, AnalysisMessage]]],
77
+ server: CrossHairLanguageServer,
78
+ ):
79
+ for uri, file_messages in list(messages.items()):
80
+ doc = server.workspace.documents.get(uri, None)
81
+ if file_messages is None:
82
+ # closed/editing, and client already notified as empty
83
+ continue
84
+ diagnostics = []
85
+ for message in file_messages.values():
86
+ if message.state < MessageType.PRE_UNSAT:
87
+ continue
88
+ # TODO: consider server.show_message_log()ing the long description
89
+ diagnostics.append(get_diagnostic(message, doc.lines if doc else []))
90
+ server.publish_diagnostics(uri, diagnostics)
91
+ if not diagnostics:
92
+ # After we publish an empty set, it's safe to forget about the file:
93
+ del messages[uri]
94
+
95
+
96
+ @dataclass
97
+ class LocalState:
98
+ watcher: Watcher
99
+ server: CrossHairLanguageServer
100
+ active_messages: Dict[str, Optional[Dict[int, AnalysisMessage]]] = field(
101
+ default_factory=lambda: defaultdict(dict)
102
+ )
103
+ should_shutdown: bool = False
104
+
105
+ def start_loop_thread(self):
106
+ self.loop = Thread(target=self.run_watch_loop)
107
+ self.loop.start()
108
+
109
+ def shutdown(self):
110
+ self.should_shutdown = True
111
+ self.watcher.shutdown()
112
+ self.loop.join(3.0) # run_iteration should check the flag 1x / second
113
+
114
+ def run_watch_loop(
115
+ self,
116
+ max_watch_iterations: int = sys.maxsize,
117
+ ) -> None:
118
+ def log(*a):
119
+ pass # self.server.show_message_log(*a)
120
+
121
+ log("loop thread started")
122
+ watcher = self.watcher
123
+ server = self.server
124
+ active_messages = self.active_messages
125
+ restart = True
126
+ stats: Counter[str] = Counter()
127
+ for _i in range(max_watch_iterations):
128
+ if self.should_shutdown:
129
+ return
130
+ if restart:
131
+ numfiles = len(watcher._modtimes)
132
+ server.show_message_log(
133
+ f"Scanning {numfiles} file(s) for properties to check."
134
+ )
135
+ max_uninteresting_iterations = (
136
+ DEFAULT_OPTIONS.get_max_uninteresting_iterations()
137
+ )
138
+ restart = False
139
+ stats = Counter()
140
+ for k, v in list(active_messages.items()):
141
+ active_messages[k] = None if v is None else {}
142
+ else:
143
+ time.sleep(0.25)
144
+ max_uninteresting_iterations = min(
145
+ sys.maxsize, 2 * max_uninteresting_iterations
146
+ )
147
+ log(f"iteration starting" + str(_i))
148
+ for curstats, messages in watcher.run_iteration(
149
+ max_uninteresting_iterations
150
+ ):
151
+ log(f"iteration yielded {curstats, messages}")
152
+ stats.update(curstats)
153
+ for message in messages:
154
+ filename, line = (message.filename, message.line)
155
+ file_messages = active_messages[Path(filename).as_uri()]
156
+ if file_messages is not None and (
157
+ line not in file_messages
158
+ or file_messages[line].state < message.state
159
+ ):
160
+ file_messages[line] = message
161
+ numpaths = stats["num_paths"]
162
+ if self.should_shutdown:
163
+ return
164
+ if numpaths > 0:
165
+ status = f"Analyzed {numpaths} paths in {len(watcher._modtimes)} files."
166
+ server.show_message_log(status)
167
+ publish_messages(active_messages, server)
168
+ if watcher._change_flag:
169
+ watcher._change_flag = False
170
+ restart = True
171
+ log(f"done run loop")
172
+
173
+
174
+ _LS: Optional[LocalState] = None
175
+
176
+
177
+ def getlocalstate(server: CrossHairLanguageServer) -> LocalState:
178
+ global _LS
179
+ if _LS is None:
180
+ server.show_message_log(env_info())
181
+ watcher = Watcher([], server.options)
182
+ watcher.startpool()
183
+ _LS = LocalState(watcher, server)
184
+ _LS.start_loop_thread()
185
+ else:
186
+ _LS.server = server
187
+ return _LS
188
+
189
+
190
+ def update_paths(server: CrossHairLanguageServer):
191
+ paths = []
192
+ for uri, doc in server.workspace.documents.items():
193
+ if not doc.filename.endswith(".py"):
194
+ continue
195
+ parsed = urlparse(uri)
196
+ if parsed.netloc != "":
197
+ continue
198
+ path = unquote(parsed.path)
199
+ if os.name == "nt":
200
+ while path.startswith("/"):
201
+ path = path[1:] # Remove leading slashes on windows
202
+ path = path.replace("/", "\\")
203
+ paths.append(pathlib.Path(path))
204
+ watcher = getlocalstate(server).watcher
205
+ server.show_message_log("New path set: " + repr(paths))
206
+ watcher.update_paths(paths)
207
+
208
+
209
+ def create_lsp_server(options: AnalysisOptionSet) -> CrossHairLanguageServer:
210
+
211
+ crosshair_lsp_server = CrossHairLanguageServer(options)
212
+
213
+ @crosshair_lsp_server.feature(TEXT_DOCUMENT_DID_CHANGE)
214
+ def did_change(
215
+ server: CrossHairLanguageServer, params: DidChangeTextDocumentParams
216
+ ):
217
+ # server.show_message_log("did_change")
218
+ uri = params.text_document.uri
219
+ getlocalstate(server).active_messages[uri] = None
220
+ server.publish_diagnostics(uri, [])
221
+
222
+ @crosshair_lsp_server.feature(TEXT_DOCUMENT_DID_CLOSE)
223
+ def did_close(server: CrossHairLanguageServer, params: DidCloseTextDocumentParams):
224
+ # server.show_message_log("did_close")
225
+ uri = params.text_document.uri
226
+ getlocalstate(server).active_messages[uri] = None
227
+ server.publish_diagnostics(uri, [])
228
+ update_paths(server)
229
+
230
+ @crosshair_lsp_server.feature(TEXT_DOCUMENT_DID_OPEN)
231
+ def did_open(server: CrossHairLanguageServer, params: DidOpenTextDocumentParams):
232
+ uri = params.text_document.uri
233
+ getlocalstate(server).active_messages[uri] = {}
234
+ update_paths(server)
235
+
236
+ @crosshair_lsp_server.feature(TEXT_DOCUMENT_DID_SAVE)
237
+ def did_save(server: CrossHairLanguageServer, params: DidOpenTextDocumentParams):
238
+ uri = params.text_document.uri
239
+ update_paths(server)
240
+ getlocalstate(server).active_messages[uri] = {}
241
+ server.publish_diagnostics(uri, [])
242
+
243
+ @crosshair_lsp_server.feature(SHUTDOWN)
244
+ def did_shutdown(
245
+ server: CrossHairLanguageServer,
246
+ params: Any,
247
+ ):
248
+ getlocalstate(server).shutdown()
249
+
250
+ return crosshair_lsp_server
@@ -0,0 +1,30 @@
1
+ import pytest
2
+ from lsprotocol.types import Diagnostic, DiagnosticSeverity, Position, Range
3
+
4
+ from crosshair.lsp_server import LocalState, create_lsp_server, get_diagnostic
5
+ from crosshair.options import AnalysisOptionSet
6
+ from crosshair.statespace import AnalysisMessage, MessageType
7
+ from crosshair.watcher import Watcher
8
+
9
+
10
+ @pytest.fixture
11
+ def state():
12
+ server = create_lsp_server(AnalysisOptionSet())
13
+ return LocalState(Watcher([]), server)
14
+
15
+
16
+ def test_get_diagnostic():
17
+ msg = AnalysisMessage(MessageType.POST_ERR, "exception raised", __file__, 2, 0, "")
18
+ assert get_diagnostic(msg, ["one", " two ", "three"]) == Diagnostic(
19
+ range=Range(
20
+ start=Position(line=1, character=2),
21
+ end=Position(line=1, character=5),
22
+ ),
23
+ severity=DiagnosticSeverity.Error,
24
+ message="exception raised",
25
+ source="CrossHair",
26
+ )
27
+
28
+
29
+ def test_watch_loop(state: LocalState):
30
+ state.run_watch_loop(max_watch_iterations=1)