Salome HOME
Base implementation of Notebook
[modules/kernel.git] / src / KERNEL_PY / import_hook.py
1 #  -*- coding: iso-8859-1 -*-
2 #  Copyright (C) 2007-2008  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 This module replaces the standard import mechanism with one
25 that filters some imports that can't be done more than once.
26
27 This is related to the multi study feature that is implemented
28 by using the Python multi interpreter feature.
29 Some modules register objects or classes by calling modules
30 implemented in C. These operations can't be done multiple times.
31 So it's very important to control these imports.
32
33 Examples:
34   - PyQt : import qt calls a C module to register classes
35   - OmniORB : import *_idl calls a C module to register CORBA interfaces
36
37 Usage:
38   - First import the module : import import_hook. This module will
39     replace the original importer mechanism
40
41   - Next register the module names or pattern names to filter out::
42      import_hook.register_name("a")
43      import_hook.register_pattern(pattern)
44
45     where pattern is a function with one parameter, the module name
46     to be imported, that returns true or false depending if this module is
47     to be filtered or not.
48
49   - Then it's done
50
51 IMPORTANT : Every subinterpretor has its own import_hook module. import_hook is not shared among subinterpretors.
52 The mechanism only works if shared_imported and pattern are shared between all subinterpretors.
53 This is done by calling init_shared_modules().
54   
55 """
56 import sys, imp, __builtin__
57
58 # Keep in shared_imported a copy of dictionnary modules
59 # that need to be imported only once in multi-study context
60 shared_imported={}
61
62 # patterns contains functions that returns 1 or 0 depending if 
63 # the module name (argument) must be filtered out or not
64 # These functions are added by calling register_pattern
65 patterns=[]
66
67 original_import=__builtin__.__import__
68
69 def register_name(name):
70     if shared_imported.has_key(name):return
71     shared_imported[name]=None
72
73 def register_pattern(pattern):
74     patterns.append(pattern)
75
76 def is_shared(name):
77     """ Indicate if module name is a shared module
78         among multiple interpreters (return value=1)
79     """
80     if shared_imported.has_key(name):return 1
81     for pattern in patterns:
82         if pattern(name) : return 1
83     return 0
84
85 def get_shared_imported(name,fromlist):
86     """ If the module is registered in shared_imported
87         update the sys.modules dict
88         Let the real import be done by original_import
89     """
90     module= shared_imported.get(name)
91     if module is None :
92        #module name is not shared or not already imported
93        #let original_import do the job
94        return None
95
96     # module is already imported and shared. Put it in sys.modules and 
97     # let original_import finish the job
98     sys.modules[name]=module
99
100 def get_real_module(mod,name):
101     """Return effective module on import
102        Standard import returns module A on import A.B
103        To get module A.B use get_real_module with name "A.B"
104     """
105     components = name.split('.')
106     for comp in components[1:]:
107         mod = getattr(mod, comp)
108     return mod
109
110 def set_shared_imported(name,module):
111     """ Register a shared module
112         Name can be a dotted name : package
113     """
114     shared_imported[name]=module
115     #print "Module %s shared registered" % name,module
116
117 def import_module(partname, fqname, parent):
118     """ Try to import module fqname
119         It's parent is module parent and has name partname
120     """
121     try:
122        m = sys.modules[fqname]
123     except KeyError:
124        pass
125     else:
126        return m
127
128 def ensure_fromlist(m, fromlist, recursive=0):
129     """ Return the real modules list to be imported
130     """
131     l=[]
132     for sub in fromlist:
133         if sub == "*":
134             if not recursive:
135                 try:
136                     all = m.__all__
137                 except AttributeError:
138                     pass
139                 else:
140                     l.extend(ensure_fromlist(m, all, 1))
141         elif hasattr(m,sub):
142             submod=getattr(m,sub)
143             if type(submod) == type(sys):
144                l.append(("%s.%s" % (m.__name__, sub),submod))
145         else:
146             subname="%s.%s" % (m.__name__, sub)
147             submod = import_module(sub, subname, m)
148             if not submod:
149                raise ImportError, "No module named " + subname
150             l.append((subname,submod))
151     return l
152
153 def import_hook(name, globals=None, locals=None, fromlist=None, *args):
154     """ Import replacement for sharing modules among multiple interpreters
155         Mostly update sys.modules before doing real import
156     """
157     #print "import_hook",name,fromlist
158     m=get_shared_imported(name,fromlist)
159
160     module= original_import(name, globals, locals, fromlist, *args)
161
162     if fromlist:
163        #when fromlist is specified, module is the real module
164        #fromlist is a list of possibly dotted name
165        m=module
166        for nam,mod in ensure_fromlist(m, fromlist):
167            if is_shared(nam):
168               set_shared_imported(nam,mod)
169     else: 
170        #when fromlist is not specified and name is a dotted name,
171        # module is the root package not the real module
172        #so we need to retrieve it
173        # note: some modules like xml.dom do not play the rule
174        # (import xml: no attribute dom, but import xml.dom OK)
175        try:
176            m=get_real_module(module,name)
177        except AttributeError:
178            m=None
179
180     if type(m) == type(sys) and is_shared(m.__name__):
181        set_shared_imported(m.__name__,m)
182
183     return module
184
185 original_reload=__builtin__.reload
186
187 def reload_hook(module):
188     if is_shared(module.__name__):
189        return module
190     return original_reload(module)
191
192 __builtin__.__import__=import_hook
193 # Reload is not replaced 
194 #__builtin__.reload=reload_hook
195
196 def init_shared_modules(shared_module):
197     global shared_imported, patterns
198     shared_imported=shared_module.shared_imported
199     patterns=       shared_module.patterns
200     for k,v in shared_imported.items():
201        if v is not None:sys.modules[k]=v
202     shared_imported["salome_shared_modules"]=shared_module
203     import salome_shared_modules
204     for m in salome_shared_modules.list_modules:
205         m.init_shared_modules()