Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/hypothesis/internal/reflection.py: 65%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

352 statements  

1# This file is part of Hypothesis, which may be found at 

2# https://github.com/HypothesisWorks/hypothesis/ 

3# 

4# Copyright the Hypothesis Authors. 

5# Individual contributors are listed in AUTHORS.rst and the git log. 

6# 

7# This Source Code Form is subject to the terms of the Mozilla Public License, 

8# v. 2.0. If a copy of the MPL was not distributed with this file, You can 

9# obtain one at https://mozilla.org/MPL/2.0/. 

10 

11"""This file can approximately be considered the collection of hypothesis going 

12to really unreasonable lengths to produce pretty output.""" 

13 

14import ast 

15import hashlib 

16import inspect 

17import linecache 

18import re 

19import sys 

20import textwrap 

21import types 

22import warnings 

23from collections.abc import MutableMapping, Sequence 

24from functools import partial, wraps 

25from inspect import Parameter, Signature 

26from io import StringIO 

27from keyword import iskeyword 

28from random import _inst as global_random_instance 

29from tokenize import COMMENT, generate_tokens, untokenize 

30from types import ModuleType 

31from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union 

32from unittest.mock import _patch as PatchType 

33from weakref import WeakKeyDictionary 

34 

35from hypothesis.errors import HypothesisWarning 

36from hypothesis.internal.cache import LRUCache 

37from hypothesis.internal.compat import EllipsisType, is_typed_named_tuple 

38from hypothesis.utils.conventions import not_set 

39from hypothesis.vendor.pretty import pretty 

40 

41if TYPE_CHECKING: 

42 from hypothesis.strategies._internal.strategies import SearchStrategy 

43 

44T = TypeVar("T") 

45 

46# we have several levels of caching for lambda descriptions. 

47# * LAMBDA_DESCRIPTION_CACHE maps a lambda f to its description _lambda_description(f). 

48# Note that _lambda_description(f) may not be identical to f as it appears in the 

49# source code file. 

50# * LAMBDA_DIGEST_DESCRIPTION_CACHE maps _lambda_source_key(f) to _lambda_description(f). 

51# _lambda_source_key implements something close to "ast equality": 

52# two syntactically identical (minus whitespace etc) lambdas appearing in 

53# different files have the same key. Cache hits here provide a fast path which 

54# avoids ast-parsing syntactic lambdas we've seen before. Two lambdas with the 

55# same _lambda_source_key will not have different _lambda_descriptions - if 

56# they do, that's a bug here. 

57# * AST_LAMBDAS_CACHE maps source code lines to a list of the lambdas found in 

58# that source code. A cache hit here avoids reparsing the ast. 

59LAMBDA_DESCRIPTION_CACHE: MutableMapping[Callable, str] = WeakKeyDictionary() 

60LAMBDA_DIGEST_DESCRIPTION_CACHE: LRUCache[tuple[Any], str] = LRUCache(max_size=1000) 

61AST_LAMBDAS_CACHE: LRUCache[tuple[str], list[ast.Lambda]] = LRUCache(max_size=100) 

62 

63 

64def is_mock(obj: object) -> bool: 

65 """Determine if the given argument is a mock type.""" 

66 

67 # We want to be able to detect these when dealing with various test 

68 # args. As they are sneaky and can look like almost anything else, 

69 # we'll check this by looking for an attribute with a name that it's really 

70 # unlikely to implement accidentally, and that anyone who implements it 

71 # deliberately should know what they're doing. This is more robust than 

72 # looking for types. 

73 return hasattr(obj, "hypothesis_internal_is_this_a_mock_check") 

74 

75 

76def _clean_source(src: str) -> bytes: 

77 """Return the source code as bytes, without decorators or comments. 

78 

79 Because this is part of our database key, we reduce the cache invalidation 

80 rate by ignoring decorators, comments, trailing whitespace, and empty lines. 

81 We can't just use the (dumped) AST directly because it changes between Python 

82 versions (e.g. ast.Constant) 

83 """ 

84 # Get the (one-indexed) line number of the function definition, and drop preceding 

85 # lines - i.e. any decorators, so that adding `@example()`s keeps the same key. 

86 try: 

87 funcdef = ast.parse(src).body[0] 

88 src = "".join(src.splitlines(keepends=True)[funcdef.lineno - 1 :]) 

89 except Exception: 

90 pass 

91 # Remove blank lines and use the tokenize module to strip out comments, 

92 # so that those can be changed without changing the database key. 

93 try: 

94 src = untokenize( 

95 t for t in generate_tokens(StringIO(src).readline) if t.type != COMMENT 

96 ) 

97 except Exception: 

98 pass 

99 # Finally, remove any trailing whitespace and empty lines as a last cleanup. 

100 return "\n".join(x.rstrip() for x in src.splitlines() if x.rstrip()).encode() 

101 

102 

103def function_digest(function: Any) -> bytes: 

104 """Returns a string that is stable across multiple invocations across 

105 multiple processes and is prone to changing significantly in response to 

106 minor changes to the function. 

107 

108 No guarantee of uniqueness though it usually will be. Digest collisions 

109 lead to unfortunate but not fatal problems during database replay. 

110 """ 

111 hasher = hashlib.sha384() 

112 try: 

113 src = inspect.getsource(function) 

114 except (OSError, TypeError): 

115 # If we can't actually get the source code, try for the name as a fallback. 

116 # NOTE: We might want to change this to always adding function.__qualname__, 

117 # to differentiate f.x. two classes having the same function implementation 

118 # with class-dependent behaviour. 

119 try: 

120 hasher.update(function.__name__.encode()) 

121 except AttributeError: 

122 pass 

123 else: 

124 hasher.update(_clean_source(src)) 

125 try: 

126 # This is additional to the source code because it can include the effects 

127 # of decorators, or of post-hoc assignment to the .__signature__ attribute. 

128 hasher.update(repr(get_signature(function)).encode()) 

129 except Exception: 

130 pass 

131 try: 

132 # We set this in order to distinguish e.g. @pytest.mark.parametrize cases. 

133 hasher.update(function._hypothesis_internal_add_digest) 

134 except AttributeError: 

135 pass 

136 return hasher.digest() 

137 

138 

139def check_signature(sig: Signature) -> None: 

140 # Backport from Python 3.11; see https://github.com/python/cpython/pull/92065 

141 for p in sig.parameters.values(): 

142 if iskeyword(p.name) and p.kind is not p.POSITIONAL_ONLY: 

143 raise ValueError( 

144 f"Signature {sig!r} contains a parameter named {p.name!r}, " 

145 f"but this is a SyntaxError because `{p.name}` is a keyword. " 

146 "You, or a library you use, must have manually created an " 

147 "invalid signature - this will be an error in Python 3.11+" 

148 ) 

149 

150 

151def get_signature( 

152 target: Any, *, follow_wrapped: bool = True, eval_str: bool = False 

153) -> Signature: 

154 # Special case for use of `@unittest.mock.patch` decorator, mimicking the 

155 # behaviour of getfullargspec instead of reporting unusable arguments. 

156 patches = getattr(target, "patchings", None) 

157 if isinstance(patches, list) and all(isinstance(p, PatchType) for p in patches): 

158 return Signature( 

159 [ 

160 Parameter("args", Parameter.VAR_POSITIONAL), 

161 Parameter("keywargs", Parameter.VAR_KEYWORD), 

162 ] 

163 ) 

164 

165 if isinstance(getattr(target, "__signature__", None), Signature): 

166 # This special case covers unusual codegen like Pydantic models 

167 sig = target.__signature__ 

168 check_signature(sig) 

169 # And *this* much more complicated block ignores the `self` argument 

170 # if that's been (incorrectly) included in the custom signature. 

171 if sig.parameters and (inspect.isclass(target) or inspect.ismethod(target)): 

172 selfy = next(iter(sig.parameters.values())) 

173 if ( 

174 selfy.name == "self" 

175 and selfy.default is Parameter.empty 

176 and selfy.kind.name.startswith("POSITIONAL_") 

177 ): 

178 return sig.replace( 

179 parameters=[v for k, v in sig.parameters.items() if k != "self"] 

180 ) 

181 return sig 

182 # eval_str is only supported by Python 3.10 and newer 

183 if sys.version_info[:2] >= (3, 10): 

184 sig = inspect.signature( 

185 target, follow_wrapped=follow_wrapped, eval_str=eval_str 

186 ) 

187 else: 

188 sig = inspect.signature( 

189 target, follow_wrapped=follow_wrapped 

190 ) # pragma: no cover 

191 check_signature(sig) 

192 return sig 

193 

194 

195def arg_is_required(param: Parameter) -> bool: 

196 return param.default is Parameter.empty and param.kind in ( 

197 Parameter.POSITIONAL_OR_KEYWORD, 

198 Parameter.KEYWORD_ONLY, 

199 ) 

200 

201 

202def required_args( 

203 target: Callable[..., Any], 

204 args: tuple["SearchStrategy[Any]", ...] = (), 

205 kwargs: Optional[dict[str, Union["SearchStrategy[Any]", EllipsisType]]] = None, 

206) -> set[str]: 

207 """Return a set of names of required args to target that were not supplied 

208 in args or kwargs. 

209 

210 This is used in builds() to determine which arguments to attempt to 

211 fill from type hints. target may be any callable (including classes 

212 and bound methods). args and kwargs should be as they are passed to 

213 builds() - that is, a tuple of values and a dict of names: values. 

214 """ 

215 kwargs = {} if kwargs is None else kwargs 

216 # We start with a workaround for NamedTuples, which don't have nice inits 

217 if inspect.isclass(target) and is_typed_named_tuple(target): 

218 provided = set(kwargs) | set(target._fields[: len(args)]) 

219 return set(target._fields) - provided 

220 # Then we try to do the right thing with inspect.signature 

221 try: 

222 sig = get_signature(target) 

223 except (ValueError, TypeError): 

224 return set() 

225 return { 

226 name 

227 for name, param in list(sig.parameters.items())[len(args) :] 

228 if arg_is_required(param) and name not in kwargs 

229 } 

230 

231 

232def convert_keyword_arguments( 

233 function: Any, args: Sequence[object], kwargs: dict[str, object] 

234) -> tuple[tuple[object, ...], dict[str, object]]: 

235 """Returns a pair of a tuple and a dictionary which would be equivalent 

236 passed as positional and keyword args to the function. Unless function has 

237 kwonlyargs or **kwargs the dictionary will always be empty. 

238 """ 

239 sig = inspect.signature(function, follow_wrapped=False) 

240 bound = sig.bind(*args, **kwargs) 

241 return bound.args, bound.kwargs 

242 

243 

244def convert_positional_arguments( 

245 function: Any, args: Sequence[object], kwargs: dict[str, object] 

246) -> tuple[tuple[object, ...], dict[str, object]]: 

247 """Return a tuple (new_args, new_kwargs) where all possible arguments have 

248 been moved to kwargs. 

249 

250 new_args will only be non-empty if function has pos-only args or *args. 

251 """ 

252 sig = inspect.signature(function, follow_wrapped=False) 

253 bound = sig.bind(*args, **kwargs) 

254 new_args = [] 

255 new_kwargs = dict(bound.arguments) 

256 for p in sig.parameters.values(): 

257 if p.name in new_kwargs: 

258 if p.kind is p.POSITIONAL_ONLY: 

259 new_args.append(new_kwargs.pop(p.name)) 

260 elif p.kind is p.VAR_POSITIONAL: 

261 new_args.extend(new_kwargs.pop(p.name)) 

262 elif p.kind is p.VAR_KEYWORD: 

263 assert set(new_kwargs[p.name]).isdisjoint(set(new_kwargs) - {p.name}) 

264 new_kwargs.update(new_kwargs.pop(p.name)) 

265 return tuple(new_args), new_kwargs 

266 

267 

268def ast_arguments_matches_signature(args: ast.arguments, sig: Signature) -> bool: 

269 expected: list[tuple[str, int]] = [] 

270 for node in args.posonlyargs: 

271 expected.append((node.arg, Parameter.POSITIONAL_ONLY)) 

272 for node in args.args: 

273 expected.append((node.arg, Parameter.POSITIONAL_OR_KEYWORD)) 

274 if args.vararg is not None: 

275 expected.append((args.vararg.arg, Parameter.VAR_POSITIONAL)) 

276 for node in args.kwonlyargs: 

277 expected.append((node.arg, Parameter.KEYWORD_ONLY)) 

278 if args.kwarg is not None: 

279 expected.append((args.kwarg.arg, Parameter.VAR_KEYWORD)) 

280 return expected == [(p.name, p.kind) for p in sig.parameters.values()] 

281 

282 

283def is_first_param_referenced_in_function(f: Any) -> bool: 

284 """Is the given name referenced within f?""" 

285 try: 

286 tree = ast.parse(textwrap.dedent(inspect.getsource(f))) 

287 except Exception: 

288 return True # Assume it's OK unless we know otherwise 

289 name = next(iter(get_signature(f).parameters)) 

290 return any( 

291 isinstance(node, ast.Name) 

292 and node.id == name 

293 and isinstance(node.ctx, ast.Load) 

294 for node in ast.walk(tree) 

295 ) 

296 

297 

298def extract_all_lambdas(tree): 

299 lambdas = [] 

300 

301 class Visitor(ast.NodeVisitor): 

302 def visit_Lambda(self, node): 

303 lambdas.append(node) 

304 

305 Visitor().visit(tree) 

306 return lambdas 

307 

308 

309def _lambda_source_key(f, *, bounded_size=False): 

310 """Returns a digest that differentiates lambdas that have different sources.""" 

311 consts_repr = repr(f.__code__.co_consts) 

312 if bounded_size and len(consts_repr) > 48: 

313 # Compress repr to avoid keeping arbitrarily large strings pinned as cache 

314 # keys. We don't do this unconditionally because hashing takes time, and is 

315 # not necessary if the key is used just for comparison (and is not stored). 

316 consts_repr = hashlib.sha384(consts_repr.encode()).digest() 

317 return ( 

318 consts_repr, 

319 inspect.signature(f), 

320 f.__code__.co_names, 

321 f.__code__.co_code, 

322 f.__code__.co_varnames, 

323 f.__code__.co_freevars, 

324 ) 

325 

326 

327def _mimic_lambda_from_source(f, source): 

328 # Compile a lambda from source where the compiled lambda mimics f as far as 

329 # possible in terms of __code__ and __closure__. The mimicry is far from perfect. 

330 if f.__closure__ is None: 

331 compiled = eval(source, f.__globals__) 

332 else: 

333 # Hack to mimic the capture of vars from local closure. In terms of code 

334 # generation they don't *need* to have the same values (cell_contents), 

335 # just the same names bound to some closure, but hey. 

336 closure = {f"___fv{i}": c.cell_contents for i, c in enumerate(f.__closure__)} 

337 assigns = [f"{name}=___fv{i}" for i, name in enumerate(f.__code__.co_freevars)] 

338 fake_globals = f.__globals__ | closure 

339 exec(f"def construct(): {';'.join(assigns)}; return ({source})", fake_globals) 

340 compiled = fake_globals["construct"]() 

341 return compiled 

342 

343 

344def _lambda_code_matches_source(f, source): 

345 try: 

346 compiled = _mimic_lambda_from_source(f, source) 

347 except (NameError, SyntaxError): # pragma: no cover 

348 return False 

349 return _lambda_source_key(f) == _lambda_source_key(compiled) 

350 

351 

352def _lambda_description(f): 

353 # You might be wondering how a lambda can have a return-type annotation? 

354 # The answer is that we add this at runtime, in new_given_signature(), 

355 # and we do support strange choices as applying @given() to a lambda. 

356 sig = inspect.signature(f) 

357 assert sig.return_annotation in (Parameter.empty, None), sig 

358 

359 # Using pytest-xdist on Python 3.13, there's an entry in the linecache for 

360 # file "<string>", which then returns nonsense to getsource. Discard it. 

361 linecache.cache.pop("<string>", None) 

362 

363 def format_lambda(body): 

364 # The signature is more informative than the corresponding ast.unparse 

365 # output, so add the signature to the unparsed body 

366 return ( 

367 f"lambda {str(sig)[1:-1]}: {body}" if sig.parameters else f"lambda: {body}" 

368 ) 

369 

370 if_confused = format_lambda("<unknown>") 

371 

372 try: 

373 source_lines, lineno0 = inspect.findsource(f) 

374 source_lines = tuple(source_lines) # make it hashable 

375 except OSError: 

376 return if_confused 

377 

378 try: 

379 all_lambdas = AST_LAMBDAS_CACHE[source_lines] 

380 except KeyError: 

381 # The source isn't already parsed, so we try to shortcut by parsing just 

382 # the local block. If that fails to produce a code-identical lambda, 

383 # fall through to the full parse. 

384 local_lines = inspect.getblock(source_lines[lineno0:]) 

385 local_block = textwrap.dedent("".join(local_lines)) 

386 if local_block.startswith("."): 

387 # The fairly common ".map(lambda x: ...)" case. This partial block 

388 # isn't valid syntax, but it might be if we remove the leading ".". 

389 local_block = local_block[1:] 

390 

391 try: 

392 local_tree = ast.parse(local_block) 

393 except SyntaxError: 

394 pass 

395 else: 

396 local_lambdas = extract_all_lambdas(local_tree) 

397 for candidate in local_lambdas: 

398 if ast_arguments_matches_signature(candidate.args, sig): 

399 source = format_lambda(ast.unparse(candidate.body)) 

400 if _lambda_code_matches_source(f, source): 

401 return source 

402 

403 # Local parse failed or didn't produce a match, go ahead with the full parse 

404 try: 

405 tree = ast.parse("".join(source_lines)) 

406 except SyntaxError: # pragma: no cover 

407 all_lambdas = [] 

408 else: 

409 all_lambdas = extract_all_lambdas(tree) 

410 AST_LAMBDAS_CACHE[source_lines] = all_lambdas 

411 

412 # Filter the lambda nodes down to those that match in signature and position, 

413 # and only consider their unique source representations. 

414 aligned_sources = { 

415 format_lambda(ast.unparse(candidate.body)) 

416 for candidate in all_lambdas 

417 if ( 

418 candidate.lineno <= lineno0 + 1 <= candidate.end_lineno 

419 and ast_arguments_matches_signature(candidate.args, sig) 

420 ) 

421 } 

422 

423 # The code-match check has a lot of false negatives in general, so we only do 

424 # that check if we really need to, i.e., if there are multiple aligned lambdas 

425 # having different source representations. If there is only a single lambda 

426 # source found, we use that one without further checking. 

427 

428 # If a user starts a hypothesis process, then edits their code, the lines in the 

429 # parsed source code might not match the live __code__ objects. 

430 # (and on sys.platform == "emscripten", this can happen regardless due to a 

431 # pyodide bug in inspect.getsource()). 

432 # There is a risk of returning source for the wrong lambda, if there is a lambda 

433 # also on the shifted line *or* if the lambda itself is changed. 

434 

435 if len(aligned_sources) == 1: 

436 return next(iter(aligned_sources)) 

437 

438 for source in aligned_sources: 

439 if _lambda_code_matches_source(f, source): 

440 return source 

441 

442 # None of the aligned lambdas match perfectly in generated code. This may be 

443 # caused by differences in code generation, missing nested-scope closures, 

444 # inner lambdas having different identities, etc. The majority of cases will 

445 # have a unique aligned lambda so it doesn't matter that much. 

446 return if_confused 

447 

448 

449def lambda_description(f): 

450 """ 

451 Returns a syntactically-valid expression describing `f`. This is often, but 

452 not always, the exact lambda definition string which appears in the source code. 

453 The difference comes from parsing the lambda ast into `tree` and then returning 

454 the result of `ast.unparse(tree)`, which may differ in whitespace, double vs 

455 single quotes, etc. 

456 

457 Returns a string indicating an unknown body if the parsing gets confused in any way. 

458 """ 

459 try: 

460 return LAMBDA_DESCRIPTION_CACHE[f] 

461 except KeyError: 

462 pass 

463 

464 key = _lambda_source_key(f, bounded_size=True) 

465 try: 

466 description = LAMBDA_DIGEST_DESCRIPTION_CACHE[key] 

467 LAMBDA_DESCRIPTION_CACHE[f] = description 

468 return description 

469 except KeyError: 

470 pass 

471 

472 description = _lambda_description(f) 

473 LAMBDA_DESCRIPTION_CACHE[f] = description 

474 LAMBDA_DIGEST_DESCRIPTION_CACHE[key] = description 

475 return description 

476 

477 

478def get_pretty_function_description(f: object) -> str: 

479 if isinstance(f, partial): 

480 return pretty(f) 

481 if not hasattr(f, "__name__"): 

482 return repr(f) 

483 name = f.__name__ # type: ignore 

484 if name == "<lambda>": 

485 return lambda_description(f) 

486 elif isinstance(f, (types.MethodType, types.BuiltinMethodType)): 

487 self = f.__self__ 

488 # Some objects, like `builtins.abs` are of BuiltinMethodType but have 

489 # their module as __self__. This might include c-extensions generally? 

490 if not (self is None or inspect.isclass(self) or inspect.ismodule(self)): 

491 if self is global_random_instance: 

492 return f"random.{name}" 

493 return f"{self!r}.{name}" 

494 elif isinstance(name, str) and getattr(dict, name, object()) is f: 

495 # special case for keys/values views in from_type() / ghostwriter output 

496 return f"dict.{name}" 

497 return name 

498 

499 

500def nicerepr(v: Any) -> str: 

501 if inspect.isfunction(v): 

502 return get_pretty_function_description(v) 

503 elif isinstance(v, type): 

504 return v.__name__ 

505 else: 

506 # With TypeVar T, show List[T] instead of TypeError on List[~T] 

507 return re.sub(r"(\[)~([A-Z][a-z]*\])", r"\g<1>\g<2>", pretty(v)) 

508 

509 

510def repr_call( 

511 f: Any, args: Sequence[object], kwargs: dict[str, object], *, reorder: bool = True 

512) -> str: 

513 # Note: for multi-line pretty-printing, see RepresentationPrinter.repr_call() 

514 if reorder: 

515 args, kwargs = convert_positional_arguments(f, args, kwargs) 

516 

517 bits = [nicerepr(x) for x in args] 

518 

519 for p in get_signature(f).parameters.values(): 

520 if p.name in kwargs and not p.kind.name.startswith("VAR_"): 

521 bits.append(f"{p.name}={nicerepr(kwargs.pop(p.name))}") 

522 if kwargs: 

523 for a in sorted(kwargs): 

524 bits.append(f"{a}={nicerepr(kwargs[a])}") 

525 

526 rep = nicerepr(f) 

527 if rep.startswith("lambda") and ":" in rep: 

528 rep = f"({rep})" 

529 repr_len = len(rep) + sum(len(b) for b in bits) # approx 

530 if repr_len > 30000: 

531 warnings.warn( 

532 "Generating overly large repr. This is an expensive operation, and with " 

533 f"a length of {repr_len//1000} kB is unlikely to be useful. Use -Wignore " 

534 "to ignore the warning, or -Werror to get a traceback.", 

535 HypothesisWarning, 

536 stacklevel=2, 

537 ) 

538 return rep + "(" + ", ".join(bits) + ")" 

539 

540 

541def check_valid_identifier(identifier: str) -> None: 

542 if not identifier.isidentifier(): 

543 raise ValueError(f"{identifier!r} is not a valid python identifier") 

544 

545 

546eval_cache: dict[str, ModuleType] = {} 

547 

548 

549def source_exec_as_module(source: str) -> ModuleType: 

550 try: 

551 return eval_cache[source] 

552 except KeyError: 

553 pass 

554 

555 hexdigest = hashlib.sha384(source.encode()).hexdigest() 

556 result = ModuleType("hypothesis_temporary_module_" + hexdigest) 

557 assert isinstance(source, str) 

558 exec(source, result.__dict__) 

559 eval_cache[source] = result 

560 return result 

561 

562 

563COPY_SIGNATURE_SCRIPT = """ 

564from hypothesis.utils.conventions import not_set 

565 

566def accept({funcname}): 

567 def {name}{signature}: 

568 return {funcname}({invocation}) 

569 return {name} 

570""".lstrip() 

571 

572 

573def get_varargs( 

574 sig: Signature, kind: int = Parameter.VAR_POSITIONAL 

575) -> Optional[Parameter]: 

576 for p in sig.parameters.values(): 

577 if p.kind is kind: 

578 return p 

579 return None 

580 

581 

582def define_function_signature(name, docstring, signature): 

583 """A decorator which sets the name, signature and docstring of the function 

584 passed into it.""" 

585 if name == "<lambda>": 

586 name = "_lambda_" 

587 check_valid_identifier(name) 

588 for a in signature.parameters: 

589 check_valid_identifier(a) 

590 

591 used_names = {*signature.parameters, name} 

592 

593 newsig = signature.replace( 

594 parameters=[ 

595 p if p.default is signature.empty else p.replace(default=not_set) 

596 for p in ( 

597 p.replace(annotation=signature.empty) 

598 for p in signature.parameters.values() 

599 ) 

600 ], 

601 return_annotation=signature.empty, 

602 ) 

603 

604 pos_args = [ 

605 p 

606 for p in signature.parameters.values() 

607 if p.kind.name.startswith("POSITIONAL_") 

608 ] 

609 

610 def accept(f): 

611 fsig = inspect.signature(f, follow_wrapped=False) 

612 must_pass_as_kwargs = [] 

613 invocation_parts = [] 

614 for p in pos_args: 

615 if p.name not in fsig.parameters and get_varargs(fsig) is None: 

616 must_pass_as_kwargs.append(p.name) 

617 else: 

618 invocation_parts.append(p.name) 

619 if get_varargs(signature) is not None: 

620 invocation_parts.append("*" + get_varargs(signature).name) 

621 for k in must_pass_as_kwargs: 

622 invocation_parts.append(f"{k}={k}") 

623 for p in signature.parameters.values(): 

624 if p.kind is p.KEYWORD_ONLY: 

625 invocation_parts.append(f"{p.name}={p.name}") 

626 varkw = get_varargs(signature, kind=Parameter.VAR_KEYWORD) 

627 if varkw: 

628 invocation_parts.append("**" + varkw.name) 

629 

630 candidate_names = ["f"] + [f"f_{i}" for i in range(1, len(used_names) + 2)] 

631 

632 for funcname in candidate_names: # pragma: no branch 

633 if funcname not in used_names: 

634 break 

635 

636 source = COPY_SIGNATURE_SCRIPT.format( 

637 name=name, 

638 funcname=funcname, 

639 signature=str(newsig), 

640 invocation=", ".join(invocation_parts), 

641 ) 

642 result = source_exec_as_module(source).accept(f) 

643 result.__doc__ = docstring 

644 result.__defaults__ = tuple( 

645 p.default 

646 for p in signature.parameters.values() 

647 if p.default is not signature.empty and "POSITIONAL" in p.kind.name 

648 ) 

649 kwdefaults = { 

650 p.name: p.default 

651 for p in signature.parameters.values() 

652 if p.default is not signature.empty and p.kind is p.KEYWORD_ONLY 

653 } 

654 if kwdefaults: 

655 result.__kwdefaults__ = kwdefaults 

656 annotations = { 

657 p.name: p.annotation 

658 for p in signature.parameters.values() 

659 if p.annotation is not signature.empty 

660 } 

661 if signature.return_annotation is not signature.empty: 

662 annotations["return"] = signature.return_annotation 

663 if annotations: 

664 result.__annotations__ = annotations 

665 return result 

666 

667 return accept 

668 

669 

670def impersonate(target): 

671 """Decorator to update the attributes of a function so that to external 

672 introspectors it will appear to be the target function. 

673 

674 Note that this updates the function in place, it doesn't return a 

675 new one. 

676 """ 

677 

678 def accept(f): 

679 # Lie shamelessly about where this code comes from, to hide the hypothesis 

680 # internals from pytest, ipython, and other runtime introspection. 

681 f.__code__ = f.__code__.replace( 

682 co_filename=target.__code__.co_filename, 

683 co_firstlineno=target.__code__.co_firstlineno, 

684 ) 

685 f.__name__ = target.__name__ 

686 f.__module__ = target.__module__ 

687 f.__doc__ = target.__doc__ 

688 f.__globals__["__hypothesistracebackhide__"] = True 

689 return f 

690 

691 return accept 

692 

693 

694def proxies(target: T) -> Callable[[Callable], T]: 

695 replace_sig = define_function_signature( 

696 target.__name__.replace("<lambda>", "_lambda_"), # type: ignore 

697 target.__doc__, 

698 get_signature(target, follow_wrapped=False), 

699 ) 

700 

701 def accept(proxy): 

702 return impersonate(target)(wraps(target)(replace_sig(proxy))) 

703 

704 return accept 

705 

706 

707def is_identity_function(f: Callable) -> bool: 

708 try: 

709 code = f.__code__ 

710 except AttributeError: 

711 try: 

712 f = f.__call__ # type: ignore 

713 code = f.__code__ 

714 except AttributeError: 

715 return False 

716 

717 # We only accept a single unbound argument. While it would be possible to 

718 # accept extra defaulted arguments, it would be pointless as they couldn't 

719 # be referenced at all in the code object (or the check below would fail). 

720 bound_args = int(inspect.ismethod(f)) 

721 if code.co_argcount != bound_args + 1 or code.co_kwonlyargcount > 0: 

722 return False 

723 

724 # We know that f accepts a single positional argument, now check that its 

725 # code object is simply "return first unbound argument". 

726 template = (lambda self, x: x) if bound_args else (lambda x: x) # type: ignore 

727 try: 

728 return code.co_code == template.__code__.co_code 

729 except AttributeError: # pragma: no cover # pypy only 

730 # In PyPy, some builtin functions have a code object ('builtin-code') 

731 # lacking co_code, perhaps because they are native-compiled and don't have 

732 # a corresponding bytecode. Regardless, since Python doesn't have any 

733 # builtin identity function it seems safe to say that this one isn't 

734 return False