xmlrpchandler.py :  » Database » PyDO » skunkweb-3.4.4 » pylibs » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Database » PyDO 
PyDO » skunkweb 3.4.4 » pylibs » xmlrpchandler.py
# Time-stamp: <03/02/08 09:37:03 smulloni>

"""
a module for serving XMLRPC from SkunkWeb.
"""
# for basic auth support
from __future__ import nested_scopes
import pydoc
import os
import re
import sys
import xmlrpclib

_tdoc=pydoc.TextDoc()

# These constants, and the multicall implementation below,
# are based on code by Eric Kidd.  Python 2.3 should have
# this constants in xmlrpclib, too.  Other code is derived
# from the standard library's SimpleXMLRPCServer.py, and
# from a cgi script by Fredrik Lundh.

INTERNAL_ERROR = -500
TYPE_ERROR = -501
INDEX_ERROR = -502
PARSE_ERROR = -503
NETWORK_ERROR = -504
TIMEOUT_ERROR = -505
NO_SUCH_METHOD_ERROR = -506
REQUEST_REFUSED_ERROR = -507
INTROSPECTION_DISABLED_ERROR = -508
LIMIT_EXCEEDED_ERROR = -509
INVALID_UTF8_ERROR = -510

# Some type names for use in method signatures;
# this is borrowed from Eric Kidd's code in xmlrpc-c's
# xmlrpc_registry.py.
INT="int"
BOOLEAN="boolean"
DOUBLE="double"
STRING="string"
DATETIME="dateTime.iso8601"
BASE64="base64"
ARRAY="array"
STRUCT="struct"
UNDEF="undef"

_multicall_doc="""
Processes an array of calls, and return an array of results.  Calls
should be structs of the form {'methodName': string, 'params': array}.
Each result will either be a single-item array containg the result
value, or a struct of the form {'faultCode': int, 'faultString': string}.
This is useful when you need to make lots of small calls without lots of
round trips.  -- [from Eric Kidd's implementation, upon which this is
based]
""".strip()

class XMLRPCServer:
    def __init__(self, add_system_methods=1, max_request=50000):
        self.funcs={}
        self.max_request=max_request
        if add_system_methods:
            self.register_function(self.funcs.keys,
                                   'system.listMethods',
                                   'Lists the names of registered methods',
                                   [[ARRAY]])
            # using lambdas to keep the pydoc method signature accurate
            self.register_function(lambda method: self._methodSignature(method),
                                   'system.methodSignature',
                                   ('Returns the signature of method requested.  '\
                                    'Raises a Fault if the method does not exist'),
                                   [[ARRAY, STRING]])
            self.register_function(lambda method: self._methodHelp(method),
                                   'system.methodHelp',
                                   ("Gives help for the method requested.  "\
                                   "Raises a Fault if the method does not exist."),
                                   [[STRING, STRING]])

            self.register_function(lambda calls: self._multicall(calls),
                                   'system.multicall',
                                   _multicall_doc,
                                   [[ARRAY, ARRAY]])

            self.register_function(lambda method: self._methodPydoc(method),
                                   'system.methodPydoc',
                                   ('Gives pydoc method signature for the method requested.  '\
                                    'Raises a Rault if the method does not exist.'),
                                   [[STRING]])

    def _methodPydoc(self, method):
        func=self.get_func(method)
        if func:
            s=pydoc.plain(_tdoc.docroutine(func)).strip()
            # remove the docstring
            cr=s.find('\n')
            if cr!=-1:
                s=s[:cr]
            # rename the method, if needed
            fname=func.__name__
            # this is a workaround for a pydoc bug
            # that will probably be fixed one day....
            if fname=='<lambda>':
                fname='lambda'
                s=re.sub(fname, method+'(', s, 1)+')'
                
            elif method != fname:
                s=re.sub(fname, method, s, 1)
            s=re.sub(r'\.\.\.', '', s)
            return s
        raise xmlrpclib.Fault(NO_SUCH_METHOD_ERROR, method)

    def _methodSignature(self, method):
        fspec=self.funcs.get(method)
        if not fspec or fspec[-1] is None:
            raise xmlrpclib.Fault(NO_SUCH_METHOD_ERROR, method)
        return fspec[-1]
            

    def _methodHelp(self, method):
        func=self.get_func(method)
        if func:
            return self.funcs[method][1]
        raise xmlrpclib.Fault(NO_SUCH_METHOD_ERROR, method)

    def _multicall (self, calls):
        results = []
        for call in calls:
            try:
                name = call['methodName']
                params = call['params']
                if name == 'system.multicall':
                    errmsg = "Recursive system.multicall forbidden"
                    return xmlrpclib.Fault(REQUEST_REFUSED_ERROR, errmsg)
                result = [self.dispatch(name, params)]
            except xmlrpclib.Fault, fault:
                result = {'faultCode': fault.faultCode,
                          'faultString': fault.faultString}
            except:
                errmsg = "%s:%s" % (sys.exc_type, sys.exc_value)
                result = {'faultCode': 1, 'faultString': errmsg}
            results.append(result)
        return results            

    def register_function(self,
                          func,
                          name=None,
                          docstring=None,
                          signature=UNDEF):
        if name is None:
            name=func.__name__
        if docstring is None:
            docstring=func.__doc__ or ""
        self.funcs[name]=(func, docstring.strip(), signature)

    def get_func(self, funcname):
        if self.funcs.has_key(funcname):
            return self.funcs[funcname][0]

    def dispatch(self, methodname, params):
        try:
            f=self.get_func(methodname)
            if not f:
                return xmlrpclib.Fault(NO_SUCH_METHOD_ERROR,
                                      methodname)
            return (f(*params),)
        except xmlrpclib.Fault, r:
            return r
        except:
            return xmlrpclib.Fault(1,
                                   "%s: %s" % (sys.exc_type,
                                               sys.exc_value))

    def handle_cgi(self, giveup=None):
        """
        for use in a cgi script.  If supplied, giveup should be
        a function with one required argument (message) and one optional
        (status).
        """
        if not giveup:
            def giveup(message, status=400):
                print "Status: %d" % status
                print
                print message
                sys.exit(0)
        
        if os.environ.get('REQUEST_METHOD')!='POST':
            giveup("precondition violated: not an HTTP POST")
        if self.max_request > 0:
            bytelen=int(os.environ.get("CONTENT_LENGTH", 0))
            if bytelen > self.max_request:
                # perhaps I should return a LIMIT_EXCEEDED fault instead?
                giveup("request too large", 413)
            
        params, method=xmlrpclib.loads(sys.stdin.read(bytelen))
        result=self.dispatch(method, params)
        mres=not isinstance(result, xmlrpclib.Fault)
        response=xmlrpclib.dumps(result, methodresponse=mres)
        
        print "Content-Type: text/xml"
        print "Content-Length: %d" % len(response)
        print
        print response


    def handle(self, connection):
        """
        connection should be a web.protocol.HttpConnection object
        (SkunkWeb only, presumably)
        """
        if connection.method!='POST':
            raise xmlrpclib.Fault(REQUEST_REFUSED_ERROR,
                                  "precondition violated: not an HTTP POST")
        if self.max_request > 0 and len(connection._stdin) > self.max_request:
            # see note in handle_cgi about returning a fault
            connection.setStatus(413)
            connection.responseHeaders['Content-type']='text/plain'
            return "request too large"
        params, method=xmlrpclib.loads(connection._stdin)
        res=self.dispatch(method, params)
        connection.setStatus(200)
        connection.responseHeaders['Content-type']='text/xml'
        mres=not isinstance(res, xmlrpclib.Fault)
        return xmlrpclib.dumps(res, methodresponse=mres)


def munge_transports():
    """
    dynamically patches the transport and ServerProxy objects
    so you can add arbitrary HTTP headers to an xmlrpc request,
    like so:
       s=ServerProxy('/whatever/')
       s.transport.more_headers={'X-Doo-Hickey' : 'Fruit Loops'}
    Hopefully, Python 2.3 will not require this brutal hack.
    """
    _old_send_request=xmlrpclib.Transport.send_request
    def send_request(self, connection, handler, request_body):
        _old_send_request(self, connection, handler, request_body)
        if hasattr(self, 'more_headers'):
            for k, v in self.more_headers.items():
                connection.putheader(k, v)
    xmlrpclib.Transport.send_request=send_request
    def get_transport(self):
        return self._ServerProxy__transport
    xmlrpclib.ServerProxy.get_transport=get_transport
            
        
    
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.