2
2
python version compatibility code
3
3
"""
4
4
from __future__ import absolute_import , division , print_function
5
- import sys
5
+
6
+ import codecs
7
+ import functools
6
8
import inspect
7
9
import re
8
- import functools
9
- import codecs
10
+ import sys
10
11
11
12
import py
12
13
25
26
_PY2 = not _PY3
26
27
27
28
29
+ if _PY3 :
30
+ from inspect import signature , Parameter as Parameter
31
+ else :
32
+ from funcsigs import signature , Parameter as Parameter
33
+
34
+
28
35
NoneType = type (None )
29
36
NOTSET = object ()
30
37
31
38
PY35 = sys .version_info [:2 ] >= (3 , 5 )
32
39
PY36 = sys .version_info [:2 ] >= (3 , 6 )
33
40
MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
34
41
35
- if hasattr (inspect , 'signature' ):
36
- def _format_args (func ):
37
- return str (inspect .signature (func ))
38
- else :
39
- def _format_args (func ):
40
- return inspect .formatargspec (* inspect .getargspec (func ))
42
+
43
+ def _format_args (func ):
44
+ return str (signature (func ))
45
+
41
46
42
47
isfunction = inspect .isfunction
43
48
isclass = inspect .isclass
@@ -63,7 +68,6 @@ def iscoroutinefunction(func):
63
68
64
69
65
70
def getlocation (function , curdir ):
66
- import inspect
67
71
fn = py .path .local (inspect .getfile (function ))
68
72
lineno = py .builtin ._getcode (function ).co_firstlineno
69
73
if fn .relto (curdir ):
@@ -83,40 +87,45 @@ def num_mock_patch_args(function):
83
87
return len (patchings )
84
88
85
89
86
- def getfuncargnames (function , startindex = None , cls = None ):
87
- """
88
- @RonnyPfannschmidt: This function should be refactored when we revisit fixtures. The
89
- fixture mechanism should ask the node for the fixture names, and not try to obtain
90
- directly from the function object well after collection has occurred.
90
+ def getfuncargnames (function , is_method = False , cls = None ):
91
+ """Returns the names of a function's mandatory arguments.
92
+
93
+ This should return the names of all function arguments that:
94
+ * Aren't bound to an instance or type as in instance or class methods.
95
+ * Don't have default values.
96
+ * Aren't bound with functools.partial.
97
+ * Aren't replaced with mocks.
98
+
99
+ The is_method and cls arguments indicate that the function should
100
+ be treated as a bound method even though it's not unless, only in
101
+ the case of cls, the function is a static method.
102
+
103
+ @RonnyPfannschmidt: This function should be refactored when we
104
+ revisit fixtures. The fixture mechanism should ask the node for
105
+ the fixture names, and not try to obtain directly from the
106
+ function object well after collection has occurred.
107
+
91
108
"""
92
- if startindex is None and cls is not None :
93
- is_staticmethod = isinstance (cls .__dict__ .get (function .__name__ , None ), staticmethod )
94
- startindex = 0 if is_staticmethod else 1
95
- # XXX merge with main.py's varnames
96
- # assert not isclass(function)
97
- realfunction = function
98
- while hasattr (realfunction , "__wrapped__" ):
99
- realfunction = realfunction .__wrapped__
100
- if startindex is None :
101
- startindex = inspect .ismethod (function ) and 1 or 0
102
- if realfunction != function :
103
- startindex += num_mock_patch_args (function )
104
- function = realfunction
105
- if isinstance (function , functools .partial ):
106
- argnames = inspect .getargs (_pytest ._code .getrawcode (function .func ))[0 ]
107
- partial = function
108
- argnames = argnames [len (partial .args ):]
109
- if partial .keywords :
110
- for kw in partial .keywords :
111
- argnames .remove (kw )
112
- else :
113
- argnames = inspect .getargs (_pytest ._code .getrawcode (function ))[0 ]
114
- defaults = getattr (function , 'func_defaults' ,
115
- getattr (function , '__defaults__' , None )) or ()
116
- numdefaults = len (defaults )
117
- if numdefaults :
118
- return tuple (argnames [startindex :- numdefaults ])
119
- return tuple (argnames [startindex :])
109
+ # The parameters attribute of a Signature object contains an
110
+ # ordered mapping of parameter names to Parameter instances. This
111
+ # creates a tuple of the names of the parameters that don't have
112
+ # defaults.
113
+ arg_names = tuple (
114
+ p .name for p in signature (function ).parameters .values ()
115
+ if (p .kind is Parameter .POSITIONAL_OR_KEYWORD
116
+ or p .kind is Parameter .KEYWORD_ONLY ) and
117
+ p .default is Parameter .empty )
118
+ # If this function should be treated as a bound method even though
119
+ # it's passed as an unbound method or function, remove the first
120
+ # parameter name.
121
+ if (is_method or
122
+ (cls and not isinstance (cls .__dict__ .get (function .__name__ , None ),
123
+ staticmethod ))):
124
+ arg_names = arg_names [1 :]
125
+ # Remove any names that will be replaced with mocks.
126
+ if hasattr (function , "__wrapped__" ):
127
+ arg_names = arg_names [num_mock_patch_args (function ):]
128
+ return arg_names
120
129
121
130
122
131
if _PY3 :
0 commit comments