(root)/
Python-3.12.0/
Lib/
test/
test_extcall.py
       1  
       2  """Doctest for method/function calls.
       3  
       4  We're going the use these types for extra testing
       5  
       6      >>> from collections import UserList
       7      >>> from collections import UserDict
       8  
       9  We're defining four helper functions
      10  
      11      >>> from test import support
      12      >>> def e(a,b):
      13      ...     print(a, b)
      14  
      15      >>> def f(*a, **k):
      16      ...     print(a, support.sortdict(k))
      17  
      18      >>> def g(x, *y, **z):
      19      ...     print(x, y, support.sortdict(z))
      20  
      21      >>> def h(j=1, a=2, h=3):
      22      ...     print(j, a, h)
      23  
      24  Argument list examples
      25  
      26      >>> f()
      27      () {}
      28      >>> f(1)
      29      (1,) {}
      30      >>> f(1, 2)
      31      (1, 2) {}
      32      >>> f(1, 2, 3)
      33      (1, 2, 3) {}
      34      >>> f(1, 2, 3, *(4, 5))
      35      (1, 2, 3, 4, 5) {}
      36      >>> f(1, 2, 3, *[4, 5])
      37      (1, 2, 3, 4, 5) {}
      38      >>> f(*[1, 2, 3], 4, 5)
      39      (1, 2, 3, 4, 5) {}
      40      >>> f(1, 2, 3, *UserList([4, 5]))
      41      (1, 2, 3, 4, 5) {}
      42      >>> f(1, 2, 3, *[4, 5], *[6, 7])
      43      (1, 2, 3, 4, 5, 6, 7) {}
      44      >>> f(1, *[2, 3], 4, *[5, 6], 7)
      45      (1, 2, 3, 4, 5, 6, 7) {}
      46      >>> f(*UserList([1, 2]), *UserList([3, 4]), 5, *UserList([6, 7]))
      47      (1, 2, 3, 4, 5, 6, 7) {}
      48  
      49  Here we add keyword arguments
      50  
      51      >>> f(1, 2, 3, **{'a':4, 'b':5})
      52      (1, 2, 3) {'a': 4, 'b': 5}
      53      >>> f(1, 2, **{'a': -1, 'b': 5}, **{'a': 4, 'c': 6})
      54      Traceback (most recent call last):
      55          ...
      56      TypeError: test.test_extcall.f() got multiple values for keyword argument 'a'
      57      >>> f(1, 2, **{'a': -1, 'b': 5}, a=4, c=6)
      58      Traceback (most recent call last):
      59          ...
      60      TypeError: test.test_extcall.f() got multiple values for keyword argument 'a'
      61      >>> f(1, 2, a=3, **{'a': 4}, **{'a': 5})
      62      Traceback (most recent call last):
      63          ...
      64      TypeError: test.test_extcall.f() got multiple values for keyword argument 'a'
      65      >>> f(1, 2, 3, *[4, 5], **{'a':6, 'b':7})
      66      (1, 2, 3, 4, 5) {'a': 6, 'b': 7}
      67      >>> f(1, 2, 3, x=4, y=5, *(6, 7), **{'a':8, 'b': 9})
      68      (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
      69      >>> f(1, 2, 3, *[4, 5], **{'c': 8}, **{'a':6, 'b':7})
      70      (1, 2, 3, 4, 5) {'a': 6, 'b': 7, 'c': 8}
      71      >>> f(1, 2, 3, *(4, 5), x=6, y=7, **{'a':8, 'b': 9})
      72      (1, 2, 3, 4, 5) {'a': 8, 'b': 9, 'x': 6, 'y': 7}
      73  
      74      >>> f(1, 2, 3, **UserDict(a=4, b=5))
      75      (1, 2, 3) {'a': 4, 'b': 5}
      76      >>> f(1, 2, 3, *(4, 5), **UserDict(a=6, b=7))
      77      (1, 2, 3, 4, 5) {'a': 6, 'b': 7}
      78      >>> f(1, 2, 3, x=4, y=5, *(6, 7), **UserDict(a=8, b=9))
      79      (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
      80      >>> f(1, 2, 3, *(4, 5), x=6, y=7, **UserDict(a=8, b=9))
      81      (1, 2, 3, 4, 5) {'a': 8, 'b': 9, 'x': 6, 'y': 7}
      82  
      83  Mix keyword arguments and dict unpacking
      84  
      85      >>> d1 = {'a':1}
      86  
      87      >>> d2 = {'c':3}
      88  
      89      >>> f(b=2, **d1, **d2)
      90      () {'a': 1, 'b': 2, 'c': 3}
      91  
      92      >>> f(**d1, b=2, **d2)
      93      () {'a': 1, 'b': 2, 'c': 3}
      94  
      95      >>> f(**d1, **d2, b=2)
      96      () {'a': 1, 'b': 2, 'c': 3}
      97  
      98      >>> f(**d1, b=2, **d2, d=4)
      99      () {'a': 1, 'b': 2, 'c': 3, 'd': 4}
     100  
     101  Examples with invalid arguments (TypeErrors). We're also testing the function
     102  names in the exception messages.
     103  
     104  Verify clearing of SF bug #733667
     105  
     106      >>> e(c=4)
     107      Traceback (most recent call last):
     108        ...
     109      TypeError: e() got an unexpected keyword argument 'c'
     110  
     111      >>> g()
     112      Traceback (most recent call last):
     113        ...
     114      TypeError: g() missing 1 required positional argument: 'x'
     115  
     116      >>> g(*())
     117      Traceback (most recent call last):
     118        ...
     119      TypeError: g() missing 1 required positional argument: 'x'
     120  
     121      >>> g(*(), **{})
     122      Traceback (most recent call last):
     123        ...
     124      TypeError: g() missing 1 required positional argument: 'x'
     125  
     126      >>> g(1)
     127      1 () {}
     128      >>> g(1, 2)
     129      1 (2,) {}
     130      >>> g(1, 2, 3)
     131      1 (2, 3) {}
     132      >>> g(1, 2, 3, *(4, 5))
     133      1 (2, 3, 4, 5) {}
     134  
     135      >>> class Nothing: pass
     136      ...
     137      >>> g(*Nothing())
     138      Traceback (most recent call last):
     139        ...
     140      TypeError: test.test_extcall.g() argument after * must be an iterable, not Nothing
     141  
     142      >>> class Nothing:
     143      ...     def __len__(self): return 5
     144      ...
     145  
     146      >>> g(*Nothing())
     147      Traceback (most recent call last):
     148        ...
     149      TypeError: test.test_extcall.g() argument after * must be an iterable, not Nothing
     150  
     151      >>> class Nothing():
     152      ...     def __len__(self): return 5
     153      ...     def __getitem__(self, i):
     154      ...         if i<3: return i
     155      ...         else: raise IndexError(i)
     156      ...
     157  
     158      >>> g(*Nothing())
     159      0 (1, 2) {}
     160  
     161      >>> class Nothing:
     162      ...     def __init__(self): self.c = 0
     163      ...     def __iter__(self): return self
     164      ...     def __next__(self):
     165      ...         if self.c == 4:
     166      ...             raise StopIteration
     167      ...         c = self.c
     168      ...         self.c += 1
     169      ...         return c
     170      ...
     171  
     172      >>> g(*Nothing())
     173      0 (1, 2, 3) {}
     174  
     175  Check for issue #4806: Does a TypeError in a generator get propagated with the
     176  right error message? (Also check with other iterables.)
     177  
     178      >>> def broken(): raise TypeError("myerror")
     179      ...
     180  
     181      >>> g(*(broken() for i in range(1)))
     182      Traceback (most recent call last):
     183        ...
     184      TypeError: myerror
     185      >>> g(*range(1), *(broken() for i in range(1)))
     186      Traceback (most recent call last):
     187        ...
     188      TypeError: myerror
     189  
     190      >>> class BrokenIterable1:
     191      ...     def __iter__(self):
     192      ...         raise TypeError('myerror')
     193      ...
     194      >>> g(*BrokenIterable1())
     195      Traceback (most recent call last):
     196        ...
     197      TypeError: myerror
     198      >>> g(*range(1), *BrokenIterable1())
     199      Traceback (most recent call last):
     200        ...
     201      TypeError: myerror
     202  
     203      >>> class BrokenIterable2:
     204      ...     def __iter__(self):
     205      ...         yield 0
     206      ...         raise TypeError('myerror')
     207      ...
     208      >>> g(*BrokenIterable2())
     209      Traceback (most recent call last):
     210        ...
     211      TypeError: myerror
     212      >>> g(*range(1), *BrokenIterable2())
     213      Traceback (most recent call last):
     214        ...
     215      TypeError: myerror
     216  
     217      >>> class BrokenSequence:
     218      ...     def __getitem__(self, idx):
     219      ...         raise TypeError('myerror')
     220      ...
     221      >>> g(*BrokenSequence())
     222      Traceback (most recent call last):
     223        ...
     224      TypeError: myerror
     225      >>> g(*range(1), *BrokenSequence())
     226      Traceback (most recent call last):
     227        ...
     228      TypeError: myerror
     229  
     230  Make sure that the function doesn't stomp the dictionary
     231  
     232      >>> d = {'a': 1, 'b': 2, 'c': 3}
     233      >>> d2 = d.copy()
     234      >>> g(1, d=4, **d)
     235      1 () {'a': 1, 'b': 2, 'c': 3, 'd': 4}
     236      >>> d == d2
     237      True
     238  
     239  What about willful misconduct?
     240  
     241      >>> def saboteur(**kw):
     242      ...     kw['x'] = 'm'
     243      ...     return kw
     244  
     245      >>> d = {}
     246      >>> kw = saboteur(a=1, **d)
     247      >>> d
     248      {}
     249  
     250  
     251      >>> g(1, 2, 3, **{'x': 4, 'y': 5})
     252      Traceback (most recent call last):
     253        ...
     254      TypeError: g() got multiple values for argument 'x'
     255  
     256      >>> f(**{1:2})
     257      Traceback (most recent call last):
     258        ...
     259      TypeError: keywords must be strings
     260  
     261      >>> h(**{'e': 2})
     262      Traceback (most recent call last):
     263        ...
     264      TypeError: h() got an unexpected keyword argument 'e'
     265  
     266      >>> h(*h)
     267      Traceback (most recent call last):
     268        ...
     269      TypeError: test.test_extcall.h() argument after * must be an iterable, not function
     270  
     271      >>> h(1, *h)
     272      Traceback (most recent call last):
     273        ...
     274      TypeError: Value after * must be an iterable, not function
     275  
     276      >>> h(*[1], *h)
     277      Traceback (most recent call last):
     278        ...
     279      TypeError: Value after * must be an iterable, not function
     280  
     281      >>> dir(*h)
     282      Traceback (most recent call last):
     283        ...
     284      TypeError: dir() argument after * must be an iterable, not function
     285  
     286      >>> nothing = None
     287      >>> nothing(*h)
     288      Traceback (most recent call last):
     289        ...
     290      TypeError: None argument after * must be an iterable, \
     291  not function
     292  
     293      >>> h(**h)
     294      Traceback (most recent call last):
     295        ...
     296      TypeError: test.test_extcall.h() argument after ** must be a mapping, not function
     297  
     298      >>> h(**[])
     299      Traceback (most recent call last):
     300        ...
     301      TypeError: test.test_extcall.h() argument after ** must be a mapping, not list
     302  
     303      >>> h(a=1, **h)
     304      Traceback (most recent call last):
     305        ...
     306      TypeError: test.test_extcall.h() argument after ** must be a mapping, not function
     307  
     308      >>> h(a=1, **[])
     309      Traceback (most recent call last):
     310        ...
     311      TypeError: test.test_extcall.h() argument after ** must be a mapping, not list
     312  
     313      >>> h(**{'a': 1}, **h)
     314      Traceback (most recent call last):
     315        ...
     316      TypeError: test.test_extcall.h() argument after ** must be a mapping, not function
     317  
     318      >>> h(**{'a': 1}, **[])
     319      Traceback (most recent call last):
     320        ...
     321      TypeError: test.test_extcall.h() argument after ** must be a mapping, not list
     322  
     323      >>> dir(**h)
     324      Traceback (most recent call last):
     325        ...
     326      TypeError: dir() argument after ** must be a mapping, not function
     327  
     328      >>> nothing(**h)
     329      Traceback (most recent call last):
     330        ...
     331      TypeError: None argument after ** must be a mapping, \
     332  not function
     333  
     334      >>> dir(b=1, **{'b': 1})
     335      Traceback (most recent call last):
     336        ...
     337      TypeError: dir() got multiple values for keyword argument 'b'
     338  
     339  Test a kwargs mapping with duplicated keys.
     340  
     341      >>> from collections.abc import Mapping
     342      >>> class MultiDict(Mapping):
     343      ...     def __init__(self, items):
     344      ...         self._items = items
     345      ...
     346      ...     def __iter__(self):
     347      ...         return (k for k, v in self._items)
     348      ...
     349      ...     def __getitem__(self, key):
     350      ...         for k, v in self._items:
     351      ...             if k == key:
     352      ...                 return v
     353      ...         raise KeyError(key)
     354      ...
     355      ...     def __len__(self):
     356      ...         return len(self._items)
     357      ...
     358      ...     def keys(self):
     359      ...         return [k for k, v in self._items]
     360      ...
     361      ...     def values(self):
     362      ...         return [v for k, v in self._items]
     363      ...
     364      ...     def items(self):
     365      ...         return [(k, v) for k, v in self._items]
     366      ...
     367      >>> g(**MultiDict([('x', 1), ('y', 2)]))
     368      1 () {'y': 2}
     369  
     370      >>> g(**MultiDict([('x', 1), ('x', 2)]))
     371      Traceback (most recent call last):
     372        ...
     373      TypeError: test.test_extcall.g() got multiple values for keyword argument 'x'
     374  
     375      >>> g(a=3, **MultiDict([('x', 1), ('x', 2)]))
     376      Traceback (most recent call last):
     377        ...
     378      TypeError: test.test_extcall.g() got multiple values for keyword argument 'x'
     379  
     380      >>> g(**MultiDict([('a', 3)]), **MultiDict([('x', 1), ('x', 2)]))
     381      Traceback (most recent call last):
     382        ...
     383      TypeError: test.test_extcall.g() got multiple values for keyword argument 'x'
     384  
     385  Call with dict subtype:
     386  
     387      >>> class MyDict(dict):
     388      ...     pass
     389  
     390      >>> def s1(**kwargs):
     391      ...     return kwargs
     392      >>> def s2(*args, **kwargs):
     393      ...     return (args, kwargs)
     394      >>> def s3(*, n, **kwargs):
     395      ...     return (n, kwargs)
     396  
     397      >>> md = MyDict({'a': 1, 'b': 2})
     398      >>> assert s1(**md) == {'a': 1, 'b': 2}
     399      >>> assert s2(*(1, 2), **md) == ((1, 2), {'a': 1, 'b': 2})
     400      >>> assert s3(**MyDict({'n': 1, 'b': 2})) == (1, {'b': 2})
     401      >>> s3(**md)
     402      Traceback (most recent call last):
     403        ...
     404      TypeError: s3() missing 1 required keyword-only argument: 'n'
     405  
     406  Another helper function
     407  
     408      >>> def f2(*a, **b):
     409      ...     return a, b
     410  
     411  
     412      >>> d = {}
     413      >>> for i in range(512):
     414      ...     key = 'k%d' % i
     415      ...     d[key] = i
     416      >>> a, b = f2(1, *(2,3), **d)
     417      >>> len(a), len(b), b == d
     418      (3, 512, True)
     419  
     420      >>> class Foo:
     421      ...     def method(self, arg1, arg2):
     422      ...         return arg1+arg2
     423  
     424      >>> x = Foo()
     425      >>> Foo.method(*(x, 1, 2))
     426      3
     427      >>> Foo.method(x, *(1, 2))
     428      3
     429      >>> Foo.method(*(1, 2, 3))
     430      5
     431      >>> Foo.method(1, *[2, 3])
     432      5
     433  
     434  A PyCFunction that takes only positional parameters should allow an
     435  empty keyword dictionary to pass without a complaint, but raise a
     436  TypeError if te dictionary is not empty
     437  
     438      >>> try:
     439      ...     silence = id(1, *{})
     440      ...     True
     441      ... except:
     442      ...     False
     443      True
     444  
     445      >>> id(1, **{'foo': 1})
     446      Traceback (most recent call last):
     447        ...
     448      TypeError: id() takes no keyword arguments
     449  
     450  A corner case of keyword dictionary items being deleted during
     451  the function call setup. See <http://bugs.python.org/issue2016>.
     452  
     453      >>> class Name(str):
     454      ...     def __eq__(self, other):
     455      ...         try:
     456      ...              del x[self]
     457      ...         except KeyError:
     458      ...              pass
     459      ...         return str.__eq__(self, other)
     460      ...     def __hash__(self):
     461      ...         return str.__hash__(self)
     462  
     463      >>> x = {Name("a"):1, Name("b"):2}
     464      >>> def f(a, b):
     465      ...     print(a,b)
     466      >>> f(**x)
     467      1 2
     468  
     469  Too many arguments:
     470  
     471      >>> def f(): pass
     472      >>> f(1)
     473      Traceback (most recent call last):
     474        ...
     475      TypeError: f() takes 0 positional arguments but 1 was given
     476      >>> def f(a): pass
     477      >>> f(1, 2)
     478      Traceback (most recent call last):
     479        ...
     480      TypeError: f() takes 1 positional argument but 2 were given
     481      >>> def f(a, b=1): pass
     482      >>> f(1, 2, 3)
     483      Traceback (most recent call last):
     484        ...
     485      TypeError: f() takes from 1 to 2 positional arguments but 3 were given
     486      >>> def f(*, kw): pass
     487      >>> f(1, kw=3)
     488      Traceback (most recent call last):
     489        ...
     490      TypeError: f() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given
     491      >>> def f(*, kw, b): pass
     492      >>> f(1, 2, 3, b=3, kw=3)
     493      Traceback (most recent call last):
     494        ...
     495      TypeError: f() takes 0 positional arguments but 3 positional arguments (and 2 keyword-only arguments) were given
     496      >>> def f(a, b=2, *, kw): pass
     497      >>> f(2, 3, 4, kw=4)
     498      Traceback (most recent call last):
     499        ...
     500      TypeError: f() takes from 1 to 2 positional arguments but 3 positional arguments (and 1 keyword-only argument) were given
     501  
     502  Too few and missing arguments:
     503  
     504      >>> def f(a): pass
     505      >>> f()
     506      Traceback (most recent call last):
     507        ...
     508      TypeError: f() missing 1 required positional argument: 'a'
     509      >>> def f(a, b): pass
     510      >>> f()
     511      Traceback (most recent call last):
     512        ...
     513      TypeError: f() missing 2 required positional arguments: 'a' and 'b'
     514      >>> def f(a, b, c): pass
     515      >>> f()
     516      Traceback (most recent call last):
     517        ...
     518      TypeError: f() missing 3 required positional arguments: 'a', 'b', and 'c'
     519      >>> def f(a, b, c, d, e): pass
     520      >>> f()
     521      Traceback (most recent call last):
     522        ...
     523      TypeError: f() missing 5 required positional arguments: 'a', 'b', 'c', 'd', and 'e'
     524      >>> def f(a, b=4, c=5, d=5): pass
     525      >>> f(c=12, b=9)
     526      Traceback (most recent call last):
     527        ...
     528      TypeError: f() missing 1 required positional argument: 'a'
     529  
     530  Same with keyword only args:
     531  
     532      >>> def f(*, w): pass
     533      >>> f()
     534      Traceback (most recent call last):
     535        ...
     536      TypeError: f() missing 1 required keyword-only argument: 'w'
     537      >>> def f(*, a, b, c, d, e): pass
     538      >>> f()
     539      Traceback (most recent call last):
     540        ...
     541      TypeError: f() missing 5 required keyword-only arguments: 'a', 'b', 'c', 'd', and 'e'
     542  
     543  """
     544  
     545  import doctest
     546  import unittest
     547  
     548  def load_tests(loader, tests, pattern):
     549      tests.addTest(doctest.DocTestSuite())
     550      return tests
     551  
     552  
     553  if __name__ == '__main__':
     554      unittest.main()