Home | History | Annotate | Download | only in test
      1 from test import test_support
      2 import types
      3 import unittest
      4 
      5 class FuncAttrsTest(unittest.TestCase):
      6     def setUp(self):
      7         class F:
      8             def a(self):
      9                 pass
     10         def b():
     11             return 3
     12         self.f = F
     13         self.fi = F()
     14         self.b = b
     15 
     16     def cannot_set_attr(self, obj, name, value, exceptions):
     17         # Helper method for other tests.
     18         try:
     19             setattr(obj, name, value)
     20         except exceptions:
     21             pass
     22         else:
     23             self.fail("shouldn't be able to set %s to %r" % (name, value))
     24         try:
     25             delattr(obj, name)
     26         except exceptions:
     27             pass
     28         else:
     29             self.fail("shouldn't be able to del %s" % name)
     30 
     31 
     32 class FunctionPropertiesTest(FuncAttrsTest):
     33     # Include the external setUp method that is common to all tests
     34     def test_module(self):
     35         self.assertEqual(self.b.__module__, __name__)
     36 
     37     def test_dir_includes_correct_attrs(self):
     38         self.b.known_attr = 7
     39         self.assertIn('known_attr', dir(self.b),
     40                         "set attributes not in dir listing of method")
     41         # Test on underlying function object of method
     42         self.f.a.im_func.known_attr = 7
     43         self.assertIn('known_attr', dir(self.f.a),
     44                         "set attribute on unbound method implementation in "
     45                         "class not in dir")
     46         self.assertIn('known_attr', dir(self.fi.a),
     47                         "set attribute on unbound method implementations, "
     48                         "should show up in next dir")
     49 
     50     def test_duplicate_function_equality(self):
     51         # Body of `duplicate' is the exact same as self.b
     52         def duplicate():
     53             'my docstring'
     54             return 3
     55         self.assertNotEqual(self.b, duplicate)
     56 
     57     def test_copying_func_code(self):
     58         def test(): pass
     59         self.assertEqual(test(), None)
     60         test.func_code = self.b.func_code
     61         self.assertEqual(test(), 3) # self.b always returns 3, arbitrarily
     62 
     63     def test_func_globals(self):
     64         self.assertIs(self.b.func_globals, globals())
     65         self.cannot_set_attr(self.b, 'func_globals', 2, TypeError)
     66 
     67     def test_func_closure(self):
     68         a = 12
     69         def f(): print a
     70         c = f.func_closure
     71         self.assertIsInstance(c, tuple)
     72         self.assertEqual(len(c), 1)
     73         # don't have a type object handy
     74         self.assertEqual(c[0].__class__.__name__, "cell")
     75         self.cannot_set_attr(f, "func_closure", c, TypeError)
     76 
     77     def test_empty_cell(self):
     78         def f(): print a
     79         try:
     80             f.func_closure[0].cell_contents
     81         except ValueError:
     82             pass
     83         else:
     84             self.fail("shouldn't be able to read an empty cell")
     85         a = 12
     86 
     87     def test_func_name(self):
     88         self.assertEqual(self.b.__name__, 'b')
     89         self.assertEqual(self.b.func_name, 'b')
     90         self.b.__name__ = 'c'
     91         self.assertEqual(self.b.__name__, 'c')
     92         self.assertEqual(self.b.func_name, 'c')
     93         self.b.func_name = 'd'
     94         self.assertEqual(self.b.__name__, 'd')
     95         self.assertEqual(self.b.func_name, 'd')
     96         # __name__ and func_name must be a string
     97         self.cannot_set_attr(self.b, '__name__', 7, TypeError)
     98         self.cannot_set_attr(self.b, 'func_name', 7, TypeError)
     99         # __name__ must be available when in restricted mode. Exec will raise
    100         # AttributeError if __name__ is not available on f.
    101         s = """def f(): pass\nf.__name__"""
    102         exec s in {'__builtins__': {}}
    103         # Test on methods, too
    104         self.assertEqual(self.f.a.__name__, 'a')
    105         self.assertEqual(self.fi.a.__name__, 'a')
    106         self.cannot_set_attr(self.f.a, "__name__", 'a', AttributeError)
    107         self.cannot_set_attr(self.fi.a, "__name__", 'a', AttributeError)
    108 
    109     def test_func_code(self):
    110         num_one, num_two = 7, 8
    111         def a(): pass
    112         def b(): return 12
    113         def c(): return num_one
    114         def d(): return num_two
    115         def e(): return num_one, num_two
    116         for func in [a, b, c, d, e]:
    117             self.assertEqual(type(func.func_code), types.CodeType)
    118         self.assertEqual(c(), 7)
    119         self.assertEqual(d(), 8)
    120         d.func_code = c.func_code
    121         self.assertEqual(c.func_code, d.func_code)
    122         self.assertEqual(c(), 7)
    123         # self.assertEqual(d(), 7)
    124         try:
    125             b.func_code = c.func_code
    126         except ValueError:
    127             pass
    128         else:
    129             self.fail("func_code with different numbers of free vars should "
    130                       "not be possible")
    131         try:
    132             e.func_code = d.func_code
    133         except ValueError:
    134             pass
    135         else:
    136             self.fail("func_code with different numbers of free vars should "
    137                       "not be possible")
    138 
    139     def test_blank_func_defaults(self):
    140         self.assertEqual(self.b.func_defaults, None)
    141         del self.b.func_defaults
    142         self.assertEqual(self.b.func_defaults, None)
    143 
    144     def test_func_default_args(self):
    145         def first_func(a, b):
    146             return a+b
    147         def second_func(a=1, b=2):
    148             return a+b
    149         self.assertEqual(first_func.func_defaults, None)
    150         self.assertEqual(second_func.func_defaults, (1, 2))
    151         first_func.func_defaults = (1, 2)
    152         self.assertEqual(first_func.func_defaults, (1, 2))
    153         self.assertEqual(first_func(), 3)
    154         self.assertEqual(first_func(3), 5)
    155         self.assertEqual(first_func(3, 5), 8)
    156         del second_func.func_defaults
    157         self.assertEqual(second_func.func_defaults, None)
    158         try:
    159             second_func()
    160         except TypeError:
    161             pass
    162         else:
    163             self.fail("func_defaults does not update; deleting it does not "
    164                       "remove requirement")
    165 
    166 
    167 class InstancemethodAttrTest(FuncAttrsTest):
    168     def test_im_class(self):
    169         self.assertEqual(self.f.a.im_class, self.f)
    170         self.assertEqual(self.fi.a.im_class, self.f)
    171         self.cannot_set_attr(self.f.a, "im_class", self.f, TypeError)
    172         self.cannot_set_attr(self.fi.a, "im_class", self.f, TypeError)
    173 
    174     def test_im_func(self):
    175         self.f.b = self.b
    176         self.assertEqual(self.f.b.im_func, self.b)
    177         self.assertEqual(self.fi.b.im_func, self.b)
    178         self.cannot_set_attr(self.f.b, "im_func", self.b, TypeError)
    179         self.cannot_set_attr(self.fi.b, "im_func", self.b, TypeError)
    180 
    181     def test_im_self(self):
    182         self.assertEqual(self.f.a.im_self, None)
    183         self.assertEqual(self.fi.a.im_self, self.fi)
    184         self.cannot_set_attr(self.f.a, "im_self", None, TypeError)
    185         self.cannot_set_attr(self.fi.a, "im_self", self.fi, TypeError)
    186 
    187     def test_im_func_non_method(self):
    188         # Behavior should be the same when a method is added via an attr
    189         # assignment
    190         self.f.id = types.MethodType(id, None, self.f)
    191         self.assertEqual(self.fi.id(), id(self.fi))
    192         self.assertNotEqual(self.fi.id(), id(self.f))
    193         # Test usage
    194         try:
    195             self.f.id.unknown_attr
    196         except AttributeError:
    197             pass
    198         else:
    199             self.fail("using unknown attributes should raise AttributeError")
    200         # Test assignment and deletion
    201         self.cannot_set_attr(self.f.id, 'unknown_attr', 2, AttributeError)
    202         self.cannot_set_attr(self.fi.id, 'unknown_attr', 2, AttributeError)
    203 
    204     def test_implicit_method_properties(self):
    205         self.f.a.im_func.known_attr = 7
    206         self.assertEqual(self.f.a.known_attr, 7)
    207         self.assertEqual(self.fi.a.known_attr, 7)
    208 
    209 
    210 class ArbitraryFunctionAttrTest(FuncAttrsTest):
    211     def test_set_attr(self):
    212         # setting attributes only works on function objects
    213         self.b.known_attr = 7
    214         self.assertEqual(self.b.known_attr, 7)
    215         for func in [self.f.a, self.fi.a]:
    216             try:
    217                 func.known_attr = 7
    218             except AttributeError:
    219                 pass
    220             else:
    221                 self.fail("setting attributes on methods should raise error")
    222 
    223     def test_delete_unknown_attr(self):
    224         try:
    225             del self.b.unknown_attr
    226         except AttributeError:
    227             pass
    228         else:
    229             self.fail("deleting unknown attribute should raise TypeError")
    230 
    231     def test_setting_attrs_duplicates(self):
    232         try:
    233             self.f.a.klass = self.f
    234         except AttributeError:
    235             pass
    236         else:
    237             self.fail("setting arbitrary attribute in unbound function "
    238                       " should raise AttributeError")
    239         self.f.a.im_func.klass = self.f
    240         for method in [self.f.a, self.fi.a, self.fi.a.im_func]:
    241             self.assertEqual(method.klass, self.f)
    242 
    243     def test_unset_attr(self):
    244         for func in [self.b, self.f.a, self.fi.a]:
    245             try:
    246                 func.non_existent_attr
    247             except AttributeError:
    248                 pass
    249             else:
    250                 self.fail("using unknown attributes should raise "
    251                           "AttributeError")
    252 
    253 
    254 class FunctionDictsTest(FuncAttrsTest):
    255     def test_setting_dict_to_invalid(self):
    256         self.cannot_set_attr(self.b, '__dict__', None, TypeError)
    257         self.cannot_set_attr(self.b, 'func_dict', None, TypeError)
    258         from UserDict import UserDict
    259         d = UserDict({'known_attr': 7})
    260         self.cannot_set_attr(self.f.a.im_func, '__dict__', d, TypeError)
    261         self.cannot_set_attr(self.fi.a.im_func, '__dict__', d, TypeError)
    262 
    263     def test_setting_dict_to_valid(self):
    264         d = {'known_attr': 7}
    265         self.b.__dict__ = d
    266         # Setting dict is only possible on the underlying function objects
    267         self.f.a.im_func.__dict__ = d
    268         # Test assignment
    269         self.assertIs(d, self.b.__dict__)
    270         self.assertIs(d, self.b.func_dict)
    271         # ... and on all the different ways of referencing the method's func
    272         self.assertIs(d, self.f.a.im_func.__dict__)
    273         self.assertIs(d, self.f.a.__dict__)
    274         self.assertIs(d, self.fi.a.im_func.__dict__)
    275         self.assertIs(d, self.fi.a.__dict__)
    276         # Test value
    277         self.assertEqual(self.b.known_attr, 7)
    278         self.assertEqual(self.b.__dict__['known_attr'], 7)
    279         self.assertEqual(self.b.func_dict['known_attr'], 7)
    280         # ... and again, on all the different method's names
    281         self.assertEqual(self.f.a.im_func.known_attr, 7)
    282         self.assertEqual(self.f.a.known_attr, 7)
    283         self.assertEqual(self.fi.a.im_func.known_attr, 7)
    284         self.assertEqual(self.fi.a.known_attr, 7)
    285 
    286     def test_delete_func_dict(self):
    287         try:
    288             del self.b.__dict__
    289         except TypeError:
    290             pass
    291         else:
    292             self.fail("deleting function dictionary should raise TypeError")
    293         try:
    294             del self.b.func_dict
    295         except TypeError:
    296             pass
    297         else:
    298             self.fail("deleting function dictionary should raise TypeError")
    299 
    300     def test_unassigned_dict(self):
    301         self.assertEqual(self.b.__dict__, {})
    302 
    303     def test_func_as_dict_key(self):
    304         value = "Some string"
    305         d = {}
    306         d[self.b] = value
    307         self.assertEqual(d[self.b], value)
    308 
    309 
    310 class FunctionDocstringTest(FuncAttrsTest):
    311     def test_set_docstring_attr(self):
    312         self.assertEqual(self.b.__doc__, None)
    313         self.assertEqual(self.b.func_doc, None)
    314         docstr = "A test method that does nothing"
    315         self.b.__doc__ = self.f.a.im_func.__doc__ = docstr
    316         self.assertEqual(self.b.__doc__, docstr)
    317         self.assertEqual(self.b.func_doc, docstr)
    318         self.assertEqual(self.f.a.__doc__, docstr)
    319         self.assertEqual(self.fi.a.__doc__, docstr)
    320         self.cannot_set_attr(self.f.a, "__doc__", docstr, AttributeError)
    321         self.cannot_set_attr(self.fi.a, "__doc__", docstr, AttributeError)
    322 
    323     def test_delete_docstring(self):
    324         self.b.__doc__ = "The docstring"
    325         del self.b.__doc__
    326         self.assertEqual(self.b.__doc__, None)
    327         self.assertEqual(self.b.func_doc, None)
    328         self.b.func_doc = "The docstring"
    329         del self.b.func_doc
    330         self.assertEqual(self.b.__doc__, None)
    331         self.assertEqual(self.b.func_doc, None)
    332 
    333 
    334 class StaticMethodAttrsTest(unittest.TestCase):
    335     def test_func_attribute(self):
    336         def f():
    337             pass
    338 
    339         c = classmethod(f)
    340         self.assertTrue(c.__func__ is f)
    341 
    342         s = staticmethod(f)
    343         self.assertTrue(s.__func__ is f)
    344 
    345 
    346 def test_main():
    347     test_support.run_unittest(FunctionPropertiesTest, InstancemethodAttrTest,
    348                               ArbitraryFunctionAttrTest, FunctionDictsTest,
    349                               FunctionDocstringTest,
    350                               StaticMethodAttrsTest)
    351 
    352 if __name__ == "__main__":
    353     test_main()
    354