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