hypothesis 6.135.7__py3-none-any.whl → 6.135.9__py3-none-any.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.
@@ -26,9 +26,11 @@ import re
26
26
  import sys
27
27
  import types
28
28
  from ast import literal_eval
29
+ from collections.abc import Sequence
29
30
  from contextlib import suppress
30
31
  from datetime import date, datetime, timedelta, timezone
31
32
  from pathlib import Path
33
+ from typing import Any, Optional
32
34
 
33
35
  import libcst as cst
34
36
  from libcst import matchers as m
@@ -43,11 +45,11 @@ try:
43
45
  except ImportError:
44
46
  black = None # type: ignore
45
47
 
46
- HEADER = f"""\
48
+ HEADER = """\
47
49
  From HEAD Mon Sep 17 00:00:00 2001
48
- From: Hypothesis {__version__} <no-reply@hypothesis.works>
49
- Date: {{when:%a, %d %b %Y %H:%M:%S}}
50
- Subject: [PATCH] {{msg}}
50
+ From: {author}
51
+ Date: {when:%a, %d %b %Y %H:%M:%S}
52
+ Subject: [PATCH] {msg}
51
53
 
52
54
  ---
53
55
  """
@@ -56,7 +58,7 @@ _space_only_re = re.compile("^ +$", re.MULTILINE)
56
58
  _leading_space_re = re.compile("(^[ ]*)(?:[^ \n])", re.MULTILINE)
57
59
 
58
60
 
59
- def dedent(text):
61
+ def dedent(text: str) -> tuple[str, str]:
60
62
  # Simplified textwrap.dedent, for valid Python source code only
61
63
  text = _space_only_re.sub("", text)
62
64
  prefix = min(_leading_space_re.findall(text), key=len)
@@ -70,7 +72,14 @@ def indent(text: str, prefix: str) -> str:
70
72
  class AddExamplesCodemod(VisitorBasedCodemodCommand):
71
73
  DESCRIPTION = "Add explicit examples to failing tests."
72
74
 
73
- def __init__(self, context, fn_examples, strip_via=(), dec="example", width=88):
75
+ def __init__(
76
+ self,
77
+ context: CodemodContext,
78
+ fn_examples: dict[str, list[tuple[cst.Call, str]]],
79
+ strip_via: tuple[str, ...] = (),
80
+ decorator: str = "example",
81
+ width: int = 88,
82
+ ):
74
83
  """Add @example() decorator(s) for failing test(s).
75
84
 
76
85
  `code` is the source code of the module where the test functions are defined.
@@ -79,9 +88,11 @@ class AddExamplesCodemod(VisitorBasedCodemodCommand):
79
88
  assert fn_examples, "This codemod does nothing without fn_examples."
80
89
  super().__init__(context)
81
90
 
82
- self.decorator_func = cst.parse_expression(dec)
91
+ self.decorator_func = cst.parse_expression(decorator)
83
92
  self.line_length = width
84
- value_in_strip_via = m.MatchIfTrue(lambda x: literal_eval(x.value) in strip_via)
93
+ value_in_strip_via: Any = m.MatchIfTrue(
94
+ lambda x: literal_eval(x.value) in strip_via
95
+ )
85
96
  self.strip_matching = m.Call(
86
97
  m.Attribute(m.Call(), m.Name("via")),
87
98
  [m.Arg(m.SimpleString() & value_in_strip_via)],
@@ -89,11 +100,17 @@ class AddExamplesCodemod(VisitorBasedCodemodCommand):
89
100
 
90
101
  # Codemod the failing examples to Call nodes usable as decorators
91
102
  self.fn_examples = {
92
- k: tuple(d for x in nodes if (d := self.__call_node_to_example_dec(*x)))
103
+ k: tuple(
104
+ d
105
+ for (node, via) in nodes
106
+ if (d := self.__call_node_to_example_dec(node, via))
107
+ )
93
108
  for k, nodes in fn_examples.items()
94
109
  }
95
110
 
96
- def __call_node_to_example_dec(self, node, via):
111
+ def __call_node_to_example_dec(
112
+ self, node: cst.Call, via: str
113
+ ) -> Optional[cst.Decorator]:
97
114
  # If we have black installed, remove trailing comma, _unless_ there's a comment
98
115
  node = node.with_changes(
99
116
  func=self.decorator_func,
@@ -112,7 +129,7 @@ class AddExamplesCodemod(VisitorBasedCodemodCommand):
112
129
  else node.args
113
130
  ),
114
131
  )
115
- via = cst.Call(
132
+ via: cst.BaseExpression = cst.Call(
116
133
  func=cst.Attribute(node, cst.Name("via")),
117
134
  args=[cst.Arg(cst.SimpleString(repr(via)))],
118
135
  )
@@ -127,7 +144,9 @@ class AddExamplesCodemod(VisitorBasedCodemodCommand):
127
144
  via = cst.parse_expression(pretty.strip())
128
145
  return cst.Decorator(via)
129
146
 
130
- def leave_FunctionDef(self, _, updated_node):
147
+ def leave_FunctionDef(
148
+ self, _original_node: cst.FunctionDef, updated_node: cst.FunctionDef
149
+ ) -> cst.FunctionDef:
131
150
  return updated_node.with_changes(
132
151
  # TODO: improve logic for where in the list to insert this decorator
133
152
  decorators=tuple(
@@ -140,30 +159,59 @@ class AddExamplesCodemod(VisitorBasedCodemodCommand):
140
159
  )
141
160
 
142
161
 
143
- def get_patch_for(func, failing_examples, *, strip_via=()):
144
- # Skip this if we're unable to find the location or source of this function.
162
+ def get_patch_for(
163
+ func: Any,
164
+ examples: Sequence[tuple[str, str]],
165
+ *,
166
+ strip_via: tuple[str, ...] = (),
167
+ ) -> Optional[tuple[str, str, str]]:
168
+ # Skip this if we're unable to find the location of this function.
145
169
  try:
146
170
  module = sys.modules[func.__module__]
147
- fname = Path(module.__file__).relative_to(Path.cwd())
148
- before = inspect.getsource(func)
171
+ file_path = Path(module.__file__) # type: ignore
149
172
  except Exception:
150
173
  return None
151
174
 
175
+ fname = (
176
+ file_path.relative_to(Path.cwd())
177
+ if file_path.is_relative_to(Path.cwd())
178
+ else file_path
179
+ )
180
+ patch = _get_patch_for(
181
+ func, examples, strip_via=strip_via, namespace=module.__dict__
182
+ )
183
+ if patch is None:
184
+ return None
185
+
186
+ (before, after) = patch
187
+ return (str(fname), before, after)
188
+
189
+
190
+ # split out for easier testing of patches in hypofuzz, where the function to
191
+ # apply the patch to may not be loaded in sys.modules.
192
+ def _get_patch_for(
193
+ func: Any,
194
+ examples: Sequence[tuple[str, str]],
195
+ *,
196
+ strip_via: tuple[str, ...] = (),
197
+ namespace: dict[str, Any],
198
+ ) -> Optional[tuple[str, str]]:
199
+ try:
200
+ before = inspect.getsource(func)
201
+ except Exception: # pragma: no cover
202
+ return None
203
+
152
204
  modules_in_test_scope = sorted(
153
- (
154
- (k, v)
155
- for (k, v) in module.__dict__.items()
156
- if isinstance(v, types.ModuleType)
157
- ),
205
+ ((k, v) for (k, v) in namespace.items() if isinstance(v, types.ModuleType)),
158
206
  key=lambda kv: len(kv[1].__name__),
159
207
  )
160
208
 
161
209
  # The printed examples might include object reprs which are invalid syntax,
162
210
  # so we parse here and skip over those. If _none_ are valid, there's no patch.
163
- call_nodes = []
164
- for ex, via in set(failing_examples):
211
+ call_nodes: list[tuple[cst.Call, str]] = []
212
+ for ex, via in set(examples):
165
213
  with suppress(Exception):
166
- node = cst.parse_module(ex)
214
+ node: Any = cst.parse_module(ex)
167
215
  the_call = node.body[0].body[0].value
168
216
  assert isinstance(the_call, cst.Call), the_call
169
217
  # Check for st.data(), which doesn't support explicit examples
@@ -180,7 +228,7 @@ def get_patch_for(func, failing_examples, *, strip_via=()):
180
228
  isinstance(anode, ast.Name)
181
229
  and isinstance(anode.ctx, ast.Load)
182
230
  and anode.id not in names
183
- and anode.id not in module.__dict__
231
+ and anode.id not in namespace
184
232
  ):
185
233
  for k, v in modules_in_test_scope:
186
234
  if anode.id in v.__dict__:
@@ -194,14 +242,15 @@ def get_patch_for(func, failing_examples, *, strip_via=()):
194
242
  with suppress(Exception):
195
243
  wrapper = cst.metadata.MetadataWrapper(node)
196
244
  kwarg_names = {
197
- a.keyword for a in m.findall(wrapper, m.Arg(keyword=m.Name()))
245
+ node.keyword # type: ignore
246
+ for node in m.findall(wrapper, m.Arg(keyword=m.Name()))
198
247
  }
199
248
  node = m.replace(
200
249
  wrapper,
201
250
  m.Name(value=m.MatchIfTrue(names.__contains__))
202
251
  & m.MatchMetadata(ExpressionContextProvider, ExpressionContext.LOAD)
203
- & m.MatchIfTrue(lambda n, k=kwarg_names: n not in k),
204
- replacement=lambda node, _, ns=names: ns[node.value],
252
+ & m.MatchIfTrue(lambda n, k=kwarg_names: n not in k), # type: ignore
253
+ replacement=lambda node, _, ns=names: ns[node.value], # type: ignore
205
254
  )
206
255
  node = node.body[0].body[0].value
207
256
  assert isinstance(node, cst.Call), node
@@ -211,8 +260,8 @@ def get_patch_for(func, failing_examples, *, strip_via=()):
211
260
  return None
212
261
 
213
262
  if (
214
- module.__dict__.get("hypothesis") is sys.modules["hypothesis"]
215
- and "given" not in module.__dict__ # more reliably present than `example`
263
+ namespace.get("hypothesis") is sys.modules["hypothesis"]
264
+ and "given" not in namespace # more reliably present than `example`
216
265
  ):
217
266
  decorator_func = "hypothesis.example"
218
267
  else:
@@ -229,22 +278,28 @@ def get_patch_for(func, failing_examples, *, strip_via=()):
229
278
  CodemodContext(),
230
279
  fn_examples={func.__name__: call_nodes},
231
280
  strip_via=strip_via,
232
- dec=decorator_func,
281
+ decorator=decorator_func,
233
282
  width=88 - len(prefix), # to match Black's default formatting
234
283
  ).transform_module(node)
235
- return (str(fname), before, indent(after.code, prefix=prefix))
284
+ return (before, indent(after.code, prefix=prefix))
236
285
 
237
286
 
238
- def make_patch(triples, *, msg="Hypothesis: add explicit examples", when=None):
287
+ def make_patch(
288
+ triples: Sequence[tuple[str, str, str]],
289
+ *,
290
+ msg: str = "Hypothesis: add explicit examples",
291
+ when: Optional[datetime] = None,
292
+ author: str = f"Hypothesis {__version__} <no-reply@hypothesis.works>",
293
+ ) -> str:
239
294
  """Create a patch for (fname, before, after) triples."""
240
295
  assert triples, "attempted to create empty patch"
241
296
  when = when or datetime.now(tz=timezone.utc)
242
297
 
243
- by_fname = {}
298
+ by_fname: dict[Path, list[tuple[str, str]]] = {}
244
299
  for fname, before, after in triples:
245
300
  by_fname.setdefault(Path(fname), []).append((before, after))
246
301
 
247
- diffs = [HEADER.format(msg=msg, when=when)]
302
+ diffs = [HEADER.format(msg=msg, when=when, author=author)]
248
303
  for fname, changes in sorted(by_fname.items()):
249
304
  source_before = source_after = fname.read_text(encoding="utf-8")
250
305
  for before, after in changes:
@@ -192,6 +192,10 @@ def _constants_from_source(source: Union[str, bytes], *, limit: bool) -> Constan
192
192
  return visitor.constants
193
193
 
194
194
 
195
+ def _constants_file_str(constants: Constants) -> str:
196
+ return str(sorted(constants, key=lambda v: (str(type(v)), v)))
197
+
198
+
195
199
  @lru_cache(4096)
196
200
  def constants_from_module(module: ModuleType, *, limit: bool = True) -> Constants:
197
201
  try:
@@ -236,7 +240,7 @@ def constants_from_module(module: ModuleType, *, limit: bool = True) -> Constant
236
240
  # somewhat arbitrary sort order. The cache file doesn't *have* to be
237
241
  # stable... but it is aesthetically pleasing, and means we could rely
238
242
  # on it in the future!
239
- + str(sorted(constants, key=lambda v: (str(type(v)), v))),
243
+ + _constants_file_str(constants),
240
244
  encoding="utf-8",
241
245
  )
242
246
  except Exception: # pragma: no cover
hypothesis/version.py CHANGED
@@ -8,5 +8,5 @@
8
8
  # v. 2.0. If a copy of the MPL was not distributed with this file, You can
9
9
  # obtain one at https://mozilla.org/MPL/2.0/.
10
10
 
11
- __version_info__ = (6, 135, 7)
11
+ __version_info__ = (6, 135, 9)
12
12
  __version__ = ".".join(map(str, __version_info__))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypothesis
3
- Version: 6.135.7
3
+ Version: 6.135.9
4
4
  Summary: A library for property-based testing
5
5
  Author-email: "David R. MacIver and Zac Hatfield-Dodds" <david@drmaciver.com>
6
6
  License-Expression: MPL-2.0
@@ -14,10 +14,10 @@ hypothesis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  hypothesis/reporting.py,sha256=f-jhl1JfAi5_tG8dsUd2qDjGcPdvxEzfF6hXmpTFQ1g,1761
15
15
  hypothesis/stateful.py,sha256=vQ8wDO7YW-8nGlBGLfR9ariwmRCS9jxJy-Ky3swXjDE,42861
16
16
  hypothesis/statistics.py,sha256=kZ5mc0fAg7gnSO6EmDo82fyz8DYhIiJ_mHe7srxOeQ0,5438
17
- hypothesis/version.py,sha256=d-zCITTEEu8cpXH2B5XV2DOcDRJYHRLHDNA5I83QjVY,498
17
+ hypothesis/version.py,sha256=2cgiH3RVZ2YHrP3mS9FXI4dZXTKhr9HPeF4V-9yoL_A,498
18
18
  hypothesis/extra/__init__.py,sha256=gx4ENVDkrzBxy5Lv3Iyfs3tvMGdWMbiHfi95B7t61CY,415
19
19
  hypothesis/extra/_array_helpers.py,sha256=PLmFckBfQpzQ4Q3dFJQqMmrbm7Qdvqxf1t9LHDCuSp0,27627
20
- hypothesis/extra/_patching.py,sha256=pLgtFBgwDEotw6Pu8ZfJbkE5Qk5F1H3IH5EPUo0Ngm4,10869
20
+ hypothesis/extra/_patching.py,sha256=A5s5EAf81itr--w4SAFyzuecSZm4eT397jM7BvbnQXU,12385
21
21
  hypothesis/extra/array_api.py,sha256=bv_wisQS56wvwg-LiC26DxvzmPxcNLzlbzmltFWZ_Jo,42609
22
22
  hypothesis/extra/cli.py,sha256=HOH_BGosyUvS4yNDsWF4Dfe672tEFGT4BBPBuZamm1s,12885
23
23
  hypothesis/extra/codemods.py,sha256=i_iM5aSnrNjSZ_uNW40yShxXo7qaXuCrDHy8OznOa7o,11224
@@ -39,7 +39,7 @@ hypothesis/internal/cache.py,sha256=5GJ92S7HNjefuiOOSUchp3IYw1XxLVRmYoFHEoL7eVk,
39
39
  hypothesis/internal/cathetus.py,sha256=q6t16mT2jzlJmBddhRlqC2wg_w7FggVDRAVkzs04W-U,2258
40
40
  hypothesis/internal/charmap.py,sha256=dIsRCxa6r1r-rF_NUy04Z-afFa2b6fCFEjWTMYsAgXY,11781
41
41
  hypothesis/internal/compat.py,sha256=CexhnEVndODX3E-iJmBzt_svqLmADF_6vWkd7lXLRQI,10970
42
- hypothesis/internal/constants_ast.py,sha256=to4zxaUpA4JU5iPXy2pOb38dWG_tibu6UZkd2Vrq-fA,10091
42
+ hypothesis/internal/constants_ast.py,sha256=VcItL6O6LbNd-NpunBuonKE9rLNVVxNlOfTDY8Lqci0,10189
43
43
  hypothesis/internal/coverage.py,sha256=u2ALnaYgwd2jSkZkA5aQYA1uMnk7gwaasMJbq_l3iIg,3380
44
44
  hypothesis/internal/detection.py,sha256=v_zf0GYsnfJKQMNw78qYv61Dh-YaXXfgqKpVIOsBvfw,1228
45
45
  hypothesis/internal/entropy.py,sha256=wgNeddIM0I3SD1yydz12Lsop6Di5vsle4e9u5fWAxIs,8014
@@ -105,9 +105,9 @@ hypothesis/utils/terminal.py,sha256=IxGYDGaE4R3b_vMfz5buWbN18XH5qVP4IxqAgNAU5as,
105
105
  hypothesis/vendor/__init__.py,sha256=gx4ENVDkrzBxy5Lv3Iyfs3tvMGdWMbiHfi95B7t61CY,415
106
106
  hypothesis/vendor/pretty.py,sha256=WEZC-UV-QQgCjUf2Iz1WWaWnbgT7Hc3s-GWEVxq-Qz0,36114
107
107
  hypothesis/vendor/tlds-alpha-by-domain.txt,sha256=W9hYvpu2BMmNgE-SfPp8-GTzEVjw0HJUviqlvHwpZu8,9588
108
- hypothesis-6.135.7.dist-info/licenses/LICENSE.txt,sha256=rIkDe6xjVQZE3OjPMsZ2Xl-rncGhzpS4n4qAXzQaZ1A,17141
109
- hypothesis-6.135.7.dist-info/METADATA,sha256=gZG_kjSYwAYhCC7U6l7rU90A1aN206QkGMDvIzbft9U,5637
110
- hypothesis-6.135.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
111
- hypothesis-6.135.7.dist-info/entry_points.txt,sha256=JDoUs9w1bYme7aG_eJ1cCtstRTWD71BzG8iRi-G2eHE,113
112
- hypothesis-6.135.7.dist-info/top_level.txt,sha256=ReGreaueiJ4d1I2kEiig_CLeA0sD4QCQ4qk_8kH1oDc,81
113
- hypothesis-6.135.7.dist-info/RECORD,,
108
+ hypothesis-6.135.9.dist-info/licenses/LICENSE.txt,sha256=rIkDe6xjVQZE3OjPMsZ2Xl-rncGhzpS4n4qAXzQaZ1A,17141
109
+ hypothesis-6.135.9.dist-info/METADATA,sha256=Y5tNxfqmY2oL2NkcTrxP3ytDT-iZnoZT5kfz1XIUKa8,5637
110
+ hypothesis-6.135.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
111
+ hypothesis-6.135.9.dist-info/entry_points.txt,sha256=JDoUs9w1bYme7aG_eJ1cCtstRTWD71BzG8iRi-G2eHE,113
112
+ hypothesis-6.135.9.dist-info/top_level.txt,sha256=ReGreaueiJ4d1I2kEiig_CLeA0sD4QCQ4qk_8kH1oDc,81
113
+ hypothesis-6.135.9.dist-info/RECORD,,