src/eric7/DebugClients/Python/ModuleLoader.py

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

eric ide

mercurial