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