crosshair-tool 0.0.56__cp39-cp39-macosx_11_0_arm64.whl → 0.0.100__cp39-cp39-macosx_11_0_arm64.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 (123) hide show
  1. _crosshair_tracers.cpython-39-darwin.so +0 -0
  2. crosshair/__init__.py +1 -1
  3. crosshair/_mark_stacks.h +51 -24
  4. crosshair/_tracers.h +9 -5
  5. crosshair/_tracers_test.py +19 -9
  6. crosshair/auditwall.py +9 -8
  7. crosshair/auditwall_test.py +31 -19
  8. crosshair/codeconfig.py +3 -2
  9. crosshair/condition_parser.py +17 -133
  10. crosshair/condition_parser_test.py +54 -96
  11. crosshair/conftest.py +1 -1
  12. crosshair/copyext.py +91 -22
  13. crosshair/copyext_test.py +33 -0
  14. crosshair/core.py +259 -203
  15. crosshair/core_and_libs.py +20 -0
  16. crosshair/core_regestered_types_test.py +82 -0
  17. crosshair/core_test.py +693 -664
  18. crosshair/diff_behavior.py +76 -21
  19. crosshair/diff_behavior_test.py +132 -23
  20. crosshair/dynamic_typing.py +128 -18
  21. crosshair/dynamic_typing_test.py +91 -4
  22. crosshair/enforce.py +1 -6
  23. crosshair/enforce_test.py +15 -23
  24. crosshair/examples/check_examples_test.py +2 -1
  25. crosshair/fnutil.py +2 -3
  26. crosshair/fnutil_test.py +0 -7
  27. crosshair/fuzz_core_test.py +70 -83
  28. crosshair/libimpl/arraylib.py +10 -7
  29. crosshair/libimpl/binascii_ch_test.py +30 -0
  30. crosshair/libimpl/binascii_test.py +67 -0
  31. crosshair/libimpl/binasciilib.py +150 -0
  32. crosshair/libimpl/bisectlib_test.py +5 -5
  33. crosshair/libimpl/builtinslib.py +1002 -682
  34. crosshair/libimpl/builtinslib_ch_test.py +108 -30
  35. crosshair/libimpl/builtinslib_test.py +431 -143
  36. crosshair/libimpl/codecslib.py +22 -2
  37. crosshair/libimpl/codecslib_test.py +41 -9
  38. crosshair/libimpl/collectionslib.py +44 -8
  39. crosshair/libimpl/collectionslib_test.py +108 -20
  40. crosshair/libimpl/copylib.py +1 -1
  41. crosshair/libimpl/copylib_test.py +18 -0
  42. crosshair/libimpl/datetimelib.py +84 -67
  43. crosshair/libimpl/datetimelib_ch_test.py +12 -7
  44. crosshair/libimpl/datetimelib_test.py +5 -6
  45. crosshair/libimpl/decimallib.py +5257 -0
  46. crosshair/libimpl/decimallib_ch_test.py +78 -0
  47. crosshair/libimpl/decimallib_test.py +76 -0
  48. crosshair/libimpl/encodings/_encutil.py +21 -11
  49. crosshair/libimpl/fractionlib.py +16 -0
  50. crosshair/libimpl/fractionlib_test.py +80 -0
  51. crosshair/libimpl/functoolslib.py +19 -7
  52. crosshair/libimpl/functoolslib_test.py +22 -6
  53. crosshair/libimpl/hashliblib.py +30 -0
  54. crosshair/libimpl/hashliblib_test.py +18 -0
  55. crosshair/libimpl/heapqlib.py +32 -5
  56. crosshair/libimpl/heapqlib_test.py +15 -12
  57. crosshair/libimpl/iolib.py +7 -4
  58. crosshair/libimpl/ipaddresslib.py +8 -0
  59. crosshair/libimpl/itertoolslib_test.py +1 -1
  60. crosshair/libimpl/mathlib.py +165 -2
  61. crosshair/libimpl/mathlib_ch_test.py +44 -0
  62. crosshair/libimpl/mathlib_test.py +59 -16
  63. crosshair/libimpl/oslib.py +7 -0
  64. crosshair/libimpl/pathliblib_test.py +10 -0
  65. crosshair/libimpl/randomlib.py +1 -0
  66. crosshair/libimpl/randomlib_test.py +6 -4
  67. crosshair/libimpl/relib.py +180 -59
  68. crosshair/libimpl/relib_ch_test.py +26 -2
  69. crosshair/libimpl/relib_test.py +77 -14
  70. crosshair/libimpl/timelib.py +35 -13
  71. crosshair/libimpl/timelib_test.py +13 -3
  72. crosshair/libimpl/typeslib.py +15 -0
  73. crosshair/libimpl/typeslib_test.py +36 -0
  74. crosshair/libimpl/unicodedatalib_test.py +3 -3
  75. crosshair/libimpl/weakreflib.py +13 -0
  76. crosshair/libimpl/weakreflib_test.py +69 -0
  77. crosshair/libimpl/zliblib.py +15 -0
  78. crosshair/libimpl/zliblib_test.py +13 -0
  79. crosshair/lsp_server.py +21 -10
  80. crosshair/main.py +48 -28
  81. crosshair/main_test.py +59 -14
  82. crosshair/objectproxy.py +39 -14
  83. crosshair/objectproxy_test.py +27 -13
  84. crosshair/opcode_intercept.py +212 -24
  85. crosshair/opcode_intercept_test.py +172 -18
  86. crosshair/options.py +0 -1
  87. crosshair/patch_equivalence_test.py +5 -21
  88. crosshair/path_cover.py +7 -5
  89. crosshair/path_search.py +6 -4
  90. crosshair/path_search_test.py +1 -2
  91. crosshair/pathing_oracle.py +53 -10
  92. crosshair/pathing_oracle_test.py +21 -0
  93. crosshair/pure_importer_test.py +5 -21
  94. crosshair/register_contract.py +16 -6
  95. crosshair/register_contract_test.py +2 -14
  96. crosshair/simplestructs.py +154 -85
  97. crosshair/simplestructs_test.py +16 -2
  98. crosshair/smtlib.py +24 -0
  99. crosshair/smtlib_test.py +14 -0
  100. crosshair/statespace.py +319 -196
  101. crosshair/statespace_test.py +45 -0
  102. crosshair/stubs_parser.py +0 -2
  103. crosshair/test_util.py +87 -25
  104. crosshair/test_util_test.py +26 -0
  105. crosshair/tools/check_init_and_setup_coincide.py +0 -3
  106. crosshair/tools/generate_demo_table.py +2 -2
  107. crosshair/tracers.py +141 -49
  108. crosshair/type_repo.py +11 -4
  109. crosshair/unicode_categories.py +1 -0
  110. crosshair/util.py +158 -76
  111. crosshair/util_test.py +13 -20
  112. crosshair/watcher.py +4 -4
  113. crosshair/z3util.py +1 -1
  114. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/METADATA +45 -36
  115. crosshair_tool-0.0.100.dist-info/RECORD +176 -0
  116. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/WHEEL +2 -1
  117. crosshair/examples/hypothesis/__init__.py +0 -2
  118. crosshair/examples/hypothesis/bugs_detected/simple_strategies.py +0 -74
  119. crosshair_tool-0.0.56.dist-info/RECORD +0 -152
  120. /crosshair/{examples/hypothesis/bugs_detected/__init__.py → py.typed} +0 -0
  121. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/entry_points.txt +0 -0
  122. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info/licenses}/LICENSE +0 -0
  123. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,8 @@
1
1
  import copy
2
2
  import itertools
3
+ import operator
3
4
  import re
5
+ import sys
4
6
  from dataclasses import dataclass
5
7
  from typing import Callable, List, Mapping, Optional, Sequence, Tuple
6
8
 
@@ -9,7 +11,7 @@ import pytest # type: ignore
9
11
  from crosshair.core import _PATCH_REGISTRATIONS
10
12
  from crosshair.core_and_libs import standalone_statespace
11
13
  from crosshair.test_util import ExecutionResult, summarize_execution
12
- from crosshair.util import debug, test_stack
14
+ from crosshair.util import ch_stack, debug
13
15
 
14
16
  """
15
17
  Tests that the builtin and standard library patches behave like their
@@ -38,6 +40,7 @@ possible_args = [
38
40
  (42, int), # isinstance
39
41
  (re.compile("(ab|a|b)"), r"\n", ""), # re methods
40
42
  (bool, [1, 1, 0]), # itertools.takewhile and friends
43
+ (operator.add, [1, 0], [1, 1]), # multi-iterable map
41
44
  ([(1, 2), (3, 4)]), # key-value pairs
42
45
  ([(1, 2), ([], 4)]), # key-value pairs w/ unhashable key
43
46
  ]
@@ -56,22 +59,6 @@ for native_fn, patched_fn in _PATCH_REGISTRATIONS.items():
56
59
  )
57
60
 
58
61
 
59
- @dataclass(init=False)
60
- class ExecutionResultWithTb:
61
- ret: object # return value
62
- exc: Optional[BaseException]
63
- tb: Optional[str]
64
- post_args: Sequence
65
- post_kwargs: Mapping[str, object]
66
-
67
- def __init__(self, result: ExecutionResult):
68
- self.ret = result.ret
69
- self.exc = result.exc
70
- self.tb = test_stack(self.exc.__traceback__) if self.exc else None
71
- self.post_args = result.post_args
72
- self.post_kwargs = result.post_kwargs
73
-
74
-
75
62
  @pytest.mark.parametrize("native_fn,patched_fn", comparisons)
76
63
  @pytest.mark.parametrize(
77
64
  "args", possible_args, ids=lambda t: re.sub(r"[\W_]", "_", str(t))
@@ -85,7 +72,4 @@ def test_patch(native_fn: Callable, patched_fn: Callable, args: Sequence[object]
85
72
  with standalone_statespace:
86
73
  patched_result = summarize_execution(patched_fn, args2, {}, detach_path=False)
87
74
  debug("Patched result: ", patched_result)
88
- if native_result != patched_result:
89
- assert ExecutionResultWithTb(native_result) == ExecutionResultWithTb(
90
- patched_result
91
- )
75
+ assert native_result == patched_result
crosshair/path_cover.py CHANGED
@@ -26,10 +26,10 @@ from crosshair.tracers import (
26
26
  )
27
27
  from crosshair.util import (
28
28
  ReferencedIdentifier,
29
+ ch_stack,
29
30
  debug,
30
31
  format_boundargs,
31
32
  name_of_type,
32
- test_stack,
33
33
  )
34
34
 
35
35
 
@@ -95,9 +95,7 @@ def path_cover(
95
95
  cov = coverage.get_results(fn)
96
96
  debug("Realized args:", formatted_pre_args)
97
97
  if exc is not None:
98
- debug(
99
- "user-level exception found", type(exc), exc, test_stack(exc_stack)
100
- )
98
+ debug("user-level exception found", type(exc), exc, ch_stack(exc_stack))
101
99
  exc_message = realize(str(exc)) if len(exc.args) > 0 else None
102
100
  paths.append(
103
101
  PathSummary(
@@ -135,7 +133,11 @@ def path_cover(
135
133
  selected: List[PathSummary] = []
136
134
  while paths:
137
135
  next_best = max(
138
- paths, key=lambda p: len(p.coverage.offsets_covered - opcodes_found)
136
+ paths,
137
+ key=lambda p: (
138
+ len(p.coverage.offsets_covered - opcodes_found), # high coverage
139
+ -len(p.formatted_args), # with small input size
140
+ ),
139
141
  )
140
142
  cur_offsets = next_best.coverage.offsets_covered
141
143
  if coverage_type == CoverageType.OPCODE:
crosshair/path_search.py CHANGED
@@ -12,15 +12,17 @@ from crosshair.options import AnalysisOptions
12
12
  from crosshair.statespace import RootNode, StateSpace, context_statespace
13
13
  from crosshair.tracers import CoverageResult, NoTracing, ResumedTracing
14
14
  from crosshair.util import (
15
- CrosshairInternal,
15
+ CrossHairInternal,
16
+ ch_stack,
16
17
  debug,
17
18
  format_boundargs_as_dictionary,
18
- test_stack,
19
19
  )
20
20
 
21
21
 
22
22
  class OptimizationKind(enum.Enum):
23
23
  SIMPLIFY = "SIMPLIFY"
24
+ # TODO: simplify mode is quite dumb atm; partly because eval_friendly_format
25
+ # realizes prior to generating the string.
24
26
  NONE = "NONE"
25
27
  MINIMIZE_INT = "MINIMIZE_INT"
26
28
 
@@ -57,7 +59,7 @@ def path_search(
57
59
  try:
58
60
  return argument_formatter(args)
59
61
  except Exception as exc:
60
- raise CrosshairInternal(str(exc)) from exc
62
+ raise CrossHairInternal(str(exc)) from exc
61
63
 
62
64
  if optimization_kind == OptimizationKind.SIMPLIFY:
63
65
  assert optimize_fn is None
@@ -99,7 +101,7 @@ def path_search(
99
101
  "Aborting path, hit exception",
100
102
  type(exc),
101
103
  exc,
102
- test_stack(exc_stack),
104
+ ch_stack(exc_stack),
103
105
  )
104
106
  return False
105
107
  debug("Path succeeded")
@@ -34,8 +34,7 @@ def do_path_search(
34
34
 
35
35
 
36
36
  def test_optimize_options() -> None:
37
- fninfo = FunctionInfo.from_fn(ten_over_difference)
38
- opts = DEFAULT_OPTIONS
37
+ opts = DEFAULT_OPTIONS.overlay(AnalysisOptionSet(max_uninteresting_iterations=10))
39
38
  ret = do_path_search(
40
39
  ten_over_difference, opts, None, optimization_kind=OptimizationKind.SIMPLIFY
41
40
  )
@@ -6,13 +6,16 @@ from z3 import ExprRef # type: ignore
6
6
 
7
7
  from crosshair.statespace import (
8
8
  AbstractPathingOracle,
9
+ DetachedPathNode,
9
10
  ModelValueNode,
10
11
  NodeLike,
11
12
  RootNode,
12
13
  SearchTreeNode,
14
+ StateSpace,
13
15
  WorstResultNode,
14
16
  )
15
- from crosshair.util import CrosshairInternal, debug, in_debug
17
+ from crosshair.util import CrossHairInternal, debug, in_debug
18
+ from crosshair.z3util import z3And, z3Not, z3Or
16
19
 
17
20
  CodeLoc = Tuple[str, ...]
18
21
 
@@ -54,9 +57,13 @@ class CoveragePathingOracle(AbstractPathingOracle):
54
57
  {},
55
58
  )
56
59
 
60
+ # TODO: This falls apart for moderately sized with_equal_probabilities
61
+ # because that has many small probability decisions.
62
+ # (even just a 10% change could be much larger than it would be otherwise)
57
63
  _delta_probabilities = {-1: 0.1, 0: 0.25, 1: 0.9}
58
64
 
59
- def pre_path_hook(self, root: RootNode) -> None:
65
+ def pre_path_hook(self, space: StateSpace) -> None:
66
+ root = space._root
60
67
  visits = self.visits
61
68
  _delta_probabilities = self._delta_probabilities
62
69
 
@@ -110,15 +117,17 @@ class CoveragePathingOracle(AbstractPathingOracle):
110
117
  def post_path_hook(self, path: Sequence[SearchTreeNode]) -> None:
111
118
  leading_locs = []
112
119
  leading_conditions: List[int] = []
113
- for step, node in enumerate(path):
120
+ for step, node in enumerate(path[:-1]):
114
121
  if not isinstance(node, NodeLike):
115
122
  continue
116
- node = node.simplify() # type: ignore
117
123
  if isinstance(node, WorstResultNode):
118
124
  key = node.stacktail
119
125
  if (key not in leading_locs) and (not isinstance(node, ModelValueNode)):
120
126
  self.summarized_positions[key] += Counter(leading_conditions)
121
127
  leading_locs.append(key)
128
+ next_node = path[step + 1]
129
+ if isinstance(next_node, DetachedPathNode):
130
+ break
122
131
  if step + 1 < len(path):
123
132
  (is_positive, root_expr) = node.normalized_expr
124
133
  expr_signature = (
@@ -126,13 +135,13 @@ class CoveragePathingOracle(AbstractPathingOracle):
126
135
  if is_positive
127
136
  else -self.internalize(root_expr)
128
137
  )
129
- if path[step + 1].simplify() == node.positive.simplify():
138
+ if next_node == node.positive:
130
139
  leading_conditions.append(expr_signature)
131
- elif path[step + 1].simplify() == node.negative.simplify():
140
+ elif next_node == node.negative:
132
141
  leading_conditions.append(-expr_signature)
133
142
  else:
134
- raise CrosshairInternal(
135
- f"{type(path[step])}{type(path[step+1])}"
143
+ raise CrossHairInternal(
144
+ f"{type(path[step])} was followed by {type(path[step+1])}"
136
145
  )
137
146
  visits = self.visits
138
147
  prev_len = len(visits)
@@ -207,16 +216,50 @@ class PreferNegativeOracle(AbstractPathingOracle):
207
216
  return 0.25
208
217
 
209
218
 
219
+ class ConstrainedOracle(AbstractPathingOracle):
220
+ """
221
+ A pathing oracle that prefers to take a path that satisfies
222
+ explicitly provided constraints.
223
+ """
224
+
225
+ def __init__(self, inner_oracle: AbstractPathingOracle):
226
+ self.inner_oracle = inner_oracle
227
+ self.exprs: List[ExprRef] = []
228
+
229
+ def prefer(self, expr: ExprRef):
230
+ self.exprs.append(expr)
231
+
232
+ def pre_path_hook(self, space: StateSpace) -> None:
233
+ self.space = space
234
+ self.exprs = []
235
+ self.inner_oracle.pre_path_hook(space)
236
+
237
+ def post_path_hook(self, path: Sequence["SearchTreeNode"]) -> None:
238
+ self.inner_oracle.post_path_hook(path)
239
+
240
+ def decide(
241
+ self, root, node: "WorstResultNode", engine_probability: Optional[float]
242
+ ) -> float:
243
+ # We always run the inner oracle in case it's tracking something about the path.
244
+ default_probability = self.inner_oracle.decide(root, node, engine_probability)
245
+ if not self.space.is_possible(z3And(*[node.expr, *self.exprs])):
246
+ return 0.0
247
+ elif not self.space.is_possible(z3And(*[z3Not(node.expr), *self.exprs])):
248
+ return 1.0
249
+ else:
250
+ return default_probability
251
+
252
+
210
253
  class RotatingOracle(AbstractPathingOracle):
211
254
  def __init__(self, oracles: List[AbstractPathingOracle]):
212
255
  self.oracles = oracles
213
256
  self.index = -1
214
257
 
215
- def pre_path_hook(self, root: "RootNode") -> None:
258
+ def pre_path_hook(self, space: StateSpace) -> None:
216
259
  oracles = self.oracles
217
260
  self.index = (self.index + 1) % len(oracles)
218
261
  for oracle in oracles:
219
- oracle.pre_path_hook(root)
262
+ oracle.pre_path_hook(space)
220
263
 
221
264
  def post_path_hook(self, path: Sequence["SearchTreeNode"]) -> None:
222
265
  for oracle in self.oracles:
@@ -0,0 +1,21 @@
1
+ import random
2
+
3
+ import z3 # type: ignore
4
+
5
+ from crosshair.pathing_oracle import ConstrainedOracle, PreferNegativeOracle
6
+ from crosshair.statespace import RootNode, SimpleStateSpace, WorstResultNode
7
+
8
+
9
+ def test_constrained_oracle():
10
+ oracle = ConstrainedOracle(PreferNegativeOracle())
11
+ x = z3.Int("x")
12
+ root = RootNode()
13
+ space = SimpleStateSpace()
14
+ oracle.pre_path_hook(space)
15
+ oracle.prefer(x >= 7)
16
+ rand = random.Random()
17
+ assert oracle.decide(root, WorstResultNode(rand, x < 7, space.solver), None) == 0.0
18
+ assert oracle.decide(root, WorstResultNode(rand, x >= 3, space.solver), None) == 1.0
19
+ assert (
20
+ oracle.decide(root, WorstResultNode(rand, x == 7, space.solver), None) == 0.25
21
+ )
@@ -7,26 +7,10 @@ import pytest
7
7
  from crosshair.pure_importer import prefer_pure_python_imports
8
8
 
9
9
 
10
- @contextmanager
11
- def _pydantic_unloaded():
12
- saved = {}
13
- for k in list(sys.modules):
14
- if k.startswith("pydantic"):
15
- saved[k] = sys.modules.pop(k)
16
- try:
17
- yield
18
- finally:
19
- sys.modules.update(saved)
20
-
21
-
22
- @pytest.mark.skipif(
23
- sys.version_info >= (3, 10), reason="no compiled pydantic on 3.10 (yet)"
10
+ @pytest.mark.skip(
11
+ reason="We used to test pydantic here, but current version doesn't use Cython"
24
12
  )
25
13
  def test_prefer_pure_python_imports():
26
- pydantic = importlib.import_module("pydantic")
27
- assert pydantic.compiled
28
-
29
- with _pydantic_unloaded():
30
- with prefer_pure_python_imports():
31
- pydantic = importlib.import_module("pydantic")
32
- assert not pydantic.compiled
14
+ with prefer_pure_python_imports():
15
+ pydantic = importlib.import_module("pydantic")
16
+ assert not pydantic.compiled
@@ -1,8 +1,9 @@
1
1
  """API for registering contracts for external libraries."""
2
+
2
3
  from dataclasses import dataclass
3
4
  from inspect import Parameter, Signature, getmodule, ismethod, signature
4
5
  from types import MethodDescriptorType, ModuleType, WrapperDescriptorType
5
- from typing import Callable, Dict, List, Optional, Set, Union
6
+ from typing import Callable, Dict, FrozenSet, List, Optional, Set, Union
6
7
  from weakref import ReferenceType
7
8
 
8
9
  from crosshair.fnutil import resolve_signature
@@ -27,11 +28,13 @@ REGISTERED_CONTRACTS: Dict[Callable, ContractOverride] = {}
27
28
  REGISTERED_MODULES: Set[ModuleType] = set()
28
29
 
29
30
  # Don't automatically register those functions.
30
- _NO_AUTO_REGISTER: Set[str] = {
31
- "__init__",
32
- "__init_subclass__",
33
- "__new__",
34
- }
31
+ _NO_AUTO_REGISTER: FrozenSet[str] = frozenset(
32
+ {
33
+ "__init__",
34
+ "__init_subclass__",
35
+ "__new__",
36
+ }
37
+ )
35
38
 
36
39
 
37
40
  def required_param_names(sig: Signature) -> Set[str]:
@@ -200,6 +203,13 @@ def register_contract(
200
203
  return _internal_register_contract(fn, pre, post, sig, skip_body)
201
204
 
202
205
 
206
+ def clear_contract_registrations():
207
+ global REGISTERED_CONTRACTS
208
+ REGISTERED_CONTRACTS.clear()
209
+ global REGISTERED_MODULES
210
+ REGISTERED_MODULES.clear()
211
+
212
+
203
213
  def get_contract(fn: Callable) -> Optional[ContractOverride]:
204
214
  """
205
215
  Get the contract associated to the given function, it the function was registered.
@@ -63,16 +63,6 @@ def test_register_randint():
63
63
  return randint(x, 10)
64
64
 
65
65
  randint_sig = None
66
- # Stub parser is not available for python < 3.8.
67
- if sys.version_info < (3, 8):
68
- randint_sig = Signature(
69
- parameters=[
70
- Parameter("self", Parameter.POSITIONAL_OR_KEYWORD, annotation=Random),
71
- Parameter("a", Parameter.POSITIONAL_OR_KEYWORD, annotation=int),
72
- Parameter("b", Parameter.POSITIONAL_OR_KEYWORD, annotation=int),
73
- ],
74
- return_annotation=int,
75
- )
76
66
  register_contract(
77
67
  Random.randint,
78
68
  pre=lambda a, b: a <= b,
@@ -109,12 +99,10 @@ def test_register_numpy_randint():
109
99
 
110
100
  def test_register_overload():
111
101
  @overload
112
- def overld(a: int) -> int:
113
- ...
102
+ def overld(a: int) -> int: ...
114
103
 
115
104
  @overload
116
- def overld(a: str) -> str:
117
- ...
105
+ def overld(a: str) -> str: ...
118
106
 
119
107
  def overld(a: Union[int, str]) -> Union[int, str]:
120
108
  if isinstance(a, int):