src/eric7/DebugClients/Python/ModuleLoader.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9473
3f23dbf37dbe
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
17 17
18 class ModuleLoader: 18 class ModuleLoader:
19 """ 19 """
20 Class implementing an import hook patching modules to support debugging. 20 Class implementing an import hook patching modules to support debugging.
21 """ 21 """
22
22 def __init__(self, debugClient): 23 def __init__(self, debugClient):
23 """ 24 """
24 Constructor 25 Constructor
25 26
26 @param debugClient reference to the debug client object 27 @param debugClient reference to the debug client object
27 @type DebugClient 28 @type DebugClient
28 """ 29 """
29 self.__dbgClient = debugClient 30 self.__dbgClient = debugClient
30 31
31 self.__enableImportHooks = set() 32 self.__enableImportHooks = set()
32 33
33 # reset already imported thread module to apply hooks at next import 34 # reset already imported thread module to apply hooks at next import
34 for moduleName in ("thread", "_thread", "threading"): 35 for moduleName in ("thread", "_thread", "threading"):
35 if moduleName in sys.modules: 36 if moduleName in sys.modules:
36 del sys.modules[moduleName] 37 del sys.modules[moduleName]
37 38
38 self.__modulesToPatch = ( 39 self.__modulesToPatch = (
39 '_thread', 'threading', 40 "_thread",
40 'greenlet', 41 "threading",
41 'subprocess', 42 "greenlet",
42 'multiprocessing', 43 "subprocess",
43 'PyQt5.QtCore', 44 "multiprocessing",
44 'PyQt6.QtCore', 45 "PyQt5.QtCore",
45 'PySide2.QtCore', 46 "PyQt6.QtCore",
46 'PySide6.QtCore', 47 "PySide2.QtCore",
48 "PySide6.QtCore",
47 ) 49 )
48 50
49 sys.meta_path.insert(0, self) 51 sys.meta_path.insert(0, self)
50 52
51 def __loadModule(self, fullname): 53 def __loadModule(self, fullname):
52 """ 54 """
53 Private method to load a module. 55 Private method to load a module.
54 56
55 @param fullname name of the module to be loaded 57 @param fullname name of the module to be loaded
56 @type str 58 @type str
57 @return reference to the loaded module 59 @return reference to the loaded module
58 @rtype module 60 @rtype module
59 """ 61 """
60 module = importlib.import_module(fullname) 62 module = importlib.import_module(fullname)
61 sys.modules[fullname] = module 63 sys.modules[fullname] = module
62 self.__enableImportHooks.remove(fullname) 64 self.__enableImportHooks.remove(fullname)
63 ## Add hook for _thread.start_new_thread 65 ## Add hook for _thread.start_new_thread
64 if ( 66 if fullname == "_thread" and not hasattr(module, "eric7_patched"):
65 fullname == '_thread' and
66 not hasattr(module, 'eric7_patched')
67 ):
68 module.eric7_patched = True 67 module.eric7_patched = True
69 self.__dbgClient.patchPyThread(module) 68 self.__dbgClient.patchPyThread(module)
70 69
71 ## Add hook for threading.run() 70 ## Add hook for threading.run()
72 elif ( 71 elif fullname == "threading" and not hasattr(module, "eric7_patched"):
73 fullname == "threading" and
74 not hasattr(module, 'eric7_patched')
75 ):
76 module.eric7_patched = True 72 module.eric7_patched = True
77 self.__dbgClient.patchPyThreading(module) 73 self.__dbgClient.patchPyThreading(module)
78 74
79 ## greenlet support 75 ## greenlet support
80 elif ( 76 elif fullname == "greenlet" and not hasattr(module, "eric7_patched"):
81 fullname == 'greenlet' and
82 not hasattr(module, 'eric7_patched')
83 ):
84 if self.__dbgClient.patchGreenlet(module): 77 if self.__dbgClient.patchGreenlet(module):
85 module.eric7_patched = True 78 module.eric7_patched = True
86 79
87 ## Add hook for subprocess.Popen() 80 ## Add hook for subprocess.Popen()
88 elif ( 81 elif fullname == "subprocess" and not hasattr(module, "eric7_patched"):
89 fullname == 'subprocess' and
90 not hasattr(module, 'eric7_patched')
91 ):
92 module.eric7_patched = True 82 module.eric7_patched = True
93 patchSubprocess(module, self.__dbgClient) 83 patchSubprocess(module, self.__dbgClient)
94 84
95 ## Add hook for multiprocessing.Process 85 ## Add hook for multiprocessing.Process
96 elif ( 86 elif fullname == "multiprocessing" and not hasattr(module, "eric7_patched"):
97 fullname == 'multiprocessing' and
98 not hasattr(module, 'eric7_patched')
99 ):
100 module.eric7_patched = True 87 module.eric7_patched = True
101 patchMultiprocessing(module, self.__dbgClient) 88 patchMultiprocessing(module, self.__dbgClient)
102 89
103 ## Add hook for *.QThread and *.QProcess 90 ## Add hook for *.QThread and *.QProcess
104 elif ( 91 elif fullname in (
105 fullname in ('PyQt5.QtCore', 'PyQt6.QtCore', 92 "PyQt5.QtCore",
106 'PySide2.QtCore', 'PySide6.QtCore') and 93 "PyQt6.QtCore",
107 not hasattr(module, 'eric7_patched') 94 "PySide2.QtCore",
108 ): 95 "PySide6.QtCore",
96 ) and not hasattr(module, "eric7_patched"):
109 module.eric7_patched = True 97 module.eric7_patched = True
110 self.__dbgClient.patchQThread(module) 98 self.__dbgClient.patchQThread(module)
111 patchQProcess(module, self.__dbgClient) 99 patchQProcess(module, self.__dbgClient)
112 100
113 return module 101 return module
114 102
115 def find_spec(self, fullname, path, target=None): 103 def find_spec(self, fullname, path, target=None):
116 """ 104 """
117 Public method returning the module spec. 105 Public method returning the module spec.
118 106
119 @param fullname name of the module to be loaded 107 @param fullname name of the module to be loaded
120 @type str 108 @type str
121 @param path path to resolve the module name 109 @param path path to resolve the module name
122 @type str 110 @type str
123 @param target module object to use for a more educated guess 111 @param target module object to use for a more educated guess
126 @return module spec object pointing to the module loader 114 @return module spec object pointing to the module loader
127 @rtype ModuleSpec 115 @rtype ModuleSpec
128 """ 116 """
129 if fullname in sys.modules or self.__dbgClient.debugging is False: 117 if fullname in sys.modules or self.__dbgClient.debugging is False:
130 return None 118 return None
131 119
132 if ( 120 if (
133 fullname in self.__modulesToPatch and 121 fullname in self.__modulesToPatch
134 fullname not in self.__enableImportHooks 122 and fullname not in self.__enableImportHooks
135 ): 123 ):
136 # Disable hook to be able to import original module 124 # Disable hook to be able to import original module
137 self.__enableImportHooks.add(fullname) 125 self.__enableImportHooks.add(fullname)
138 return importlib.machinery.ModuleSpec(fullname, self) 126 return importlib.machinery.ModuleSpec(fullname, self)
139 127
140 return None 128 return None
141 129
142 def create_module(self, spec): 130 def create_module(self, spec):
143 """ 131 """
144 Public method to create a module based on the passed in spec. 132 Public method to create a module based on the passed in spec.
145 133
146 @param spec module spec object for loading the module 134 @param spec module spec object for loading the module
147 @type ModuleSpec 135 @type ModuleSpec
148 @return created and patched module 136 @return created and patched module
149 @rtype module 137 @rtype module
150 """ 138 """
151 return self.__loadModule(spec.name) 139 return self.__loadModule(spec.name)
152 140
153 def exec_module(self, module): 141 def exec_module(self, module):
154 """ 142 """
155 Public method to execute the created module. 143 Public method to execute the created module.
156 144
157 @param module module to be executed 145 @param module module to be executed
158 @type module 146 @type module
159 """ 147 """
160 pass 148 pass

eric ide

mercurial