diff -r 04ff1cc40d62 Doc/library/inspect.rst --- a/Doc/library/inspect.rst Fri Oct 04 11:38:59 2013 -0400 +++ b/Doc/library/inspect.rst Sun Oct 06 23:15:03 2013 +0800 @@ -341,7 +341,16 @@ Return in a single string any lines of comments immediately preceding the object's source code (for a class, function, or method), or at the top of the - Python source file (if the object is a module). + Python source file (if the object is a module). An :exc:`OSError` is raised + if the source code cannot be retrieved, and a :exc:`TypeError` is raised if + the object is a built-in module, class or function. + + .. versionchanged:: 3.4 + :exc:`OSError` is raised if the source code cannot be retrieved, rather + than returning ``None``. + + :exc:`TypeError` is raised if the object is a built-in module, class or + function, rather than returning ``None``. .. function:: getfile(object) diff -r 04ff1cc40d62 Lib/inspect.py --- a/Lib/inspect.py Fri Oct 04 11:38:59 2013 -0400 +++ b/Lib/inspect.py Sun Oct 06 23:15:03 2013 +0800 @@ -691,12 +691,10 @@ def getcomments(object): """Get lines of comments immediately preceding an object's source code. - Returns None when source can't be found. + An OSError is raised if the source code cannot be retrieved. A TypeError + is raised if object is a built-in module, class or function. """ - try: - lines, lnum = findsource(object) - except (OSError, TypeError): - return None + lines, lnum = findsource(object) if ismodule(object): # Look for a comment block at the top of the file. diff -r 04ff1cc40d62 Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py Fri Oct 04 11:38:59 2013 -0400 +++ b/Lib/test/test_inspect.py Sun Oct 06 23:15:03 2013 +0800 @@ -10,14 +10,18 @@ import shutil import functools import importlib +import textwrap +import imp from os.path import normcase try: from concurrent.futures import ThreadPoolExecutor except ImportError: ThreadPoolExecutor = None -from test.support import run_unittest, TESTFN, DirsOnSysPath -from test.script_helper import assert_python_ok, assert_python_failure +from test.support import run_unittest, TESTFN, DirsOnSysPath, unlink +from test.script_helper import (assert_python_ok, + assert_python_failure, + make_script) from test import inspect_fodder as mod from test import inspect_fodder2 as mod2 @@ -223,6 +227,14 @@ class TestRetrievingSourceCode(GetSourceBase): fodderModule = mod + source_content = textwrap.dedent("""\ + # line 1 + 'A module docstring.' + + # line 20 + class StupidGit: + pass + """) def test_getclasses(self): classes = inspect.getmembers(mod, inspect.isclass) @@ -282,6 +294,18 @@ def test_getcomments(self): self.assertEqual(inspect.getcomments(mod), '# line 1\n') self.assertEqual(inspect.getcomments(mod.StupidGit), '# line 20\n') + self.assertRaisesRegex(TypeError, 'built-in', + inspect.getcomments, list) + source_file = TESTFN + '.py' + self.addCleanup(unlink, source_file) + make_script('', TESTFN, self.source_content) + spam = imp.load_source('spam', source_file) + self.assertEqual(inspect.getcomments(spam), '# line 1\n') + self.assertEqual(inspect.getcomments(spam.StupidGit), '# line 20\n') + # If the source file is unavailable, it should raise OSError. + unlink(source_file) + del linecache.cache[source_file] + self.assertRaises(OSError, inspect.getcomments, spam) def test_getmodule(self): # Check actual module @@ -300,6 +324,15 @@ def test_getsource(self): self.assertSourceEqual(git.abuse, 29, 39) self.assertSourceEqual(mod.StupidGit, 21, 46) + source_file = TESTFN + '.py' + self.addCleanup(unlink, source_file) + make_script('', TESTFN, self.source_content) + spam = imp.load_source('spam', source_file) + self.assertEqual(inspect.getsource(spam), self.source_content) + # If the source file is unavailable, it should raise OSError. + unlink(source_file) + del linecache.cache[source_file] + self.assertRaises(OSError, inspect.getsource, spam) def test_getsourcefile(self): self.assertEqual(normcase(inspect.getsourcefile(mod.spam)), modfile)