Home | History | Annotate | Download | only in test
      1 """Unit tests for the keyword only argument specified in PEP 3102."""
      2 
      3 __author__ = "Jiwon Seo"
      4 __email__ = "seojiwon at gmail dot com"
      5 
      6 import unittest
      7 
      8 def posonly_sum(pos_arg1, *arg, **kwarg):
      9     return pos_arg1 + sum(arg) + sum(kwarg.values())
     10 def keywordonly_sum(*, k1=0, k2):
     11     return k1 + k2
     12 def keywordonly_nodefaults_sum(*, k1, k2):
     13     return k1 + k2
     14 def keywordonly_and_kwarg_sum(*, k1, k2, **kwarg):
     15     return k1 + k2 + sum(kwarg.values())
     16 def mixedargs_sum(a, b=0, *arg, k1, k2=0):
     17     return a + b + k1 + k2 + sum(arg)
     18 def mixedargs_sum2(a, b=0, *arg, k1, k2=0, **kwargs):
     19     return a + b + k1 + k2 + sum(arg) + sum(kwargs.values())
     20 
     21 def sortnum(*nums, reverse=False):
     22     return sorted(list(nums), reverse=reverse)
     23 
     24 def sortwords(*words, reverse=False, **kwargs):
     25     return sorted(list(words), reverse=reverse)
     26 
     27 class Foo:
     28     def __init__(self, *, k1, k2=0):
     29         self.k1 = k1
     30         self.k2 = k2
     31     def set(self, p1, *, k1, k2):
     32         self.k1 = k1
     33         self.k2 = k2
     34     def sum(self):
     35         return self.k1 + self.k2
     36 
     37 class KeywordOnlyArgTestCase(unittest.TestCase):
     38     def assertRaisesSyntaxError(self, codestr):
     39         def shouldRaiseSyntaxError(s):
     40             compile(s, "<test>", "single")
     41         self.assertRaises(SyntaxError, shouldRaiseSyntaxError, codestr)
     42 
     43     def testSyntaxErrorForFunctionDefinition(self):
     44         self.assertRaisesSyntaxError("def f(p, *):\n  pass\n")
     45         self.assertRaisesSyntaxError("def f(p1, *, p1=100):\n  pass\n")
     46         self.assertRaisesSyntaxError("def f(p1, *k1, k1=100):\n  pass\n")
     47         self.assertRaisesSyntaxError("def f(p1, *, k1, k1=100):\n  pass\n")
     48         self.assertRaisesSyntaxError("def f(p1, *, **k1):\n  pass\n")
     49         self.assertRaisesSyntaxError("def f(p1, *, k1, **k1):\n  pass\n")
     50         self.assertRaisesSyntaxError("def f(p1, *, None, **k1):\n  pass\n")
     51         self.assertRaisesSyntaxError("def f(p, *, (k1, k2), **kw):\n  pass\n")
     52 
     53     def testSyntaxForManyArguments(self):
     54         # more than 255 positional arguments, should compile ok
     55         fundef = "def f(%s):\n  pass\n" % ', '.join('i%d' % i for i in range(300))
     56         compile(fundef, "<test>", "single")
     57         # more than 255 keyword-only arguments, should compile ok
     58         fundef = "def f(*, %s):\n  pass\n" % ', '.join('i%d' % i for i in range(300))
     59         compile(fundef, "<test>", "single")
     60 
     61     def testTooManyPositionalErrorMessage(self):
     62         def f(a, b=None, *, c=None):
     63             pass
     64         with self.assertRaises(TypeError) as exc:
     65             f(1, 2, 3)
     66         expected = "f() takes from 1 to 2 positional arguments but 3 were given"
     67         self.assertEqual(str(exc.exception), expected)
     68 
     69     def testSyntaxErrorForFunctionCall(self):
     70         self.assertRaisesSyntaxError("f(p, k=1, p2)")
     71         self.assertRaisesSyntaxError("f(p, k1=50, *(1,2), k1=100)")
     72 
     73     def testRaiseErrorFuncallWithUnexpectedKeywordArgument(self):
     74         self.assertRaises(TypeError, keywordonly_sum, ())
     75         self.assertRaises(TypeError, keywordonly_nodefaults_sum, ())
     76         self.assertRaises(TypeError, Foo, ())
     77         try:
     78             keywordonly_sum(k2=100, non_existing_arg=200)
     79             self.fail("should raise TypeError")
     80         except TypeError:
     81             pass
     82         try:
     83             keywordonly_nodefaults_sum(k2=2)
     84             self.fail("should raise TypeError")
     85         except TypeError:
     86             pass
     87 
     88     def testFunctionCall(self):
     89         self.assertEqual(1, posonly_sum(1))
     90         self.assertEqual(1+2, posonly_sum(1,**{"2":2}))
     91         self.assertEqual(1+2+3, posonly_sum(1,*(2,3)))
     92         self.assertEqual(1+2+3+4, posonly_sum(1,*(2,3),**{"4":4}))
     93 
     94         self.assertEqual(1, keywordonly_sum(k2=1))
     95         self.assertEqual(1+2, keywordonly_sum(k1=1, k2=2))
     96 
     97         self.assertEqual(1+2, keywordonly_and_kwarg_sum(k1=1, k2=2))
     98         self.assertEqual(1+2+3, keywordonly_and_kwarg_sum(k1=1, k2=2, k3=3))
     99         self.assertEqual(1+2+3+4,
    100                          keywordonly_and_kwarg_sum(k1=1, k2=2,
    101                                                     **{"a":3,"b":4}))
    102 
    103         self.assertEqual(1+2, mixedargs_sum(1, k1=2))
    104         self.assertEqual(1+2+3, mixedargs_sum(1, 2, k1=3))
    105         self.assertEqual(1+2+3+4, mixedargs_sum(1, 2, k1=3, k2=4))
    106         self.assertEqual(1+2+3+4+5, mixedargs_sum(1, 2, 3, k1=4, k2=5))
    107 
    108         self.assertEqual(1+2, mixedargs_sum2(1, k1=2))
    109         self.assertEqual(1+2+3, mixedargs_sum2(1, 2, k1=3))
    110         self.assertEqual(1+2+3+4, mixedargs_sum2(1, 2, k1=3, k2=4))
    111         self.assertEqual(1+2+3+4+5, mixedargs_sum2(1, 2, 3, k1=4, k2=5))
    112         self.assertEqual(1+2+3+4+5+6,
    113                          mixedargs_sum2(1, 2, 3, k1=4, k2=5, k3=6))
    114         self.assertEqual(1+2+3+4+5+6,
    115                          mixedargs_sum2(1, 2, 3, k1=4, **{'k2':5, 'k3':6}))
    116 
    117         self.assertEqual(1, Foo(k1=1).sum())
    118         self.assertEqual(1+2, Foo(k1=1,k2=2).sum())
    119 
    120         self.assertEqual([1,2,3], sortnum(3,2,1))
    121         self.assertEqual([3,2,1], sortnum(1,2,3, reverse=True))
    122 
    123         self.assertEqual(['a','b','c'], sortwords('a','c','b'))
    124         self.assertEqual(['c','b','a'], sortwords('a','c','b', reverse=True))
    125         self.assertEqual(['c','b','a'],
    126                          sortwords('a','c','b', reverse=True, ignore='ignore'))
    127 
    128     def testKwDefaults(self):
    129         def foo(p1,p2=0, *, k1, k2=0):
    130             return p1 + p2 + k1 + k2
    131 
    132         self.assertEqual(2, foo.__code__.co_kwonlyargcount)
    133         self.assertEqual({"k2":0}, foo.__kwdefaults__)
    134         foo.__kwdefaults__ = {"k1":0}
    135         try:
    136             foo(1,k1=10)
    137             self.fail("__kwdefaults__ is not properly changed")
    138         except TypeError:
    139             pass
    140 
    141     def test_kwonly_methods(self):
    142         class Example:
    143             def f(self, *, k1=1, k2=2):
    144                 return k1, k2
    145 
    146         self.assertEqual(Example().f(k1=1, k2=2), (1, 2))
    147         self.assertEqual(Example.f(Example(), k1=1, k2=2), (1, 2))
    148         self.assertRaises(TypeError, Example.f, k1=1, k2=2)
    149 
    150     def test_issue13343(self):
    151         # The Python compiler must scan all symbols of a function to
    152         # determine their scope: global, local, cell...
    153         # This was not done for the default values of keyword
    154         # arguments in a lambda definition, and the following line
    155         # used to fail with a SystemError.
    156         lambda *, k1=unittest: None
    157 
    158     def test_mangling(self):
    159         class X:
    160             def f(self, *, __a=42):
    161                 return __a
    162         self.assertEqual(X().f(), 42)
    163 
    164     def test_default_evaluation_order(self):
    165         # See issue 16967
    166         a = 42
    167         with self.assertRaises(NameError) as err:
    168             def f(v=a, x=b, *, y=c, z=d):
    169                 pass
    170         self.assertEqual(str(err.exception), "name 'b' is not defined")
    171         with self.assertRaises(NameError) as err:
    172             f = lambda v=a, x=b, *, y=c, z=d: None
    173         self.assertEqual(str(err.exception), "name 'b' is not defined")
    174 
    175 
    176 if __name__ == "__main__":
    177     unittest.main()
    178