23
23
import warnings
24
24
from collections .abc import MutableMapping , Sequence
25
25
from functools import partial , wraps
26
+ from inspect import Parameter , Signature
26
27
from io import StringIO
27
28
from keyword import iskeyword
28
29
from random import _inst as global_random_instance
29
30
from tokenize import COMMENT , detect_encoding , generate_tokens , untokenize
30
31
from types import ModuleType
31
- from typing import Any , Callable , TypeVar
32
+ from typing import Any , Callable , Optional , TypeVar
32
33
from unittest .mock import _patch as PatchType
33
34
from weakref import WeakKeyDictionary
34
35
43
44
LAMBDA_SOURCE_CACHE : MutableMapping [Callable , str ] = WeakKeyDictionary ()
44
45
45
46
46
- def is_mock (obj ) :
47
+ def is_mock (obj : object ) -> bool :
47
48
"""Determine if the given argument is a mock type."""
48
49
49
50
# We want to be able to detect these when dealing with various test
@@ -118,7 +119,7 @@ def function_digest(function: Any) -> bytes:
118
119
return hasher .digest ()
119
120
120
121
121
- def check_signature (sig : inspect . Signature ) -> None :
122
+ def check_signature (sig : Signature ) -> None :
122
123
# Backport from Python 3.11; see https://github.com/python/cpython/pull/92065
123
124
for p in sig .parameters .values ():
124
125
if iskeyword (p .name ) and p .kind is not p .POSITIONAL_ONLY :
@@ -132,17 +133,19 @@ def check_signature(sig: inspect.Signature) -> None:
132
133
133
134
def get_signature (
134
135
target : Any , * , follow_wrapped : bool = True , eval_str : bool = False
135
- ) -> inspect . Signature :
136
+ ) -> Signature :
136
137
# Special case for use of `@unittest.mock.patch` decorator, mimicking the
137
138
# behaviour of getfullargspec instead of reporting unusable arguments.
138
139
patches = getattr (target , "patchings" , None )
139
140
if isinstance (patches , list ) and all (isinstance (p , PatchType ) for p in patches ):
140
- P = inspect .Parameter
141
- return inspect .Signature (
142
- [P ("args" , P .VAR_POSITIONAL ), P ("keywargs" , P .VAR_KEYWORD )]
141
+ return Signature (
142
+ [
143
+ Parameter ("args" , Parameter .VAR_POSITIONAL ),
144
+ Parameter ("keywargs" , Parameter .VAR_KEYWORD ),
145
+ ]
143
146
)
144
147
145
- if isinstance (getattr (target , "__signature__" , None ), inspect . Signature ):
148
+ if isinstance (getattr (target , "__signature__" , None ), Signature ):
146
149
# This special case covers unusual codegen like Pydantic models
147
150
sig = target .__signature__
148
151
check_signature (sig )
@@ -152,7 +155,7 @@ def get_signature(
152
155
selfy = next (iter (sig .parameters .values ()))
153
156
if (
154
157
selfy .name == "self"
155
- and selfy .default is inspect . Parameter .empty
158
+ and selfy .default is Parameter .empty
156
159
and selfy .kind .name .startswith ("POSITIONAL_" )
157
160
):
158
161
return sig .replace (
@@ -172,10 +175,10 @@ def get_signature(
172
175
return sig
173
176
174
177
175
- def arg_is_required (param ) :
176
- return param .default is inspect . Parameter .empty and param .kind in (
177
- inspect . Parameter .POSITIONAL_OR_KEYWORD ,
178
- inspect . Parameter .KEYWORD_ONLY ,
178
+ def arg_is_required (param : Parameter ) -> bool :
179
+ return param .default is Parameter .empty and param .kind in (
180
+ Parameter .POSITIONAL_OR_KEYWORD ,
181
+ Parameter .KEYWORD_ONLY ,
179
182
)
180
183
181
184
@@ -204,7 +207,9 @@ def required_args(target, args=(), kwargs=()):
204
207
}
205
208
206
209
207
- def convert_keyword_arguments (function , args , kwargs ):
210
+ def convert_keyword_arguments (
211
+ function : Any , args : Sequence [object ], kwargs : dict [str , object ]
212
+ ) -> tuple [tuple [object , ...], dict [str , object ]]:
208
213
"""Returns a pair of a tuple and a dictionary which would be equivalent
209
214
passed as positional and keyword args to the function. Unless function has
210
215
kwonlyargs or **kwargs the dictionary will always be empty.
@@ -238,24 +243,22 @@ def convert_positional_arguments(
238
243
return tuple (new_args ), new_kwargs
239
244
240
245
241
- def ast_arguments_matches_signature (args , sig ):
242
- assert isinstance (args , ast .arguments )
243
- assert isinstance (sig , inspect .Signature )
244
- expected = []
246
+ def ast_arguments_matches_signature (args : ast .arguments , sig : Signature ) -> bool :
247
+ expected : list [tuple [str , int ]] = []
245
248
for node in args .posonlyargs :
246
- expected .append ((node .arg , inspect . Parameter .POSITIONAL_ONLY ))
249
+ expected .append ((node .arg , Parameter .POSITIONAL_ONLY ))
247
250
for node in args .args :
248
- expected .append ((node .arg , inspect . Parameter .POSITIONAL_OR_KEYWORD ))
251
+ expected .append ((node .arg , Parameter .POSITIONAL_OR_KEYWORD ))
249
252
if args .vararg is not None :
250
- expected .append ((args .vararg .arg , inspect . Parameter .VAR_POSITIONAL ))
253
+ expected .append ((args .vararg .arg , Parameter .VAR_POSITIONAL ))
251
254
for node in args .kwonlyargs :
252
- expected .append ((node .arg , inspect . Parameter .KEYWORD_ONLY ))
255
+ expected .append ((node .arg , Parameter .KEYWORD_ONLY ))
253
256
if args .kwarg is not None :
254
- expected .append ((args .kwarg .arg , inspect . Parameter .VAR_KEYWORD ))
257
+ expected .append ((args .kwarg .arg , Parameter .VAR_KEYWORD ))
255
258
return expected == [(p .name , p .kind ) for p in sig .parameters .values ()]
256
259
257
260
258
- def is_first_param_referenced_in_function (f ) :
261
+ def is_first_param_referenced_in_function (f : Any ) -> bool :
259
262
"""Is the given name referenced within f?"""
260
263
try :
261
264
tree = ast .parse (textwrap .dedent (inspect .getsource (f )))
@@ -301,7 +304,7 @@ def _extract_lambda_source(f):
301
304
# The answer is that we add this at runtime, in new_given_signature(),
302
305
# and we do support strange choices as applying @given() to a lambda.
303
306
sig = inspect .signature (f )
304
- assert sig .return_annotation in (inspect . Parameter .empty , None ), sig
307
+ assert sig .return_annotation in (Parameter .empty , None ), sig
305
308
306
309
# Using pytest-xdist on Python 3.13, there's an entry in the linecache for
307
310
# file "<string>", which then returns nonsense to getsource. Discard it.
@@ -459,7 +462,7 @@ def get_pretty_function_description(f: object) -> str:
459
462
return name
460
463
461
464
462
- def nicerepr (v ) :
465
+ def nicerepr (v : Any ) -> str :
463
466
if inspect .isfunction (v ):
464
467
return get_pretty_function_description (v )
465
468
elif isinstance (v , type ):
@@ -500,15 +503,15 @@ def repr_call(
500
503
return rep + "(" + ", " .join (bits ) + ")"
501
504
502
505
503
- def check_valid_identifier (identifier ) :
506
+ def check_valid_identifier (identifier : str ) -> None :
504
507
if not identifier .isidentifier ():
505
508
raise ValueError (f"{ identifier !r} is not a valid python identifier" )
506
509
507
510
508
- eval_cache : dict = {}
511
+ eval_cache : dict [ str , ModuleType ] = {}
509
512
510
513
511
- def source_exec_as_module (source ) :
514
+ def source_exec_as_module (source : str ) -> ModuleType :
512
515
try :
513
516
return eval_cache [source ]
514
517
except KeyError :
@@ -532,7 +535,9 @@ def {name}{signature}:
532
535
""" .lstrip ()
533
536
534
537
535
- def get_varargs (sig , kind = inspect .Parameter .VAR_POSITIONAL ):
538
+ def get_varargs (
539
+ sig : Signature , kind : int = Parameter .VAR_POSITIONAL
540
+ ) -> Optional [Parameter ]:
536
541
for p in sig .parameters .values ():
537
542
if p .kind is kind :
538
543
return p
@@ -583,7 +588,7 @@ def accept(f):
583
588
for p in signature .parameters .values ():
584
589
if p .kind is p .KEYWORD_ONLY :
585
590
invocation_parts .append (f"{ p .name } ={ p .name } " )
586
- varkw = get_varargs (signature , kind = inspect . Parameter .VAR_KEYWORD )
591
+ varkw = get_varargs (signature , kind = Parameter .VAR_KEYWORD )
587
592
if varkw :
588
593
invocation_parts .append ("**" + varkw .name )
589
594
0 commit comments