"""
[ urlimport.py ]
Enables remote module importing.

version:   0.42b
author:    Jure Vrscaj <jure@codeshift.net>
homepage:  http://urlimport.codeshift.net
license:   GNU GPL

== history ==
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

re_url_ok = re.compile(r'^http://|^ftp://|^https://')
re_url_split = re.compile('^(.+):\/\/(.+?)(?::(\d+))?(\/.*)$')

settings = sys.__dict__.setdefault(
    'urlimport_settings',
    {'ssl_cert': '', 'ssl_key': '', 'debug': 1}
)

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/
        """

        fullname = fullname.split('.')[-1]

        for url, path in [
         (self.path + fullname + '.py',          None),
         (self.path + fullname + '/__init__.py', self.path + fullname + '/')]:
            try:
                source = self.get_source(url)
            except Exception, e:
                debug("find_module: failed to get '%s'. (%s)" % (url, e), lvl=3)
            else:
                debug("find_module: got '%s'." % url, lvl=1)
                return UrlLoader(url, path, source)

        return None

    def get_source(self, url):
        """Download the source from given url.
        """
        from urllib2 import urlopen

        src = ''

        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
            src = response.read()
        else:
            # handle everything else
            src = urlopen(url).read()
        
        src = src.replace("\r\n", "\n")    
        return src

class UrlLoader:
    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=0 )

# 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=0)
debug("Use urlimport.config(key=value) to manipulate settings:", lvl=0)

# print settings
config()

debug("", lvl=0)
debug("This stuff is experimental, use at your own risk. Enjoy.", lvl=0)

