1
2
3
4 import __builtin__
5 import os
6 import re
7 import sys
8 import threading
9 import traceback
10 from gluon import current
11
12 NATIVE_IMPORTER = __builtin__.__import__
13 INVALID_MODULES = set(('', 'gluon', 'applications', 'custom_import'))
14
15
16
17
22
23
25 assert track in (True, False), "must be True or False"
26 current.request._custom_import_track_changes = track
27
28
31
32
35
36
37 -def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
38 """
39 The web2py custom importer. Like the standard Python importer but it
40 tries to transform import statements as something like
41 "import applications.app_name.modules.x".
42 If the import failed, fall back on naive_importer
43 """
44
45 globals = globals or {}
46 locals = locals or {}
47 fromlist = fromlist or []
48
49 try:
50 if current.request._custom_import_track_changes:
51 base_importer = TRACK_IMPORTER
52 else:
53 base_importer = NATIVE_IMPORTER
54 except:
55 base_importer = NATIVE_IMPORTER
56
57
58 if hasattr(current, 'request') \
59 and level <= 0 \
60 and not name.split('.')[0] in INVALID_MODULES \
61 and isinstance(globals, dict):
62 import_tb = None
63 try:
64 try:
65 oname = name if not name.startswith('.') else '.'+name
66 return NATIVE_IMPORTER(oname, globals, locals, fromlist, level)
67 except ImportError:
68 items = current.request.folder.split(os.path.sep)
69 if not items[-1]:
70 items = items[:-1]
71 modules_prefix = '.'.join(items[-2:]) + '.modules'
72 if not fromlist:
73
74 result = None
75 for itemname in name.split("."):
76 new_mod = base_importer(
77 modules_prefix, globals, locals, [itemname], level)
78 try:
79 result = result or new_mod.__dict__[itemname]
80 except KeyError, e:
81 raise ImportError, 'Cannot import module %s' % str(e)
82 modules_prefix += "." + itemname
83 return result
84 else:
85
86 pname = modules_prefix + "." + name
87 return base_importer(pname, globals, locals, fromlist, level)
88 except ImportError, e1:
89 import_tb = sys.exc_info()[2]
90 try:
91 return NATIVE_IMPORTER(name, globals, locals, fromlist, level)
92 except ImportError, e3:
93 raise ImportError, e1, import_tb
94 except Exception, e2:
95 raise e2
96 finally:
97 if import_tb:
98 import_tb = None
99
100 return NATIVE_IMPORTER(name, globals, locals, fromlist, level)
101
102
104 """
105 An importer tracking the date of the module files and reloading them when
106 they have changed.
107 """
108
109 THREAD_LOCAL = threading.local()
110 PACKAGE_PATH_SUFFIX = os.path.sep + "__init__.py"
111
113 self._import_dates = {}
114
115 - def __call__(self, name, globals=None, locals=None, fromlist=None, level=-1):
116 """
117 The import method itself.
118 """
119 globals = globals or {}
120 locals = locals or {}
121 fromlist = fromlist or []
122 if not hasattr(self.THREAD_LOCAL, '_modules_loaded'):
123 self.THREAD_LOCAL._modules_loaded = set()
124 try:
125
126 self._update_dates(name, globals, locals, fromlist, level)
127
128 result = NATIVE_IMPORTER(name, globals, locals, fromlist, level)
129
130 self._update_dates(name, globals, locals, fromlist, level)
131 return result
132 except Exception, e:
133 raise
134
135 - def _update_dates(self, name, globals, locals, fromlist, level):
136 """
137 Update all the dates associated to the statement import. A single
138 import statement may import many modules.
139 """
140
141 self._reload_check(name, globals, locals, level)
142 for fromlist_name in fromlist or []:
143 pname = "%s.%s" % (name, fromlist_name)
144 self._reload_check(pname, globals, locals, level)
145
147 """
148 Update the date associated to the module and reload the module if
149 the file has changed.
150 """
151 module = sys.modules.get(name)
152 file = self._get_module_file(module)
153 if file:
154 date = self._import_dates.get(file)
155 new_date = None
156 reload_mod = False
157 mod_to_pack = False
158 try:
159 new_date = os.path.getmtime(file)
160 except:
161 self._import_dates.pop(file, None)
162
163
164 if file.endswith(".py"):
165
166 file = os.path.splitext(file)[0]
167 reload_mod = os.path.isdir(file) \
168 and os.path.isfile(file + self.PACKAGE_PATH_SUFFIX)
169 mod_to_pack = reload_mod
170 else:
171 file += ".py"
172 reload_mod = os.path.isfile(file)
173 if reload_mod:
174 new_date = os.path.getmtime(file)
175 if reload_mod or not date or new_date > date:
176 self._import_dates[file] = new_date
177 if reload_mod or (date and new_date > date):
178 if module not in self.THREAD_LOCAL._modules_loaded:
179 if mod_to_pack:
180
181 mod_name = module.__name__
182 del sys.modules[mod_name]
183
184 NATIVE_IMPORTER(mod_name, globals, locals, [], level)
185 else:
186 reload(module)
187 self.THREAD_LOCAL._modules_loaded.add(module)
188
190 """
191 Get the absolute path file associated to the module or None.
192 """
193 file = getattr(module, "__file__", None)
194 if file:
195
196 file = os.path.splitext(file)[0] + ".py"
197 if file.endswith(self.PACKAGE_PATH_SUFFIX):
198 file = os.path.dirname(file)
199 return file
200
201 TRACK_IMPORTER = TrackImporter()
202