diff options
| -rw-r--r-- | indra/lib/python/indra/base/config.py | 18 | ||||
| -rw-r--r-- | indra/lib/python/indra/base/llsd.py | 45 | ||||
| -rw-r--r-- | indra/lib/python/indra/base/lluuid.py | 9 | ||||
| -rw-r--r-- | indra/lib/python/indra/ipc/llsdhttp.py | 246 | ||||
| -rw-r--r-- | indra/lib/python/indra/ipc/servicebuilder.py | 17 | ||||
| -rw-r--r-- | indra/lib/python/indra/ipc/xml_rpc.py | 26 | 
6 files changed, 114 insertions, 247 deletions
| diff --git a/indra/lib/python/indra/base/config.py b/indra/lib/python/indra/base/config.py index d88801aa0d..86000cf98d 100644 --- a/indra/lib/python/indra/base/config.py +++ b/indra/lib/python/indra/base/config.py @@ -27,16 +27,20 @@ def load(indra_xml_file=None):          config_file.close()          #print "loaded config from",indra_xml_file,"into",_g_config_dict -def update(xml_file): +def update(new_conf):      """Load an XML file and apply its map as overrides or additions      to the existing config.  The dataserver does this with indra.xml      and dataserver.xml."""      global _g_config_dict      if _g_config_dict == None: -        raise Exception("no config loaded before config.update()") -    config_file = file(xml_file) -    overrides = llsd.LLSD().parse(config_file.read()) -    config_file.close() +        _g_config_dict = {} +    if isinstance(new_conf, dict): +        overrides = new_conf +    else: +        config_file = file(new_conf) +        overrides = llsd.LLSD().parse(config_file.read()) +        config_file.close() +              _g_config_dict.update(overrides)  def get(key, default = None): @@ -44,3 +48,7 @@ def get(key, default = None):      if _g_config_dict == None:          load()      return _g_config_dict.get(key, default) + +def as_dict(): +    global _g_config_dict +    return _g_config_dict diff --git a/indra/lib/python/indra/base/llsd.py b/indra/lib/python/indra/base/llsd.py index 462a8787ae..a03893bfc3 100644 --- a/indra/lib/python/indra/base/llsd.py +++ b/indra/lib/python/indra/base/llsd.py @@ -136,7 +136,8 @@ class LLSDXMLFormatter(object):              list : self.ARRAY,              tuple : self.ARRAY,              types.GeneratorType : self.ARRAY, -            dict : self.MAP +            dict : self.MAP, +            LLSD : self.LLSD          }      def elt(self, name, contents=None): @@ -148,6 +149,8 @@ class LLSDXMLFormatter(object):      def xml_esc(self, v):          return v.replace('&', '&').replace('<', '<').replace('>', '>') +    def LLSD(self, v): +        return self.generate(v.thing)      def UNDEF(self, v):          return self.elt('undef')      def BOOLEAN(self, v): @@ -212,9 +215,12 @@ class LLSDNotationFormatter(object):              list : self.ARRAY,              tuple : self.ARRAY,              types.GeneratorType : self.ARRAY, -            dict : self.MAP +            dict : self.MAP, +            LLSD : self.LLSD          } +    def LLSD(self, v): +        return self.generate(v.thing)      def UNDEF(self, v):          return '!'      def BOOLEAN(self, v): @@ -739,6 +745,8 @@ def format_binary(something):  def _format_binary_recurse(something):      if something is None:          return '!' +    elif isinstance(something, LLSD): +        return _format_binary_recurse(something.thing)      elif isinstance(something, bool):          if something:              return '1' @@ -807,3 +815,36 @@ class LLSD(object):  undef = LLSD(None) +# register converters for stacked, if stacked is available +try: +    from mulib import stacked +except ImportError, e: +    print "Not able to import stacked, skipping registering llsd converters." +    pass  # don't bother with the converters +else: +    def llsd_convert_json(llsd_stuff, request): +        callback = request.get_header('callback') +        if callback is not None: +            ## See Yahoo's ajax documentation for information about using this +            ## callback style of programming +            ## http://developer.yahoo.com/common/json.html#callbackparam +            req.write("%s(%s)" % (callback, simplejson.dumps(llsd_stuff))) +        else: +            req.write(simplejson.dumps(llsd_stuff)) + +    def llsd_convert_xml(llsd_stuff, request): +        request.write(format_xml(llsd_stuff)) + +    def llsd_convert_binary(llsd_stuff, request): +        request.write(format_binary(llsd_stuff)) + +    for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]: +        stacked.add_producer(typ, llsd_convert_json, 'application/json') + +        stacked.add_producer(typ, llsd_convert_xml, 'application/llsd+xml') +        stacked.add_producer(typ, llsd_convert_xml, 'application/xml') +        stacked.add_producer(typ, llsd_convert_xml, 'text/xml') + +        stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary') + +    stacked.add_producer(LLSD, llsd_convert_xml, '*/*') diff --git a/indra/lib/python/indra/base/lluuid.py b/indra/lib/python/indra/base/lluuid.py index 62ed365cab..f07446bba3 100644 --- a/indra/lib/python/indra/base/lluuid.py +++ b/indra/lib/python/indra/base/lluuid.py @@ -258,11 +258,12 @@ def uuid_bits_to_uuid(bits):  try: -    from mulib import mu +    from mulib import stacked  except ImportError:      print "Couldn't import mulib, not registering UUID converter"  else: -    def convertUUID(req, uuid): -        return str(uuid) +    def convertUUID(uuid, req): +        req.write(str(uuid)) -    mu.registerConverter(UUID, convertUUID) +    stacked.add_producer(UUID, convertUUID, "*/*") +    stacked.add_producer(UUID, convertUUID, "text/html") diff --git a/indra/lib/python/indra/ipc/llsdhttp.py b/indra/lib/python/indra/ipc/llsdhttp.py index cffff6b24b..b5c15d1c2b 100644 --- a/indra/lib/python/indra/ipc/llsdhttp.py +++ b/indra/lib/python/indra/ipc/llsdhttp.py @@ -10,251 +10,55 @@ import os.path  import os  import urlparse - -from eventlet import httpc as httputil - - -  from indra.base import llsd -LLSD = llsd.LLSD() - - -class ConnectionError(Exception): -    def __init__(self, method, host, port, path, status, reason, body): -        self.method = method -        self.host = host -        self.port = port -        self.path = path -        self.status = status -        self.reason = reason -        self.body = body -        Exception.__init__(self) - -    def __str__(self): -        return "%s(%r, %r, %r, %r, %r, %r, %r)" % ( -            self.__class__.__name__, -            self.method, self.host, self.port, -            self.path, self.status, self.reason, self.body) - - -class NotFound(ConnectionError): -    pass - -class Forbidden(ConnectionError): -    pass - -class MalformedResponse(ConnectionError): -    pass - -class NotImplemented(ConnectionError): -    pass - -def runConnection(connection, raise_parse_errors=True): -    response = connection.getresponse() -    if (response.status not in [200, 201, 204]): -        klass = {404:NotFound, -                 403:Forbidden, -                 501:NotImplemented}.get(response.status, ConnectionError) -        raise klass( -            connection.method, connection.host, connection.port, -            connection.path, response.status, response.reason, response.read()) -    content_length = response.getheader('content-length') - -    if content_length: # Check to see whether it is not None -        content_length = int(content_length) - -    if content_length: # Check to see whether the length is not 0 -        body = response.read(content_length) -    else: -        body = '' - -    if not body.strip(): -        #print "No body:", `body` -        return None -    try: -        return LLSD.parse(body) -    except Exception, e: -        if raise_parse_errors: -            print "Exception: %s, Could not parse LLSD: " % (e), body -            raise MalformedResponse( -            connection.method, connection.host, connection.port, -            connection.path, response.status, response.reason, body) -        else: -            return None - -class FileScheme(object): -    """Retarded scheme to local file wrapper.""" -    host = '<file>' -    port = '<file>' -    reason = '<none>' - -    def __init__(self, location): -        pass -    def request(self, method, fullpath, body='', headers=None): -        self.status = 200 -        self.path = fullpath.split('?')[0] -        self.method = method = method.lower() -        assert method in ('get', 'put', 'delete') -        if method == 'delete': -            try: -                os.remove(self.path) -            except OSError: -                pass  # don't complain if already deleted -        elif method == 'put': -            try: -                f = file(self.path, 'w') -                f.write(body) -                f.close() -            except IOError, e: -                self.status = 500 -                self.raise_connection_error() -        elif method == 'get': -            if not os.path.exists(self.path): -                self.status = 404 -                self.raise_connection_error(NotFound) +from eventlet import httpc -    def connect(self): -        pass -    def getresponse(self): -        return self +get, put, delete, post = httpc.make_suite( +    llsd.format_xml, llsd.parse, 'application/xml+llsd') -    def getheader(self, header): -        if header == 'content-length': -            try: -                return os.path.getsize(self.path) -            except OSError: -                return 0 -    def read(self, howmuch=None): -        if self.method == 'get': -            try: -                return file(self.path, 'r').read(howmuch) -            except IOError: -                self.status = 500 -                self.raise_connection_error() -        return '' +for x in (httpc.ConnectionError, httpc.NotFound, httpc.Forbidden): +    globals()[x.__name__] = x -    def raise_connection_error(self, klass=ConnectionError): -        raise klass( -            self.method, self.host, self.port, -            self.path, self.status, self.reason, '') -scheme_to_factory_map = { -    'http': httputil.HttpClient, -    'https': httputil.HttpsClient, -    'file': FileScheme, -} - -def makeConnection(scheme, location, use_proxy): -    if use_proxy: -        if "http_proxy" in os.environ: -            location = os.environ["http_proxy"] -        elif "ALL_PROXY" in os.environ: -            location = os.environ["ALL_PROXY"] -        else: -            location = "localhost:3128" #default to local squid -        if location.startswith("http://"): -            location = location[len("http://"):] -    return scheme_to_factory_map[scheme](location) - - -def get(url, headers=None, use_proxy=False): -    if headers is None: -        headers = {} -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=use_proxy) -    fullpath = path -    if query: -        fullpath += "?"+query -    connection.connect() -    if headers is None: -        headers=dict() -    if use_proxy: -        headers.update({ "Host" : location }) -        connection.request("GET", url, headers=headers) -    else: -        connection.request("GET", fullpath, headers=headers) - -    return runConnection(connection) - -def put(url, data, headers=None): -    body = LLSD.toXML(data) -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=False) -    if headers is None: -        headers = {} -    headers.update({'Content-Type': 'application/xml'}) -    fullpath = path -    if query: -        fullpath += "?"+query -    connection.request("PUT", fullpath, body, headers) -    return runConnection(connection, raise_parse_errors=False) - -def delete(url): -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=False) -    connection.request("DELETE", path+"?"+query) -    return runConnection(connection, raise_parse_errors=False) - -def post(url, data=None): -    body = LLSD.toXML(data) -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=False) -    connection.request("POST", path+"?"+query, body, {'Content-Type': 'application/xml'}) -    return runConnection(connection) - -def postFile(url, filename): +def postFile(url, filename, verbose=False):      f = open(filename)      body = f.read()      f.close() -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=False) -    connection.request("POST", path+"?"+query, body, {'Content-Type': 'application/octet-stream'}) -    return runConnection(connection) +    llsd_body = llsd.parse(bodY) +    return post(url, llsd_body, verbose=verbose) +  def getStatus(url, use_proxy=False): -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=use_proxy) -    if use_proxy: -        connection.request("GET", url, headers={ "Host" : location }) -    else: -        connection.request("GET", path+"?"+query) -    return connection.getresponse().status +    status, _headers, _body = get(url, use_proxy=use_proxy, verbose=True) +    return status +  def putStatus(url, data): -    body = LLSD.toXML(data) -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=False) -    connection.request("PUT", path+"?"+query, body, {'Content-Type': 'application/xml'}) -    return connection.getresponse().status +    status, _headers, _body = put(url, data, verbose=True) +    return status +  def deleteStatus(url): -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=False) -    connection.request("DELETE", path+"?"+query) -    return connection.getresponse().status +    status, _headers, _body = delete(url, verbose=True) +    return status +  def postStatus(url, data): -    body = LLSD.toXML(data) -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=False) -    connection.request("POST", path+"?"+query, body) -    return connection.getresponse().status +    status, _headers, _body = post(url, data, verbose=True) +    return status +  def postFileStatus(url, filename): -    f = open(filename) -    body = f.read() -    f.close() -    scheme, location, path, params, query, id = urlparse.urlparse(url) -    connection = makeConnection(scheme, location, use_proxy=False) -    connection.request("POST", path+"?"+query, body, {'Content-Type': 'application/octet-stream'}) -    response = connection.getresponse() -    return response.status, response.read() +    status, _headers, body = postFile(url, filename, verbose=True) +    return status, body +  def getFromSimulator(path, use_proxy=False):      return get('http://' + simulatorHostAndPort + path, use_proxy=use_proxy) +  def postToSimulator(path, data=None):      return post('http://' + simulatorHostAndPort + path, data) diff --git a/indra/lib/python/indra/ipc/servicebuilder.py b/indra/lib/python/indra/ipc/servicebuilder.py index 05de1ee5c5..289b9589d2 100644 --- a/indra/lib/python/indra/ipc/servicebuilder.py +++ b/indra/lib/python/indra/ipc/servicebuilder.py @@ -11,15 +11,28 @@ from indra.base import config  from indra.ipc import llsdhttp  from indra.ipc import russ +# *NOTE: agent presence relies on this variable existing and being current, it is a huge hack  services_config = {}  try:      services_config = llsdhttp.get(config.get('services-config'))  except:      pass -# convenience method for when you can't be arsed to make your own object (i.e. all the time)  _g_builder = None -def build(name, context): +def build(name, context={}, **kwargs): +    """ Convenience method for using a global, singleton, service builder.  Pass arguments either via a dict or via python keyword arguments, or both! + +    Example use: +     > context = {'channel':'Second Life Release', 'version':'1.18.2.0'} +     > servicebuilder.build('version-manager-version', context) +       'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.2.0' +     > servicebuilder.build('version-manager-version', channel='Second Life Release', version='1.18.2.0') +       'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.2.0' +     > servicebuilder.build('version-manager-version', context, version='1.18.1.2') +       'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.1.2' +    """ +    context = context.copy()  # shouldn't modify the caller's dictionary +    context.update(kwargs)      global _g_builder      if _g_builder is None:          _g_builder = ServiceBuilder() diff --git a/indra/lib/python/indra/ipc/xml_rpc.py b/indra/lib/python/indra/ipc/xml_rpc.py index 66375b4ac3..c5b5bcd567 100644 --- a/indra/lib/python/indra/ipc/xml_rpc.py +++ b/indra/lib/python/indra/ipc/xml_rpc.py @@ -189,19 +189,19 @@ def handle(yielder):      return result -VALUE = mu.tagFactory('value') -BOOLEAN = mu.tagFactory('boolean') -INT = mu.tagFactory('int') -STRUCT = mu.tagFactory('struct') -MEMBER = mu.tagFactory('member') -NAME = mu.tagFactory('name') -ARRAY = mu.tagFactory('array') -DATA = mu.tagFactory('data') -STRING = mu.tagFactory('string') -DOUBLE = mu.tagFactory('double') -METHODRESPONSE = mu.tagFactory('methodResponse') -PARAMS = mu.tagFactory('params') -PARAM = mu.tagFactory('param') +VALUE = mu.tag_factory('value') +BOOLEAN = mu.tag_factory('boolean') +INT = mu.tag_factory('int') +STRUCT = mu.tag_factory('struct') +MEMBER = mu.tag_factory('member') +NAME = mu.tag_factory('name') +ARRAY = mu.tag_factory('array') +DATA = mu.tag_factory('data') +STRING = mu.tag_factory('string') +DOUBLE = mu.tag_factory('double') +METHODRESPONSE = mu.tag_factory('methodResponse') +PARAMS = mu.tag_factory('params') +PARAM = mu.tag_factory('param')  mu.inline_elements['string'] = True  mu.inline_elements['boolean'] = True | 
