Close #18626: add a basic CLI for the inspect module

This commit is contained in:
Nick Coghlan 2013-09-22 22:46:49 +10:00
parent 4c7fe6a5ad
commit f94a16b494
5 changed files with 130 additions and 3 deletions

View File

@ -1006,3 +1006,20 @@ updated as expected:
return an empty dictionary.
.. versionadded:: 3.3
Command Line Interface
----------------------
The :mod:`inspect` module also provides a basic introspection capability
from the command line.
.. program:: inspect
By default, accepts the name of a module and prints the source of that
module. A class or function within the module can be printed instead by
appended a colon and the qualified name of the target object.
.. cmdoption:: --details
Print information about the specified object rather than the source code

View File

@ -264,11 +264,15 @@ New :func:`functools.singledispatch` decorator: see the :pep:`443`.
inspect
-------
The inspect module now offers a basic command line interface to quickly
display source code and other information for modules, classes and
functions.
:func:`~inspect.unwrap` makes it easy to unravel wrapper function chains
created by :func:`functools.wraps` (and any other API that sets the
``__wrapped__`` attribute on a wrapper function).
mmap
----

View File

@ -2109,3 +2109,64 @@ class Signature:
rendered += ' -> {}'.format(anno)
return rendered
def _main():
""" Logic for inspecting an object given at command line """
import argparse
import importlib
parser = argparse.ArgumentParser()
parser.add_argument(
'object',
help="The object to be analysed. "
"It supports the 'module:qualname' syntax")
parser.add_argument(
'-d', '--details', action='store_true',
help='Display info about the module rather than its source code')
args = parser.parse_args()
target = args.object
mod_name, has_attrs, attrs = target.partition(":")
try:
obj = module = importlib.import_module(mod_name)
except Exception as exc:
msg = "Failed to import {} ({}: {})".format(mod_name,
type(exc).__name__,
exc)
print(msg, file=sys.stderr)
exit(2)
if has_attrs:
parts = attrs.split(".")
obj = module
for part in parts:
obj = getattr(obj, part)
if module.__name__ in sys.builtin_module_names:
print("Can't get info for builtin modules.", file=sys.stderr)
exit(1)
if args.details:
print('Target: {}'.format(target))
print('Origin: {}'.format(getsourcefile(module)))
print('Cached: {}'.format(module.__cached__))
if obj is module:
print('Loader: {}'.format(repr(module.__loader__)))
if hasattr(module, '__path__'):
print('Submodule search path: {}'.format(module.__path__))
else:
try:
__, lineno = findsource(obj)
except Exception:
pass
else:
print('Line: {}'.format(lineno))
print('\n')
else:
print(getsource(obj))
if __name__ == "__main__":
_main()

View File

@ -9,10 +9,11 @@ import collections
import os
import shutil
import functools
import importlib
from os.path import normcase
from test.support import run_unittest, TESTFN, DirsOnSysPath
from test.script_helper import assert_python_ok, assert_python_failure
from test import inspect_fodder as mod
from test import inspect_fodder2 as mod2
@ -2372,6 +2373,47 @@ class TestUnwrap(unittest.TestCase):
__wrapped__ = func
self.assertIsNone(inspect.unwrap(C()))
class TestMain(unittest.TestCase):
def test_only_source(self):
module = importlib.import_module('unittest')
rc, out, err = assert_python_ok('-m', 'inspect',
'unittest')
lines = out.decode().splitlines()
# ignore the final newline
self.assertEqual(lines[:-1], inspect.getsource(module).splitlines())
self.assertEqual(err, b'')
def test_qualname_source(self):
module = importlib.import_module('concurrent.futures')
member = getattr(module, 'ThreadPoolExecutor')
rc, out, err = assert_python_ok('-m', 'inspect',
'concurrent.futures:ThreadPoolExecutor')
lines = out.decode().splitlines()
# ignore the final newline
self.assertEqual(lines[:-1],
inspect.getsource(member).splitlines())
self.assertEqual(err, b'')
def test_builtins(self):
module = importlib.import_module('unittest')
_, out, err = assert_python_failure('-m', 'inspect',
'sys')
lines = err.decode().splitlines()
self.assertEqual(lines, ["Can't get info for builtin modules."])
def test_details(self):
module = importlib.import_module('unittest')
rc, out, err = assert_python_ok('-m', 'inspect',
'unittest', '--details')
output = out.decode()
# Just a quick sanity check on the output
self.assertIn(module.__name__, output)
self.assertIn(module.__file__, output)
self.assertIn(module.__cached__, output)
self.assertEqual(err, b'')
def test_main():
run_unittest(
@ -2380,7 +2422,7 @@ def test_main():
TestGetcallargsFunctions, TestGetcallargsMethods,
TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState,
TestNoEOL, TestSignatureObject, TestSignatureBind, TestParameterObject,
TestBoundArguments, TestGetClosureVars, TestUnwrap
TestBoundArguments, TestGetClosureVars, TestUnwrap, TestMain
)
if __name__ == "__main__":

View File

@ -12,6 +12,9 @@ Core and Builtins
Library
-------
- Issue #18626: the inspect module now offers a basic command line
introspection interface (Initial patch by Claudiu Popa)
- Issue #3015: Fixed tkinter with wantobject=False. Any Tcl command call
returned empty string.