1from test import test_support 2import types 3import unittest 4 5class 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 32class 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 167class 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 210class 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 254class 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 310class 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 334class 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 346def test_main(): 347 test_support.run_unittest(FunctionPropertiesTest, InstancemethodAttrTest, 348 ArbitraryFunctionAttrTest, FunctionDictsTest, 349 FunctionDocstringTest, 350 StaticMethodAttrsTest) 351 352if __name__ == "__main__": 353 test_main() 354