crosshair-tool 0.0.87__cp38-cp38-musllinux_1_2_x86_64.whl → 0.0.92__cp38-cp38-musllinux_1_2_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.
Potentially problematic release.
This version of crosshair-tool might be problematic. Click here for more details.
- _crosshair_tracers.cpython-38-x86_64-linux-gnu.so +0 -0
- crosshair/__init__.py +1 -1
- crosshair/_tracers.h +6 -5
- crosshair/libimpl/builtinslib.py +4 -0
- crosshair/libimpl/builtinslib_test.py +21 -11
- crosshair/libimpl/copylib_test.py +18 -0
- crosshair/libimpl/datetimelib.py +8 -2
- crosshair/main_test.py +1 -1
- crosshair/opcode_intercept_test.py +16 -0
- crosshair/pathing_oracle.py +5 -6
- crosshair/statespace.py +66 -90
- crosshair/test_util.py +39 -15
- crosshair/test_util_test.py +10 -0
- crosshair/type_repo.py +2 -2
- crosshair/util.py +2 -0
- {crosshair_tool-0.0.87.dist-info → crosshair_tool-0.0.92.dist-info}/METADATA +1 -1
- {crosshair_tool-0.0.87.dist-info → crosshair_tool-0.0.92.dist-info}/RECORD +21 -20
- {crosshair_tool-0.0.87.dist-info → crosshair_tool-0.0.92.dist-info}/LICENSE +0 -0
- {crosshair_tool-0.0.87.dist-info → crosshair_tool-0.0.92.dist-info}/WHEEL +0 -0
- {crosshair_tool-0.0.87.dist-info → crosshair_tool-0.0.92.dist-info}/entry_points.txt +0 -0
- {crosshair_tool-0.0.87.dist-info → crosshair_tool-0.0.92.dist-info}/top_level.txt +0 -0
|
Binary file
|
crosshair/__init__.py
CHANGED
|
@@ -15,7 +15,7 @@ from crosshair.statespace import StateSpace
|
|
|
15
15
|
from crosshair.tracers import NoTracing, ResumedTracing
|
|
16
16
|
from crosshair.util import IgnoreAttempt, debug
|
|
17
17
|
|
|
18
|
-
__version__ = "0.0.
|
|
18
|
+
__version__ = "0.0.92" # Do not forget to update in setup.py!
|
|
19
19
|
__author__ = "Phillip Schanely"
|
|
20
20
|
__license__ = "MIT"
|
|
21
21
|
__status__ = "Alpha"
|
crosshair/_tracers.h
CHANGED
|
@@ -50,10 +50,11 @@ typedef struct HandlerTable {
|
|
|
50
50
|
} HandlerTable;
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
typedef struct
|
|
54
|
-
|
|
53
|
+
typedef struct FrameNextIandCallback {
|
|
54
|
+
PyFrameObject* frame;
|
|
55
|
+
int expected_i;
|
|
55
56
|
PyObject* callback;
|
|
56
|
-
}
|
|
57
|
+
} FrameNextIandCallback;
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
typedef struct CodeAndStacks {
|
|
@@ -62,7 +63,7 @@ typedef struct CodeAndStacks {
|
|
|
62
63
|
} CodeAndStacks;
|
|
63
64
|
|
|
64
65
|
|
|
65
|
-
DEFINE_VEC(
|
|
66
|
+
DEFINE_VEC(FrameNextIandCallbackVec, FrameNextIandCallback, init_framecbvec, push_framecb);
|
|
66
67
|
DEFINE_VEC(ModuleVec, PyObject*, init_modulevec, push_module);
|
|
67
68
|
DEFINE_VEC(TableVec, HandlerTable, init_tablevec, push_table_entry)
|
|
68
69
|
|
|
@@ -70,7 +71,7 @@ typedef struct CTracer {
|
|
|
70
71
|
PyObject_HEAD
|
|
71
72
|
ModuleVec modules;
|
|
72
73
|
TableVec handlers;
|
|
73
|
-
|
|
74
|
+
FrameNextIandCallbackVec postop_callbacks;
|
|
74
75
|
BOOL enabled;
|
|
75
76
|
BOOL handling;
|
|
76
77
|
BOOL trace_all_opcodes;
|
crosshair/libimpl/builtinslib.py
CHANGED
|
@@ -1425,6 +1425,10 @@ class PreciseIeeeSymbolicFloat(SymbolicFloat):
|
|
|
1425
1425
|
# __hash__ has to be explicitly reassigned because we define __eq__
|
|
1426
1426
|
__hash__ = SymbolicFloat.__hash__
|
|
1427
1427
|
|
|
1428
|
+
def __bool__(self):
|
|
1429
|
+
with NoTracing():
|
|
1430
|
+
return not SymbolicBool(z3.fpIsZero(self.var))
|
|
1431
|
+
|
|
1428
1432
|
def __ne__(self, other):
|
|
1429
1433
|
with NoTracing():
|
|
1430
1434
|
coerced = type(self)._coerce_to_smt_sort(other)
|
|
@@ -85,7 +85,6 @@ from crosshair.util import (
|
|
|
85
85
|
CrossHairValue,
|
|
86
86
|
IgnoreAttempt,
|
|
87
87
|
UnknownSatisfiability,
|
|
88
|
-
set_debug,
|
|
89
88
|
)
|
|
90
89
|
|
|
91
90
|
|
|
@@ -3618,18 +3617,29 @@ def test_deep_realization(space, typ):
|
|
|
3618
3617
|
assert concrete == symbolic
|
|
3619
3618
|
|
|
3620
3619
|
|
|
3621
|
-
def
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3620
|
+
def test_float_round_to_zero(space):
|
|
3621
|
+
space.extra(ModelingDirector).global_representations[
|
|
3622
|
+
float
|
|
3623
|
+
] = PreciseIeeeSymbolicFloat
|
|
3624
|
+
n = proxy_for_type(float, "n")
|
|
3625
|
+
d = proxy_for_type(float, "d")
|
|
3626
|
+
with ResumedTracing():
|
|
3627
|
+
space.add(d != 0.0)
|
|
3628
|
+
# This is possible for floats, but not reals:
|
|
3629
|
+
assert space.is_possible(n / d != 0.0)
|
|
3630
|
+
assert space.is_possible(n / d == 0.0)
|
|
3627
3631
|
|
|
3628
|
-
Postcondition holds in a math-sense, but not when floating point precision
|
|
3629
|
-
comes into play (e.g. it's false for 13 / 99)
|
|
3630
|
-
"""
|
|
3631
3632
|
|
|
3632
|
-
|
|
3633
|
+
def test_float_neg_zero_is_falsey(space):
|
|
3634
|
+
space.extra(ModelingDirector).global_representations[
|
|
3635
|
+
float
|
|
3636
|
+
] = PreciseIeeeSymbolicFloat
|
|
3637
|
+
x = proxy_for_type(float, "x")
|
|
3638
|
+
with ResumedTracing():
|
|
3639
|
+
space.add(x == -10.0)
|
|
3640
|
+
negzero = x / math.inf
|
|
3641
|
+
assert not space.is_possible(negzero != 0.0)
|
|
3642
|
+
assert bool(negzero) is False
|
|
3633
3643
|
|
|
3634
3644
|
|
|
3635
3645
|
def TODO_test_int_mod_float():
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import datetime
|
|
3
|
+
|
|
4
|
+
from crosshair.core import proxy_for_type
|
|
5
|
+
from crosshair.statespace import StateSpace
|
|
6
|
+
from crosshair.tracers import ResumedTracing
|
|
7
|
+
from crosshair.util import debug
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_date_copy(space: StateSpace) -> None:
|
|
11
|
+
concrete_date = datetime.date(2000, 2, 3)
|
|
12
|
+
symbolic_date = proxy_for_type(datetime.date, "d")
|
|
13
|
+
with ResumedTracing():
|
|
14
|
+
copied_symbolic = copy.deepcopy(symbolic_date)
|
|
15
|
+
copied_concrete = copy.deepcopy(concrete_date)
|
|
16
|
+
assert not space.is_possible(copied_concrete != concrete_date)
|
|
17
|
+
assert not space.is_possible(copied_concrete != datetime.date(2000, 2, 3))
|
|
18
|
+
assert not space.is_possible(copied_symbolic != symbolic_date)
|
crosshair/libimpl/datetimelib.py
CHANGED
|
@@ -855,7 +855,7 @@ class date:
|
|
|
855
855
|
|
|
856
856
|
"""
|
|
857
857
|
|
|
858
|
-
def __init__(self, year, month, day):
|
|
858
|
+
def __init__(self, year, month=None, day=None):
|
|
859
859
|
"""
|
|
860
860
|
Constructor.
|
|
861
861
|
|
|
@@ -863,7 +863,13 @@ class date:
|
|
|
863
863
|
:param month: month, starting at 1
|
|
864
864
|
:param day: day, starting at 1
|
|
865
865
|
"""
|
|
866
|
-
|
|
866
|
+
if month is None:
|
|
867
|
+
# We can receive a string/bytes single argument when unpickling a concrete date
|
|
868
|
+
with NoTracing():
|
|
869
|
+
dt = real_date(realize(year)) # type: ignore
|
|
870
|
+
year, month, day = dt.year, dt.month, dt.day
|
|
871
|
+
else:
|
|
872
|
+
year, month, day = _check_date_fields(year, month, day)
|
|
867
873
|
self._year = year
|
|
868
874
|
self._month = month
|
|
869
875
|
self._day = day
|
crosshair/main_test.py
CHANGED
|
@@ -515,7 +515,7 @@ def test_mypycrosshair_command():
|
|
|
515
515
|
capture_output=True,
|
|
516
516
|
text=True,
|
|
517
517
|
)
|
|
518
|
-
assert completion.stderr.strip() == ""
|
|
518
|
+
assert completion.stderr.strip() == "", f"stderr was '''{completion.stderr}'''"
|
|
519
519
|
if completion.returncode != 1:
|
|
520
520
|
print(completion.stdout)
|
|
521
521
|
assert False, f"Return code was {completion.returncode}"
|
|
@@ -286,3 +286,19 @@ def test_identity_operator_does_not_realize_on_differing_types():
|
|
|
286
286
|
fourty_two = 42 # assignment just to avoid lint errors
|
|
287
287
|
b1 is fourty_two
|
|
288
288
|
assert len(space.choices_made) == choices_made_at_start
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class IExplodeOnRepr:
|
|
292
|
+
def __repr__(self):
|
|
293
|
+
raise ValueError("boom")
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_postop_callback_skipped_on_exception_handler_jump(space):
|
|
297
|
+
with ResumedTracing():
|
|
298
|
+
elements = IExplodeOnRepr()
|
|
299
|
+
try:
|
|
300
|
+
ret = f"these are them: {elements!r}"
|
|
301
|
+
except ValueError: # pragma: no cover
|
|
302
|
+
ret = None
|
|
303
|
+
# need to do something(anything) with elements so that it's on the stack:
|
|
304
|
+
type(elements)
|
crosshair/pathing_oracle.py
CHANGED
|
@@ -6,7 +6,7 @@ from z3 import ExprRef # type: ignore
|
|
|
6
6
|
|
|
7
7
|
from crosshair.statespace import (
|
|
8
8
|
AbstractPathingOracle,
|
|
9
|
-
|
|
9
|
+
DetachedPathNode,
|
|
10
10
|
ModelValueNode,
|
|
11
11
|
NodeLike,
|
|
12
12
|
RootNode,
|
|
@@ -117,14 +117,13 @@ class CoveragePathingOracle(AbstractPathingOracle):
|
|
|
117
117
|
for step, node in enumerate(path[:-1]):
|
|
118
118
|
if not isinstance(node, NodeLike):
|
|
119
119
|
continue
|
|
120
|
-
node = node.simplify() # type: ignore
|
|
121
120
|
if isinstance(node, WorstResultNode):
|
|
122
121
|
key = node.stacktail
|
|
123
122
|
if (key not in leading_locs) and (not isinstance(node, ModelValueNode)):
|
|
124
123
|
self.summarized_positions[key] += Counter(leading_conditions)
|
|
125
124
|
leading_locs.append(key)
|
|
126
|
-
next_node = path[step + 1]
|
|
127
|
-
if isinstance(next_node,
|
|
125
|
+
next_node = path[step + 1]
|
|
126
|
+
if isinstance(next_node, DetachedPathNode):
|
|
128
127
|
break
|
|
129
128
|
if step + 1 < len(path):
|
|
130
129
|
(is_positive, root_expr) = node.normalized_expr
|
|
@@ -133,9 +132,9 @@ class CoveragePathingOracle(AbstractPathingOracle):
|
|
|
133
132
|
if is_positive
|
|
134
133
|
else -self.internalize(root_expr)
|
|
135
134
|
)
|
|
136
|
-
if next_node == node.positive
|
|
135
|
+
if next_node == node.positive:
|
|
137
136
|
leading_conditions.append(expr_signature)
|
|
138
|
-
elif next_node == node.negative
|
|
137
|
+
elif next_node == node.negative:
|
|
139
138
|
leading_conditions.append(-expr_signature)
|
|
140
139
|
else:
|
|
141
140
|
raise CrossHairInternal(
|
crosshair/statespace.py
CHANGED
|
@@ -290,49 +290,10 @@ class NodeLike:
|
|
|
290
290
|
"""
|
|
291
291
|
raise NotImplementedError
|
|
292
292
|
|
|
293
|
-
def is_stem(self) -> bool:
|
|
294
|
-
return False
|
|
295
|
-
|
|
296
|
-
def grow_into(self, node: _N) -> _N:
|
|
297
|
-
raise NotImplementedError
|
|
298
|
-
|
|
299
|
-
def simplify(self) -> "NodeLike":
|
|
300
|
-
return self
|
|
301
|
-
|
|
302
293
|
def stats(self) -> StateSpaceCounter:
|
|
303
294
|
raise NotImplementedError
|
|
304
295
|
|
|
305
296
|
|
|
306
|
-
class NodeStem(NodeLike):
|
|
307
|
-
evolution: Optional["SearchTreeNode"] = None
|
|
308
|
-
|
|
309
|
-
def is_exhausted(self) -> bool:
|
|
310
|
-
return False if self.evolution is None else self.evolution.is_exhausted()
|
|
311
|
-
|
|
312
|
-
def get_result(self) -> CallAnalysis:
|
|
313
|
-
return (
|
|
314
|
-
CallAnalysis(VerificationStatus.UNKNOWN)
|
|
315
|
-
if self.evolution is None
|
|
316
|
-
else self.evolution.get_result()
|
|
317
|
-
)
|
|
318
|
-
|
|
319
|
-
def is_stem(self) -> bool:
|
|
320
|
-
return self.evolution is None or self.evolution.is_stem()
|
|
321
|
-
|
|
322
|
-
def grow_into(self, node: _N) -> _N:
|
|
323
|
-
self.evolution = node
|
|
324
|
-
return node
|
|
325
|
-
|
|
326
|
-
def simplify(self):
|
|
327
|
-
return self if self.evolution is None else self.evolution
|
|
328
|
-
|
|
329
|
-
def stats(self) -> StateSpaceCounter:
|
|
330
|
-
return StateSpaceCounter() if self.evolution is None else self.evolution.stats()
|
|
331
|
-
|
|
332
|
-
def __repr__(self) -> str:
|
|
333
|
-
return "NodeStem()"
|
|
334
|
-
|
|
335
|
-
|
|
336
297
|
class SearchTreeNode(NodeLike):
|
|
337
298
|
"""A node in the execution path tree."""
|
|
338
299
|
|
|
@@ -364,6 +325,27 @@ class SearchTreeNode(NodeLike):
|
|
|
364
325
|
raise NotImplementedError
|
|
365
326
|
|
|
366
327
|
|
|
328
|
+
class NodeStem(NodeLike):
|
|
329
|
+
def __init__(self, parent: SearchTreeNode, parent_attr_name: str):
|
|
330
|
+
self.parent = parent
|
|
331
|
+
self.parent_attr_name = parent_attr_name
|
|
332
|
+
|
|
333
|
+
def grow(self, node: SearchTreeNode):
|
|
334
|
+
setattr(self.parent, self.parent_attr_name, node)
|
|
335
|
+
|
|
336
|
+
def is_exhausted(self) -> bool:
|
|
337
|
+
return False
|
|
338
|
+
|
|
339
|
+
def get_result(self) -> CallAnalysis:
|
|
340
|
+
return CallAnalysis(VerificationStatus.UNKNOWN)
|
|
341
|
+
|
|
342
|
+
def stats(self) -> StateSpaceCounter:
|
|
343
|
+
return StateSpaceCounter()
|
|
344
|
+
|
|
345
|
+
def __repr__(self) -> str:
|
|
346
|
+
return "NodeStem()"
|
|
347
|
+
|
|
348
|
+
|
|
367
349
|
def solver_is_sat(solver, *exprs) -> bool:
|
|
368
350
|
ret = solver.check(*exprs)
|
|
369
351
|
if ret == z3.unknown:
|
|
@@ -411,7 +393,7 @@ class SinglePathNode(SearchTreeNode):
|
|
|
411
393
|
|
|
412
394
|
def __init__(self, decision: bool):
|
|
413
395
|
self.decision = decision
|
|
414
|
-
self.child = NodeStem()
|
|
396
|
+
self.child = NodeStem(self, "child")
|
|
415
397
|
self._random = newrandom()
|
|
416
398
|
|
|
417
399
|
def choose(
|
|
@@ -420,7 +402,7 @@ class SinglePathNode(SearchTreeNode):
|
|
|
420
402
|
return (self.decision, 1.0, self.child)
|
|
421
403
|
|
|
422
404
|
def compute_result(self, leaf_analysis: CallAnalysis) -> Tuple[CallAnalysis, bool]:
|
|
423
|
-
self.child
|
|
405
|
+
assert isinstance(self.child, SearchTreeNode)
|
|
424
406
|
return (self.child.get_result(), self.child.is_exhausted())
|
|
425
407
|
|
|
426
408
|
def stats(self) -> StateSpaceCounter:
|
|
@@ -449,7 +431,7 @@ class RootNode(SinglePathNode):
|
|
|
449
431
|
self.iteration = 0
|
|
450
432
|
|
|
451
433
|
|
|
452
|
-
class
|
|
434
|
+
class DetachedPathNode(SinglePathNode):
|
|
453
435
|
def __init__(self):
|
|
454
436
|
super().__init__(True)
|
|
455
437
|
# Seems like `exhausted` should be True, but we set to False until we can
|
|
@@ -459,7 +441,6 @@ class DeatchedPathNode(SinglePathNode):
|
|
|
459
441
|
self._stats = None
|
|
460
442
|
|
|
461
443
|
def compute_result(self, leaf_analysis: CallAnalysis) -> Tuple[CallAnalysis, bool]:
|
|
462
|
-
self.child = self.child.simplify()
|
|
463
444
|
return (leaf_analysis, True)
|
|
464
445
|
|
|
465
446
|
def stats(self) -> StateSpaceCounter:
|
|
@@ -494,8 +475,8 @@ class RandomizedBinaryPathNode(BinaryPathNode):
|
|
|
494
475
|
def __init__(self, rand: random.Random):
|
|
495
476
|
super().__init__()
|
|
496
477
|
self._random = rand
|
|
497
|
-
self.positive = NodeStem()
|
|
498
|
-
self.negative = NodeStem()
|
|
478
|
+
self.positive = NodeStem(self, "positive")
|
|
479
|
+
self.negative = NodeStem(self, "negative")
|
|
499
480
|
|
|
500
481
|
def probability_true(
|
|
501
482
|
self, space: "StateSpace", requested_probability: Optional[float] = None
|
|
@@ -520,10 +501,6 @@ class RandomizedBinaryPathNode(BinaryPathNode):
|
|
|
520
501
|
else:
|
|
521
502
|
return (positive_ok, 1.0, self.positive if positive_ok else self.negative)
|
|
522
503
|
|
|
523
|
-
def _simplify(self) -> None:
|
|
524
|
-
self.positive = self.positive.simplify()
|
|
525
|
-
self.negative = self.negative.simplify()
|
|
526
|
-
|
|
527
504
|
|
|
528
505
|
class ParallelNode(RandomizedBinaryPathNode):
|
|
529
506
|
"""Choose either path; the first complete result will be used."""
|
|
@@ -537,7 +514,6 @@ class ParallelNode(RandomizedBinaryPathNode):
|
|
|
537
514
|
return f"ParallelNode(false_pct={self._false_probability}, {self._desc})"
|
|
538
515
|
|
|
539
516
|
def compute_result(self, leaf_analysis: CallAnalysis) -> Tuple[CallAnalysis, bool]:
|
|
540
|
-
self._simplify()
|
|
541
517
|
positive, negative = self.positive, self.negative
|
|
542
518
|
pos_exhausted = positive.is_exhausted()
|
|
543
519
|
neg_exhausted = negative.is_exhausted()
|
|
@@ -655,7 +631,6 @@ class WorstResultNode(RandomizedBinaryPathNode):
|
|
|
655
631
|
)
|
|
656
632
|
|
|
657
633
|
def compute_result(self, leaf_analysis: CallAnalysis) -> Tuple[CallAnalysis, bool]:
|
|
658
|
-
self._simplify()
|
|
659
634
|
positive, negative = self.positive, self.negative
|
|
660
635
|
exhausted = self._is_exhausted()
|
|
661
636
|
if node_status(positive) == VerificationStatus.REFUTED or (
|
|
@@ -697,7 +672,6 @@ class ModelValueNode(WorstResultNode):
|
|
|
697
672
|
|
|
698
673
|
def debug_path_tree(node, highlights, prefix="") -> List[str]:
|
|
699
674
|
highlighted = node in highlights
|
|
700
|
-
node = node.simplify()
|
|
701
675
|
highlighted |= node in highlights
|
|
702
676
|
if isinstance(node, BinaryPathNode):
|
|
703
677
|
if isinstance(node, WorstResultNode) and node.forced_path is not None:
|
|
@@ -806,23 +780,27 @@ class StateSpace:
|
|
|
806
780
|
return value # type: ignore
|
|
807
781
|
|
|
808
782
|
def stats_lookahead(self) -> Tuple[StateSpaceCounter, StateSpaceCounter]:
|
|
809
|
-
node = self._search_position
|
|
810
|
-
if node
|
|
783
|
+
node = self._search_position
|
|
784
|
+
if isinstance(node, NodeStem):
|
|
811
785
|
return (StateSpaceCounter(), StateSpaceCounter())
|
|
812
|
-
assert isinstance(
|
|
813
|
-
node, BinaryPathNode
|
|
814
|
-
), f"node {node} {node.is_stem()} is not a binarypathnode"
|
|
786
|
+
assert isinstance(node, BinaryPathNode), f"node {node} is not a binarypathnode"
|
|
815
787
|
return node.stats_lookahead()
|
|
816
788
|
|
|
789
|
+
def grow_into(self, node: _N) -> _N:
|
|
790
|
+
assert isinstance(self._search_position, NodeStem)
|
|
791
|
+
self._search_position.grow(node)
|
|
792
|
+
node.iteration = self._root.iteration
|
|
793
|
+
self._search_position = node
|
|
794
|
+
return node
|
|
795
|
+
|
|
817
796
|
def fork_parallel(self, false_probability: float, desc: str = "") -> bool:
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
)
|
|
797
|
+
node = self._search_position
|
|
798
|
+
if isinstance(node, NodeStem):
|
|
799
|
+
node = self.grow_into(ParallelNode(self._random, false_probability, desc))
|
|
800
|
+
node.stacktail = self.gen_stack_descriptions()
|
|
822
801
|
assert isinstance(node, ParallelNode)
|
|
823
802
|
self._search_position = node
|
|
824
803
|
else:
|
|
825
|
-
node = self._search_position.simplify()
|
|
826
804
|
if not isinstance(node, ParallelNode):
|
|
827
805
|
self.raise_not_deterministic(
|
|
828
806
|
node, "Wrong node type (expected ParallelNode)"
|
|
@@ -878,33 +856,34 @@ class StateSpace:
|
|
|
878
856
|
# NOTE: format_stack() is more human readable, but it pulls source file contents,
|
|
879
857
|
# so it is (1) slow, and (2) unstable when source code changes while we are checking.
|
|
880
858
|
stacktail = self.gen_stack_descriptions()
|
|
881
|
-
if self._search_position
|
|
882
|
-
|
|
883
|
-
# to think about how mutating an existing path branch would work:
|
|
884
|
-
self.check_timeout()
|
|
885
|
-
node = self._search_position.grow_into(
|
|
886
|
-
WorstResultNode(self._random, expr, self.solver)
|
|
887
|
-
)
|
|
888
|
-
node.iteration = self._root.iteration
|
|
889
|
-
node.stacktail = stacktail
|
|
890
|
-
else:
|
|
891
|
-
node = self._search_position.simplify() # type: ignore
|
|
859
|
+
if isinstance(self._search_position, SearchTreeNode):
|
|
860
|
+
node = self._search_position
|
|
892
861
|
not_deterministic_reason = (
|
|
893
862
|
(
|
|
894
863
|
(not isinstance(node, WorstResultNode))
|
|
895
|
-
and "Wrong node type (expected WorstResultNode)"
|
|
864
|
+
and f"Wrong node type (is {name_of_type(type(node))}, expected WorstResultNode)"
|
|
896
865
|
)
|
|
897
866
|
# TODO: Not clear whether we want this stack trace check.
|
|
898
867
|
# A stack change usually indicates a serious problem, but not 100% of the time.
|
|
899
868
|
# Keeping it would mean that we fail earlier.
|
|
900
869
|
# But also see https://github.com/HypothesisWorks/hypothesis/pull/4034#issuecomment-2606415404
|
|
901
870
|
# or (node.stacktail != stacktail and "Stack trace changed")
|
|
902
|
-
or (
|
|
871
|
+
or (
|
|
872
|
+
(hasattr(node, "expr") and (not z3.eq(node.expr, expr)))
|
|
873
|
+
and "SMT expression changed"
|
|
874
|
+
)
|
|
903
875
|
)
|
|
904
876
|
if not_deterministic_reason:
|
|
905
877
|
self.raise_not_deterministic(
|
|
906
878
|
node, not_deterministic_reason, expr=expr, stacktail=stacktail
|
|
907
879
|
)
|
|
880
|
+
else:
|
|
881
|
+
# We only allow time outs at stems - that's because we don't want
|
|
882
|
+
# to think about how mutating an existing path branch would work:
|
|
883
|
+
self.check_timeout()
|
|
884
|
+
node = self.grow_into(WorstResultNode(self._random, expr, self.solver))
|
|
885
|
+
node.stacktail = stacktail
|
|
886
|
+
|
|
908
887
|
self._search_position = node
|
|
909
888
|
choose_true, chosen_probability, stem = node.choose(
|
|
910
889
|
self, probability_true=probability_true
|
|
@@ -937,10 +916,10 @@ class StateSpace:
|
|
|
937
916
|
currently_handling: Optional[BaseException] = None,
|
|
938
917
|
) -> NoReturn:
|
|
939
918
|
lines = ["*** Begin Not Deterministic Debug ***"]
|
|
940
|
-
if
|
|
941
|
-
lines.append(f"Previous iteration: {node.iteration}")
|
|
919
|
+
if getattr(node, "iteration", None) is not None:
|
|
920
|
+
lines.append(f"Previous iteration: {node.iteration}") # type: ignore
|
|
942
921
|
if hasattr(node, "expr"):
|
|
943
|
-
lines.append(f"Previous SMT expression: {node.expr}")
|
|
922
|
+
lines.append(f"Previous SMT expression: {node.expr}") # type: ignore
|
|
944
923
|
if expr is not None:
|
|
945
924
|
lines.append(f"Current SMT expression: {expr}")
|
|
946
925
|
if not stacktail:
|
|
@@ -966,11 +945,11 @@ class StateSpace:
|
|
|
966
945
|
def find_model_value(self, expr: z3.ExprRef) -> Any:
|
|
967
946
|
with NoTracing():
|
|
968
947
|
while True:
|
|
969
|
-
if self._search_position
|
|
970
|
-
self._search_position = self.
|
|
948
|
+
if isinstance(self._search_position, NodeStem):
|
|
949
|
+
self._search_position = self.grow_into(
|
|
971
950
|
ModelValueNode(self._random, expr, self.solver)
|
|
972
951
|
)
|
|
973
|
-
node = self._search_position
|
|
952
|
+
node = self._search_position
|
|
974
953
|
if isinstance(node, SearchLeaf):
|
|
975
954
|
raise CrossHairInternal(
|
|
976
955
|
f"Cannot use symbolics; path is already terminated"
|
|
@@ -1109,14 +1088,14 @@ class StateSpace:
|
|
|
1109
1088
|
if not prefer_true(check_ret):
|
|
1110
1089
|
raise IgnoreAttempt("deferred assumption failed: " + description)
|
|
1111
1090
|
self.is_detached = True
|
|
1112
|
-
if not self._search_position
|
|
1091
|
+
if not isinstance(self._search_position, NodeStem):
|
|
1113
1092
|
self.raise_not_deterministic(
|
|
1114
1093
|
self._search_position,
|
|
1115
|
-
f"Expect to detach path at a stem node, not at this node: {self._search_position
|
|
1094
|
+
f"Expect to detach path at a stem node, not at this node: {self._search_position}",
|
|
1116
1095
|
currently_handling=currently_handling,
|
|
1117
1096
|
)
|
|
1118
|
-
node = self.
|
|
1119
|
-
assert node.child
|
|
1097
|
+
node = self.grow_into(DetachedPathNode())
|
|
1098
|
+
assert isinstance(node.child, NodeStem)
|
|
1120
1099
|
self.choices_made.append(node)
|
|
1121
1100
|
self._search_position = node.child
|
|
1122
1101
|
debug("Detached from search tree")
|
|
@@ -1130,12 +1109,9 @@ class StateSpace:
|
|
|
1130
1109
|
self, analysis: CallAnalysis
|
|
1131
1110
|
) -> Tuple[Optional[CallAnalysis], bool]:
|
|
1132
1111
|
# In some cases, we might ignore an attempt while not at a leaf.
|
|
1133
|
-
if self._search_position
|
|
1134
|
-
self._search_position = self.
|
|
1135
|
-
SearchLeaf(analysis)
|
|
1136
|
-
)
|
|
1112
|
+
if isinstance(self._search_position, NodeStem):
|
|
1113
|
+
self._search_position = self.grow_into(SearchLeaf(analysis))
|
|
1137
1114
|
else:
|
|
1138
|
-
self._search_position = self._search_position.simplify()
|
|
1139
1115
|
assert isinstance(self._search_position, SearchTreeNode)
|
|
1140
1116
|
self._search_position.exhausted = True
|
|
1141
1117
|
self._search_position.result = analysis
|
crosshair/test_util.py
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import pathlib
|
|
2
2
|
import sys
|
|
3
|
-
from collections.abc import Container
|
|
4
3
|
from copy import deepcopy
|
|
5
4
|
from dataclasses import dataclass, replace
|
|
6
|
-
from
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from math import isnan
|
|
7
|
+
from numbers import Real
|
|
8
|
+
from typing import (
|
|
9
|
+
Callable,
|
|
10
|
+
Collection,
|
|
11
|
+
Iterable,
|
|
12
|
+
List,
|
|
13
|
+
Mapping,
|
|
14
|
+
Optional,
|
|
15
|
+
Sequence,
|
|
16
|
+
Set,
|
|
17
|
+
Tuple,
|
|
18
|
+
)
|
|
7
19
|
|
|
8
20
|
from crosshair.core import (
|
|
9
21
|
AnalysisMessage,
|
|
@@ -29,6 +41,13 @@ from crosshair.util import (
|
|
|
29
41
|
ComparableLists = Tuple[List, List]
|
|
30
42
|
|
|
31
43
|
|
|
44
|
+
class _Missing:
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_MISSING = _Missing()
|
|
49
|
+
|
|
50
|
+
|
|
32
51
|
def simplefs(path: pathlib.Path, files: dict) -> None:
|
|
33
52
|
for name, contents in files.items():
|
|
34
53
|
subpath = path / name
|
|
@@ -123,34 +142,39 @@ def check_messages(checkables: Iterable[Checkable], **kw) -> ComparableLists:
|
|
|
123
142
|
return (msgs, [AnalysisMessage(**kw)])
|
|
124
143
|
|
|
125
144
|
|
|
126
|
-
|
|
145
|
+
_NAN_ABLE = (Decimal, Real)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def flexible_equal(a: object, b: object) -> bool:
|
|
127
149
|
if a is b:
|
|
128
150
|
return True
|
|
129
151
|
if type(a) is type(b) and type(a).__eq__ is object.__eq__:
|
|
130
152
|
# If types match and it uses identity-equals, we can't do much. Assume equal.
|
|
131
153
|
return True
|
|
132
|
-
if a
|
|
154
|
+
if isinstance(a, _NAN_ABLE) and isinstance(b, _NAN_ABLE) and isnan(a) and isnan(b):
|
|
133
155
|
return True
|
|
134
156
|
if (
|
|
135
157
|
is_iterable(a)
|
|
136
|
-
and not isinstance(a,
|
|
158
|
+
and not isinstance(a, Collection)
|
|
137
159
|
and is_iterable(b)
|
|
138
|
-
and not isinstance(b,
|
|
160
|
+
and not isinstance(b, Collection)
|
|
139
161
|
): # unsized iterables compare by contents
|
|
140
|
-
a, b = list(a), list(b)
|
|
141
|
-
if
|
|
162
|
+
a, b = list(a), list(b) # type: ignore
|
|
163
|
+
if (
|
|
164
|
+
type(a) == type(b)
|
|
165
|
+
and isinstance(a, Collection)
|
|
166
|
+
and not isinstance(a, (str, bytes, Set))
|
|
167
|
+
):
|
|
142
168
|
# Recursively apply flexible_equal for most containers:
|
|
169
|
+
if len(a) != len(b): # type: ignore
|
|
170
|
+
return False
|
|
143
171
|
if isinstance(a, Mapping):
|
|
144
|
-
if len(a) != len(b):
|
|
145
|
-
return False
|
|
146
172
|
for k, v in a.items():
|
|
147
|
-
if not flexible_equal(v, b
|
|
173
|
+
if not flexible_equal(v, b.get(k, _MISSING)): # type: ignore
|
|
148
174
|
return False
|
|
149
175
|
return True
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return False
|
|
153
|
-
return all(flexible_equal(ai, bi) for ai, bi in zip(a, b))
|
|
176
|
+
else:
|
|
177
|
+
return all(flexible_equal(ai, bi) for ai, bi in zip(a, b)) # type: ignore
|
|
154
178
|
|
|
155
179
|
return a == b
|
|
156
180
|
|
crosshair/test_util_test.py
CHANGED
|
@@ -14,3 +14,13 @@ def test_flexible_equal():
|
|
|
14
14
|
assert flexible_equal(gen(), iter([11, 22]))
|
|
15
15
|
assert not flexible_equal(gen(), iter([11, 22, 33]))
|
|
16
16
|
assert not flexible_equal(gen(), iter([11]))
|
|
17
|
+
|
|
18
|
+
ordered_set_1 = {10_000, 20_000} | {30_000}
|
|
19
|
+
ordered_set_2 = {30_000, 20_000} | {10_000}
|
|
20
|
+
assert list(ordered_set_1) != list(ordered_set_2) # (different orderings)
|
|
21
|
+
assert flexible_equal(ordered_set_1, ordered_set_2)
|
|
22
|
+
|
|
23
|
+
ordered_dict_1 = {1: 2, 3: 4}
|
|
24
|
+
ordered_dict_2 = {3: 4, 1: 2}
|
|
25
|
+
assert list(ordered_dict_1.items()) != list(ordered_dict_2.items())
|
|
26
|
+
assert flexible_equal(ordered_dict_1, ordered_dict_2)
|
crosshair/type_repo.py
CHANGED
|
@@ -20,6 +20,8 @@ _IGNORED_MODULE_ROOTS = {
|
|
|
20
20
|
"pkg_resources",
|
|
21
21
|
"pytest",
|
|
22
22
|
"py", # (part of pytest)
|
|
23
|
+
# Disabled because the import attempt can cause problems:
|
|
24
|
+
"numpy", # importing numpy/testing/_private/utils.py attempts a subprocess call
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
|
|
@@ -59,8 +61,6 @@ def get_subclass_map() -> Dict[type, List[type]]:
|
|
|
59
61
|
if module_name.split(".", 1)[0] in _IGNORED_MODULE_ROOTS:
|
|
60
62
|
continue
|
|
61
63
|
if module is None:
|
|
62
|
-
# We set the internal _datetime module to None, ensuring that
|
|
63
|
-
# we don't load the C implementation.
|
|
64
64
|
continue
|
|
65
65
|
try:
|
|
66
66
|
module_classes = inspect.getmembers(module, inspect.isclass)
|
crosshair/util.py
CHANGED
|
@@ -54,6 +54,8 @@ from crosshair.tracers import COMPOSITE_TRACER, NoTracing, ResumedTracing, is_tr
|
|
|
54
54
|
_DEBUG_STREAM: Optional[TextIO] = None
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
# NOTE: many of these is_* functions should use a TypeGuard in 3.10 (or even TypeIs in 3.13)
|
|
58
|
+
|
|
57
59
|
if sys.version_info >= (3, 12):
|
|
58
60
|
from collections.abc import Buffer
|
|
59
61
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
_crosshair_tracers.cpython-38-x86_64-linux-gnu.so,sha256=
|
|
2
|
-
crosshair/__init__.py,sha256=
|
|
1
|
+
_crosshair_tracers.cpython-38-x86_64-linux-gnu.so,sha256=I2PQ2mjtjrfVX3uIISbP3TZ2i1ZIigIgZIWJxRFSX2Q,75984
|
|
2
|
+
crosshair/__init__.py,sha256=pMTYFz7JCsH9F93C11wN9P-sZKW9G1H1Dxfu_XdZHkg,936
|
|
3
3
|
crosshair/__main__.py,sha256=zw9Ylf8v2fGocE57o4FqvD0lc7U4Ld2GbeCGxRWrpqo,252
|
|
4
4
|
crosshair/_mark_stacks.h,sha256=j86qubOUvVhoR19d74iQ084RrTEq8M6oT4wJsGQUySY,28678
|
|
5
5
|
crosshair/_preliminaries_test.py,sha256=r2PohNNMfIkDqsnvI6gKlJTbwBaZA9NQJueQfJMN2Eo,504
|
|
6
|
-
crosshair/_tracers.h,sha256=
|
|
6
|
+
crosshair/_tracers.h,sha256=QFBklLqMWmIpUzBIn_A4SKdpjwHs-N7ttx-F5jtWWCQ,2174
|
|
7
7
|
crosshair/_tracers_pycompat.h,sha256=6IYnbQxrYkhBsLDAHSX25DPOwo1oYHCZUVWZ8c7YCnQ,14356
|
|
8
8
|
crosshair/_tracers_test.py,sha256=KpCGspjOUeZuhwTYgn_RxI4M4wMbT5rldusFDgneQ6M,3596
|
|
9
9
|
crosshair/abcstring.py,sha256=ROU8LzS7kfEU2L_D3QfhVxIjrYr1VctwUWfylC7KlCc,6549
|
|
@@ -32,11 +32,11 @@ crosshair/fuzz_core_test.py,sha256=q7WsZt6bj5OJrXaVsT3JaRYWWnL8X_1flSfty4Z7CcA,1
|
|
|
32
32
|
crosshair/lsp_server.py,sha256=j7SX4pdVwa2MrtkNIjajLilzl5CZTY6PrBQsa26cdNo,8670
|
|
33
33
|
crosshair/lsp_server_test.py,sha256=7LO1Qqxkper3Xt2krgOlGqF1O_uDObo76o4FZbIqykY,969
|
|
34
34
|
crosshair/main.py,sha256=6RXjj1FIyBK6i6xOx-bs-CsHLgOZcc9hL3U1JRwxpA0,34378
|
|
35
|
-
crosshair/main_test.py,sha256=
|
|
35
|
+
crosshair/main_test.py,sha256=eEoK4LHKSaX-6iJx3tAVbwCaBJc0ri3VoCPtLD5em2U,14424
|
|
36
36
|
crosshair/objectproxy.py,sha256=Uc6mNkJBInvETGWBHI10GSNEIrBYDIxlAZ30M3PAIhQ,8935
|
|
37
37
|
crosshair/objectproxy_test.py,sha256=KykRJLSHCDA7jb_XPBDhXHnS6Q1fG4oIJ579CeSEz3k,567
|
|
38
38
|
crosshair/opcode_intercept.py,sha256=z4Yb9prYE2UK21AxhjAeXyXAk5IriDuCSSCeNhbDu2A,21880
|
|
39
|
-
crosshair/opcode_intercept_test.py,sha256=
|
|
39
|
+
crosshair/opcode_intercept_test.py,sha256=Si3rJQR5cs5d4uB8uwE2K8MjP8rE1a4yHkjXzhfS10A,9241
|
|
40
40
|
crosshair/options.py,sha256=htQNgnrpoRjSNq6rfLBAF8nos-NNIwmP6tQYyI8ugsM,6775
|
|
41
41
|
crosshair/options_test.py,sha256=lzA-XtwEwQPa4wV1wwhCRKhyLOvIhThU9WK5QRaRbxQ,379
|
|
42
42
|
crosshair/patch_equivalence_test.py,sha256=mvYpsbHZS2AiQra-jK8T8QywAG1gNNCUNQPPWI9k5t8,2506
|
|
@@ -44,7 +44,7 @@ crosshair/path_cover.py,sha256=wV0Vy8IPDzqXQ2VI8a94FxltS9p-Y1oF17OKePjvpgs,6710
|
|
|
44
44
|
crosshair/path_cover_test.py,sha256=U46zw4-m7yAXhu8-3Xnhvf-_9Ov5ivfCAm5euGwpRFA,4089
|
|
45
45
|
crosshair/path_search.py,sha256=wwZjp-3E4dENnJa6BlnSq8FARkIx0PyUYc7kvH32A2k,5588
|
|
46
46
|
crosshair/path_search_test.py,sha256=7cqzAMXUYAtA00mq9XR5AaZChqeQyXyCfuuv53_51pk,1692
|
|
47
|
-
crosshair/pathing_oracle.py,sha256=
|
|
47
|
+
crosshair/pathing_oracle.py,sha256=3M93zXMorWr8m5c1KM2zGU5X1M3lcV-AIw0lX74eN00,9219
|
|
48
48
|
crosshair/pure_importer.py,sha256=-t4eowrZOQmfqK1N2tjI5POoaxRGavytwMmbRivelFg,878
|
|
49
49
|
crosshair/pure_importer_test.py,sha256=Xjtlwn1mj7g-6VA87lrvzfUADCjlmn1wgHtbrnc0uuY,421
|
|
50
50
|
crosshair/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -54,18 +54,18 @@ crosshair/simplestructs.py,sha256=CiZSuHH_j_bYitaW-n7vWd_42xSyV6Jh8es3BQLlcHk,34
|
|
|
54
54
|
crosshair/simplestructs_test.py,sha256=6uDdrSISHLhwnFuESkR8mUGw7m1llM6vCNDFChkfSs8,8639
|
|
55
55
|
crosshair/smtlib.py,sha256=hh-P32KHoH9BCq3oDYGp2PfOeOb8CwDj8tTkgqroLD8,689
|
|
56
56
|
crosshair/smtlib_test.py,sha256=edzEn19u2YYHxSzG9RrMiu2HTiEexAuehC3IlG9LuJM,511
|
|
57
|
-
crosshair/statespace.py,sha256=
|
|
57
|
+
crosshair/statespace.py,sha256=TVjUhFZU0x72oLPN7-eRu7PoMh1OiQOy0uMASsFb_Qw,41236
|
|
58
58
|
crosshair/statespace_test.py,sha256=LOblIarBbcB9oD_gVR5kK_4P2PWQymVGgJr3wNqP3Fs,2621
|
|
59
59
|
crosshair/stubs_parser.py,sha256=rlBTQus5BlZ3Ygg6Xzk5dbQbDtRpv6w9i2HQmGrPVmc,14240
|
|
60
60
|
crosshair/stubs_parser_test.py,sha256=0itTT0Udul_51RJXNv6KB97z44gYze6NZfKJL7yIDzA,1228
|
|
61
|
-
crosshair/test_util.py,sha256=
|
|
62
|
-
crosshair/test_util_test.py,sha256=
|
|
61
|
+
crosshair/test_util.py,sha256=D9-f-DdzJemfAUkQL0cwKxPL8RZ-5gkVmghyRcKlBJI,10367
|
|
62
|
+
crosshair/test_util_test.py,sha256=_r8DtAI5b1Yn1ruv9o51FWHmARII3-WDkWWnnY1iaAw,943
|
|
63
63
|
crosshair/tracers.py,sha256=_jaSDgZ_pYdqacWE_msXn7W7CoSdQ_-7hlrxa891oHo,17139
|
|
64
64
|
crosshair/tracers_test.py,sha256=EBK_ZCy2MsxqmEaGjo0uw9zAztW9O6fhCW_0PJxyTS8,3270
|
|
65
|
-
crosshair/type_repo.py,sha256=
|
|
65
|
+
crosshair/type_repo.py,sha256=x_eK-YlcHv_dxDKy6m7ty0zNO6y058o3r6QJ55RcG3s,4664
|
|
66
66
|
crosshair/unicode_categories.py,sha256=g4pnUPanx8KkpwI06ZUGx8GR8Myruf_EpTjyti_V4z8,333519
|
|
67
67
|
crosshair/unicode_categories_test.py,sha256=ZAU37IDGm9PDvwy_CGFcrF9Waa8JuUNdI4aq74wkB6c,739
|
|
68
|
-
crosshair/util.py,sha256=
|
|
68
|
+
crosshair/util.py,sha256=sIfaKFvNHR5w5VCno2d15d_LyfwoewKqRzJ_3ZNV-Vc,22218
|
|
69
69
|
crosshair/util_test.py,sha256=_KTQ0O4cLhF1pAeB8Y8Cyqbd0UyZf5KxJUaiA-ew-tE,4676
|
|
70
70
|
crosshair/watcher.py,sha256=kCCMlLe2KhW5MbEbMmixNRjRAvu5CypIAGd1V_YZ9QM,10048
|
|
71
71
|
crosshair/watcher_test.py,sha256=Ef1YSwy68wWPR5nPjwvEKPqxltI9pE9lTbnesmDy3Bk,2764
|
|
@@ -100,16 +100,17 @@ crosshair/libimpl/binascii_ch_test.py,sha256=hFqSfF1Q8jl2LNBIWaQ6vBJIIshPOmSwrR0
|
|
|
100
100
|
crosshair/libimpl/binascii_test.py,sha256=LOBqLAJ77Kx8vorjVTaT3X0Z93zw4P5BvwUapMCiSLg,1970
|
|
101
101
|
crosshair/libimpl/binasciilib.py,sha256=9w4C37uxRNOmz9EUuhJduHlMKn0f7baY5fwwdvx1uto,5070
|
|
102
102
|
crosshair/libimpl/bisectlib_test.py,sha256=ZQtYmBYD0Pb1IiFelsgdvqyeUMKaqaDb1BRb87LTSbI,753
|
|
103
|
-
crosshair/libimpl/builtinslib.py,sha256=
|
|
103
|
+
crosshair/libimpl/builtinslib.py,sha256=0NRP8l_weL0kh050cOKMrqK8y-glh3NySYLJYjdHVz8,171622
|
|
104
104
|
crosshair/libimpl/builtinslib_ch_test.py,sha256=W4wWapqlxSjsC5XgREfgxS9e_iwKxgNQhbFE3umUfNI,30504
|
|
105
|
-
crosshair/libimpl/builtinslib_test.py,sha256=
|
|
105
|
+
crosshair/libimpl/builtinslib_test.py,sha256=Y_jRe5J6pPaJ_Nuk1lJ1biP5yczsfCj--NgNhwcbAfQ,90654
|
|
106
106
|
crosshair/libimpl/codecslib.py,sha256=lB87T1EYSBh4JXaqzjSpQG9CMfKtgckwA7f6OIR0S-Q,2668
|
|
107
107
|
crosshair/libimpl/codecslib_test.py,sha256=8K64njhxnTe7qLh-_onARNsm_qSG7BWM5dXgADYi54A,2536
|
|
108
108
|
crosshair/libimpl/collectionslib.py,sha256=VuDIOUOGORiUT9J0tDVECr6oC-yUR3WAiFnMeVGzi-o,8583
|
|
109
109
|
crosshair/libimpl/collectionslib_ch_test.py,sha256=PYitnmXXEZfm25FzBodEX1hOpwqnDspbqt5aqzeVar0,5855
|
|
110
110
|
crosshair/libimpl/collectionslib_test.py,sha256=3h7XTToKWauMhjrEwLXVI0jT8FZKBlkvlw0oiPMmuKM,9164
|
|
111
111
|
crosshair/libimpl/copylib.py,sha256=icHJWeFK_tsPSdJhvlS5fVcBwlh0uJh0nzXsRJCGtzs,527
|
|
112
|
-
crosshair/libimpl/
|
|
112
|
+
crosshair/libimpl/copylib_test.py,sha256=VpS9ICvIjzKw0kccsl1v5FB1chHHEIWdOAerEQ_pGMw,705
|
|
113
|
+
crosshair/libimpl/datetimelib.py,sha256=kVWcoHMX-dPW-un7XoEIURfWlVOst6h_JFmjrhFiE1U,79168
|
|
113
114
|
crosshair/libimpl/datetimelib_ch_test.py,sha256=r_V9H34a4POlEeffi46mEtVI9ek80DHaEOiCTKON9Us,9283
|
|
114
115
|
crosshair/libimpl/datetimelib_test.py,sha256=v9Cg512AXIGy7_dsN2y_ZD7W7fqfSSz07eiCUZukM60,2659
|
|
115
116
|
crosshair/libimpl/decimallib.py,sha256=zBKDrDZcg45oCKUkf6SIDuVpjA1Web7tD1MEQo5cjxI,177348
|
|
@@ -166,9 +167,9 @@ crosshair/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
166
167
|
crosshair/tools/check_help_in_doc.py,sha256=P21AH3mYrTVuBgWD6v65YXqBqmqpQDUTQeoZ10rB6TU,8235
|
|
167
168
|
crosshair/tools/check_init_and_setup_coincide.py,sha256=kv61bXqKSKF_5J-kLNEhCrCPyszg7iZQWDu_Scnec98,3502
|
|
168
169
|
crosshair/tools/generate_demo_table.py,sha256=0SeO0xQdiT-mbLNHt4rYL0wcc2DMh0v3qtzBdoQonDk,3831
|
|
169
|
-
crosshair_tool-0.0.
|
|
170
|
-
crosshair_tool-0.0.
|
|
171
|
-
crosshair_tool-0.0.
|
|
172
|
-
crosshair_tool-0.0.
|
|
173
|
-
crosshair_tool-0.0.
|
|
174
|
-
crosshair_tool-0.0.
|
|
170
|
+
crosshair_tool-0.0.92.dist-info/LICENSE,sha256=NVyMvNqn1pH6RSHs6RWRcJyJvORnpgGFBlF73buqYJ0,4459
|
|
171
|
+
crosshair_tool-0.0.92.dist-info/METADATA,sha256=RUQQoXFwTyS_7UN78XYKSPjCDReOdnGfYkZibT-jZKU,6505
|
|
172
|
+
crosshair_tool-0.0.92.dist-info/WHEEL,sha256=AtKzrIIwO6LyEQPNa-CKogjoLSeXFnST8-hqmpwwZQA,110
|
|
173
|
+
crosshair_tool-0.0.92.dist-info/entry_points.txt,sha256=u5FIPVn1jqn4Kzg5K_iNnbP6L4hQw5FWjQ0UMezG2VE,96
|
|
174
|
+
crosshair_tool-0.0.92.dist-info/top_level.txt,sha256=2jLWtM-BWg_ZYNbNfrcds0HFZD62a6J7ZIbcgcQrRk4,29
|
|
175
|
+
crosshair_tool-0.0.92.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|