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