155155import tokenize
156156import token
157157import types
158+ import weakref
158159import functools
159160import builtins
160161from keyword import iskeyword
@@ -918,6 +919,14 @@ def getfile(object):
918919 if object .__module__ == '__main__' :
919920 raise OSError ('source code not available' )
920921 raise TypeError ('{!r} is a built-in class' .format (object ))
922+ if code := _getcode (object ):
923+ return code .co_filename
924+ raise TypeError ('module, class, method, function, traceback, frame, or '
925+ 'code object was expected, got {}' .format (
926+ type (object ).__name__ ))
927+
928+ def _getcode (object ):
929+ """Get the code object an object is associated with, if available."""
921930 if ismethod (object ):
922931 object = object .__func__
923932 if isfunction (object ):
@@ -927,10 +936,8 @@ def getfile(object):
927936 if isframe (object ):
928937 object = object .f_code
929938 if iscode (object ):
930- return object .co_filename
931- raise TypeError ('module, class, method, function, traceback, frame, or '
932- 'code object was expected, got {}' .format (
933- type (object ).__name__ ))
939+ return object
940+ return None
934941
935942def getmodulename (path ):
936943 """Return the module name for a given file, or None."""
@@ -980,6 +987,22 @@ def getabsfile(object, _filename=None):
980987
981988modulesbyfile = {}
982989_filesbymodname = {}
990+ _moduleless = OrderedDict ()
991+ _MAX_MODULELESS = 200
992+
993+ def _is_cached_moduleless (code ):
994+ if ref := _moduleless .get (id (code )):
995+ if ref () is code :
996+ _moduleless .move_to_end (id (code ), last = True )
997+ return True
998+ return False
999+
1000+ def _cache_moduleless (code ):
1001+ if code is None :
1002+ return
1003+ _moduleless [id (code )] = weakref .ref (code )
1004+ if len (_moduleless ) > _MAX_MODULELESS :
1005+ _moduleless .popitem (last = False )
9831006
9841007def getmodule (object , _filename = None ):
9851008 """Return the module an object was defined in, or None if not found."""
@@ -990,31 +1013,26 @@ def getmodule(object, _filename=None):
9901013 # Try the filename to modulename cache
9911014 if _filename is not None and _filename in modulesbyfile :
9921015 return sys .modules .get (modulesbyfile [_filename ])
1016+ # Check for moduleless objects
1017+ code = _getcode (object )
1018+ if _is_cached_moduleless (code ):
1019+ return None
9931020 # Try the cache again with the absolute file name
9941021 try :
9951022 file = getabsfile (object , _filename )
9961023 except (TypeError , FileNotFoundError ):
1024+ _cache_moduleless (code )
9971025 return None
9981026 if file in modulesbyfile :
9991027 return sys .modules .get (modulesbyfile [file ])
10001028 # Update the filename to module name cache and check yet again
1001- # Copy sys.modules in order to cope with changes while iterating
1002- for modname , module in sys .modules .copy ().items ():
1003- if ismodule (module ) and hasattr (module , '__file__' ):
1004- f = module .__file__
1005- if f == _filesbymodname .get (modname , None ):
1006- # Have already mapped this module, so skip it
1007- continue
1008- _filesbymodname [modname ] = f
1009- f = getabsfile (module )
1010- # Always map to the name the module knows itself by
1011- modulesbyfile [f ] = modulesbyfile [
1012- os .path .realpath (f )] = module .__name__
1029+ _update_module_filename_cache ()
10131030 if file in modulesbyfile :
10141031 return sys .modules .get (modulesbyfile [file ])
10151032 # Check the main module
10161033 main = sys .modules ['__main__' ]
10171034 if not hasattr (object , '__name__' ):
1035+ _cache_moduleless (code )
10181036 return None
10191037 if hasattr (main , object .__name__ ):
10201038 mainobject = getattr (main , object .__name__ )
@@ -1026,6 +1044,23 @@ def getmodule(object, _filename=None):
10261044 builtinobject = getattr (builtin , object .__name__ )
10271045 if builtinobject is object :
10281046 return builtin
1047+ _cache_moduleless (code )
1048+ return None
1049+
1050+ def _update_module_filename_cache ():
1051+ """Update the filename to module name cache."""
1052+ # Copy sys.modules in order to cope with changes while iterating
1053+ for modname , module in sys .modules .copy ().items ():
1054+ if ismodule (module ) and hasattr (module , '__file__' ):
1055+ f = module .__file__
1056+ if f == _filesbymodname .get (modname , None ):
1057+ # Have already mapped this module, so skip it
1058+ continue
1059+ _filesbymodname [modname ] = f
1060+ f = getabsfile (module )
1061+ # Always map to the name the module knows itself by
1062+ modulesbyfile [f ] = modulesbyfile [
1063+ os .path .realpath (f )] = module .__name__
10291064
10301065
10311066class ClassFoundException (Exception ):
0 commit comments