1# Copyright 2001-2013 Python Software Foundation; All Rights Reserved
2from __future__ import absolute_import, division, print_function
3import collections
4import sys
5
6try:
7    import unittest2 as unittest
8except ImportError:
9    import unittest
10
11import funcsigs as inspect
12
13
14class TestSignatureObject(unittest.TestCase):
15    @staticmethod
16    def signature(func):
17        sig = inspect.signature(func)
18        return (tuple((param.name,
19                       (Ellipsis if param.default is param.empty else param.default),
20                       (Ellipsis if param.annotation is param.empty
21                                                        else param.annotation),
22                       str(param.kind).lower())
23                                    for param in sig.parameters.values()),
24                (Ellipsis if sig.return_annotation is sig.empty
25                                            else sig.return_annotation))
26
27    def __init__(self, *args, **kwargs):
28        unittest.TestCase.__init__(self, *args, **kwargs)
29        if not hasattr(self, 'assertRaisesRegex'):
30            self.assertRaisesRegex = self.assertRaisesRegexp
31
32    if sys.version_info[0] > 2:
33        exec("""
34def test_signature_object(self):
35    S = inspect.Signature
36    P = inspect.Parameter
37
38    self.assertEqual(str(S()), '()')
39
40    def test(po, pk, *args, ko, **kwargs):
41        pass
42    sig = inspect.signature(test)
43    po = sig.parameters['po'].replace(kind=P.POSITIONAL_ONLY)
44    pk = sig.parameters['pk']
45    args = sig.parameters['args']
46    ko = sig.parameters['ko']
47    kwargs = sig.parameters['kwargs']
48
49    S((po, pk, args, ko, kwargs))
50
51    with self.assertRaisesRegex(ValueError, 'wrong parameter order'):
52        S((pk, po, args, ko, kwargs))
53
54    with self.assertRaisesRegex(ValueError, 'wrong parameter order'):
55        S((po, args, pk, ko, kwargs))
56
57    with self.assertRaisesRegex(ValueError, 'wrong parameter order'):
58        S((args, po, pk, ko, kwargs))
59
60    with self.assertRaisesRegex(ValueError, 'wrong parameter order'):
61        S((po, pk, args, kwargs, ko))
62
63    kwargs2 = kwargs.replace(name='args')
64    with self.assertRaisesRegex(ValueError, 'duplicate parameter name'):
65        S((po, pk, args, kwargs2, ko))
66""")
67
68    def test_signature_immutability(self):
69        def test(a):
70            pass
71        sig = inspect.signature(test)
72
73        with self.assertRaises(AttributeError):
74            sig.foo = 'bar'
75
76        # Python2 does not have MappingProxyType class
77        if sys.version_info[:2] < (3, 3):
78            return
79
80        with self.assertRaises(TypeError):
81            sig.parameters['a'] = None
82
83    def test_signature_on_noarg(self):
84        def test():
85            pass
86        self.assertEqual(self.signature(test), ((), Ellipsis))
87
88    if sys.version_info[0] > 2:
89        exec("""
90def test_signature_on_wargs(self):
91    def test(a, b:'foo') -> 123:
92        pass
93    self.assertEqual(self.signature(test),
94                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
95                       ('b', Ellipsis, 'foo', "positional_or_keyword")),
96                      123))
97""")
98
99    if sys.version_info[0] > 2:
100        exec("""
101def test_signature_on_wkwonly(self):
102    def test(*, a:float, b:str) -> int:
103        pass
104    self.assertEqual(self.signature(test),
105                     ((('a', Ellipsis, float, "keyword_only"),
106                       ('b', Ellipsis, str, "keyword_only")),
107                       int))
108""")
109
110    if sys.version_info[0] > 2:
111        exec("""
112def test_signature_on_complex_args(self):
113    def test(a, b:'foo'=10, *args:'bar', spam:'baz', ham=123, **kwargs:int):
114        pass
115    self.assertEqual(self.signature(test),
116                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
117                       ('b', 10, 'foo', "positional_or_keyword"),
118                       ('args', Ellipsis, 'bar', "var_positional"),
119                       ('spam', Ellipsis, 'baz', "keyword_only"),
120                       ('ham', 123, Ellipsis, "keyword_only"),
121                       ('kwargs', Ellipsis, int, "var_keyword")),
122                      Ellipsis))
123""")
124
125    def test_signature_on_builtin_function(self):
126        with self.assertRaisesRegex(ValueError, 'not supported by signature'):
127            inspect.signature(type)
128        with self.assertRaisesRegex(ValueError, 'not supported by signature'):
129            # support for 'wrapper_descriptor'
130            inspect.signature(type.__call__)
131            if hasattr(sys, 'pypy_version_info'):
132                raise ValueError('not supported by signature')
133        with self.assertRaisesRegex(ValueError, 'not supported by signature'):
134            # support for 'method-wrapper'
135            inspect.signature(min.__call__)
136            if hasattr(sys, 'pypy_version_info'):
137                raise ValueError('not supported by signature')
138        with self.assertRaisesRegex(ValueError,
139                                     'no signature found for builtin function'):
140            # support for 'method-wrapper'
141            inspect.signature(min)
142
143    def test_signature_on_non_function(self):
144        with self.assertRaisesRegex(TypeError, 'is not a callable object'):
145            inspect.signature(42)
146
147        with self.assertRaisesRegex(TypeError, 'is not a Python function'):
148            inspect.Signature.from_function(42)
149
150    if sys.version_info[0] > 2:
151        exec("""
152def test_signature_on_method(self):
153    class Test:
154        def foo(self, arg1, arg2=1) -> int:
155            pass
156
157    meth = Test().foo
158
159    self.assertEqual(self.signature(meth),
160                     ((('arg1', Ellipsis, Ellipsis, "positional_or_keyword"),
161                       ('arg2', 1, Ellipsis, "positional_or_keyword")),
162                      int))
163""")
164
165    if sys.version_info[0] > 2:
166        exec("""
167def test_signature_on_classmethod(self):
168    class Test:
169        @classmethod
170        def foo(cls, arg1, *, arg2=1):
171            pass
172
173    meth = Test().foo
174    self.assertEqual(self.signature(meth),
175                     ((('arg1', Ellipsis, Ellipsis, "positional_or_keyword"),
176                       ('arg2', 1, Ellipsis, "keyword_only")),
177                      Ellipsis))
178
179    meth = Test.foo
180    self.assertEqual(self.signature(meth),
181                     ((('arg1', Ellipsis, Ellipsis, "positional_or_keyword"),
182                       ('arg2', 1, Ellipsis, "keyword_only")),
183                      Ellipsis))
184""")
185
186    if sys.version_info[0] > 2:
187        exec("""
188def test_signature_on_staticmethod(self):
189    class Test:
190        @staticmethod
191        def foo(cls, *, arg):
192            pass
193
194    meth = Test().foo
195    self.assertEqual(self.signature(meth),
196                     ((('cls', Ellipsis, Ellipsis, "positional_or_keyword"),
197                       ('arg', Ellipsis, Ellipsis, "keyword_only")),
198                      Ellipsis))
199
200    meth = Test.foo
201    self.assertEqual(self.signature(meth),
202                     ((('cls', Ellipsis, Ellipsis, "positional_or_keyword"),
203                       ('arg', Ellipsis, Ellipsis, "keyword_only")),
204                      Ellipsis))
205""")
206
207    if sys.version_info[0] > 2:
208        exec("""
209def test_signature_on_partial(self):
210    from functools import partial
211
212    def test():
213        pass
214
215    self.assertEqual(self.signature(partial(test)), ((), Ellipsis))
216
217    with self.assertRaisesRegex(ValueError, "has incorrect arguments"):
218        inspect.signature(partial(test, 1))
219
220    with self.assertRaisesRegex(ValueError, "has incorrect arguments"):
221        inspect.signature(partial(test, a=1))
222
223    def test(a, b, *, c, d):
224        pass
225
226    self.assertEqual(self.signature(partial(test)),
227                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
228                       ('b', Ellipsis, Ellipsis, "positional_or_keyword"),
229                       ('c', Ellipsis, Ellipsis, "keyword_only"),
230                       ('d', Ellipsis, Ellipsis, "keyword_only")),
231                      Ellipsis))
232
233    self.assertEqual(self.signature(partial(test, 1)),
234                     ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),
235                       ('c', Ellipsis, Ellipsis, "keyword_only"),
236                       ('d', Ellipsis, Ellipsis, "keyword_only")),
237                      Ellipsis))
238
239    self.assertEqual(self.signature(partial(test, 1, c=2)),
240                     ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),
241                       ('c', 2, Ellipsis, "keyword_only"),
242                       ('d', Ellipsis, Ellipsis, "keyword_only")),
243                      Ellipsis))
244
245    self.assertEqual(self.signature(partial(test, b=1, c=2)),
246                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
247                       ('b', 1, Ellipsis, "positional_or_keyword"),
248                       ('c', 2, Ellipsis, "keyword_only"),
249                       ('d', Ellipsis, Ellipsis, "keyword_only")),
250                      Ellipsis))
251
252    self.assertEqual(self.signature(partial(test, 0, b=1, c=2)),
253                     ((('b', 1, Ellipsis, "positional_or_keyword"),
254                       ('c', 2, Ellipsis, "keyword_only"),
255                       ('d', Ellipsis, Ellipsis, "keyword_only"),),
256                      Ellipsis))
257
258    def test(a, *args, b, **kwargs):
259        pass
260
261    self.assertEqual(self.signature(partial(test, 1)),
262                     ((('args', Ellipsis, Ellipsis, "var_positional"),
263                       ('b', Ellipsis, Ellipsis, "keyword_only"),
264                       ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
265                      Ellipsis))
266
267    self.assertEqual(self.signature(partial(test, 1, 2, 3)),
268                     ((('args', Ellipsis, Ellipsis, "var_positional"),
269                       ('b', Ellipsis, Ellipsis, "keyword_only"),
270                       ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
271                      Ellipsis))
272
273
274    self.assertEqual(self.signature(partial(test, 1, 2, 3, test=True)),
275                     ((('args', Ellipsis, Ellipsis, "var_positional"),
276                       ('b', Ellipsis, Ellipsis, "keyword_only"),
277                       ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
278                      Ellipsis))
279
280    self.assertEqual(self.signature(partial(test, 1, 2, 3, test=1, b=0)),
281                     ((('args', Ellipsis, Ellipsis, "var_positional"),
282                       ('b', 0, Ellipsis, "keyword_only"),
283                       ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
284                      Ellipsis))
285
286    self.assertEqual(self.signature(partial(test, b=0)),
287                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
288                       ('args', Ellipsis, Ellipsis, "var_positional"),
289                       ('b', 0, Ellipsis, "keyword_only"),
290                       ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
291                      Ellipsis))
292
293    self.assertEqual(self.signature(partial(test, b=0, test=1)),
294                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
295                       ('args', Ellipsis, Ellipsis, "var_positional"),
296                       ('b', 0, Ellipsis, "keyword_only"),
297                       ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
298                      Ellipsis))
299
300    def test(a, b, c:int) -> 42:
301        pass
302
303    sig = test.__signature__ = inspect.signature(test)
304
305    self.assertEqual(self.signature(partial(partial(test, 1))),
306                     ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),
307                       ('c', Ellipsis, int, "positional_or_keyword")),
308                      42))
309
310    self.assertEqual(self.signature(partial(partial(test, 1), 2)),
311                     ((('c', Ellipsis, int, "positional_or_keyword"),),
312                      42))
313
314    psig = inspect.signature(partial(partial(test, 1), 2))
315
316    def foo(a):
317        return a
318    _foo = partial(partial(foo, a=10), a=20)
319    self.assertEqual(self.signature(_foo),
320                     ((('a', 20, Ellipsis, "positional_or_keyword"),),
321                      Ellipsis))
322    # check that we don't have any side-effects in signature(),
323    # and the partial object is still functioning
324    self.assertEqual(_foo(), 20)
325
326    def foo(a, b, c):
327        return a, b, c
328    _foo = partial(partial(foo, 1, b=20), b=30)
329    self.assertEqual(self.signature(_foo),
330                     ((('b', 30, Ellipsis, "positional_or_keyword"),
331                       ('c', Ellipsis, Ellipsis, "positional_or_keyword")),
332                      Ellipsis))
333    self.assertEqual(_foo(c=10), (1, 30, 10))
334    _foo = partial(_foo, 2) # now 'b' has two values -
335                            # positional and keyword
336    with self.assertRaisesRegex(ValueError, "has incorrect arguments"):
337        inspect.signature(_foo)
338
339    def foo(a, b, c, *, d):
340        return a, b, c, d
341    _foo = partial(partial(foo, d=20, c=20), b=10, d=30)
342    self.assertEqual(self.signature(_foo),
343                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
344                       ('b', 10, Ellipsis, "positional_or_keyword"),
345                       ('c', 20, Ellipsis, "positional_or_keyword"),
346                       ('d', 30, Ellipsis, "keyword_only")),
347                      Ellipsis))
348    ba = inspect.signature(_foo).bind(a=200, b=11)
349    self.assertEqual(_foo(*ba.args, **ba.kwargs), (200, 11, 20, 30))
350
351    def foo(a=1, b=2, c=3):
352        return a, b, c
353    _foo = partial(foo, a=10, c=13)
354    ba = inspect.signature(_foo).bind(11)
355    self.assertEqual(_foo(*ba.args, **ba.kwargs), (11, 2, 13))
356    ba = inspect.signature(_foo).bind(11, 12)
357    self.assertEqual(_foo(*ba.args, **ba.kwargs), (11, 12, 13))
358    ba = inspect.signature(_foo).bind(11, b=12)
359    self.assertEqual(_foo(*ba.args, **ba.kwargs), (11, 12, 13))
360    ba = inspect.signature(_foo).bind(b=12)
361    self.assertEqual(_foo(*ba.args, **ba.kwargs), (10, 12, 13))
362    _foo = partial(_foo, b=10)
363    ba = inspect.signature(_foo).bind(12, 14)
364    self.assertEqual(_foo(*ba.args, **ba.kwargs), (12, 14, 13))
365""")
366
367    if sys.version_info[0] > 2:
368        exec("""
369def test_signature_on_decorated(self):
370    import functools
371
372    def decorator(func):
373        @functools.wraps(func)
374        def wrapper(*args, **kwargs) -> int:
375            return func(*args, **kwargs)
376        return wrapper
377
378    class Foo:
379        @decorator
380        def bar(self, a, b):
381            pass
382
383    self.assertEqual(self.signature(Foo.bar),
384                     ((('self', Ellipsis, Ellipsis, "positional_or_keyword"),
385                       ('a', Ellipsis, Ellipsis, "positional_or_keyword"),
386                       ('b', Ellipsis, Ellipsis, "positional_or_keyword")),
387                      Ellipsis))
388
389    self.assertEqual(self.signature(Foo().bar),
390                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
391                       ('b', Ellipsis, Ellipsis, "positional_or_keyword")),
392                      Ellipsis))
393
394    # Test that we handle method wrappers correctly
395    def decorator(func):
396        @functools.wraps(func)
397        def wrapper(*args, **kwargs) -> int:
398            return func(42, *args, **kwargs)
399        sig = inspect.signature(func)
400        new_params = tuple(sig.parameters.values())[1:]
401        wrapper.__signature__ = sig.replace(parameters=new_params)
402        return wrapper
403
404    class Foo:
405        @decorator
406        def __call__(self, a, b):
407            pass
408
409    self.assertEqual(self.signature(Foo.__call__),
410                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
411                       ('b', Ellipsis, Ellipsis, "positional_or_keyword")),
412                      Ellipsis))
413
414    self.assertEqual(self.signature(Foo().__call__),
415                     ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),),
416                      Ellipsis))
417""")
418
419    if sys.version_info[0] > 2:
420        exec("""
421def test_signature_on_class(self):
422    class C:
423        def __init__(self, a):
424            pass
425
426    self.assertEqual(self.signature(C),
427                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
428                      Ellipsis))
429
430    class CM(type):
431        def __call__(cls, a):
432            pass
433    class C(metaclass=CM):
434        def __init__(self, b):
435            pass
436
437    self.assertEqual(self.signature(C),
438                     ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
439                      Ellipsis))
440
441    class CM(type):
442        def __new__(mcls, name, bases, dct, *, foo=1):
443            return super().__new__(mcls, name, bases, dct)
444    class C(metaclass=CM):
445        def __init__(self, b):
446            pass
447
448    self.assertEqual(self.signature(C),
449                     ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),),
450                      Ellipsis))
451
452    self.assertEqual(self.signature(CM),
453                     ((('name', Ellipsis, Ellipsis, "positional_or_keyword"),
454                       ('bases', Ellipsis, Ellipsis, "positional_or_keyword"),
455                       ('dct', Ellipsis, Ellipsis, "positional_or_keyword"),
456                       ('foo', 1, Ellipsis, "keyword_only")),
457                      Ellipsis))
458
459    class CMM(type):
460        def __new__(mcls, name, bases, dct, *, foo=1):
461            return super().__new__(mcls, name, bases, dct)
462        def __call__(cls, nm, bs, dt):
463            return type(nm, bs, dt)
464    class CM(type, metaclass=CMM):
465        def __new__(mcls, name, bases, dct, *, bar=2):
466            return super().__new__(mcls, name, bases, dct)
467    class C(metaclass=CM):
468        def __init__(self, b):
469            pass
470
471    self.assertEqual(self.signature(CMM),
472                     ((('name', Ellipsis, Ellipsis, "positional_or_keyword"),
473                       ('bases', Ellipsis, Ellipsis, "positional_or_keyword"),
474                       ('dct', Ellipsis, Ellipsis, "positional_or_keyword"),
475                       ('foo', 1, Ellipsis, "keyword_only")),
476                      Ellipsis))
477
478    self.assertEqual(self.signature(CM),
479                     ((('nm', Ellipsis, Ellipsis, "positional_or_keyword"),
480                       ('bs', Ellipsis, Ellipsis, "positional_or_keyword"),
481                       ('dt', Ellipsis, Ellipsis, "positional_or_keyword")),
482                      Ellipsis))
483
484    self.assertEqual(self.signature(C),
485                     ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),),
486                      Ellipsis))
487
488    class CM(type):
489        def __init__(cls, name, bases, dct, *, bar=2):
490            return super().__init__(name, bases, dct)
491    class C(metaclass=CM):
492        def __init__(self, b):
493            pass
494
495    self.assertEqual(self.signature(CM),
496                     ((('name', Ellipsis, Ellipsis, "positional_or_keyword"),
497                       ('bases', Ellipsis, Ellipsis, "positional_or_keyword"),
498                       ('dct', Ellipsis, Ellipsis, "positional_or_keyword"),
499                       ('bar', 2, Ellipsis, "keyword_only")),
500                      Ellipsis))
501""")
502
503    def test_signature_on_callable_objects(self):
504        class Foo(object):
505            def __call__(self, a):
506                pass
507
508        self.assertEqual(self.signature(Foo()),
509                         ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
510                          Ellipsis))
511
512        class Spam(object):
513            pass
514        with self.assertRaisesRegex(TypeError, "is not a callable object"):
515            inspect.signature(Spam())
516
517        class Bar(Spam, Foo):
518            pass
519
520        self.assertEqual(self.signature(Bar()),
521                         ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
522                          Ellipsis))
523
524        class ToFail(object):
525            __call__ = type
526        with self.assertRaisesRegex(ValueError, "not supported by signature"):
527            inspect.signature(ToFail())
528
529        if sys.version_info[0] < 3:
530            return
531
532        class Wrapped(object):
533            pass
534        Wrapped.__wrapped__ = lambda a: None
535        self.assertEqual(self.signature(Wrapped),
536                         ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
537                          Ellipsis))
538
539    def test_signature_on_lambdas(self):
540        self.assertEqual(self.signature((lambda a=10: a)),
541                         ((('a', 10, Ellipsis, "positional_or_keyword"),),
542                          Ellipsis))
543
544    if sys.version_info[0] > 2:
545        exec("""
546def test_signature_equality(self):
547    def foo(a, *, b:int) -> float: pass
548    self.assertNotEqual(inspect.signature(foo), 42)
549
550    def bar(a, *, b:int) -> float: pass
551    self.assertEqual(inspect.signature(foo), inspect.signature(bar))
552
553    def bar(a, *, b:int) -> int: pass
554    self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
555
556    def bar(a, *, b:int): pass
557    self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
558
559    def bar(a, *, b:int=42) -> float: pass
560    self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
561
562    def bar(a, *, c) -> float: pass
563    self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
564
565    def bar(a, b:int) -> float: pass
566    self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
567    def spam(b:int, a) -> float: pass
568    self.assertNotEqual(inspect.signature(spam), inspect.signature(bar))
569
570    def foo(*, a, b, c): pass
571    def bar(*, c, b, a): pass
572    self.assertEqual(inspect.signature(foo), inspect.signature(bar))
573
574    def foo(*, a=1, b, c): pass
575    def bar(*, c, b, a=1): pass
576    self.assertEqual(inspect.signature(foo), inspect.signature(bar))
577
578    def foo(pos, *, a=1, b, c): pass
579    def bar(pos, *, c, b, a=1): pass
580    self.assertEqual(inspect.signature(foo), inspect.signature(bar))
581
582    def foo(pos, *, a, b, c): pass
583    def bar(pos, *, c, b, a=1): pass
584    self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
585
586    def foo(pos, *args, a=42, b, c, **kwargs:int): pass
587    def bar(pos, *args, c, b, a=42, **kwargs:int): pass
588    self.assertEqual(inspect.signature(foo), inspect.signature(bar))
589""")
590
591    def test_signature_unhashable(self):
592        def foo(a): pass
593        sig = inspect.signature(foo)
594        with self.assertRaisesRegex(TypeError, 'unhashable type'):
595            hash(sig)
596
597
598    if sys.version_info[0] > 2:
599        exec("""
600def test_signature_str(self):
601    def foo(a:int=1, *, b, c=None, **kwargs) -> 42:
602        pass
603    self.assertEqual(str(inspect.signature(foo)),
604                     '(a:int=1, *, b, c=None, **kwargs) -> 42')
605
606    def foo(a:int=1, *args, b, c=None, **kwargs) -> 42:
607        pass
608    self.assertEqual(str(inspect.signature(foo)),
609                     '(a:int=1, *args, b, c=None, **kwargs) -> 42')
610
611    def foo():
612        pass
613    self.assertEqual(str(inspect.signature(foo)), '()')
614""")
615
616    if sys.version_info[0] > 2:
617        exec("""
618def test_signature_str_positional_only(self):
619    P = inspect.Parameter
620
621    def test(a_po, *, b, **kwargs):
622        return a_po, kwargs
623
624    sig = inspect.signature(test)
625    new_params = list(sig.parameters.values())
626    new_params[0] = new_params[0].replace(kind=P.POSITIONAL_ONLY)
627    test.__signature__ = sig.replace(parameters=new_params)
628
629    self.assertEqual(str(inspect.signature(test)),
630                     '(<a_po>, *, b, **kwargs)')
631
632    sig = inspect.signature(test)
633    new_params = list(sig.parameters.values())
634    new_params[0] = new_params[0].replace(name=None)
635    test.__signature__ = sig.replace(parameters=new_params)
636    self.assertEqual(str(inspect.signature(test)),
637                     '(<0>, *, b, **kwargs)')
638""")
639
640    if sys.version_info[0] > 2:
641        exec("""
642def test_signature_replace_anno(self):
643    def test() -> 42:
644        pass
645
646    sig = inspect.signature(test)
647    sig = sig.replace(return_annotation=None)
648    self.assertIs(sig.return_annotation, None)
649    sig = sig.replace(return_annotation=sig.empty)
650    self.assertIs(sig.return_annotation, sig.empty)
651    sig = sig.replace(return_annotation=42)
652    self.assertEqual(sig.return_annotation, 42)
653    self.assertEqual(sig, inspect.signature(test))
654""")
655
656
657class TestParameterObject(unittest.TestCase):
658
659    def __init__(self, *args, **kwargs):
660        unittest.TestCase.__init__(self, *args, **kwargs)
661        if not hasattr(self, 'assertRaisesRegex'):
662            self.assertRaisesRegex = self.assertRaisesRegexp
663
664    def test_signature_parameter_kinds(self):
665        P = inspect.Parameter
666        self.assertTrue(P.POSITIONAL_ONLY < P.POSITIONAL_OR_KEYWORD < \
667                        P.VAR_POSITIONAL < P.KEYWORD_ONLY < P.VAR_KEYWORD)
668
669        self.assertEqual(str(P.POSITIONAL_ONLY), 'POSITIONAL_ONLY')
670        self.assertTrue('POSITIONAL_ONLY' in repr(P.POSITIONAL_ONLY))
671
672    def test_signature_parameter_object(self):
673        p = inspect.Parameter('foo', default=10,
674                              kind=inspect.Parameter.POSITIONAL_ONLY)
675        self.assertEqual(p.name, 'foo')
676        self.assertEqual(p.default, 10)
677        self.assertIs(p.annotation, p.empty)
678        self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
679
680        with self.assertRaisesRegex(ValueError, 'invalid value'):
681            inspect.Parameter('foo', default=10, kind='123')
682
683        with self.assertRaisesRegex(ValueError, 'not a valid parameter name'):
684            inspect.Parameter('1', kind=inspect.Parameter.VAR_KEYWORD)
685
686        with self.assertRaisesRegex(ValueError,
687                                     'non-positional-only parameter'):
688            inspect.Parameter(None, kind=inspect.Parameter.VAR_KEYWORD)
689
690        with self.assertRaisesRegex(ValueError, 'cannot have default values'):
691            inspect.Parameter('a', default=42,
692                              kind=inspect.Parameter.VAR_KEYWORD)
693
694        with self.assertRaisesRegex(ValueError, 'cannot have default values'):
695            inspect.Parameter('a', default=42,
696                              kind=inspect.Parameter.VAR_POSITIONAL)
697
698        p = inspect.Parameter('a', default=42,
699                              kind=inspect.Parameter.POSITIONAL_OR_KEYWORD)
700        with self.assertRaisesRegex(ValueError, 'cannot have default values'):
701            p.replace(kind=inspect.Parameter.VAR_POSITIONAL)
702
703        self.assertTrue(repr(p).startswith('<Parameter'))
704
705    def test_signature_parameter_equality(self):
706        P = inspect.Parameter
707        p = P('foo', default=42, kind=inspect.Parameter.KEYWORD_ONLY)
708
709        self.assertEqual(p, p)
710        self.assertNotEqual(p, 42)
711
712        self.assertEqual(p, P('foo', default=42,
713                              kind=inspect.Parameter.KEYWORD_ONLY))
714
715    def test_signature_parameter_unhashable(self):
716        p = inspect.Parameter('foo', default=42,
717                              kind=inspect.Parameter.KEYWORD_ONLY)
718
719        with self.assertRaisesRegex(TypeError, 'unhashable type'):
720            hash(p)
721
722    def test_signature_parameter_replace(self):
723        p = inspect.Parameter('foo', default=42,
724                              kind=inspect.Parameter.KEYWORD_ONLY)
725
726        self.assertIsNot(p, p.replace())
727        self.assertEqual(p, p.replace())
728
729        p2 = p.replace(annotation=1)
730        self.assertEqual(p2.annotation, 1)
731        p2 = p2.replace(annotation=p2.empty)
732        self.assertEqual(p, p2)
733
734        p2 = p2.replace(name='bar')
735        self.assertEqual(p2.name, 'bar')
736        self.assertNotEqual(p2, p)
737
738        with self.assertRaisesRegex(ValueError, 'not a valid parameter name'):
739            p2 = p2.replace(name=p2.empty)
740
741        p2 = p2.replace(name='foo', default=None)
742        self.assertIs(p2.default, None)
743        self.assertNotEqual(p2, p)
744
745        p2 = p2.replace(name='foo', default=p2.empty)
746        self.assertIs(p2.default, p2.empty)
747
748
749        p2 = p2.replace(default=42, kind=p2.POSITIONAL_OR_KEYWORD)
750        self.assertEqual(p2.kind, p2.POSITIONAL_OR_KEYWORD)
751        self.assertNotEqual(p2, p)
752
753        with self.assertRaisesRegex(ValueError, 'invalid value for'):
754            p2 = p2.replace(kind=p2.empty)
755
756        p2 = p2.replace(kind=p2.KEYWORD_ONLY)
757        self.assertEqual(p2, p)
758
759    def test_signature_parameter_positional_only(self):
760        p = inspect.Parameter(None, kind=inspect.Parameter.POSITIONAL_ONLY)
761        self.assertEqual(str(p), '<>')
762
763        p = p.replace(name='1')
764        self.assertEqual(str(p), '<1>')
765
766    def test_signature_parameter_immutability(self):
767        p = inspect.Parameter(None, kind=inspect.Parameter.POSITIONAL_ONLY)
768
769        with self.assertRaises(AttributeError):
770            p.foo = 'bar'
771
772        with self.assertRaises(AttributeError):
773            p.kind = 123
774
775
776class TestSignatureBind(unittest.TestCase):
777    @staticmethod
778    def call(func, *args, **kwargs):
779        sig = inspect.signature(func)
780        ba = sig.bind(*args, **kwargs)
781        return func(*ba.args, **ba.kwargs)
782
783    def __init__(self, *args, **kwargs):
784        unittest.TestCase.__init__(self, *args, **kwargs)
785        if not hasattr(self, 'assertRaisesRegex'):
786            self.assertRaisesRegex = self.assertRaisesRegexp
787
788    def test_signature_bind_empty(self):
789        def test():
790            return 42
791
792        self.assertEqual(self.call(test), 42)
793        with self.assertRaisesRegex(TypeError, 'too many positional arguments'):
794            self.call(test, 1)
795        with self.assertRaisesRegex(TypeError, 'too many positional arguments'):
796            self.call(test, 1, spam=10)
797        with self.assertRaisesRegex(TypeError, 'too many keyword arguments'):
798            self.call(test, spam=1)
799
800    def test_signature_bind_var(self):
801        def test(*args, **kwargs):
802            return args, kwargs
803
804        self.assertEqual(self.call(test), ((), {}))
805        self.assertEqual(self.call(test, 1), ((1,), {}))
806        self.assertEqual(self.call(test, 1, 2), ((1, 2), {}))
807        self.assertEqual(self.call(test, foo='bar'), ((), {'foo': 'bar'}))
808        self.assertEqual(self.call(test, 1, foo='bar'), ((1,), {'foo': 'bar'}))
809        self.assertEqual(self.call(test, args=10), ((), {'args': 10}))
810        self.assertEqual(self.call(test, 1, 2, foo='bar'),
811                         ((1, 2), {'foo': 'bar'}))
812
813    def test_signature_bind_just_args(self):
814        def test(a, b, c):
815            return a, b, c
816
817        self.assertEqual(self.call(test, 1, 2, 3), (1, 2, 3))
818
819        with self.assertRaisesRegex(TypeError, 'too many positional arguments'):
820            self.call(test, 1, 2, 3, 4)
821
822        with self.assertRaisesRegex(TypeError, "'b' parameter lacking default"):
823            self.call(test, 1)
824
825        with self.assertRaisesRegex(TypeError, "'a' parameter lacking default"):
826            self.call(test)
827
828        def test(a, b, c=10):
829            return a, b, c
830        self.assertEqual(self.call(test, 1, 2, 3), (1, 2, 3))
831        self.assertEqual(self.call(test, 1, 2), (1, 2, 10))
832
833        def test(a=1, b=2, c=3):
834            return a, b, c
835        self.assertEqual(self.call(test, a=10, c=13), (10, 2, 13))
836        self.assertEqual(self.call(test, a=10), (10, 2, 3))
837        self.assertEqual(self.call(test, b=10), (1, 10, 3))
838
839    def test_signature_bind_varargs_order(self):
840        def test(*args):
841            return args
842
843        self.assertEqual(self.call(test), ())
844        self.assertEqual(self.call(test, 1, 2, 3), (1, 2, 3))
845
846    def test_signature_bind_args_and_varargs(self):
847        def test(a, b, c=3, *args):
848            return a, b, c, args
849
850        self.assertEqual(self.call(test, 1, 2, 3, 4, 5), (1, 2, 3, (4, 5)))
851        self.assertEqual(self.call(test, 1, 2), (1, 2, 3, ()))
852        self.assertEqual(self.call(test, b=1, a=2), (2, 1, 3, ()))
853        self.assertEqual(self.call(test, 1, b=2), (1, 2, 3, ()))
854
855        with self.assertRaisesRegex(TypeError,
856                                     "multiple values for argument 'c'"):
857            self.call(test, 1, 2, 3, c=4)
858
859    def test_signature_bind_just_kwargs(self):
860        def test(**kwargs):
861            return kwargs
862
863        self.assertEqual(self.call(test), {})
864        self.assertEqual(self.call(test, foo='bar', spam='ham'),
865                         {'foo': 'bar', 'spam': 'ham'})
866
867    def test_signature_bind_args_and_kwargs(self):
868        def test(a, b, c=3, **kwargs):
869            return a, b, c, kwargs
870
871        self.assertEqual(self.call(test, 1, 2), (1, 2, 3, {}))
872        self.assertEqual(self.call(test, 1, 2, foo='bar', spam='ham'),
873                         (1, 2, 3, {'foo': 'bar', 'spam': 'ham'}))
874        self.assertEqual(self.call(test, b=2, a=1, foo='bar', spam='ham'),
875                         (1, 2, 3, {'foo': 'bar', 'spam': 'ham'}))
876        self.assertEqual(self.call(test, a=1, b=2, foo='bar', spam='ham'),
877                         (1, 2, 3, {'foo': 'bar', 'spam': 'ham'}))
878        self.assertEqual(self.call(test, 1, b=2, foo='bar', spam='ham'),
879                         (1, 2, 3, {'foo': 'bar', 'spam': 'ham'}))
880        self.assertEqual(self.call(test, 1, b=2, c=4, foo='bar', spam='ham'),
881                         (1, 2, 4, {'foo': 'bar', 'spam': 'ham'}))
882        self.assertEqual(self.call(test, 1, 2, 4, foo='bar'),
883                         (1, 2, 4, {'foo': 'bar'}))
884        self.assertEqual(self.call(test, c=5, a=4, b=3),
885                         (4, 3, 5, {}))
886
887    if sys.version_info[0] > 2:
888        exec("""
889def test_signature_bind_kwonly(self):
890    def test(*, foo):
891        return foo
892    with self.assertRaisesRegex(TypeError,
893                                 'too many positional arguments'):
894        self.call(test, 1)
895    self.assertEqual(self.call(test, foo=1), 1)
896
897    def test(a, *, foo=1, bar):
898        return foo
899    with self.assertRaisesRegex(TypeError,
900                                 "'bar' parameter lacking default value"):
901        self.call(test, 1)
902
903    def test(foo, *, bar):
904        return foo, bar
905    self.assertEqual(self.call(test, 1, bar=2), (1, 2))
906    self.assertEqual(self.call(test, bar=2, foo=1), (1, 2))
907
908    with self.assertRaisesRegex(TypeError,
909                                 'too many keyword arguments'):
910        self.call(test, bar=2, foo=1, spam=10)
911
912    with self.assertRaisesRegex(TypeError,
913                                 'too many positional arguments'):
914        self.call(test, 1, 2)
915
916    with self.assertRaisesRegex(TypeError,
917                                 'too many positional arguments'):
918        self.call(test, 1, 2, bar=2)
919
920    with self.assertRaisesRegex(TypeError,
921                                 'too many keyword arguments'):
922        self.call(test, 1, bar=2, spam='ham')
923
924    with self.assertRaisesRegex(TypeError,
925                                 "'bar' parameter lacking default value"):
926        self.call(test, 1)
927
928    def test(foo, *, bar, **bin):
929        return foo, bar, bin
930    self.assertEqual(self.call(test, 1, bar=2), (1, 2, {}))
931    self.assertEqual(self.call(test, foo=1, bar=2), (1, 2, {}))
932    self.assertEqual(self.call(test, 1, bar=2, spam='ham'),
933                     (1, 2, {'spam': 'ham'}))
934    self.assertEqual(self.call(test, spam='ham', foo=1, bar=2),
935                     (1, 2, {'spam': 'ham'}))
936    with self.assertRaisesRegex(TypeError,
937                                 "'foo' parameter lacking default value"):
938        self.call(test, spam='ham', bar=2)
939    self.assertEqual(self.call(test, 1, bar=2, bin=1, spam=10),
940                     (1, 2, {'bin': 1, 'spam': 10}))
941""")
942#
943    if sys.version_info[0] > 2:
944        exec("""
945def test_signature_bind_arguments(self):
946    def test(a, *args, b, z=100, **kwargs):
947        pass
948    sig = inspect.signature(test)
949    ba = sig.bind(10, 20, b=30, c=40, args=50, kwargs=60)
950    # we won't have 'z' argument in the bound arguments object, as we didn't
951    # pass it to the 'bind'
952    self.assertEqual(tuple(ba.arguments.items()),
953                     (('a', 10), ('args', (20,)), ('b', 30),
954                      ('kwargs', {'c': 40, 'args': 50, 'kwargs': 60})))
955    self.assertEqual(ba.kwargs,
956                     {'b': 30, 'c': 40, 'args': 50, 'kwargs': 60})
957    self.assertEqual(ba.args, (10, 20))
958""")
959#
960    if sys.version_info[0] > 2:
961        exec("""
962def test_signature_bind_positional_only(self):
963    P = inspect.Parameter
964
965    def test(a_po, b_po, c_po=3, foo=42, *, bar=50, **kwargs):
966        return a_po, b_po, c_po, foo, bar, kwargs
967
968    sig = inspect.signature(test)
969    new_params = collections.OrderedDict(tuple(sig.parameters.items()))
970    for name in ('a_po', 'b_po', 'c_po'):
971        new_params[name] = new_params[name].replace(kind=P.POSITIONAL_ONLY)
972    new_sig = sig.replace(parameters=new_params.values())
973    test.__signature__ = new_sig
974
975    self.assertEqual(self.call(test, 1, 2, 4, 5, bar=6),
976                     (1, 2, 4, 5, 6, {}))
977
978    with self.assertRaisesRegex(TypeError, "parameter is positional only"):
979        self.call(test, 1, 2, c_po=4)
980
981    with self.assertRaisesRegex(TypeError, "parameter is positional only"):
982        self.call(test, a_po=1, b_po=2)
983""")
984
985
986class TestBoundArguments(unittest.TestCase):
987
988    def __init__(self, *args, **kwargs):
989        unittest.TestCase.__init__(self, *args, **kwargs)
990        if not hasattr(self, 'assertRaisesRegex'):
991            self.assertRaisesRegex = self.assertRaisesRegexp
992
993    def test_signature_bound_arguments_unhashable(self):
994        def foo(a): pass
995        ba = inspect.signature(foo).bind(1)
996
997        with self.assertRaisesRegex(TypeError, 'unhashable type'):
998            hash(ba)
999
1000    def test_signature_bound_arguments_equality(self):
1001        def foo(a): pass
1002        ba = inspect.signature(foo).bind(1)
1003        self.assertEqual(ba, ba)
1004
1005        ba2 = inspect.signature(foo).bind(1)
1006        self.assertEqual(ba, ba2)
1007
1008        ba3 = inspect.signature(foo).bind(2)
1009        self.assertNotEqual(ba, ba3)
1010        ba3.arguments['a'] = 1
1011        self.assertEqual(ba, ba3)
1012
1013        def bar(b): pass
1014        ba4 = inspect.signature(bar).bind(1)
1015        self.assertNotEqual(ba, ba4)
1016
1017
1018if __name__ == "__main__":
1019    unittest.begin()
1020