""" [ urlimport.py ] Enables remote module importing. version: 0.50m (myevan fixed) author: Jure Vrscaj homepage: http://urlimport.codeshift.net license: GNU GPL == history == v0.50m 2010-03-16 - directory module importing bug fixed v0.49m 2010-03-16 - p4 recursive importing fixed v0.48m 2010-02-26 - pydXX support v0.47m 2010-02-04 - reset function added v0.46m 2010-02-03 - perforce bug fix v0.45m 2010-02-03 - pyd bug fix v0.44m 2010-01-31 - perforce support v0.43m 2010-01-25 - pyd support v0.42m 2010-01-22 - added cache function & hid some logs v0.42b 2006-12-30 - added support for DOS-style source files - eval() chokes on "\r\n" v0.42 2006-06-26 - added verbose mode setting v0.41 2006-06-05 - client ssl certificate support v0.32 2006-05-10 - ftp, https support :) v0.31 2006-02-24 - recursion patch: non-packages now have no __path__ - load_module now returns the module from sys.modules, in case the module itself was messing with sys.modules v0.30 2006-02-23 package importing now possible v0.02 2006-02-23 remote modules now first check own url when they have to import sth v0.01 2006-02-19 made basic (single-file) importing v0.00 2006-02-18 playing with path_hooks """ import sys, os, re import imp import copy from urllib2 import urlopen DOT_PYD = ".pyd%x%x" % ((sys.hexversion >> 24), (sys.hexversion >> 16) & 0xf) p4conn = None cacheDir = "pycache" re_url_ok = re.compile(r'^http://|^ftp://|^https://|^p4://') re_url_split = re.compile('^(.+):\/\/(.+?)(?::(\d+))?(\/.*)$') settings = sys.__dict__.setdefault( 'urlimport_settings', {'ssl_cert': '', 'ssl_key': '', 'debug': 0} ) def debug(s, pf='| |', lvl=1): if lvl <= settings.get('debug'): print "%s %s" % (pf, s) class UrlFinder: def __init__(self, path): if re_url_ok.match(path): debug("UrlFinder: accepting '%s'." % path, lvl=2) self.path = path if not self.path.endswith("/"): self.path += '/' else: debug("UrlFinder: rejecting non-url path item: '%s'" % path, lvl=3) raise ImportError def find_module(self, fullname, mpath=None): """try to locate the remote module, do this: a) try to get fullname.py from http://self.path/ b) try to get __init__.py from http://self.path/fullname/ """ debug("find_module %s" % (fullname), lvl=2) fullname = fullname.split('.')[-1] head = self.path + fullname for tail, path in [ ('.py', None), (DOT_PYD, None), ('/__init__.py', head + '/')]: url = head + tail debug("check_url %s" % (url), lvl=2) try: global cacheDir if url[-len(DOT_PYD):] == DOT_PYD: fileData = self.get_file_data(url) if cacheDir: cachePath = os.path.join(cacheDir, fullname + ".pyd") open(cachePath, "wb").write(fileData) debug("find_dynamic_module: got '%s'." % url, lvl=1) return UrlDynamicLoader(cachePath) else: source = self.get_source(url) if cacheDir: cachePath = os.path.join(cacheDir, fullname + tail) cachePath = os.path.join(cacheDir, fullname + tail) try: open(cachePath, "w").write(source) except IOError: cacheDir, cacheName = os.path.split(cachePath) os.makedirs(cacheDir) open(cachePath, "w").write(source) debug("find_source_module: got '%s'." % url, lvl=1) return UrlSourceLoader(url, path, source) except Exception, e: debug("find_module: failed to get '%s'. (%s)" % (url, e), lvl=3) debug("not_found_module %s" % (fullname), lvl=2) def get_file_data(self, url): """Download the file data from given url. """ if url[:5] == "p4://": global p4conn if not p4conn: # for protecting recursive sysPathBackup = copy.copy(sys.path) sys.path = [path for path in sys.path if path[:5] != "p4://"] from P4 import P4 sys.path = sysPathBackup p4conn = P4() p4conn.connect() debug("p4 connect", lvl=1) from P4 import P4Exception debug(url, lvl=1) p4url = url[3:] try: fstats = p4conn.run_fstat(p4url) except P4Exception: for w in p4conn.warnings: debug("P4WARNING: %s" % e, lvl=0) for e in p4conn.errors: debug("P4ERROR: %s" % e, lvl=0) raise StandardError, "PERFORCE_FSTAT_ERROR" debug("p4 fstat: %s" % p4url, lvl=1) for fstat in fstats: try: return open(fstat["clientFile"]).read() except IOError: p4conn.run_sync(p4url) return open(fstat["clientFile"]).read() else: raise StandardError, "PERFORCE_NO_FSTAT" else: key = settings.get('ssl_key') cert = settings.get('ssl_cert') proto, host, port, path = re_url_split.findall(url)[0] try: port = int(port) except: port = 443 if proto == 'https' and cert: # handle http over ssl with client certificate import httplib conn = httplib.HTTPSConnection( host=host, port=port, key_file=key, cert_file=cert, ) conn.putrequest('GET', path) conn.endheaders() response = conn.getresponse() if response.status != 200: raise StandardError, "HTTPS Error: %d"%response.status return response.read() else: # handle everything else return urlopen(url).read() def get_source(self, url): """Download the source from given url. """ return self.get_file_data(url).replace("\r\n", "\n") class UrlDynamicLoader: def __init__(self, cachePath): self.cachePath = cachePath def load_module(self, fullname): """add the new module to sys.modules, execute its source and return it """ return imp.load_dynamic(fullname, self.cachePath) class UrlSourceLoader: def __init__(self, url, path, source): self.url = url self.path = path self.source = source self._files = {} def load_module(self, fullname): """add the new module to sys.modules, execute its source and return it """ mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) mod.__file__ = "%s" % self.url mod.__loader__ = self if self.path: mod.__path__ = [self.path] for line in self.source.split('\n'): debug(line, pf='|>|', lvl=4) debug("load_module: executing %s's source..." % fullname, lvl=2) exec self.source in mod.__dict__ mod = sys.modules[fullname] return mod def config(**kwargs): """config(key=value) - Set key to value. config() - Display settings. """ settings.update(kwargs) for k,v in (kwargs or settings).iteritems(): debug(" "+str(k)+"="+repr(v), lvl=1 ) # register The Hook sys.path_hooks = [x for x in sys.path_hooks if x.__name__ != 'UrlFinder'] sys.path_hooks.append(UrlFinder) #sys.path_importer_cache.clear() debug("Url importing enabled. Add urls to sys.path.", lvl=1) debug("Use urlimport.config(key=value) to manipulate settings:", lvl=1) # print settings config() debug("", lvl=1) debug("This stuff is experimental, use at your own risk. Enjoy.", lvl=1) if cacheDir: if not os.access(cacheDir, os.W_OK): debug("Make a cache directory(%s) for urlimport" % (cacheDir), lvl=0) os.makedirs(cacheDir) sys.path.append(cacheDir) def reset(): if cacheDir: for base, dirs, names in os.walk(cacheDir): for name in names: os.remove(os.path.join(base, name)) if __name__ == "__main__": import sys sys.path.append("//code/main/Tools") import p4ver