[Python-checkins] python/dist/src/Lib SimpleXMLRPCServer.py,1.2,1.3

loewis@users.sourceforge.net loewis@users.sourceforge.net
2003年1月15日 03:37:25 -0800


Update of /cvsroot/python/python/dist/src/Lib
In directory sc8-pr-cvs1:/tmp/cvs-serv3409/Lib
Modified Files:
	SimpleXMLRPCServer.py 
Log Message:
Patch #473586: Implement CGIXMLRPCRequestHandler.
Index: SimpleXMLRPCServer.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/SimpleXMLRPCServer.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** SimpleXMLRPCServer.py	29 Sep 2001 04:54:33 -0000	1.2
--- SimpleXMLRPCServer.py	15 Jan 2003 11:37:23 -0000	1.3
***************
*** 3,9 ****
 This module can be used to create simple XML-RPC servers
 by creating a server and either installing functions, a
! class instance, or by extending the SimpleXMLRPCRequestHandler
 class.
 
 A list of possible usage patterns follows:
 
--- 3,12 ----
 This module can be used to create simple XML-RPC servers
 by creating a server and either installing functions, a
! class instance, or by extending the SimpleXMLRPCServer
 class.
 
+ It can also be used to handle XML-RPC requests in a CGI
+ environment using CGIXMLRPCRequestHandler.
+ 
 A list of possible usage patterns follows:
 
***************
*** 23,29 ****
--- 26,39 ----
 import string
 self.string = string
+ def _listMethods(self):
+ # implement this method so that system.listMethods
+ # knows to advertise the strings methods
+ return list_public_methods(self) + \
+ ['string.' + method for method in list_public_methods(self.string)]
 def pow(self, x, y): return pow(x, y)
 def add(self, x, y) : return x + y
+ 
 server = SimpleXMLRPCServer(("localhost", 8000))
+ server.register_introspection_functions()
 server.register_instance(MyFuncs())
 server.serve_forever()
***************
*** 32,49 ****
 
 class Math:
 def _dispatch(self, method, params):
 if method == 'pow':
! return apply(pow, params)
 elif method == 'add':
 return params[0] + params[1]
 else:
 raise 'bad method'
 server = SimpleXMLRPCServer(("localhost", 8000))
 server.register_instance(Math())
 server.serve_forever()
 
! 4. Subclass SimpleXMLRPCRequestHandler:
 
! class MathHandler(SimpleXMLRPCRequestHandler):
 def _dispatch(self, method, params):
 try:
--- 42,76 ----
 
 class Math:
+ def _listMethods(self):
+ # this method must be present for system.listMethods
+ # to work
+ return ['add', 'pow']
+ def _methodHelp(self, method):
+ # this method must be present for system.methodHelp
+ # to work
+ if method == 'add':
+ return "add(2,3) => 5"
+ elif method == 'pow':
+ return "pow(x, y[, z]) => number"
+ else:
+ # By convention, return empty
+ # string if no help is available
+ return ""
 def _dispatch(self, method, params):
 if method == 'pow':
! return pow(*params)
 elif method == 'add':
 return params[0] + params[1]
 else:
 raise 'bad method'
+ 
 server = SimpleXMLRPCServer(("localhost", 8000))
+ server.register_introspection_functions()
 server.register_instance(Math())
 server.serve_forever()
 
! 4. Subclass SimpleXMLRPCServer:
 
! class MathServer(SimpleXMLRPCServer):
 def _dispatch(self, method, params):
 try:
***************
*** 55,68 ****
 raise Exception('method "%s" is not supported' % method)
 else:
! return apply(func, params)
! 
! def log_message(self, format, *args):
! pass # maybe do something fancy like write the messages to a file
 
 def export_add(self, x, y):
 return x + y
 
! server = SimpleXMLRPCServer(("localhost", 8000), MathHandler)
 server.serve_forever()
 """
 
--- 82,98 ----
 raise Exception('method "%s" is not supported' % method)
 else:
! return func(*params)
 
 def export_add(self, x, y):
 return x + y
 
! server = MathServer(("localhost", 8000))
 server.serve_forever()
+ 
+ 5. CGI script:
+ 
+ server = CGIXMLRPCRequestHandler()
+ server.register_function(pow)
+ server.handle_request()
 """
 
***************
*** 71,130 ****
 
 import xmlrpclib
 import SocketServer
 import BaseHTTPServer
 import sys
 
! class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
! """Simple XML-RPC request handler class.
 
! Handles all HTTP POST requests and attempts to decode them as
! XML-RPC requests.
 
! XML-RPC requests are dispatched to the _dispatch method, which
! may be overriden by subclasses. The default implementation attempts
! to dispatch XML-RPC calls to the functions or instance installed
! in the server.
 """
 
! def do_POST(self):
! """Handles the HTTP POST request.
 
! Attempts to interpret all HTTP POST requests as XML-RPC calls,
! which are forwarded to the _dispatch method for handling.
 """
 
! try:
! # get arguments
! data = self.rfile.read(int(self.headers["content-length"]))
! params, method = xmlrpclib.loads(data)
 
! # generate response
! try:
 response = self._dispatch(method, params)
! # wrap response in a singleton tuple
! response = (response,)
! except:
! # report exception back to server
! response = xmlrpclib.dumps(
! xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value))
! )
! else:
! response = xmlrpclib.dumps(response, methodresponse=1)
 except:
! # internal error, report as HTTP server error
! self.send_response(500)
! self.end_headers()
 else:
! # got a valid XML RPC response
! self.send_response(200)
! self.send_header("Content-type", "text/xml")
! self.send_header("Content-length", str(len(response)))
! self.end_headers()
! self.wfile.write(response)
 
! # shut down the connection
! self.wfile.flush()
! self.connection.shutdown(1)
 
 def _dispatch(self, method, params):
 """Dispatches the XML-RPC method.
--- 101,342 ----
 
 import xmlrpclib
+ from xmlrpclib import Fault
 import SocketServer
 import BaseHTTPServer
 import sys
+ import types
+ import os
 
! def resolve_dotted_attribute(obj, attr):
! """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
 
! Resolves a dotted attribute name to an object. Raises
! an AttributeError if any attribute in the chain starts with a '_'.
! """
 
! for i in attr.split('.'):
! if i.startswith('_'):
! raise AttributeError(
! 'attempt to access private attribute "%s"' % i
! )
! else:
! obj = getattr(obj,i)
! return obj
! 
! def list_public_methods(obj):
! """Returns a list of attribute strings, found in the specified
! object, which represent callable attributes"""
! 
! return [member for member in dir(obj)
! if not member.startswith('_') and
! callable(getattr(obj, member))]
! 
! def remove_duplicates(lst):
! """remove_duplicates([2,2,2,1,3,3]) => [3,1,2]
! 
! Returns a copy of a list without duplicates. Every list
! item must be hashable and the order of the items in the
! resulting list is not defined.
! """ 
! u = {}
! for x in lst:
! u[x] = 1
! 
! return u.keys()
! 
! class SimpleXMLRPCDispatcher:
! """Mix-in class that dispatches XML-RPC requests.
! 
! This class is used to register XML-RPC method handlers
! and then to dispatch them. There should never be any
! reason to instantiate this class directly.
 """
+ 
+ def __init__(self):
+ self.funcs = {}
+ self.instance = None
 
! def register_instance(self, instance):
! """Registers an instance to respond to XML-RPC requests.
 
! Only one instance can be installed at a time.
! 
! If the registered instance has a _dispatch method then that
! method will be called with the name of the XML-RPC method and
! it's parameters as a tuple
! e.g. instance._dispatch('add',(2,3))
! 
! If the registered instance does not have a _dispatch method
! then the instance will be searched to find a matching method
! and, if found, will be called. Methods beginning with an '_'
! are considered private and will not be called by
! SimpleXMLRPCServer.
! 
! If a registered function matches a XML-RPC request, then it
! will be called instead of the registered instance.
 """
 
! self.instance = instance
 
! def register_function(self, function, name = None):
! """Registers a function to respond to XML-RPC requests.
! 
! The optional name argument can be used to set a Unicode name
! for the function.
! """
! 
! if name is None:
! name = function.__name__
! self.funcs[name] = function
! 
! def register_introspection_functions(self):
! """Registers the XML-RPC introspection methods in the system
! namespace.
! 
! see http://xmlrpc.usefulinc.com/doc/reserved.html
! """
! 
! self.funcs.update({'system.listMethods' : self.system_listMethods,
! 'system.methodSignature' : self.system_methodSignature,
! 'system.methodHelp' : self.system_methodHelp})
! 
! def register_multicall_functions(self):
! """Registers the XML-RPC multicall method in the system
! namespace.
! 
! see http://www.xmlrpc.com/discuss/msgReader1208ドル"""
! 
! self.funcs.update({'system.multicall' : self.system_multicall})
! 
! def _marshaled_dispatch(self, data, dispatch_method = None):
! """Dispatches an XML-RPC method from marshalled (XML) data.
! 
! XML-RPC methods are dispatched from the marshalled (XML) data
! using the _dispatch method and the result is returned as
! marshalled data. For backwards compatibility, a dispatch
! function can be provided as an argument (see comment in 
! SimpleXMLRPCRequestHandler.do_POST) but overriding the
! existing method through subclassing is the prefered means
! of changing method dispatch behavior.
! """
! 
! params, method = xmlrpclib.loads(data)
! 
! # generate response
! try:
! if dispatch_method is not None:
! response = dispatch_method(method, params)
! else: 
 response = self._dispatch(method, params)
! # wrap response in a singleton tuple
! response = (response,)
! response = xmlrpclib.dumps(response, methodresponse=1)
! except Fault, fault:
! response = xmlrpclib.dumps(fault)
 except:
! # report exception back to server
! response = xmlrpclib.dumps(
! xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value))
! )
! 
! return response
! 
! def system_listMethods(self):
! """system.listMethods() => ['add', 'subtract', 'multiple']
! 
! Returns a list of the methods supported by the server."""
! 
! methods = self.funcs.keys()
! if self.instance is not None:
! # Instance can implement _listMethod to return a list of
! # methods
! if hasattr(self.instance, '_listMethods'):
! methods = remove_duplicates(
! methods + self.instance._listMethods()
! )
! # if the instance has a _dispatch method then we
! # don't have enough information to provide a list
! # of methods
! elif not hasattr(self.instance, '_dispatch'):
! methods = remove_duplicates(
! methods + list_public_methods(self.instance)
! )
! methods.sort()
! return methods
! 
! def system_methodSignature(self, method_name):
! """system.methodSignature('add') => [double, int, int]
! 
! Returns a list describing the signiture of the method. In the
! above example, the add method takes two integers as arguments
! and returns a double result.
! 
! This server does NOT support system.methodSignature."""
! 
! # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
! 
! return 'signatures not supported'
! 
! def system_methodHelp(self, method_name):
! """system.methodHelp('add') => "Adds two integers together"
! 
! Returns a string containing documentation for the specified method."""
! 
! method = None
! if self.funcs.has_key(method_name):
! method = self.funcs[method_name]
! elif self.instance is not None:
! # Instance can implement _methodHelp to return help for a method
! if hasattr(self.instance, '_methodHelp'):
! return self.instance._methodHelp(method_name)
! # if the instance has a _dispatch method then we
! # don't have enough information to provide help
! elif not hasattr(self.instance, '_dispatch'):
! try:
! method = resolve_dotted_attribute(
! self.instance,
! method_name
! )
! except AttributeError:
! pass
! 
! # Note that we aren't checking that the method actually
! # be a callable object of some kind
! if method is None:
! return ""
 else:
! return pydoc.getdoc(method)
 
! def system_multicall(self, call_list):
! """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
! [[4], ...]
! 
! Allows the caller to package multiple XML-RPC calls into a single
! request.
 
+ See http://www.xmlrpc.com/discuss/msgReader1208ドル 
+ """
+ 
+ results = []
+ for call in call_list:
+ method_name = call['methodName']
+ params = call['params']
+ 
+ try:
+ # XXX A marshalling error in any response will fail the entire
+ # multicall. If someone cares they should fix this.
+ results.append([self._dispatch(method_name, params)])
+ except Fault, fault:
+ results.append(
+ {'faultCode' : fault.faultCode,
+ 'faultString' : fault.faultString}
+ )
+ except:
+ results.append(
+ {'faultCode' : 1,
+ 'faultString' : "%s:%s" % (sys.exc_type, sys.exc_value)}
+ )
+ return results
+ 
 def _dispatch(self, method, params):
 """Dispatches the XML-RPC method.
***************
*** 145,149 ****
 
 Methods beginning with an '_' are considered private and will
! not be called by SimpleXMLRPCServer.
 """
 
--- 357,361 ----
 
 Methods beginning with an '_' are considered private and will
! not be called.
 """
 
***************
*** 151,165 ****
 try:
 # check to see if a matching function has been registered
! func = self.server.funcs[method]
 except KeyError:
! if self.server.instance is not None:
 # check for a _dispatch method
! if hasattr(self.server.instance, '_dispatch'):
! return self.server.instance._dispatch(method, params)
 else:
 # call instance method directly
 try:
! func = _resolve_dotted_attribute(
! self.server.instance,
 method
 )
--- 363,377 ----
 try:
 # check to see if a matching function has been registered
! func = self.funcs[method]
 except KeyError:
! if self.instance is not None:
 # check for a _dispatch method
! if hasattr(self.instance, '_dispatch'):
! return self.instance._dispatch(method, params)
 else:
 # call instance method directly
 try:
! func = resolve_dotted_attribute(
! self.instance,
 method
 )
***************
*** 168,247 ****
 
 if func is not None:
! return apply(func, params)
 else:
 raise Exception('method "%s" is not supported' % method)
 
! def log_request(self, code='-', size='-'):
! """Selectively log an accepted request."""
! 
! if self.server.logRequests:
! BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
 
 
! def _resolve_dotted_attribute(obj, attr):
! """Resolves a dotted attribute name to an object. Raises
! an AttributeError if any attribute in the chain starts with a '_'.
! """
! for i in attr.split('.'):
! if i.startswith('_'):
! raise AttributeError(
! 'attempt to access private attribute "%s"' % i
 )
 else:
! obj = getattr(obj,i)
! return obj
 
 
! class SimpleXMLRPCServer(SocketServer.TCPServer):
 """Simple XML-RPC server.
 
 Simple XML-RPC server that allows functions and a single instance
! to be installed to handle requests.
 """
 
 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
 logRequests=1):
- self.funcs = {}
 self.logRequests = logRequests
! self.instance = None
 SocketServer.TCPServer.__init__(self, addr, requestHandler)
 
! def register_instance(self, instance):
! """Registers an instance to respond to XML-RPC requests.
! 
! Only one instance can be installed at a time.
! 
! If the registered instance has a _dispatch method then that
! method will be called with the name of the XML-RPC method and
! it's parameters as a tuple
! e.g. instance._dispatch('add',(2,3))
! 
! If the registered instance does not have a _dispatch method
! then the instance will be searched to find a matching method
! and, if found, will be called.
 
! Methods beginning with an '_' are considered private and will
! not be called by SimpleXMLRPCServer.
 
! If a registered function matches a XML-RPC request, then it
! will be called instead of the registered instance.
 """
 
! self.instance = instance
! 
! def register_function(self, function, name = None):
! """Registers a function to respond to XML-RPC requests.
! 
! The optional name argument can be used to set a Unicode name
! for the function.
! 
! If an instance is also registered then it will only be called
! if a matching function is not found.
 """
 
! if name is None:
! name = function.__name__
! self.funcs[name] = function
! 
 if __name__ == '__main__':
 server = SimpleXMLRPCServer(("localhost", 8000))
--- 380,509 ----
 
 if func is not None:
! return func(*params)
 else:
 raise Exception('method "%s" is not supported' % method)
+ 
+ class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ """Simple XML-RPC request handler class.
 
! Handles all HTTP POST requests and attempts to decode them as
! XML-RPC requests.
! """
 
+ def do_POST(self):
+ """Handles the HTTP POST request.
 
! Attempts to interpret all HTTP POST requests as XML-RPC calls,
! which are forwarded to the server's _dispatch method for handling.
! """
! 
! try:
! # get arguments
! data = self.rfile.read(int(self.headers["content-length"]))
! # In previous versions of SimpleXMLRPCServer, _dispatch
! # could be overridden in this class, instead of in
! # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
! # check to see if a subclass implements _dispatch and dispatch
! # using that method if present.
! response = self.server._marshaled_dispatch(
! data, getattr(self, '_dispatch', None)
 )
+ except: # This should only happen if the module is buggy
+ # internal error, report as HTTP server error
+ self.send_response(500)
+ self.end_headers()
 else:
! # got a valid XML RPC response
! self.send_response(200)
! self.send_header("Content-type", "text/xml")
! self.send_header("Content-length", str(len(response)))
! self.end_headers()
! self.wfile.write(response)
 
+ # shut down the connection
+ self.wfile.flush()
+ self.connection.shutdown(1)
+ 
+ def log_request(self, code='-', size='-'):
+ """Selectively log an accepted request."""
 
! if self.server.logRequests:
! BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
! 
! class SimpleXMLRPCServer(SocketServer.TCPServer, 
! SimpleXMLRPCDispatcher):
 """Simple XML-RPC server.
 
 Simple XML-RPC server that allows functions and a single instance
! to be installed to handle requests. The default implementation
! attempts to dispatch XML-RPC calls to the functions or instance
! installed in the server. Override the _dispatch method inhereted
! from SimpleXMLRPCDispatcher to change this behavior.
 """
 
 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
 logRequests=1):
 self.logRequests = logRequests
! 
! SimpleXMLRPCDispatcher.__init__(self)
 SocketServer.TCPServer.__init__(self, addr, requestHandler)
+ 
+ class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
+ """Simple handler for XML-RPC data passed through CGI."""
+ 
+ def __init__(self):
+ SimpleXMLRPCDispatcher.__init__(self)
 
! def handle_xmlrpc(self, request_text):
! """Handle a single XML-RPC request"""
! 
! response = self._marshaled_dispatch(request_text)
! 
! print 'Content-Type: text/xml'
! print 'Content-Length: %d' % len(response)
! print
! print response
 
! def handle_get(self):
! """Handle a single HTTP GET request.
 
! Default implementation indicates an error because
! XML-RPC uses the POST method.
 """
 
! code = 400
! message, explain = \
! BaseHTTPServer.BaseHTTPRequestHandler.responses[code]
! 
! response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \
! {
! 'code' : code, 
! 'message' : message, 
! 'explain' : explain
! }
! print 'Status: %d %s' % (code, message)
! print 'Content-Type: text/html'
! print 'Content-Length: %d' % len(response)
! print
! print response
! 
! def handle_request(self, request_text = None):
! """Handle a single XML-RPC request passed through a CGI post method.
! 
! If no XML data is given then it is read from stdin. The resulting
! XML-RPC response is printed to stdout along with the correct HTTP
! headers.
 """
+ 
+ if request_text is None and \
+ os.environ.get('REQUEST_METHOD', None) == 'GET':
+ self.handle_get()
+ else:
+ # POST data is normally available through stdin
+ if request_text is None:
+ request_text = sys.stdin.read() 
 
! self.handle_xmlrpc(request_text)
! 
 if __name__ == '__main__':
 server = SimpleXMLRPCServer(("localhost", 8000))

AltStyle によって変換されたページ (->オリジナル) /