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