22import sys
33import inspect
44import warnings
5- from typing import Iterable
5+ from collections . abc import Iterable
66from functools import wraps
7- from types import MethodType as MethodType
87from collections import namedtuple
9-
10- try :
11- from unittest import mock
12- except ImportError :
13- try :
14- import mock
15- except ImportError :
16- mock = None
17-
18- try :
19- from collections import OrderedDict as MaybeOrderedDict
20- except ImportError :
21- MaybeOrderedDict = dict
22-
23- from unittest import TestCase
24-
25- try :
26- from unittest import SkipTest
27- except ImportError :
28- class SkipTest (Exception ):
29- pass
30-
31- # NOTE: even though Python 2 support has been dropped, these checks have been
32- # left in place to avoid merge conflicts. They can be removed in the future, and
33- # future code can be written to assume Python 3.
34- PY3 = sys .version_info [0 ] == 3
35- PY2 = sys .version_info [0 ] == 2
36-
37-
38- if PY3 :
39- # Python 3 doesn't have an InstanceType, so just use a dummy type.
40- class InstanceType ():
41- pass
42- lzip = lambda * a : list (zip (* a ))
43- text_type = str
44- string_types = str ,
45- bytes_type = bytes
46- def make_method (func , instance , type ):
47- if instance is None :
48- return func
49- return MethodType (func , instance )
50- else :
51- from types import InstanceType
52- lzip = zip
53- text_type = unicode
54- bytes_type = str
55- string_types = basestring ,
56- def make_method (func , instance , type ):
57- return MethodType (func , instance , type )
58-
59- def to_text (x ):
60- if isinstance (x , text_type ):
61- return x
62- try :
63- return text_type (x , "utf-8" )
64- except UnicodeDecodeError :
65- return text_type (x , "latin1" )
66-
67- CompatArgSpec = namedtuple ("CompatArgSpec" , "args varargs keywords defaults" )
68-
69-
70- def getargspec (func ):
71- if PY2 :
72- return CompatArgSpec (* inspect .getargspec (func ))
73- args = inspect .getfullargspec (func )
74- if args .kwonlyargs :
75- raise TypeError ((
76- "parameterized does not (yet) support functions with keyword "
77- "only arguments, but %r has keyword only arguments. "
78- "Please open an issue with your usecase if this affects you: "
79- "https://github.com/wolever/parameterized/issues/new"
80- ) % (func , ))
81- return CompatArgSpec (* args [:4 ])
8+ from unittest import SkipTest , TestCase , mock
829
8310
8411def skip_on_empty_helper (* a , ** kw ):
@@ -142,10 +69,7 @@ class DummyPatchTarget(object):
14269
14370 @staticmethod
14471 def create_dummy_patch ():
145- if mock is not None :
146- return mock .patch .object (DummyPatchTarget (), "dummy_attribute" , new = None )
147- else :
148- raise ImportError ("Missing mock package" )
72+ return mock .patch .object (DummyPatchTarget (), "dummy_attribute" , new = None )
14973
15074
15175def delete_patches_if_need (func ):
@@ -158,6 +82,7 @@ def delete_patches_if_need(func):
15882
15983_param = namedtuple ("param" , "args kwargs" )
16084
85+
16186class param (_param ):
16287 """ Represents a single parameter to a test case.
16388
@@ -180,7 +105,7 @@ def test_stuff(foo, bar=16):
180105 pass
181106 """
182107
183- def __new__ (cls , * args , ** kwargs ):
108+ def __new__ (cls , * args , ** kwargs ):
184109 return _param .__new__ (cls , args , kwargs )
185110
186111 @classmethod
@@ -225,13 +150,6 @@ def __repr__(self):
225150 return "param(*%r, **%r)" % self
226151
227152
228- class QuietOrderedDict (MaybeOrderedDict ):
229- """ When OrderedDict is available, use it to make sure that the kwargs in
230- doc strings are consistently ordered. """
231- __str__ = dict .__str__
232- __repr__ = dict .__repr__
233-
234-
235153def parameterized_argument_value_pairs (func , p ):
236154 """Return tuples of parameterized arguments and their values.
237155
@@ -261,12 +179,19 @@ def parameterized_argument_value_pairs(func, p):
261179 >>> parameterized_argument_value_pairs(func, p)
262180 [("foo", 1), ("*args", (16, ))]
263181 """
264- argspec = getargspec (func )
182+ argspec = inspect .getfullargspec (func )
183+ if argspec .kwonlyargs :
184+ raise TypeError ((
185+ "parameterized does not (yet) support functions with keyword "
186+ "only arguments, but %r has keyword only arguments. "
187+ "Please open an issue with your usecase if this affects you: "
188+ "https://github.com/wolever/parameterized/issues/new"
189+ ) % (func , ))
265190 arg_offset = 1 if argspec .args [:1 ] == ["self" ] else 0
266191
267192 named_args = argspec .args [arg_offset :]
268193
269- result = lzip ( named_args , p .args )
194+ result = list ( zip ( named_args , p .args ) )
270195 named_args = argspec .args [len (result ) + arg_offset :]
271196 varargs = p .args [len (result ):]
272197
@@ -276,8 +201,8 @@ def parameterized_argument_value_pairs(func, p):
276201 in zip (named_args , argspec .defaults or [])
277202 ])
278203
279- seen_arg_names = set ([ n for (n , _ ) in result ])
280- keywords = QuietOrderedDict (sorted ([
204+ seen_arg_names = { n for (n , _ ) in result }
205+ keywords = dict (sorted ([
281206 (name , p .kwargs [name ])
282207 for name in p .kwargs
283208 if name not in seen_arg_names
@@ -287,7 +212,7 @@ def parameterized_argument_value_pairs(func, p):
287212 result .append (("*%s" % (argspec .varargs , ), tuple (varargs )))
288213
289214 if keywords :
290- result .append (("**%s" % (argspec .keywords , ), keywords ))
215+ result .append (("**%s" % (argspec .varkw , ), keywords ))
291216
292217 return result
293218
@@ -301,7 +226,7 @@ def short_repr(x, n=64):
301226 u"12...89"
302227 """
303228
304- x_repr = to_text ( repr (x ) )
229+ x_repr = repr (x )
305230 if len (x_repr ) > n :
306231 x_repr = x_repr [:n // 2 ] + "..." + x_repr [len (x_repr ) - n // 2 :]
307232 return x_repr
@@ -325,24 +250,21 @@ def default_doc_func(func, num, p):
325250 suffix = "."
326251 first = first [:- 1 ]
327252 args = "%s[with %s]" % (len (first ) and " " or "" , ", " .join (descs ))
328- return "" .join (
329- to_text (x )
330- for x in [first .rstrip (), args , suffix , nl , rest ]
331- )
253+ return "" .join ([first .rstrip (), args , suffix , nl , rest ])
332254
333255
334256def default_name_func (func , num , p ):
335257 base_name = func .__name__
336258 name_suffix = "_%s" % (num , )
337259
338- if len (p .args ) > 0 and isinstance (p .args [0 ], string_types ):
260+ if len (p .args ) > 0 and isinstance (p .args [0 ], str ):
339261 name_suffix += "_" + parameterized .to_safe_name (p .args [0 ])
340262 return base_name + name_suffix
341263
342264
343265_test_runner_override = None
344266_test_runner_guess = False
345- _test_runners = set ([ "unittest" , "unittest2" , "nose" , "nose2" , "pytest" ])
267+ _test_runners = { "unittest" , "unittest2" , "nose" , "nose2" , "pytest" }
346268_test_runner_aliases = {
347269 "_pytest" : "pytest" ,
348270}
@@ -384,7 +306,6 @@ def detect_runner():
384306 return _test_runner_guess
385307
386308
387-
388309class parameterized (object ):
389310 """ Parameterize a test case::
390311
@@ -417,20 +338,10 @@ def __call__(self, test_func):
417338 @wraps (test_func )
418339 def wrapper (test_self = None ):
419340 test_cls = test_self and type (test_self )
420- if test_self is not None :
421- if issubclass (test_cls , InstanceType ):
422- raise TypeError ((
423- "@parameterized can't be used with old-style classes, but "
424- "%r has an old-style class. Consider using a new-style "
425- "class, or '@parameterized.expand' "
426- "(see http://stackoverflow.com/q/54867/71522 for more "
427- "information on old-style classes)."
428- ) % (test_self , ))
429-
430341 original_doc = wrapper .__doc__
431342 for num , args in enumerate (wrapper .parameterized_input ):
432343 p = param .from_decorator (args )
433- unbound_func , nose_tuple = self .param_as_nose_tuple (test_self , test_func , num , p )
344+ nose_tuple = self .param_as_nose_tuple (test_self , test_func , num , p )
434345 try :
435346 wrapper .__doc__ = nose_tuple [0 ].__doc__
436347 # Nose uses `getattr(instance, test_func.__name__)` to get
@@ -439,7 +350,7 @@ def wrapper(test_self=None):
439350 # tests were being enumerated). Set a value here to make
440351 # sure nose can get the correct test method.
441352 if test_self is not None :
442- setattr (test_cls , test_func .__name__ , unbound_func )
353+ setattr (test_cls , test_func .__name__ , nose_tuple [ 0 ]. __func__ )
443354 yield nose_tuple
444355 finally :
445356 if test_self is not None :
@@ -465,21 +376,9 @@ def wrapper(test_self=None):
465376 def param_as_nose_tuple (self , test_self , func , num , p ):
466377 nose_func = wraps (func )(lambda * args : func (* args [:- 1 ], ** args [- 1 ]))
467378 nose_func .__doc__ = self .doc_func (func , num , p )
468- # Track the unbound function because we need to setattr the unbound
469- # function onto the class for nose to work (see comments above), and
470- # Python 3 doesn't let us pull the function out of a bound method.
471- unbound_func = nose_func
472379 if test_self is not None :
473- # Under nose on Py2 we need to return an unbound method to make
474- # sure that the `self` in the method is properly shared with the
475- # `self` used in `setUp` and `tearDown`. But only there. Everyone
476- # else needs a bound method.
477- func_self = (
478- None if PY2 and detect_runner () == "nose" else
479- test_self
480- )
481- nose_func = make_method (nose_func , func_self , type (test_self ))
482- return unbound_func , (nose_func , ) + p .args + (p .kwargs or {}, )
380+ nose_func = nose_func .__get__ (test_self )
381+ return (nose_func , * p .args , p .kwargs or {})
483382
484383 def assert_not_in_testcase_subclass (self ):
485384 parent_classes = self ._terrible_magic_get_defining_classes ()
@@ -521,7 +420,7 @@ def check_input_values(cls, input_values):
521420 # https://github.com/wolever/nose-parameterized/pull/31)
522421 if not isinstance (input_values , list ):
523422 input_values = list (input_values )
524- return [ param .from_decorator (p ) for p in input_values ]
423+ return [param .from_decorator (p ) for p in input_values ]
525424
526425 @classmethod
527426 def expand (cls , input , name_func = None , doc_func = None , skip_on_empty = False ,
@@ -666,7 +565,7 @@ class TestUserAccessLevel(TestCase):
666565
667566 """
668567
669- if isinstance (attrs , string_types ):
568+ if isinstance (attrs , str ):
670569 attrs = [attrs ]
671570
672571 input_dicts = (
@@ -713,13 +612,9 @@ def get_class_name_suffix(params_dict):
713612 if "name" in params_dict :
714613 return parameterized .to_safe_name (params_dict ["name" ])
715614
716- params_vals = (
717- params_dict .values () if PY3 else
718- (v for (_ , v ) in sorted (params_dict .items ()))
719- )
720615 return parameterized .to_safe_name (next ((
721- v for v in params_vals
722- if isinstance (v , string_types )
616+ v for v in params_dict . values ()
617+ if isinstance (v , str )
723618 ), "" ))
724619
725620
0 commit comments