Skip to content

Commit 58703a5

Browse files
committed
gh-92041: Address getmodule review findings
1 parent 2bcf546 commit 58703a5

3 files changed

Lines changed: 44 additions & 15 deletions

File tree

Lib/inspect.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -930,16 +930,22 @@ def getmodule(object, _filename=None):
930930
"""Return the module an object was defined in, or None if not found."""
931931
if ismodule(object):
932932
return object
933+
if hasattr(object, '__module__'):
934+
return sys.modules.get(object.__module__)
933935
if istraceback(object):
934936
object = object.tb_frame
935937
if isframe(object):
936-
object_globals = object.f_globals
937-
module = sys.modules.get(object_globals.get('__name__'))
938-
if module is not None and module.__dict__ is object_globals:
939-
return module
940-
return None
941-
if hasattr(object, '__module__'):
942-
return sys.modules.get(object.__module__)
938+
# Frame globals identify the execution namespace directly. Preserve
939+
# the private filename override when it names a different file.
940+
if _filename is None or _filename == object.f_code.co_filename:
941+
object_globals = object.f_globals
942+
module_name = object_globals.get('__name__')
943+
if not isinstance(module_name, str):
944+
return None
945+
module = sys.modules.get(module_name)
946+
if ismodule(module) and module.__dict__ is object_globals:
947+
return module
948+
return None
943949

944950
# Try the filename to modulename cache
945951
if _filename is not None and _filename in modulesbyfile:

Lib/test/test_inspect/test_inspect.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,10 @@ def test_getmodule(self):
831831
self.assertEqual(inspect.getmodule(str), sys.modules["builtins"])
832832
# Check filename override
833833
self.assertEqual(inspect.getmodule(None, modfile), mod)
834-
# Check frame and traceback objects
834+
self.assertEqual(
835+
inspect.getmodule(inspect.currentframe(), modfile), mod)
836+
# Check code, frame, and traceback objects
837+
self.assertIs(inspect.getmodule(mod.eggs.__code__), mod)
835838
self.assertIs(inspect.getmodule(inspect.currentframe()),
836839
sys.modules[__name__])
837840
try:
@@ -841,12 +844,32 @@ def test_getmodule(self):
841844
sys.modules[__name__])
842845

843846
def test_getmodule_unregistered_exec_frame(self):
844-
namespace = {"inspect": inspect, "__name__": "not_registered"}
845-
exec(compile("frame = inspect.currentframe()", modfile, "exec"),
846-
namespace)
847-
# The frame globals are authoritative, even though the code filename
848-
# happens to match an imported module.
849-
self.assertIsNone(inspect.getmodule(namespace["frame"]))
847+
def exec_namespace(namespace):
848+
exec(compile(textwrap.dedent("""
849+
frame = inspect.currentframe()
850+
try:
851+
1 / 0
852+
except ZeroDivisionError as error:
853+
traceback = error.__traceback__
854+
"""), modfile, "exec"), namespace)
855+
self.assertIsNone(inspect.getmodule(namespace["frame"]))
856+
self.assertIsNone(inspect.getmodule(namespace["traceback"]))
857+
858+
# Missing and invalid module names identify no registered namespace.
859+
exec_namespace({"inspect": inspect})
860+
exec_namespace({"inspect": inspect, "__name__": []})
861+
862+
module_name = f"{__name__}.not_registered"
863+
for module in (None, object(), types.ModuleType(module_name)):
864+
with self.subTest(module=module):
865+
sys.modules[module_name] = module
866+
try:
867+
exec_namespace({
868+
"inspect": inspect,
869+
"__name__": module_name,
870+
})
871+
finally:
872+
del sys.modules[module_name]
850873

851874
def test_getmodule_file_not_found(self):
852875
# See bpo-45406
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Improve :func:`inspect.getmodule` performance for frame and traceback objects by resolving their module directly from the frame globals.
1+
Improve :func:`inspect.getmodule` performance for frame and traceback objects by resolving registered modules directly from frame globals. Frames executing in unregistered globals now return ``None`` instead of being associated with a module solely by filename.

0 commit comments

Comments
 (0)