Index: Objects/moduleobject.c =================================================================== --- Objects/moduleobject.c (revision 83050) +++ Objects/moduleobject.c (working copy) @@ -364,6 +364,28 @@ return PyUnicode_FromFormat("", name, filename); } +static PyObject* +module_getattr(PyObject *m, PyObject *name) +{ + PyObject *attr = PyObject_GenericGetAttr(m, name); + if (attr != NULL) + return attr; + PyErr_Clear(); + char *mod_name_str = "(uninitialized module)"; + PyModuleObject *module = (PyModuleObject*)m; + if (module->md_dict != NULL) { + PyObject *mod_name = PyDict_GetItemString(module->md_dict, "__name__"); + if (mod_name != NULL) + /*Here the mod_name is a borrowed reference.*/ + mod_name_str = _PyUnicode_AsString(mod_name); + else if (PyErr_Occurred()) + PyErr_Clear(); + } + PyErr_Format(PyExc_AttributeError, + "module '%s' has no attribute '%U'", mod_name_str, name); + return NULL; +} + static int module_traverse(PyModuleObject *m, visitproc visit, void *arg) { @@ -412,7 +434,7 @@ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ + module_getattr, /* tp_getattro */ PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Index: Lib/unittest/test/test_loader.py =================================================================== --- Lib/unittest/test/test_loader.py (revision 83050) +++ Lib/unittest/test/test_loader.py (working copy) @@ -255,7 +255,8 @@ try: loader.loadTestsFromName('unittest.sdasfasfasdf') except AttributeError as e: - self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + self.assertEqual(str(e), + "module 'unittest' has no attribute 'sdasfasfasdf'") else: self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") @@ -272,7 +273,8 @@ try: loader.loadTestsFromName('sdasfasfasdf', unittest) except AttributeError as e: - self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + self.assertEqual(str(e), + "module 'unittest' has no attribute 'sdasfasfasdf'") else: self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") @@ -635,7 +637,8 @@ try: loader.loadTestsFromNames(['unittest.sdasfasfasdf', 'unittest']) except AttributeError as e: - self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + self.assertEqual(str(e), + "module 'unittest' has no attribute 'sdasfasfasdf'") else: self.fail("TestLoader.loadTestsFromNames failed to raise AttributeError") @@ -654,7 +657,8 @@ try: loader.loadTestsFromNames(['sdasfasfasdf'], unittest) except AttributeError as e: - self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + self.assertEqual(str(e), + "module 'unittest' has no attribute 'sdasfasfasdf'") else: self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") @@ -673,7 +677,8 @@ try: loader.loadTestsFromNames(['TestCase', 'sdasfasfasdf'], unittest) except AttributeError as e: - self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + self.assertEqual(str(e), + "module 'unittest' has no attribute 'sdasfasfasdf'") else: self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") Index: Lib/test/test_doctest.py =================================================================== --- Lib/test/test_doctest.py (revision 83050) +++ Lib/test/test_doctest.py (working copy) @@ -1945,7 +1945,7 @@ >>> test.test_doctest.sillySetup Traceback (most recent call last): ... - AttributeError: 'module' object has no attribute 'sillySetup' + AttributeError: module 'test.test_doctest' has no attribute 'sillySetup' The setUp and tearDown funtions are passed test objects. Here we'll use the setUp function to supply the missing variable y: @@ -2091,7 +2091,7 @@ >>> test.test_doctest.sillySetup Traceback (most recent call last): ... - AttributeError: 'module' object has no attribute 'sillySetup' + AttributeError: module 'test.test_doctest' has no attribute 'sillySetup' The setUp and tearDown funtions are passed test objects. Here, we'll use a setUp function to set the favorite color in Index: Lib/test/test_module.py =================================================================== --- Lib/test/test_module.py (revision 83050) +++ Lib/test/test_module.py (working copy) @@ -63,6 +63,31 @@ return foo self.assertEqual(f().__dict__["bar"], 4) + def test_module_getattr(self): + #test an initialized module's getattr + import sys + non_exist_attr = '_non_exist_attr' + if hasattr(sys, non_exist_attr): + delattr(sys, non_exist_attr) + try: + getattr(sys, non_exist_attr) + except AttributeError as e: + self.assertEqual(str(e), + "module '%s' has no attribute '%s'" % ('sys', non_exist_attr)) + else: + self.fail('sys.%s should raise AttributeError.' % non_exist_attr) + + #Here test uninitialized module.getattr + foo = ModuleType.__new__(ModuleType) + try: + getattr(foo, '__name__') + except AttributeError as e: + self.assertEqual(str(e), + "module '(uninitialized module)' has no attribute '__name__'") + else: + self.fail('foo.__name__ should raise AttributeError.') + + def test_main(): run_unittest(ModuleTests)