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

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

354 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 os 

19import re 

20import sys 

21import textwrap 

22import types 

23import warnings 

24from collections.abc import MutableMapping, Sequence 

25from functools import partial, wraps 

26from inspect import Parameter, Signature 

27from io import StringIO 

28from keyword import iskeyword 

29from random import _inst as global_random_instance 

30from tokenize import COMMENT, detect_encoding, generate_tokens, untokenize 

31from types import ModuleType 

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

33from unittest.mock import _patch as PatchType 

34from weakref import WeakKeyDictionary 

35 

36from hypothesis.errors import HypothesisWarning 

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 

46READTHEDOCS = os.environ.get("READTHEDOCS", None) == "True" 

47LAMBDA_SOURCE_CACHE: MutableMapping[Callable, str] = WeakKeyDictionary() 

48 

49 

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

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

52 

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

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

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

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

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

58 # looking for types. 

59 return hasattr(obj, "hypothesis_internal_is_this_a_mock_check") 

60 

61 

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

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

64 

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

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

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

68 versions (e.g. ast.Constant) 

69 """ 

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

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

72 try: 

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

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

75 except Exception: 

76 pass 

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

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

79 try: 

80 src = untokenize( 

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

82 ) 

83 except Exception: 

84 pass 

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

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

87 

88 

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

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

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

92 minor changes to the function. 

93 

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

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

96 """ 

97 hasher = hashlib.sha384() 

98 try: 

99 src = inspect.getsource(function) 

100 except (OSError, TypeError): 

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

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

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

104 # with class-dependent behaviour. 

105 try: 

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

107 except AttributeError: 

108 pass 

109 else: 

110 hasher.update(_clean_source(src)) 

111 try: 

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

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

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

115 except Exception: 

116 pass 

117 try: 

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

119 hasher.update(function._hypothesis_internal_add_digest) 

120 except AttributeError: 

121 pass 

122 return hasher.digest() 

123 

124 

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

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

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

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

129 raise ValueError( 

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

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

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

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

134 ) 

135 

136 

137def get_signature( 

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

139) -> Signature: 

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

141 # behaviour of getfullargspec instead of reporting unusable arguments. 

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

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

144 return Signature( 

145 [ 

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

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

148 ] 

149 ) 

150 

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

152 # This special case covers unusual codegen like Pydantic models 

153 sig = target.__signature__ 

154 check_signature(sig) 

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

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

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

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

159 if ( 

160 selfy.name == "self" 

161 and selfy.default is Parameter.empty 

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

163 ): 

164 return sig.replace( 

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

166 ) 

167 return sig 

168 # eval_str is only supported by Python 3.10 and newer 

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

170 sig = inspect.signature( 

171 target, follow_wrapped=follow_wrapped, eval_str=eval_str 

172 ) 

173 else: 

174 sig = inspect.signature( 

175 target, follow_wrapped=follow_wrapped 

176 ) # pragma: no cover 

177 check_signature(sig) 

178 return sig 

179 

180 

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

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

183 Parameter.POSITIONAL_OR_KEYWORD, 

184 Parameter.KEYWORD_ONLY, 

185 ) 

186 

187 

188def required_args( 

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

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

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

192) -> set[str]: 

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

194 in args or kwargs. 

195 

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

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

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

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

200 """ 

201 kwargs = {} if kwargs is None else kwargs 

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

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

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

205 return set(target._fields) - provided 

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

207 try: 

208 sig = get_signature(target) 

209 except (ValueError, TypeError): 

210 return set() 

211 return { 

212 name 

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

214 if arg_is_required(param) and name not in kwargs 

215 } 

216 

217 

218def convert_keyword_arguments( 

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

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

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

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

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

224 """ 

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

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

227 return bound.args, bound.kwargs 

228 

229 

230def convert_positional_arguments( 

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

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

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

234 been moved to kwargs. 

235 

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

237 """ 

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

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

240 new_args = [] 

241 new_kwargs = dict(bound.arguments) 

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

243 if p.name in new_kwargs: 

244 if p.kind is p.POSITIONAL_ONLY: 

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

246 elif p.kind is p.VAR_POSITIONAL: 

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

248 elif p.kind is p.VAR_KEYWORD: 

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

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

251 return tuple(new_args), new_kwargs 

252 

253 

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

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

256 for node in args.posonlyargs: 

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

258 for node in args.args: 

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

260 if args.vararg is not None: 

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

262 for node in args.kwonlyargs: 

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

264 if args.kwarg is not None: 

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

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

267 

268 

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

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

271 try: 

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

273 except Exception: 

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

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

276 return any( 

277 isinstance(node, ast.Name) 

278 and node.id == name 

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

280 for node in ast.walk(tree) 

281 ) 

282 

283 

284def extract_all_lambdas(tree, matching_signature): 

285 lambdas = [] 

286 

287 class Visitor(ast.NodeVisitor): 

288 def visit_Lambda(self, node): 

289 if ast_arguments_matches_signature(node.args, matching_signature): 

290 lambdas.append(node) 

291 

292 Visitor().visit(tree) 

293 

294 return lambdas 

295 

296 

297LINE_CONTINUATION = re.compile(r"\\\n") 

298WHITESPACE = re.compile(r"\s+") 

299PROBABLY_A_COMMENT = re.compile("""#[^'"]*$""") 

300SPACE_FOLLOWS_OPEN_BRACKET = re.compile(r"\( ") 

301SPACE_PRECEDES_CLOSE_BRACKET = re.compile(r" \)") 

302 

303 

304def _extract_lambda_source(f): 

305 """Extracts a single lambda expression from the string source. Returns a 

306 string indicating an unknown body if it gets confused in any way. 

307 

308 This is not a good function and I am sorry for it. Forgive me my 

309 sins, oh lord 

310 """ 

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

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

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

314 sig = inspect.signature(f) 

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

316 

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

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

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

320 

321 if sig.parameters: 

322 if_confused = f"lambda {str(sig)[1:-1]}: <unknown>" 

323 else: 

324 if_confused = "lambda: <unknown>" 

325 try: 

326 source = inspect.getsource(f) 

327 except OSError: 

328 return if_confused 

329 

330 source = LINE_CONTINUATION.sub(" ", source) 

331 source = WHITESPACE.sub(" ", source) 

332 source = source.strip() 

333 if "lambda" not in source and sys.platform == "emscripten": # pragma: no cover 

334 return if_confused # work around Pyodide bug in inspect.getsource() 

335 assert "lambda" in source, source 

336 

337 tree = None 

338 

339 try: 

340 tree = ast.parse(source) 

341 except SyntaxError: 

342 for i in range(len(source) - 1, len("lambda"), -1): 

343 prefix = source[:i] 

344 if "lambda" not in prefix: 

345 break 

346 try: 

347 tree = ast.parse(prefix) 

348 source = prefix 

349 break 

350 except SyntaxError: 

351 continue 

352 if tree is None and source.startswith(("@", ".")): 

353 # This will always eventually find a valid expression because the 

354 # decorator or chained operator must be a valid Python function call, 

355 # so will eventually be syntactically valid and break out of the loop. 

356 # Thus, this loop can never terminate normally. 

357 for i in range(len(source) + 1): 

358 p = source[1:i] 

359 if "lambda" in p: 

360 try: 

361 tree = ast.parse(p) 

362 source = p 

363 break 

364 except SyntaxError: 

365 pass 

366 else: 

367 raise NotImplementedError("expected to be unreachable") 

368 

369 if tree is None: 

370 return if_confused 

371 

372 aligned_lambdas = extract_all_lambdas(tree, matching_signature=sig) 

373 if len(aligned_lambdas) != 1: 

374 return if_confused 

375 lambda_ast = aligned_lambdas[0] 

376 assert lambda_ast.lineno == 1 

377 

378 # If the source code contains Unicode characters, the bytes of the original 

379 # file don't line up with the string indexes, and `col_offset` doesn't match 

380 # the string we're using. We need to convert the source code into bytes 

381 # before slicing. 

382 # 

383 # Under the hood, the inspect module is using `tokenize.detect_encoding` to 

384 # detect the encoding of the original source file. We'll use the same 

385 # approach to get the source code as bytes. 

386 # 

387 # See https://github.com/HypothesisWorks/hypothesis/issues/1700 for an 

388 # example of what happens if you don't correct for this. 

389 # 

390 # Note: if the code doesn't come from a file (but, for example, a doctest), 

391 # `getsourcefile` will return `None` and the `open()` call will fail with 

392 # an OSError. Or if `f` is a built-in function, in which case we get a 

393 # TypeError. In both cases, fall back to splitting the Unicode string. 

394 # It's not perfect, but it's the best we can do. 

395 try: 

396 with open(inspect.getsourcefile(f), "rb") as src_f: 

397 encoding, _ = detect_encoding(src_f.readline) 

398 

399 source_bytes = source.encode(encoding) 

400 source_bytes = source_bytes[lambda_ast.col_offset :].strip() 

401 source = source_bytes.decode(encoding) 

402 except (OSError, TypeError): 

403 source = source[lambda_ast.col_offset :].strip() 

404 

405 # This ValueError can be thrown in Python 3 if: 

406 # 

407 # - There's a Unicode character in the line before the Lambda, and 

408 # - For some reason we can't detect the source encoding of the file 

409 # 

410 # because slicing on `lambda_ast.col_offset` will account for bytes, but 

411 # the slice will be on Unicode characters. 

412 # 

413 # In practice this seems relatively rare, so we just give up rather than 

414 # trying to recover. 

415 try: 

416 source = source[source.index("lambda") :] 

417 except ValueError: 

418 return if_confused 

419 

420 for i in range(len(source), len("lambda"), -1): # pragma: no branch 

421 try: 

422 parsed = ast.parse(source[:i]) 

423 assert len(parsed.body) == 1 

424 assert parsed.body 

425 if isinstance(parsed.body[0].value, ast.Lambda): 

426 source = source[:i] 

427 break 

428 except SyntaxError: 

429 pass 

430 lines = source.split("\n") 

431 lines = [PROBABLY_A_COMMENT.sub("", l) for l in lines] 

432 source = "\n".join(lines) 

433 

434 source = WHITESPACE.sub(" ", source) 

435 source = SPACE_FOLLOWS_OPEN_BRACKET.sub("(", source) 

436 source = SPACE_PRECEDES_CLOSE_BRACKET.sub(")", source) 

437 return source.strip() 

438 

439 

440def extract_lambda_source(f): 

441 try: 

442 return LAMBDA_SOURCE_CACHE[f] 

443 except KeyError: 

444 pass 

445 

446 source = _extract_lambda_source(f) 

447 LAMBDA_SOURCE_CACHE[f] = source 

448 return source 

449 

450 

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

452 if isinstance(f, partial): 

453 return pretty(f) 

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

455 return repr(f) 

456 name = f.__name__ # type: ignore 

457 if name == "<lambda>": 

458 return extract_lambda_source(f) 

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

460 self = f.__self__ 

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

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

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

464 if self is global_random_instance: 

465 return f"random.{name}" 

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

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

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

469 return f"dict.{name}" 

470 return name 

471 

472 

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

474 if inspect.isfunction(v): 

475 return get_pretty_function_description(v) 

476 elif isinstance(v, type): 

477 return v.__name__ 

478 else: 

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

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

481 

482 

483def repr_call( 

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

485) -> str: 

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

487 if reorder: 

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

489 

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

491 

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

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

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

495 if kwargs: 

496 for a in sorted(kwargs): 

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

498 

499 rep = nicerepr(f) 

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

501 rep = f"({rep})" 

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

503 if repr_len > 30000: 

504 warnings.warn( 

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

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

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

508 HypothesisWarning, 

509 stacklevel=2, 

510 ) 

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

512 

513 

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

515 if not identifier.isidentifier(): 

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

517 

518 

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

520 

521 

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

523 try: 

524 return eval_cache[source] 

525 except KeyError: 

526 pass 

527 

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

529 result = ModuleType("hypothesis_temporary_module_" + hexdigest) 

530 assert isinstance(source, str) 

531 exec(source, result.__dict__) 

532 eval_cache[source] = result 

533 return result 

534 

535 

536COPY_SIGNATURE_SCRIPT = """ 

537from hypothesis.utils.conventions import not_set 

538 

539def accept({funcname}): 

540 def {name}{signature}: 

541 return {funcname}({invocation}) 

542 return {name} 

543""".lstrip() 

544 

545 

546def get_varargs( 

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

548) -> Optional[Parameter]: 

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

550 if p.kind is kind: 

551 return p 

552 return None 

553 

554 

555def define_function_signature(name, docstring, signature): 

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

557 passed into it.""" 

558 if name == "<lambda>": 

559 name = "_lambda_" 

560 check_valid_identifier(name) 

561 for a in signature.parameters: 

562 check_valid_identifier(a) 

563 

564 used_names = {*signature.parameters, name} 

565 

566 newsig = signature.replace( 

567 parameters=[ 

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

569 for p in ( 

570 p.replace(annotation=signature.empty) 

571 for p in signature.parameters.values() 

572 ) 

573 ], 

574 return_annotation=signature.empty, 

575 ) 

576 

577 pos_args = [ 

578 p 

579 for p in signature.parameters.values() 

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

581 ] 

582 

583 def accept(f): 

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

585 must_pass_as_kwargs = [] 

586 invocation_parts = [] 

587 for p in pos_args: 

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

589 must_pass_as_kwargs.append(p.name) 

590 else: 

591 invocation_parts.append(p.name) 

592 if get_varargs(signature) is not None: 

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

594 for k in must_pass_as_kwargs: 

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

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

597 if p.kind is p.KEYWORD_ONLY: 

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

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

600 if varkw: 

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

602 

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

604 

605 for funcname in candidate_names: # pragma: no branch 

606 if funcname not in used_names: 

607 break 

608 

609 source = COPY_SIGNATURE_SCRIPT.format( 

610 name=name, 

611 funcname=funcname, 

612 signature=str(newsig), 

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

614 ) 

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

616 result.__doc__ = docstring 

617 result.__defaults__ = tuple( 

618 p.default 

619 for p in signature.parameters.values() 

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

621 ) 

622 kwdefaults = { 

623 p.name: p.default 

624 for p in signature.parameters.values() 

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

626 } 

627 if kwdefaults: 

628 result.__kwdefaults__ = kwdefaults 

629 annotations = { 

630 p.name: p.annotation 

631 for p in signature.parameters.values() 

632 if p.annotation is not signature.empty 

633 } 

634 if signature.return_annotation is not signature.empty: 

635 annotations["return"] = signature.return_annotation 

636 if annotations: 

637 result.__annotations__ = annotations 

638 return result 

639 

640 return accept 

641 

642 

643def impersonate(target): 

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

645 introspectors it will appear to be the target function. 

646 

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

648 new one. 

649 """ 

650 

651 def accept(f): 

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

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

654 f.__code__ = f.__code__.replace( 

655 co_filename=target.__code__.co_filename, 

656 co_firstlineno=target.__code__.co_firstlineno, 

657 ) 

658 f.__name__ = target.__name__ 

659 f.__module__ = target.__module__ 

660 f.__doc__ = target.__doc__ 

661 f.__globals__["__hypothesistracebackhide__"] = True 

662 return f 

663 

664 return accept 

665 

666 

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

668 replace_sig = define_function_signature( 

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

670 target.__doc__, 

671 get_signature(target, follow_wrapped=False), 

672 ) 

673 

674 def accept(proxy): 

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

676 

677 return accept 

678 

679 

680def is_identity_function(f: object) -> bool: 

681 # TODO: pattern-match the AST to handle `def ...` identity functions too 

682 return bool(re.fullmatch(r"lambda (\w+): \1", get_pretty_function_description(f)))