Salome HOME
Join modifications from BR_Dev_For_4_0 tag V4_1_1.
[modules/kernel.git] / src / KERNEL_PY / import_hook.py
1 # Copyright (C) 2005  OPEN CASCADE, CEA, EDF R&D, LEG
2 #           PRINCIPIA R&D, EADS CCR, Lip6, BV, CEDRAT
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either 
6 # version 2.1 of the License.
7
8 # This library is distributed in the hope that it will be useful 
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
11 # Lesser General Public License for more details.
12
13 # You should have received a copy of the GNU Lesser General Public  
14 # License along with this library; if not, write to the Free Software 
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18
19 """
20 This module replaces the standard import mechanism with one
21 that filters some imports that can't be done more than once.
22
23 This is related to the multi study feature that is implemented
24 by using the Python multi interpreter feature.
25 Some modules register objects or classes by calling modules
26 implemented in C. These operations can't be done multiple times.
27 So it's very important to control these imports.
28
29 Examples:
30   - PyQt : import qt calls a C module to register classes
31   - OmniORB : import *_idl calls a C module to register CORBA interfaces
32
33 Usage:
34   - First import the module : import import_hook. This module will
35     replace the original importer mechanism
36
37   - Next register the module names or pattern names to filter out::
38      import_hook.register_name("a")
39      import_hook.register_pattern(pattern)
40
41     where pattern is a function with one parameter, the module name
42     to be imported, that returns true or false depending if this module is
43     to be filtered or not.
44
45   - Then it's done
46
47 IMPORTANT : Every subinterpretor has its own import_hook module. import_hook is not shared among subinterpretors.
48 The mechanism only works if shared_imported and pattern are shared between all subinterpretors.
49 This is done by calling init_shared_modules().
50   
51 """
52 import sys, imp, __builtin__
53
54 # Keep in shared_imported a copy of dictionnary modules
55 # that need to be imported only once in multi-study context
56 shared_imported={}
57
58 # patterns contains functions that returns 1 or 0 depending if 
59 # the module name (argument) must be filtered out or not
60 # These functions are added by calling register_pattern
61 patterns=[]
62
63 original_import=__builtin__.__import__
64
65 def register_name(name):
66     if shared_imported.has_key(name):return
67     shared_imported[name]=None
68
69 def register_pattern(pattern):
70     patterns.append(pattern)
71
72 def is_shared(name):
73     """ Indicate if module name is a shared module
74         among multiple interpreters (return value=1)
75     """
76     if shared_imported.has_key(name):return 1
77     for pattern in patterns:
78         if pattern(name) : return 1
79     return 0
80
81 def get_shared_imported(name,fromlist):
82     """ If the module is registered in shared_imported
83         update the sys.modules dict
84         Let the real import be done by original_import
85     """
86     module= shared_imported.get(name)
87     if module is None :
88        #module name is not shared or not already imported
89        #let original_import do the job
90        return None
91
92     # module is already imported and shared. Put it in sys.modules and 
93     # let original_import finish the job
94     sys.modules[name]=module
95
96 def get_real_module(mod,name):
97     """Return effective module on import
98        Standard import returns module A on import A.B
99        To get module A.B use get_real_module with name "A.B"
100     """
101     components = name.split('.')
102     for comp in components[1:]:
103         mod = getattr(mod, comp)
104     return mod
105
106 def set_shared_imported(name,module):
107     """ Register a shared module
108         Name can be a dotted name : package
109     """
110     shared_imported[name]=module
111     #print "Module %s shared registered" % name,module
112
113 def import_module(partname, fqname, parent):
114     """ Try to import module fqname
115         It's parent is module parent and has name partname
116     """
117     try:
118        m = sys.modules[fqname]
119     except KeyError:
120        pass
121     else:
122        return m
123
124 def ensure_fromlist(m, fromlist, recursive=0):
125     """ Return the real modules list to be imported
126     """
127     l=[]
128     for sub in fromlist:
129         if sub == "*":
130             if not recursive:
131                 try:
132                     all = m.__all__
133                 except AttributeError:
134                     pass
135                 else:
136                     l.extend(ensure_fromlist(m, all, 1))
137         elif hasattr(m,sub):
138             submod=getattr(m,sub)
139             if type(submod) == type(sys):
140                l.append(("%s.%s" % (m.__name__, sub),submod))
141         else:
142             subname="%s.%s" % (m.__name__, sub)
143             submod = import_module(sub, subname, m)
144             if not submod:
145                raise ImportError, "No module named " + subname
146             l.append((subname,submod))
147     return l
148
149 def import_hook(name, globals=None, locals=None, fromlist=None):
150     """ Import replacement for sharing modules among multiple interpreters
151         Mostly update sys.modules before doing real import
152     """
153     #print "import_hook",name,fromlist
154     m=get_shared_imported(name,fromlist)
155
156     module= original_import(name, globals, locals, fromlist)
157
158     if fromlist:
159        #when fromlist is specified, module is the real module
160        #fromlist is a list of possibly dotted name
161        m=module
162        for nam,mod in ensure_fromlist(m, fromlist):
163            if is_shared(nam):
164               set_shared_imported(nam,mod)
165     else: 
166        #when fromlist is not specified and name is a dotted name,
167        # module is the root package not the real module
168        #so we need to retrieve it
169        # note: some modules like xml.dom do not play the rule
170        # (import xml: no attribute dom, but import xml.dom OK)
171        try:
172            m=get_real_module(module,name)
173        except AttributeError:
174            m=None
175
176     if type(m) == type(sys) and is_shared(m.__name__):
177        set_shared_imported(m.__name__,m)
178
179     return module
180
181 original_reload=__builtin__.reload
182
183 def reload_hook(module):
184     if is_shared(module.__name__):
185        return module
186     return original_reload(module)
187
188 __builtin__.__import__=import_hook
189 # Reload is not replaced 
190 #__builtin__.reload=reload_hook
191
192 def init_shared_modules(shared_module):
193     global shared_imported, patterns
194     shared_imported=shared_module.shared_imported
195     patterns=       shared_module.patterns
196     for k,v in shared_imported.items():
197        if v is not None:sys.modules[k]=v
198     shared_imported["salome_shared_modules"]=shared_module
199     import salome_shared_modules
200     for m in salome_shared_modules.list_modules:
201         m.init_shared_modules()