src/eric7/DebugClients/Python/ModuleLoader.py

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

eric ide

mercurial