Package Dropbox :: Package web2py :: Package gluon :: Module custom_import
[hide private]
[frames] | no frames]

Source Code for Module Dropbox.web2py.gluon.custom_import

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  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  # backward compatibility API 
 16   
 17   
18 -def custom_import_install():
19 if __builtin__.__import__ == NATIVE_IMPORTER: 20 INVALID_MODULES.update(sys.modules.keys()) 21 __builtin__.__import__ = custom_importer
22 23
24 -def track_changes(track=True):
25 assert track in (True, False), "must be True or False" 26 current.request._custom_import_track_changes = track
27 28
29 -def is_tracking_changes():
30 return current.request._custom_import_track_changes
31 32
33 -class CustomImportException(ImportError):
34 pass
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: # there is no current.request (should never happen) 55 base_importer = NATIVE_IMPORTER 56 57 # if not relative and not from applications: 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 items = current.request.folder.split(os.path.sep) 65 if not items[-1]: 66 items = items[:-1] 67 modules_prefix = '.'.join(items[-2:]) + '.modules' 68 if not fromlist: 69 # import like "import x" or "import x.y" 70 result = None 71 for itemname in name.split("."): 72 new_mod = base_importer( 73 modules_prefix, globals, locals, [itemname], level) 74 try: 75 result = result or new_mod.__dict__[itemname] 76 except KeyError, e: 77 raise ImportError, 'Cannot import module %s' % str(e) 78 modules_prefix += "." + itemname 79 return result 80 else: 81 # import like "from x import a, b, ..." 82 pname = modules_prefix + "." + name 83 return base_importer(pname, globals, locals, fromlist, level) 84 except ImportError, e1: 85 import_tb = sys.exc_info()[2] 86 try: 87 return NATIVE_IMPORTER(name, globals, locals, fromlist, level) 88 except ImportError, e3: 89 raise ImportError, e1, import_tb # there an import error in the module 90 except Exception, e2: 91 raise e2 # there is an error in the module 92 finally: 93 if import_tb: 94 import_tb = None 95 96 return NATIVE_IMPORTER(name, globals, locals, fromlist, level)
97 98
99 -class TrackImporter(object):
100 """ 101 An importer tracking the date of the module files and reloading them when 102 they have changed. 103 """ 104 105 THREAD_LOCAL = threading.local() 106 PACKAGE_PATH_SUFFIX = os.path.sep + "__init__.py" 107
108 - def __init__(self):
109 self._import_dates = {} # Import dates of the files of the modules
110
111 - def __call__(self, name, globals=None, locals=None, fromlist=None, level=-1):
112 """ 113 The import method itself. 114 """ 115 globals = globals or {} 116 locals = locals or {} 117 fromlist = fromlist or [] 118 if not hasattr(self.THREAD_LOCAL, '_modules_loaded'): 119 self.THREAD_LOCAL._modules_loaded = set() 120 try: 121 # Check the date and reload if needed: 122 self._update_dates(name, globals, locals, fromlist, level) 123 # Try to load the module and update the dates if it works: 124 result = NATIVE_IMPORTER(name, globals, locals, fromlist, level) 125 # Module maybe loaded for the 1st time so we need to set the date 126 self._update_dates(name, globals, locals, fromlist, level) 127 return result 128 except Exception, e: 129 raise # Don't hide something that went wrong
130
131 - def _update_dates(self, name, globals, locals, fromlist, level):
132 """ 133 Update all the dates associated to the statement import. A single 134 import statement may import many modules. 135 """ 136 137 self._reload_check(name, globals, locals, level) 138 for fromlist_name in fromlist or []: 139 pname = "%s.%s" % (name, fromlist_name) 140 self._reload_check(pname, globals, locals, level)
141
142 - def _reload_check(self, name, globals, locals, level):
143 """ 144 Update the date associated to the module and reload the module if 145 the file has changed. 146 """ 147 module = sys.modules.get(name) 148 file = self._get_module_file(module) 149 if file: 150 date = self._import_dates.get(file) 151 new_date = None 152 reload_mod = False 153 mod_to_pack = False # Module turning into a package? (special case) 154 try: 155 new_date = os.path.getmtime(file) 156 except: 157 self._import_dates.pop(file, None) # Clean up 158 # Handle module changing in package and 159 #package changing in module: 160 if file.endswith(".py"): 161 # Get path without file ext: 162 file = os.path.splitext(file)[0] 163 reload_mod = os.path.isdir(file) \ 164 and os.path.isfile(file + self.PACKAGE_PATH_SUFFIX) 165 mod_to_pack = reload_mod 166 else: # Package turning into module? 167 file += ".py" 168 reload_mod = os.path.isfile(file) 169 if reload_mod: 170 new_date = os.path.getmtime(file) # Refresh file date 171 if reload_mod or not date or new_date > date: 172 self._import_dates[file] = new_date 173 if reload_mod or (date and new_date > date): 174 if module not in self.THREAD_LOCAL._modules_loaded: 175 if mod_to_pack: 176 # Module turning into a package: 177 mod_name = module.__name__ 178 del sys.modules[mod_name] # Delete the module 179 # Reload the module: 180 NATIVE_IMPORTER(mod_name, globals, locals, [], level) 181 else: 182 reload(module) 183 self.THREAD_LOCAL._modules_loaded.add(module)
184
185 - def _get_module_file(self, module):
186 """ 187 Get the absolute path file associated to the module or None. 188 """ 189 file = getattr(module, "__file__", None) 190 if file: 191 # Make path absolute if not: 192 file = os.path.splitext(file)[0] + ".py" # Change .pyc for .py 193 if file.endswith(self.PACKAGE_PATH_SUFFIX): 194 file = os.path.dirname(file) # Track dir for packages 195 return file
196 197 TRACK_IMPORTER = TrackImporter() 198