eric6/DebugClients/Python/ModuleLoader.py

branch
multi_processing
changeset 7404
663f1c3d6f53
child 7407
a0b6acee2c20
equal deleted inserted replaced
7403:7446a7eacfc3 7404:663f1c3d6f53
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2020 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
14 class ModuleLoader(object):
15 """
16 Class implementing an import hook patching modules to support debugging.
17 """
18 def __init__(self, debugClient):
19 """
20 Constructor
21
22 @param debugClient reference to the debug client object
23 @type DebugClient
24 """
25 self.__dbgClient = debugClient
26
27 self.__enableImportHooks = True
28
29 # TODO: check if needed
30 if sys.version_info[0] == 2:
31 self.threadModName = 'thread'
32 else:
33 self.threadModName = '_thread'
34
35 # reset already imported thread module to apply hooks at next import
36 for moduleName in ("thread", "_thread", "threading"):
37 if moduleName in sys.modules:
38 del sys.modules[moduleName]
39
40 self.__modulesToPatch = (
41 'thread', '_thread', 'threading',
42 'greenlet',
43 'PyQt4.QtCore', 'PyQt5.QtCore',
44 'PySide.QtCore', 'PySide2.QtCore',
45 )
46
47 sys.meta_path.insert(0, self)
48
49 def __loadModule(self, fullname):
50 """
51 Public method to load a module.
52
53 @param fullname name of the module to be loaded
54 @type str
55 @return reference to the loaded module
56 @rtype module
57 """
58 module = importlib.import_module(fullname)
59 sys.modules[fullname] = module
60
61 ## Add hook for _thread.start_new_thread
62 if (
63 fullname in ('thread', '_thread') and
64 not hasattr(module, 'eric6_patched')
65 ):
66 module.eric6_patched = True
67 self.__dbgClient.patchPyThread(module)
68
69 ## Add hook for threading.run()
70 elif (
71 fullname == "threading" and
72 not hasattr(module, 'eric6_patched')
73 ):
74 module.eric6_patched = True
75 self.__dbgClient.patchPyThreading(module)
76
77 ## greenlet support
78 elif (
79 fullname == 'greenlet' and
80 not hasattr(module, 'eric6_patched')
81 ):
82 if self.__dbgClient.patchGreenlet(module):
83 module.eric6_patched = True
84
85 ## Add hook for *.QThread
86 elif (
87 fullname in ('PyQt4.QtCore', 'PyQt5.QtCore',
88 'PySide.QtCore', 'PySide2.QtCore') and
89 not hasattr(module, 'eric6_patched')
90 ):
91 module.eric6_patched = True
92 self.__dbgClient.patchQThread(module)
93
94 self.__enableImportHooks = True
95 return module
96
97 if sys.version_info >= (3, 4):
98 def find_spec(self, fullname, path, target=None):
99 """
100 Public method returning the module spec.
101
102 @param fullname name of the module to be loaded
103 @type str
104 @param path path to resolve the module name
105 @type str
106 @param target module object to use for a more educated guess
107 about what spec to return
108 @type module
109 @return module spec object pointing to the module loader
110 @type ModuleSpec
111 """
112 if fullname in sys.modules or not self.__dbgClient.debugging:
113 return None
114
115 if (
116 fullname in self.__modulesToPatch and
117 self.__enableImportHooks
118 ):
119 # Disable hook to be able to import original module
120 self.__enableImportHooks = False
121 return importlib.machinery.ModuleSpec(fullname, self)
122
123 return None
124
125 def create_module(self, spec):
126 """
127 Public method to create a module based on the passed in spec.
128
129 @param spec module spec object for loading the module
130 @type ModuleSpec
131 @return created and patched module
132 @rtype module
133 """
134 return self.__loadModule(spec.name)
135
136 def exec_module(self, module):
137 """
138 Public method to execute the created module
139
140 @param module module to be executed
141 @type module
142 """
143 pass
144
145 else:
146 def find_module(self, fullname, path=None):
147 """
148 Public method returning the module loader.
149
150 @param fullname name of the module to be loaded
151 @type str
152 @param path path to resolve the module name
153 @type str
154 @return module loader object
155 @rtype object
156 """
157 if fullname in sys.modules or not self.__dbgClient.debugging:
158 return None
159
160 if (
161 fullname in self.__modulesToPatch and
162 self.__enableImportHooks
163 ):
164 # Disable hook to be able to import original module
165 self.__enableImportHooks = False
166 return self
167
168 return None
169
170 def load_module(self, fullname):
171 """
172 Public method to load a module.
173
174 @param fullname name of the module to be loaded
175 @type str
176 @return reference to the loaded module
177 @rtype module
178 """
179 return self.__loadModule(fullname)

eric ide

mercurial