eric7/DebugClients/Python/ModuleLoader.py

branch
eric7
changeset 8312
800c432b34c8
parent 8207
d359172d11be
child 8314
e3642a6a1e71
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2020 - 2021 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 = True
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', '_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
63 ## Add hook for _thread.start_new_thread
64 if (
65 fullname in ('thread', '_thread') and
66 not hasattr(module, 'eric6_patched')
67 ):
68 module.eric6_patched = True
69 self.__dbgClient.patchPyThread(module)
70
71 ## Add hook for threading.run()
72 elif (
73 fullname == "threading" and
74 not hasattr(module, 'eric6_patched')
75 ):
76 module.eric6_patched = True
77 self.__dbgClient.patchPyThreading(module)
78
79 ## greenlet support
80 elif (
81 fullname == 'greenlet' and
82 not hasattr(module, 'eric6_patched')
83 ):
84 if self.__dbgClient.patchGreenlet(module):
85 module.eric6_patched = True
86
87 ## Add hook for subprocess.Popen()
88 elif (
89 fullname == 'subprocess' and
90 not hasattr(module, 'eric6_patched')
91 ):
92 module.eric6_patched = True
93 patchSubprocess(module, self.__dbgClient)
94
95 ## Add hook for multiprocessing.Process
96 elif (
97 fullname == 'multiprocessing' and
98 not hasattr(module, 'eric6_patched')
99 ):
100 module.eric6_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, 'eric6_patched')
108 ):
109 module.eric6_patched = True
110 self.__dbgClient.patchQThread(module)
111 patchQProcess(module, self.__dbgClient)
112
113 self.__enableImportHooks = True
114 return module
115
116 def find_spec(self, fullname, path, target=None):
117 """
118 Public method returning the module spec.
119
120 @param fullname name of the module to be loaded
121 @type str
122 @param path path to resolve the module name
123 @type str
124 @param target module object to use for a more educated guess
125 about what spec to return
126 @type module
127 @return module spec object pointing to the module loader
128 @rtype ModuleSpec
129 """
130 if fullname in sys.modules or not self.__dbgClient.debugging:
131 return None
132
133 if (
134 fullname in self.__modulesToPatch and
135 self.__enableImportHooks
136 ):
137 # Disable hook to be able to import original module
138 self.__enableImportHooks = False
139 return importlib.machinery.ModuleSpec(fullname, self)
140
141 return None
142
143 def create_module(self, spec):
144 """
145 Public method to create a module based on the passed in spec.
146
147 @param spec module spec object for loading the module
148 @type ModuleSpec
149 @return created and patched module
150 @rtype module
151 """
152 return self.__loadModule(spec.name)
153
154 def exec_module(self, module):
155 """
156 Public method to execute the created module.
157
158 @param module module to be executed
159 @type module
160 """
161 pass

eric ide

mercurial