Create xmlrpc package. Issue #2886.

This commit is contained in:
Georg Brandl 2008-05-26 11:14:17 +00:00
parent 7f986acb01
commit 38eceaaf0c
17 changed files with 462 additions and 668 deletions

View File

@ -1,95 +0,0 @@
:mod:`DocXMLRPCServer` --- Self-documenting XML-RPC server
==========================================================
.. module:: DocXMLRPCServer
:synopsis: Self-documenting XML-RPC server implementation.
.. moduleauthor:: Brian Quinlan <brianq@activestate.com>
.. sectionauthor:: Brian Quinlan <brianq@activestate.com>
The :mod:`DocXMLRPCServer` module extends the classes found in
:mod:`SimpleXMLRPCServer` to serve HTML documentation in response to HTTP GET
requests. Servers can either be free standing, using :class:`DocXMLRPCServer`,
or embedded in a CGI environment, using :class:`DocCGIXMLRPCRequestHandler`.
.. class:: DocXMLRPCServer(addr[, requestHandler[, logRequests[, allow_none[, encoding[, bind_and_activate]]]]])
Create a new server instance. All parameters have the same meaning as for
:class:`SimpleXMLRPCServer.SimpleXMLRPCServer`; *requestHandler* defaults to
:class:`DocXMLRPCRequestHandler`.
.. class:: DocCGIXMLRPCRequestHandler()
Create a new instance to handle XML-RPC requests in a CGI environment.
.. class:: DocXMLRPCRequestHandler()
Create a new request handler instance. This request handler supports XML-RPC
POST requests, documentation GET requests, and modifies logging so that the
*logRequests* parameter to the :class:`DocXMLRPCServer` constructor parameter is
honored.
.. _doc-xmlrpc-servers:
DocXMLRPCServer Objects
-----------------------
The :class:`DocXMLRPCServer` class is derived from
:class:`SimpleXMLRPCServer.SimpleXMLRPCServer` and provides a means of creating
self-documenting, stand alone XML-RPC servers. HTTP POST requests are handled as
XML-RPC method calls. HTTP GET requests are handled by generating pydoc-style
HTML documentation. This allows a server to provide its own web-based
documentation.
.. method:: DocXMLRPCServer.set_server_title(server_title)
Set the title used in the generated HTML documentation. This title will be used
inside the HTML "title" element.
.. method:: DocXMLRPCServer.set_server_name(server_name)
Set the name used in the generated HTML documentation. This name will appear at
the top of the generated documentation inside a "h1" element.
.. method:: DocXMLRPCServer.set_server_documentation(server_documentation)
Set the description used in the generated HTML documentation. This description
will appear as a paragraph, below the server name, in the documentation.
DocCGIXMLRPCRequestHandler
--------------------------
The :class:`DocCGIXMLRPCRequestHandler` class is derived from
:class:`SimpleXMLRPCServer.CGIXMLRPCRequestHandler` and provides a means of
creating self-documenting, XML-RPC CGI scripts. HTTP POST requests are handled
as XML-RPC method calls. HTTP GET requests are handled by generating pydoc-style
HTML documentation. This allows a server to provide its own web-based
documentation.
.. method:: DocCGIXMLRPCRequestHandler.set_server_title(server_title)
Set the title used in the generated HTML documentation. This title will be used
inside the HTML "title" element.
.. method:: DocCGIXMLRPCRequestHandler.set_server_name(server_name)
Set the name used in the generated HTML documentation. This name will appear at
the top of the generated documentation inside a "h1" element.
.. method:: DocCGIXMLRPCRequestHandler.set_server_documentation(server_documentation)
Set the description used in the generated HTML documentation. This description
will appear as a paragraph, below the server name, in the documentation.

View File

@ -42,6 +42,5 @@ is currently supported on most popular platforms. Here is an overview:
cgihttpserver.rst cgihttpserver.rst
cookielib.rst cookielib.rst
cookie.rst cookie.rst
xmlrpclib.rst xmlrpc.client.rst
simplexmlrpcserver.rst xmlrpc.server.rst
docxmlrpcserver.rst

View File

@ -23,4 +23,5 @@ The list of modules described in this chapter is:
shelve.rst shelve.rst
marshal.rst marshal.rst
dbm.rst dbm.rst
bsddb.rst
sqlite3.rst sqlite3.rst

View File

@ -1,7 +1,7 @@
:mod:`xmlrpclib` --- XML-RPC client access :mod:`xmlrpc.client` --- XML-RPC client access
========================================== ==============================================
.. module:: xmlrpclib .. module:: xmlrpc.client
:synopsis: XML-RPC client access. :synopsis: XML-RPC client access.
.. moduleauthor:: Fredrik Lundh <fredrik@pythonware.com> .. moduleauthor:: Fredrik Lundh <fredrik@pythonware.com>
.. sectionauthor:: Eric S. Raymond <esr@snark.thyrsus.com> .. sectionauthor:: Eric S. Raymond <esr@snark.thyrsus.com>
@ -86,7 +86,7 @@ between conformable Python objects and XML on the wire.
raise a special :exc:`Fault` instance, used to signal XML-RPC server errors, or raise a special :exc:`Fault` instance, used to signal XML-RPC server errors, or
:exc:`ProtocolError` used to signal an error in the HTTP/HTTPS transport layer. :exc:`ProtocolError` used to signal an error in the HTTP/HTTPS transport layer.
Both :exc:`Fault` and :exc:`ProtocolError` derive from a base class called Both :exc:`Fault` and :exc:`ProtocolError` derive from a base class called
:exc:`Error`. Note that the xmlrpclib module currently does not marshal :exc:`Error`. Note that the xmlrpc client module currently does not marshal
instances of subclasses of builtin types. instances of subclasses of builtin types.
When passing strings, characters special to XML such as ``<``, ``>``, and ``&`` When passing strings, characters special to XML such as ``<``, ``>``, and ``&``
@ -169,28 +169,9 @@ grouped under the reserved :attr:`system` member:
string may contain HTML markup. string may contain HTML markup.
.. _boolean-objects:
Boolean Objects
---------------
This class may be initialized from any Python value; the instance returned
depends only on its truth value. It supports various Python operators through
:meth:`__cmp__`, :meth:`__repr__`, :meth:`__int__`, and :meth:`__bool__`
methods, all implemented in the obvious ways.
It also has the following method, supported mainly for internal use by the
unmarshalling code:
.. method:: Boolean.encode(out)
Write the XML-RPC encoding of this Boolean item to the out stream object.
A working example follows. The server code:: A working example follows. The server code::
import xmlrpclib from xmlrpc.server import SimpleXMLRPCServer
from SimpleXMLRPCServer import SimpleXMLRPCServer
def is_even(n): def is_even(n):
return n%2 == 0 return n%2 == 0
@ -202,9 +183,9 @@ A working example follows. The server code::
The client code for the preceding server:: The client code for the preceding server::
import xmlrpclib import xmlrpc.client
proxy = xmlrpclib.ServerProxy("http://localhost:8000/") proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
print("3 is even: %s" % str(proxy.is_even(3))) print("3 is even: %s" % str(proxy.is_even(3)))
print("100 is even: %s" % str(proxy.is_even(100))) print("100 is even: %s" % str(proxy.is_even(100)))
@ -235,12 +216,12 @@ and :meth:`__repr__` methods.
A working example follows. The server code:: A working example follows. The server code::
import datetime import datetime
from SimpleXMLRPCServer import SimpleXMLRPCServer from xmlrpc.server import SimpleXMLRPCServer
import xmlrpclib import xmlrpc.client
def today(): def today():
today = datetime.datetime.today() today = datetime.datetime.today()
return xmlrpclib.DateTime(today) return xmlrpc.client.DateTime(today)
server = SimpleXMLRPCServer(("localhost", 8000)) server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...") print("Listening on port 8000...")
@ -249,10 +230,10 @@ A working example follows. The server code::
The client code for the preceding server:: The client code for the preceding server::
import xmlrpclib import xmlrpc.client
import datetime import datetime
proxy = xmlrpclib.ServerProxy("http://localhost:8000/") proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
today = proxy.today() today = proxy.today()
# convert the ISO8601 string to a datetime object # convert the ISO8601 string to a datetime object
@ -298,12 +279,12 @@ It also supports certain of Python's built-in operators through a
Example usage of the binary objects. We're going to transfer an image over Example usage of the binary objects. We're going to transfer an image over
XMLRPC:: XMLRPC::
from SimpleXMLRPCServer import SimpleXMLRPCServer from xmlrpc.server import SimpleXMLRPCServer
import xmlrpclib import xmlrpc.client
def python_logo(): def python_logo():
handle = open("python_logo.jpg") handle = open("python_logo.jpg")
return xmlrpclib.Binary(handle.read()) return xmlrpc.client.Binary(handle.read())
handle.close() handle.close()
server = SimpleXMLRPCServer(("localhost", 8000)) server = SimpleXMLRPCServer(("localhost", 8000))
@ -314,9 +295,9 @@ XMLRPC::
The client gets the image and saves it to a file:: The client gets the image and saves it to a file::
import xmlrpclib import xmlrpc.client
proxy = xmlrpclib.ServerProxy("http://localhost:8000/") proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
handle = open("fetched_python_logo.jpg", "w") handle = open("fetched_python_logo.jpg", "w")
handle.write(proxy.python_logo().data) handle.write(proxy.python_logo().data)
handle.close() handle.close()
@ -342,7 +323,7 @@ objects have the following members:
In the following example we're going to intentionally cause a :exc:`Fault` by In the following example we're going to intentionally cause a :exc:`Fault` by
returning a complex type object. The server code:: returning a complex type object. The server code::
from SimpleXMLRPCServer import SimpleXMLRPCServer from xmlrpc.server import SimpleXMLRPCServer
# A marshalling error is going to occur because we're returning a # A marshalling error is going to occur because we're returning a
# complex number # complex number
@ -357,12 +338,12 @@ returning a complex type object. The server code::
The client code for the preceding server:: The client code for the preceding server::
import xmlrpclib import xmlrpc.client
proxy = xmlrpclib.ServerProxy("http://localhost:8000/") proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
try: try:
proxy.add(2, 5) proxy.add(2, 5)
except xmlrpclib.Fault, err: except xmlrpc.client.Fault, err:
print("A fault occured") print("A fault occured")
print("Fault code: %d" % err.faultCode) print("Fault code: %d" % err.faultCode)
print("Fault string: %s" % err.faultString) print("Fault string: %s" % err.faultString)
@ -402,14 +383,14 @@ does not exist). It has the following members:
In the following example we're going to intentionally cause a :exc:`ProtocolError` In the following example we're going to intentionally cause a :exc:`ProtocolError`
by providing an invalid URI:: by providing an invalid URI::
import xmlrpclib import xmlrpc.client
# create a ServerProxy with an invalid URI # create a ServerProxy with an invalid URI
proxy = xmlrpclib.ServerProxy("http://invalidaddress/") proxy = xmlrpc.client.ServerProxy("http://invalidaddress/")
try: try:
proxy.some_method() proxy.some_method()
except xmlrpclib.ProtocolError, err: except xmlrpc.client.ProtocolError, err:
print("A protocol error occured") print("A protocol error occured")
print("URL: %s" % err.url) print("URL: %s" % err.url)
print("HTTP/HTTPS headers: %s" % err.headers) print("HTTP/HTTPS headers: %s" % err.headers)
@ -435,7 +416,7 @@ encapsulate multiple calls to a remote server into a single request.
A usage example of this class follows. The server code :: A usage example of this class follows. The server code ::
from SimpleXMLRPCServer import SimpleXMLRPCServer from xmlrpc.server import SimpleXMLRPCServer
def add(x,y): def add(x,y):
return x+y return x+y
@ -461,10 +442,10 @@ A usage example of this class follows. The server code ::
The client code for the preceding server:: The client code for the preceding server::
import xmlrpclib import xmlrpc.client
proxy = xmlrpclib.ServerProxy("http://localhost:8000/") proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
multicall = xmlrpclib.MultiCall(proxy) multicall = xmlrpc.client.MultiCall(proxy)
multicall.add(7,3) multicall.add(7,3)
multicall.subtract(7,3) multicall.subtract(7,3)
multicall.multiply(7,3) multicall.multiply(7,3)
@ -477,13 +458,6 @@ The client code for the preceding server::
Convenience Functions Convenience Functions
--------------------- ---------------------
.. function:: boolean(value)
Convert any Python value to one of the XML-RPC Boolean constants, ``True`` or
``False``.
.. function:: dumps(params[, methodname[, methodresponse[, encoding[, allow_none]]]]) .. function:: dumps(params[, methodname[, methodresponse[, encoding[, allow_none]]]])
Convert *params* into an XML-RPC request. or into a response if *methodresponse* Convert *params* into an XML-RPC request. or into a response if *methodresponse*
@ -513,7 +487,7 @@ Example of Client Usage
:: ::
# simple test program (from the XML-RPC specification) # simple test program (from the XML-RPC specification)
from xmlrpclib import ServerProxy, Error from xmlrpc.client import ServerProxy, Error
# server = ServerProxy("http://localhost:8000") # local server # server = ServerProxy("http://localhost:8000") # local server
server = ServerProxy("http://betty.userland.com") server = ServerProxy("http://betty.userland.com")
@ -532,9 +506,9 @@ transport. The following example shows how:
:: ::
import xmlrpclib, httplib import xmlrpc.client, httplib
class ProxiedTransport(xmlrpclib.Transport): class ProxiedTransport(xmlrpc.client.Transport):
def set_proxy(self, proxy): def set_proxy(self, proxy):
self.proxy = proxy self.proxy = proxy
def make_connection(self, host): def make_connection(self, host):
@ -548,7 +522,7 @@ transport. The following example shows how:
p = ProxiedTransport() p = ProxiedTransport()
p.set_proxy('proxy-server:8080') p.set_proxy('proxy-server:8080')
server = xmlrpclib.Server('http://time.xmlrpc.com/RPC2', transport=p) server = xmlrpc.client.Server('http://time.xmlrpc.com/RPC2', transport=p)
print(server.currentTime.getCurrentTime()) print(server.currentTime.getCurrentTime())

View File

@ -1,15 +1,14 @@
:mod:`xmlrpc.server` --- Basic XML-RPC servers
==============================================
:mod:`SimpleXMLRPCServer` --- Basic XML-RPC server .. module:: xmlrpc.server
================================================== :synopsis: Basic XML-RPC server implementations.
.. module:: SimpleXMLRPCServer
:synopsis: Basic XML-RPC server implementation.
.. moduleauthor:: Brian Quinlan <brianq@activestate.com> .. moduleauthor:: Brian Quinlan <brianq@activestate.com>
.. sectionauthor:: Fred L. Drake, Jr. <fdrake@acm.org> .. sectionauthor:: Fred L. Drake, Jr. <fdrake@acm.org>
The :mod:`SimpleXMLRPCServer` module provides a basic server framework for The :mod:`xmlrpc.server` module provides a basic server framework for XML-RPC
XML-RPC servers written in Python. Servers can either be free standing, using servers written in Python. Servers can either be free standing, using
:class:`SimpleXMLRPCServer`, or embedded in a CGI environment, using :class:`SimpleXMLRPCServer`, or embedded in a CGI environment, using
:class:`CGIXMLRPCRequestHandler`. :class:`CGIXMLRPCRequestHandler`.
@ -23,7 +22,7 @@ XML-RPC servers written in Python. Servers can either be free standing, using
are passed to the :class:`socketserver.TCPServer` constructor. If *logRequests* are passed to the :class:`socketserver.TCPServer` constructor. If *logRequests*
is true (the default), requests will be logged; setting this parameter to false is true (the default), requests will be logged; setting this parameter to false
will turn off logging. The *allow_none* and *encoding* parameters are passed will turn off logging. The *allow_none* and *encoding* parameters are passed
on to :mod:`xmlrpclib` and control the XML-RPC responses that will be returned on to :mod:`xmlrpc.client` and control the XML-RPC responses that will be returned
from the server. The *bind_and_activate* parameter controls whether from the server. The *bind_and_activate* parameter controls whether
:meth:`server_bind` and :meth:`server_activate` are called immediately by the :meth:`server_bind` and :meth:`server_activate` are called immediately by the
constructor; it defaults to true. Setting it to false allows code to manipulate constructor; it defaults to true. Setting it to false allows code to manipulate
@ -33,8 +32,8 @@ XML-RPC servers written in Python. Servers can either be free standing, using
.. class:: CGIXMLRPCRequestHandler([allow_none[, encoding]]) .. class:: CGIXMLRPCRequestHandler([allow_none[, encoding]])
Create a new instance to handle XML-RPC requests in a CGI environment. The Create a new instance to handle XML-RPC requests in a CGI environment. The
*allow_none* and *encoding* parameters are passed on to :mod:`xmlrpclib` and *allow_none* and *encoding* parameters are passed on to :mod:`xmlrpc.client`
control the XML-RPC responses that will be returned from the server. and control the XML-RPC responses that will be returned from the server.
.. class:: SimpleXMLRPCRequestHandler() .. class:: SimpleXMLRPCRequestHandler()
@ -115,8 +114,8 @@ SimpleXMLRPCServer Example
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
Server code:: Server code::
from SimpleXMLRPCServer import SimpleXMLRPCServer from xmlrpc.server import SimpleXMLRPCServer
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler from xmlrpc.server import SimpleXMLRPCRequestHandler
# Restrict to a particular path. # Restrict to a particular path.
class RequestHandler(SimpleXMLRPCRequestHandler): class RequestHandler(SimpleXMLRPCRequestHandler):
@ -150,9 +149,9 @@ Server code::
The following client code will call the methods made available by the preceding The following client code will call the methods made available by the preceding
server:: server::
import xmlrpclib import xmlrpc.client
s = xmlrpclib.ServerProxy('http://localhost:8000') s = xmlrpc.client.ServerProxy('http://localhost:8000')
print(s.pow(2,3)) # Returns 2**3 = 8 print(s.pow(2,3)) # Returns 2**3 = 8
print(s.add(2,3)) # Returns 5 print(s.add(2,3)) # Returns 5
print(s.mul(5,2)) # Returns 5*2 = 10 print(s.mul(5,2)) # Returns 5*2 = 10
@ -220,3 +219,89 @@ Example::
handler.register_instance(MyFuncs()) handler.register_instance(MyFuncs())
handler.handle_request() handler.handle_request()
Documenting XMLRPC server
-------------------------
These classes extend the above classes to serve HTML documentation in response
to HTTP GET requests. Servers can either be free standing, using
:class:`DocXMLRPCServer`, or embedded in a CGI environment, using
:class:`DocCGIXMLRPCRequestHandler`.
.. class:: DocXMLRPCServer(addr[, requestHandler[, logRequests[, allow_none[, encoding[, bind_and_activate]]]]])
Create a new server instance. All parameters have the same meaning as for
:class:`SimpleXMLRPCServer`; *requestHandler* defaults to
:class:`DocXMLRPCRequestHandler`.
.. class:: DocCGIXMLRPCRequestHandler()
Create a new instance to handle XML-RPC requests in a CGI environment.
.. class:: DocXMLRPCRequestHandler()
Create a new request handler instance. This request handler supports XML-RPC
POST requests, documentation GET requests, and modifies logging so that the
*logRequests* parameter to the :class:`DocXMLRPCServer` constructor parameter is
honored.
.. _doc-xmlrpc-servers:
DocXMLRPCServer Objects
-----------------------
The :class:`DocXMLRPCServer` class is derived from :class:`SimpleXMLRPCServer`
and provides a means of creating self-documenting, stand alone XML-RPC
servers. HTTP POST requests are handled as XML-RPC method calls. HTTP GET
requests are handled by generating pydoc-style HTML documentation. This allows a
server to provide its own web-based documentation.
.. method:: DocXMLRPCServer.set_server_title(server_title)
Set the title used in the generated HTML documentation. This title will be used
inside the HTML "title" element.
.. method:: DocXMLRPCServer.set_server_name(server_name)
Set the name used in the generated HTML documentation. This name will appear at
the top of the generated documentation inside a "h1" element.
.. method:: DocXMLRPCServer.set_server_documentation(server_documentation)
Set the description used in the generated HTML documentation. This description
will appear as a paragraph, below the server name, in the documentation.
DocCGIXMLRPCRequestHandler
--------------------------
The :class:`DocCGIXMLRPCRequestHandler` class is derived from
:class:`CGIXMLRPCRequestHandler` and provides a means of creating
self-documenting, XML-RPC CGI scripts. HTTP POST requests are handled as XML-RPC
method calls. HTTP GET requests are handled by generating pydoc-style HTML
documentation. This allows a server to provide its own web-based documentation.
.. method:: DocCGIXMLRPCRequestHandler.set_server_title(server_title)
Set the title used in the generated HTML documentation. This title will be used
inside the HTML "title" element.
.. method:: DocCGIXMLRPCRequestHandler.set_server_name(server_name)
Set the name used in the generated HTML documentation. This name will appear at
the top of the generated documentation inside a "h1" element.
.. method:: DocCGIXMLRPCRequestHandler.set_server_documentation(server_documentation)
Set the description used in the generated HTML documentation. This description
will appear as a paragraph, below the server name, in the documentation.

View File

@ -570,7 +570,7 @@ The :mod:`uu` module contains the following notice::
XML Remote Procedure Calls XML Remote Procedure Calls
-------------------------- --------------------------
The :mod:`xmlrpclib` module contains the following notice:: The :mod:`xmlrpc.client` module contains the following notice::
The XML-RPC client interface is The XML-RPC client interface is

View File

@ -294,7 +294,7 @@ Batteries Included
Python has a "batteries included" philosophy. This is best seen through the Python has a "batteries included" philosophy. This is best seen through the
sophisticated and robust capabilities of its larger packages. For example: sophisticated and robust capabilities of its larger packages. For example:
* The :mod:`xmlrpclib` and :mod:`SimpleXMLRPCServer` modules make implementing * The :mod:`xmlrpc.client` and :mod:`xmlrpc.server` modules make implementing
remote procedure calls into an almost trivial task. Despite the modules remote procedure calls into an almost trivial task. Despite the modules
names, no direct knowledge or handling of XML is needed. names, no direct knowledge or handling of XML is needed.

View File

@ -1,283 +0,0 @@
"""Self documenting XML-RPC Server.
This module can be used to create XML-RPC servers that
serve pydoc-style documentation in response to HTTP
GET requests. This documentation is dynamically generated
based on the functions and methods registered with the
server.
This module is built upon the pydoc and SimpleXMLRPCServer
modules.
"""
import pydoc
import inspect
import re
import sys
from SimpleXMLRPCServer import (SimpleXMLRPCServer,
SimpleXMLRPCRequestHandler,
CGIXMLRPCRequestHandler,
resolve_dotted_attribute)
class ServerHTMLDoc(pydoc.HTMLDoc):
"""Class used to generate pydoc HTML document for a server"""
def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
"""Mark up some plain text, given a context of symbols to look for.
Each context dictionary maps object names to anchor names."""
escape = escape or self.escape
results = []
here = 0
# XXX Note that this regular expression does not allow for the
# hyperlinking of arbitrary strings being used as method
# names. Only methods with names consisting of word characters
# and '.'s are hyperlinked.
pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
r'RFC[- ]?(\d+)|'
r'PEP[- ]?(\d+)|'
r'(self\.)?((?:\w|\.)+))\b')
while 1:
match = pattern.search(text, here)
if not match: break
start, end = match.span()
results.append(escape(text[here:start]))
all, scheme, rfc, pep, selfdot, name = match.groups()
if scheme:
url = escape(all).replace('"', '&quot;')
results.append('<a href="%s">%s</a>' % (url, url))
elif rfc:
url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
results.append('<a href="%s">%s</a>' % (url, escape(all)))
elif pep:
url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
results.append('<a href="%s">%s</a>' % (url, escape(all)))
elif text[end:end+1] == '(':
results.append(self.namelink(name, methods, funcs, classes))
elif selfdot:
results.append('self.<strong>%s</strong>' % name)
else:
results.append(self.namelink(name, classes))
here = end
results.append(escape(text[here:]))
return ''.join(results)
def docroutine(self, object, name, mod=None,
funcs={}, classes={}, methods={}, cl=None):
"""Produce HTML documentation for a function or method object."""
anchor = (cl and cl.__name__ or '') + '-' + name
note = ''
title = '<a name="%s"><strong>%s</strong></a>' % (
self.escape(anchor), self.escape(name))
if inspect.ismethod(object):
args, varargs, varkw, defaults = inspect.getargspec(object)
# exclude the argument bound to the instance, it will be
# confusing to the non-Python user
argspec = inspect.formatargspec (
args[1:],
varargs,
varkw,
defaults,
formatvalue=self.formatvalue
)
elif inspect.isfunction(object):
args, varargs, varkw, defaults = inspect.getargspec(object)
argspec = inspect.formatargspec(
args, varargs, varkw, defaults, formatvalue=self.formatvalue)
else:
argspec = '(...)'
if isinstance(object, tuple):
argspec = object[0] or argspec
docstring = object[1] or ""
else:
docstring = pydoc.getdoc(object)
decl = title + argspec + (note and self.grey(
'<font face="helvetica, arial">%s</font>' % note))
doc = self.markup(
docstring, self.preformat, funcs, classes, methods)
doc = doc and '<dd><tt>%s</tt></dd>' % doc
return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
def docserver(self, server_name, package_documentation, methods):
"""Produce HTML documentation for an XML-RPC server."""
fdict = {}
for key, value in methods.items():
fdict[key] = '#-' + key
fdict[value] = fdict[key]
server_name = self.escape(server_name)
head = '<big><big><strong>%s</strong></big></big>' % server_name
result = self.heading(head, '#ffffff', '#7799ee')
doc = self.markup(package_documentation, self.preformat, fdict)
doc = doc and '<tt>%s</tt>' % doc
result = result + '<p>%s</p>\n' % doc
contents = []
method_items = sorted(methods.items())
for key, value in method_items:
contents.append(self.docroutine(value, key, funcs=fdict))
result = result + self.bigsection(
'Methods', '#ffffff', '#eeaa77', ''.join(contents))
return result
class XMLRPCDocGenerator:
"""Generates documentation for an XML-RPC server.
This class is designed as mix-in and should not
be constructed directly.
"""
def __init__(self):
# setup variables used for HTML documentation
self.server_name = 'XML-RPC Server Documentation'
self.server_documentation = \
"This server exports the following methods through the XML-RPC "\
"protocol."
self.server_title = 'XML-RPC Server Documentation'
def set_server_title(self, server_title):
"""Set the HTML title of the generated server documentation"""
self.server_title = server_title
def set_server_name(self, server_name):
"""Set the name of the generated HTML server documentation"""
self.server_name = server_name
def set_server_documentation(self, server_documentation):
"""Set the documentation string for the entire server."""
self.server_documentation = server_documentation
def generate_html_documentation(self):
"""generate_html_documentation() => html documentation for the server
Generates HTML documentation for the server using introspection for
installed functions and instances that do not implement the
_dispatch method. Alternatively, instances can choose to implement
the _get_method_argstring(method_name) method to provide the
argument string used in the documentation and the
_methodHelp(method_name) method to provide the help text used
in the documentation."""
methods = {}
for method_name in self.system_listMethods():
if method_name in self.funcs:
method = self.funcs[method_name]
elif self.instance is not None:
method_info = [None, None] # argspec, documentation
if hasattr(self.instance, '_get_method_argstring'):
method_info[0] = self.instance._get_method_argstring(method_name)
if hasattr(self.instance, '_methodHelp'):
method_info[1] = self.instance._methodHelp(method_name)
method_info = tuple(method_info)
if method_info != (None, None):
method = method_info
elif not hasattr(self.instance, '_dispatch'):
try:
method = resolve_dotted_attribute(
self.instance,
method_name
)
except AttributeError:
method = method_info
else:
method = method_info
else:
assert 0, "Could not find method in self.functions and no "\
"instance installed"
methods[method_name] = method
documenter = ServerHTMLDoc()
documentation = documenter.docserver(
self.server_name,
self.server_documentation,
methods
)
return documenter.page(self.server_title, documentation)
class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
"""XML-RPC and documentation request handler class.
Handles all HTTP POST requests and attempts to decode them as
XML-RPC requests.
Handles all HTTP GET requests and interprets them as requests
for documentation.
"""
def do_GET(self):
"""Handles the HTTP GET request.
Interpret all HTTP GET requests as requests for server
documentation.
"""
# Check that the path is legal
if not self.is_rpc_path_valid():
self.report_404()
return
response = self.server.generate_html_documentation()
self.send_response(200)
self.send_header("Content-type", "text/html")
self.send_header("Content-length", str(len(response)))
self.end_headers()
self.wfile.write(response.encode())
# shut down the connection
self.wfile.flush()
self.connection.shutdown(1)
class DocXMLRPCServer( SimpleXMLRPCServer,
XMLRPCDocGenerator):
"""XML-RPC and HTML documentation server.
Adds the ability to serve server documentation to the capabilities
of SimpleXMLRPCServer.
"""
def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
logRequests=1, allow_none=False, encoding=None,
bind_and_activate=True):
SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
allow_none, encoding, bind_and_activate)
XMLRPCDocGenerator.__init__(self)
class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
XMLRPCDocGenerator):
"""Handler for XML-RPC data and documentation requests passed through
CGI"""
def handle_get(self):
"""Handles the HTTP GET request.
Interpret all HTTP GET requests as requests for server
documentation.
"""
response = self.generate_html_documentation()
print('Content-Type: text/html')
print('Content-Length: %d' % len(response))
print()
sys.stdout.write(response)
def __init__(self):
CGIXMLRPCRequestHandler.__init__(self)
XMLRPCDocGenerator.__init__(self)

View File

@ -1,147 +0,0 @@
#! /usr/bin/env python
"""Test script for the dbm.open function based on testdumbdbm.py"""
import os
import unittest
import dbm
import glob
import test.support
_fname = test.support.TESTFN
#
# Iterates over every database module supported by dbm currently available,
# setting dbm to use each in turn, and yielding that module
#
def dbm_iterator():
old_default = dbm._defaultmod
for module in dbm._modules.values():
dbm._defaultmod = module
yield module
dbm._defaultmod = old_default
#
# Clean up all scratch databases we might have created during testing
#
def delete_files():
# we don't know the precise name the underlying database uses
# so we use glob to locate all names
for f in glob.glob(_fname + "*"):
test.support.unlink(f)
class AnyDBMTestCase(unittest.TestCase):
_dict = {'0': b'',
'a': b'Python:',
'b': b'Programming',
'c': b'the',
'd': b'way',
'f': b'Guido',
'g': b'intended',
}
def __init__(self, *args):
unittest.TestCase.__init__(self, *args)
def test_anydbm_creation(self):
f = dbm.open(_fname, 'c')
self.assertEqual(list(f.keys()), [])
for key in self._dict:
f[key.encode("ascii")] = self._dict[key]
self.read_helper(f)
f.close()
def test_anydbm_modification(self):
self.init_db()
f = dbm.open(_fname, 'c')
self._dict['g'] = f[b'g'] = b"indented"
self.read_helper(f)
f.close()
def test_anydbm_read(self):
self.init_db()
f = dbm.open(_fname, 'r')
self.read_helper(f)
f.close()
def test_anydbm_keys(self):
self.init_db()
f = dbm.open(_fname, 'r')
keys = self.keys_helper(f)
f.close()
def test_anydbm_access(self):
self.init_db()
f = dbm.open(_fname, 'r')
key = "a".encode("ascii")
assert(key in f)
assert(f[key] == b"Python:")
f.close()
def read_helper(self, f):
keys = self.keys_helper(f)
for key in self._dict:
self.assertEqual(self._dict[key], f[key.encode("ascii")])
def init_db(self):
f = dbm.open(_fname, 'n')
for k in self._dict:
f[k.encode("ascii")] = self._dict[k]
f.close()
def keys_helper(self, f):
keys = sorted(k.decode("ascii") for k in f.keys())
dkeys = sorted(self._dict.keys())
self.assertEqual(keys, dkeys)
return keys
def tearDown(self):
delete_files()
def setUp(self):
delete_files()
class WhichDBTestCase(unittest.TestCase):
# Actual test methods are added to namespace after class definition.
def __init__(self, *args):
unittest.TestCase.__init__(self, *args)
def test_whichdb(self):
for module in dbm_iterator():
# Check whether whichdb correctly guesses module name
# for databases opened with "module" module.
# Try with empty files first
name = module.__name__
if name == 'dbm.dumb':
continue # whichdb can't support dbm.dumb
test.support.unlink(_fname)
f = module.open(_fname, 'c')
f.close()
self.assertEqual(name, dbm.whichdb(_fname))
# Now add a key
f = module.open(_fname, 'w')
f[b"1"] = b"1"
# and test that we can find it
self.assertTrue(b"1" in f)
# and read it
self.assertTrue(f[b"1"] == b"1")
f.close()
self.assertEqual(name, dbm.whichdb(_fname))
def tearDown(self):
delete_files()
def setUp(self):
delete_files()
def test_main():
try:
for module in dbm_iterator():
test.support.run_unittest(AnyDBMTestCase, WhichDBTestCase)
finally:
delete_files()
if __name__ == "__main__":
test_main()

View File

@ -1,10 +1,9 @@
from DocXMLRPCServer import DocXMLRPCServer from xmlrpc.server import DocXMLRPCServer
import httplib import httplib
from test import support from test import support
import threading import threading
import time import time
import unittest import unittest
import xmlrpclib
PORT = None PORT = None

View File

@ -3,8 +3,8 @@ import datetime
import sys import sys
import time import time
import unittest import unittest
import xmlrpclib import xmlrpc.client as xmlrpclib
import SimpleXMLRPCServer import xmlrpc.server
import threading import threading
import mimetools import mimetools
import httplib import httplib
@ -160,9 +160,9 @@ class FaultTestCase(unittest.TestCase):
# this will raise AttirebuteError because code don't want us to use # this will raise AttirebuteError because code don't want us to use
# private methods # private methods
self.assertRaises(AttributeError, self.assertRaises(AttributeError,
SimpleXMLRPCServer.resolve_dotted_attribute, str, '__add') xmlrpc.server.resolve_dotted_attribute, str, '__add')
self.assert_(SimpleXMLRPCServer.resolve_dotted_attribute(str, 'title')) self.assert_(xmlrpc.server.resolve_dotted_attribute(str, 'title'))
class DateTimeTestCase(unittest.TestCase): class DateTimeTestCase(unittest.TestCase):
def test_default(self): def test_default(self):
@ -249,7 +249,7 @@ def http_server(evt, numrequests):
'''This is my function''' '''This is my function'''
return True return True
class MyXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer): class MyXMLRPCServer(xmlrpc.server.SimpleXMLRPCServer):
def get_request(self): def get_request(self):
# Ensure the socket is always non-blocking. On Linux, socket # Ensure the socket is always non-blocking. On Linux, socket
# attributes are not inherited like they are on *BSD and Windows. # attributes are not inherited like they are on *BSD and Windows.
@ -306,7 +306,7 @@ def is_unavailable_exception(e):
class SimpleServerTestCase(unittest.TestCase): class SimpleServerTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
# enable traceback reporting # enable traceback reporting
SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = True
self.evt = threading.Event() self.evt = threading.Event()
# start server thread to handle requests # start server thread to handle requests
@ -326,7 +326,7 @@ class SimpleServerTestCase(unittest.TestCase):
raise RuntimeError("timeout reached, test has failed") raise RuntimeError("timeout reached, test has failed")
# disable traceback reporting # disable traceback reporting
SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = False xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = False
def test_simple1(self): def test_simple1(self):
try: try:
@ -443,9 +443,9 @@ class SimpleServerTestCase(unittest.TestCase):
def test_dotted_attribute(self): def test_dotted_attribute(self):
# Raises an AttributeError because private methods are not allowed. # Raises an AttributeError because private methods are not allowed.
self.assertRaises(AttributeError, self.assertRaises(AttributeError,
SimpleXMLRPCServer.resolve_dotted_attribute, str, '__add') xmlrpc.server.resolve_dotted_attribute, str, '__add')
self.assert_(SimpleXMLRPCServer.resolve_dotted_attribute(str, 'title')) self.assert_(xmlrpc.server.resolve_dotted_attribute(str, 'title'))
# Get the test to run faster by sending a request with test_simple1. # Get the test to run faster by sending a request with test_simple1.
# This avoids waiting for the socket timeout. # This avoids waiting for the socket timeout.
self.test_simple1() self.test_simple1()
@ -475,17 +475,17 @@ class FailingServerTestCase(unittest.TestCase):
# wait on the server thread to terminate # wait on the server thread to terminate
self.evt.wait() self.evt.wait()
# reset flag # reset flag
SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = False xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = False
# reset message class # reset message class
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = mimetools.Message xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = mimetools.Message
def test_basic(self): def test_basic(self):
# check that flag is false by default # check that flag is false by default
flagval = SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header flagval = xmlrpc.server.SimpleXMLRPCServer._send_traceback_header
self.assertEqual(flagval, False) self.assertEqual(flagval, False)
# enable traceback reporting # enable traceback reporting
SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = True
# test a call that shouldn't fail just as a smoke test # test a call that shouldn't fail just as a smoke test
try: try:
@ -499,7 +499,7 @@ class FailingServerTestCase(unittest.TestCase):
def test_fail_no_info(self): def test_fail_no_info(self):
# use the broken message class # use the broken message class
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass
try: try:
p = xmlrpclib.ServerProxy('http://localhost:%d' % PORT) p = xmlrpclib.ServerProxy('http://localhost:%d' % PORT)
@ -515,11 +515,11 @@ class FailingServerTestCase(unittest.TestCase):
def test_fail_with_info(self): def test_fail_with_info(self):
# use the broken message class # use the broken message class
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass
# Check that errors in the server send back exception/traceback # Check that errors in the server send back exception/traceback
# info when flag is set # info when flag is set
SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = True
try: try:
p = xmlrpclib.ServerProxy('http://localhost:%d' % PORT) p = xmlrpclib.ServerProxy('http://localhost:%d' % PORT)
@ -536,7 +536,7 @@ class FailingServerTestCase(unittest.TestCase):
class CGIHandlerTestCase(unittest.TestCase): class CGIHandlerTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.cgi = SimpleXMLRPCServer.CGIXMLRPCRequestHandler() self.cgi = xmlrpc.server.CGIXMLRPCRequestHandler()
def tearDown(self): def tearDown(self):
self.cgi = None self.cgi = None

View File

@ -6,7 +6,7 @@ import sys
import unittest import unittest
from test import support from test import support
import xmlrpclib import xmlrpclib.client as xmlrpclib
class CurrentTimeTest(unittest.TestCase): class CurrentTimeTest(unittest.TestCase):

1
Lib/xmlrpc/__init__.py Normal file
View File

@ -0,0 +1 @@
# This directory is a Python package.

View File

@ -108,7 +108,6 @@ Exported classes:
ServerProxy Represents a logical connection to an XML-RPC server ServerProxy Represents a logical connection to an XML-RPC server
MultiCall Executor of boxcared xmlrpc requests MultiCall Executor of boxcared xmlrpc requests
Boolean boolean wrapper to generate a "boolean" XML-RPC value
DateTime dateTime wrapper for an ISO 8601 string or time tuple or DateTime dateTime wrapper for an ISO 8601 string or time tuple or
localtime integer value to generate a "dateTime.iso8601" localtime integer value to generate a "dateTime.iso8601"
XML-RPC value XML-RPC value
@ -127,7 +126,6 @@ Exported constants:
Exported functions: Exported functions:
boolean Convert any Python value to an XML-RPC boolean
getparser Create instance of the fastest available parser & attach getparser Create instance of the fastest available parser & attach
to an unmarshalling object to an unmarshalling object
dumps Convert an argument tuple or a Fault instance to an XML-RPC dumps Convert an argument tuple or a Fault instance to an XML-RPC
@ -147,11 +145,6 @@ try:
except ImportError: except ImportError:
datetime = None datetime = None
try:
_bool_is_builtin = False.__class__.__name__ == "bool"
except NameError:
_bool_is_builtin = 0
def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search): def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
# decode non-ascii string (if possible) # decode non-ascii string (if possible)
if encoding and is8bit(data): if encoding and is8bit(data):
@ -265,12 +258,7 @@ class Fault(Error):
# Special values # Special values
## ##
# Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and # Backwards compatibility
# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
# generate boolean XML-RPC values.
#
# @param value A boolean value. Any true value is interpreted as True,
# all other values are interpreted as False.
boolean = Boolean = bool boolean = Boolean = bool
@ -451,26 +439,10 @@ def _binary(data):
return value return value
WRAPPERS = (DateTime, Binary) WRAPPERS = (DateTime, Binary)
if not _bool_is_builtin:
WRAPPERS = WRAPPERS + (Boolean,)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# XML parsers # XML parsers
try:
# optional xmlrpclib accelerator
import _xmlrpclib
FastParser = _xmlrpclib.Parser
FastUnmarshaller = _xmlrpclib.Unmarshaller
except (AttributeError, ImportError):
FastParser = FastUnmarshaller = None
try:
import _xmlrpclib
FastMarshaller = _xmlrpclib.Marshaller
except (AttributeError, ImportError):
FastMarshaller = None
# #
# the SGMLOP parser is about 15x faster than Python's builtin # the SGMLOP parser is about 15x faster than Python's builtin
# XML parser. SGMLOP sources can be downloaded from: # XML parser. SGMLOP sources can be downloaded from:
@ -640,12 +612,11 @@ class Marshaller:
write("</int></value>\n") write("</int></value>\n")
#dispatch[int] = dump_int #dispatch[int] = dump_int
if _bool_is_builtin: def dump_bool(self, value, write):
def dump_bool(self, value, write): write("<value><boolean>")
write("<value><boolean>") write(value and "1" or "0")
write(value and "1" or "0") write("</boolean></value>\n")
write("</boolean></value>\n") dispatch[bool] = dump_bool
dispatch[bool] = dump_bool
def dump_long(self, value, write): def dump_long(self, value, write):
if value > MAXINT or value < MININT: if value > MAXINT or value < MININT:
@ -968,6 +939,8 @@ class MultiCall:
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# convenience functions # convenience functions
FastMarshaller = FastParser = FastUnmarshaller = None
## ##
# Create a parser object, and connect it to an unmarshalling instance. # Create a parser object, and connect it to an unmarshalling instance.
# This function picks the fastest available XML parser. # This function picks the fastest available XML parser.

View File

@ -1,4 +1,4 @@
"""Simple XML-RPC Server. """XML-RPC Servers.
This module can be used to create simple XML-RPC servers This module can be used to create simple XML-RPC servers
by creating a server and either installing functions, a by creating a server and either installing functions, a
@ -8,6 +8,12 @@ class.
It can also be used to handle XML-RPC requests in a CGI It can also be used to handle XML-RPC requests in a CGI
environment using CGIXMLRPCRequestHandler. environment using CGIXMLRPCRequestHandler.
The Doc* classes can be used to create XML-RPC servers that
serve pydoc-style documentation in response to HTTP
GET requests. This documentation is dynamically generated
based on the functions and methods registered with the
server.
A list of possible usage patterns follows: A list of possible usage patterns follows:
1. Install functions: 1. Install functions:
@ -98,12 +104,14 @@ server.handle_request()
# Written by Brian Quinlan (brian@sweetapp.com). # Written by Brian Quinlan (brian@sweetapp.com).
# Based on code written by Fredrik Lundh. # Based on code written by Fredrik Lundh.
import xmlrpclib from xmlrpc.client import Fault, dumps, loads
from xmlrpclib import Fault
import socketserver import socketserver
import BaseHTTPServer import BaseHTTPServer
import sys import sys
import os import os
import re
import pydoc
import inspect
import traceback import traceback
try: try:
import fcntl import fcntl
@ -235,7 +243,7 @@ class SimpleXMLRPCDispatcher:
""" """
try: try:
params, method = xmlrpclib.loads(data) params, method = loads(data)
# generate response # generate response
if dispatch_method is not None: if dispatch_method is not None:
@ -244,16 +252,16 @@ class SimpleXMLRPCDispatcher:
response = self._dispatch(method, params) response = self._dispatch(method, params)
# wrap response in a singleton tuple # wrap response in a singleton tuple
response = (response,) response = (response,)
response = xmlrpclib.dumps(response, methodresponse=1, response = dumps(response, methodresponse=1,
allow_none=self.allow_none, encoding=self.encoding) allow_none=self.allow_none, encoding=self.encoding)
except Fault as fault: except Fault as fault:
response = xmlrpclib.dumps(fault, allow_none=self.allow_none, response = dumps(fault, allow_none=self.allow_none,
encoding=self.encoding) encoding=self.encoding)
except: except:
# report exception back to server # report exception back to server
exc_type, exc_value, exc_tb = sys.exc_info() exc_type, exc_value, exc_tb = sys.exc_info()
response = xmlrpclib.dumps( response = dumps(
xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)), Fault(1, "%s:%s" % (exc_type, exc_value)),
encoding=self.encoding, allow_none=self.allow_none, encoding=self.encoding, allow_none=self.allow_none,
) )
@ -585,6 +593,273 @@ class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
self.handle_xmlrpc(request_text) self.handle_xmlrpc(request_text)
# -----------------------------------------------------------------------------
# Self documenting XML-RPC Server.
class ServerHTMLDoc(pydoc.HTMLDoc):
"""Class used to generate pydoc HTML document for a server"""
def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
"""Mark up some plain text, given a context of symbols to look for.
Each context dictionary maps object names to anchor names."""
escape = escape or self.escape
results = []
here = 0
# XXX Note that this regular expression does not allow for the
# hyperlinking of arbitrary strings being used as method
# names. Only methods with names consisting of word characters
# and '.'s are hyperlinked.
pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
r'RFC[- ]?(\d+)|'
r'PEP[- ]?(\d+)|'
r'(self\.)?((?:\w|\.)+))\b')
while 1:
match = pattern.search(text, here)
if not match: break
start, end = match.span()
results.append(escape(text[here:start]))
all, scheme, rfc, pep, selfdot, name = match.groups()
if scheme:
url = escape(all).replace('"', '&quot;')
results.append('<a href="%s">%s</a>' % (url, url))
elif rfc:
url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
results.append('<a href="%s">%s</a>' % (url, escape(all)))
elif pep:
url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
results.append('<a href="%s">%s</a>' % (url, escape(all)))
elif text[end:end+1] == '(':
results.append(self.namelink(name, methods, funcs, classes))
elif selfdot:
results.append('self.<strong>%s</strong>' % name)
else:
results.append(self.namelink(name, classes))
here = end
results.append(escape(text[here:]))
return ''.join(results)
def docroutine(self, object, name, mod=None,
funcs={}, classes={}, methods={}, cl=None):
"""Produce HTML documentation for a function or method object."""
anchor = (cl and cl.__name__ or '') + '-' + name
note = ''
title = '<a name="%s"><strong>%s</strong></a>' % (
self.escape(anchor), self.escape(name))
if inspect.ismethod(object):
args, varargs, varkw, defaults = inspect.getargspec(object)
# exclude the argument bound to the instance, it will be
# confusing to the non-Python user
argspec = inspect.formatargspec (
args[1:],
varargs,
varkw,
defaults,
formatvalue=self.formatvalue
)
elif inspect.isfunction(object):
args, varargs, varkw, defaults = inspect.getargspec(object)
argspec = inspect.formatargspec(
args, varargs, varkw, defaults, formatvalue=self.formatvalue)
else:
argspec = '(...)'
if isinstance(object, tuple):
argspec = object[0] or argspec
docstring = object[1] or ""
else:
docstring = pydoc.getdoc(object)
decl = title + argspec + (note and self.grey(
'<font face="helvetica, arial">%s</font>' % note))
doc = self.markup(
docstring, self.preformat, funcs, classes, methods)
doc = doc and '<dd><tt>%s</tt></dd>' % doc
return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
def docserver(self, server_name, package_documentation, methods):
"""Produce HTML documentation for an XML-RPC server."""
fdict = {}
for key, value in methods.items():
fdict[key] = '#-' + key
fdict[value] = fdict[key]
server_name = self.escape(server_name)
head = '<big><big><strong>%s</strong></big></big>' % server_name
result = self.heading(head, '#ffffff', '#7799ee')
doc = self.markup(package_documentation, self.preformat, fdict)
doc = doc and '<tt>%s</tt>' % doc
result = result + '<p>%s</p>\n' % doc
contents = []
method_items = sorted(methods.items())
for key, value in method_items:
contents.append(self.docroutine(value, key, funcs=fdict))
result = result + self.bigsection(
'Methods', '#ffffff', '#eeaa77', ''.join(contents))
return result
class XMLRPCDocGenerator:
"""Generates documentation for an XML-RPC server.
This class is designed as mix-in and should not
be constructed directly.
"""
def __init__(self):
# setup variables used for HTML documentation
self.server_name = 'XML-RPC Server Documentation'
self.server_documentation = \
"This server exports the following methods through the XML-RPC "\
"protocol."
self.server_title = 'XML-RPC Server Documentation'
def set_server_title(self, server_title):
"""Set the HTML title of the generated server documentation"""
self.server_title = server_title
def set_server_name(self, server_name):
"""Set the name of the generated HTML server documentation"""
self.server_name = server_name
def set_server_documentation(self, server_documentation):
"""Set the documentation string for the entire server."""
self.server_documentation = server_documentation
def generate_html_documentation(self):
"""generate_html_documentation() => html documentation for the server
Generates HTML documentation for the server using introspection for
installed functions and instances that do not implement the
_dispatch method. Alternatively, instances can choose to implement
the _get_method_argstring(method_name) method to provide the
argument string used in the documentation and the
_methodHelp(method_name) method to provide the help text used
in the documentation."""
methods = {}
for method_name in self.system_listMethods():
if method_name in self.funcs:
method = self.funcs[method_name]
elif self.instance is not None:
method_info = [None, None] # argspec, documentation
if hasattr(self.instance, '_get_method_argstring'):
method_info[0] = self.instance._get_method_argstring(method_name)
if hasattr(self.instance, '_methodHelp'):
method_info[1] = self.instance._methodHelp(method_name)
method_info = tuple(method_info)
if method_info != (None, None):
method = method_info
elif not hasattr(self.instance, '_dispatch'):
try:
method = resolve_dotted_attribute(
self.instance,
method_name
)
except AttributeError:
method = method_info
else:
method = method_info
else:
assert 0, "Could not find method in self.functions and no "\
"instance installed"
methods[method_name] = method
documenter = ServerHTMLDoc()
documentation = documenter.docserver(
self.server_name,
self.server_documentation,
methods
)
return documenter.page(self.server_title, documentation)
class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
"""XML-RPC and documentation request handler class.
Handles all HTTP POST requests and attempts to decode them as
XML-RPC requests.
Handles all HTTP GET requests and interprets them as requests
for documentation.
"""
def do_GET(self):
"""Handles the HTTP GET request.
Interpret all HTTP GET requests as requests for server
documentation.
"""
# Check that the path is legal
if not self.is_rpc_path_valid():
self.report_404()
return
response = self.server.generate_html_documentation()
self.send_response(200)
self.send_header("Content-type", "text/html")
self.send_header("Content-length", str(len(response)))
self.end_headers()
self.wfile.write(response.encode())
# shut down the connection
self.wfile.flush()
self.connection.shutdown(1)
class DocXMLRPCServer( SimpleXMLRPCServer,
XMLRPCDocGenerator):
"""XML-RPC and HTML documentation server.
Adds the ability to serve server documentation to the capabilities
of SimpleXMLRPCServer.
"""
def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
logRequests=1, allow_none=False, encoding=None,
bind_and_activate=True):
SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
allow_none, encoding, bind_and_activate)
XMLRPCDocGenerator.__init__(self)
class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
XMLRPCDocGenerator):
"""Handler for XML-RPC data and documentation requests passed through
CGI"""
def handle_get(self):
"""Handles the HTTP GET request.
Interpret all HTTP GET requests as requests for server
documentation.
"""
response = self.generate_html_documentation()
print('Content-Type: text/html')
print('Content-Length: %d' % len(response))
print()
sys.stdout.write(response)
def __init__(self):
CGIXMLRPCRequestHandler.__init__(self)
XMLRPCDocGenerator.__init__(self)
if __name__ == '__main__': if __name__ == '__main__':
print('Running XML-RPC server on port 8000') print('Running XML-RPC server on port 8000')
server = SimpleXMLRPCServer(("localhost", 8000)) server = SimpleXMLRPCServer(("localhost", 8000))

View File

@ -56,6 +56,17 @@ Extension Modules
Library Library
------- -------
- The ``xmlrpc`` package was created; it contains the old
``xmlrpclib`` module as ``xmlrpc.client`` and the content of
the old ``SimpleXMLRPCServer`` and ``DocXMLRPCServer`` modules
as ``xmlrpc.server``.
- The ``dbm`` package was created, containing the old modules
``anydbm`` and ``whichdb`` in its ``__init__.py``, and having
``dbm.gnu`` (was ``gdbm``), ``dbm.bsd`` (was ``dbhash``),
``dbm.ndbm`` (was ``dbm``) and ``dbm.dumb`` (was ``dumbdbm``)
as submodules.
- The ``repr`` module has been renamed to ``reprlib``. - The ``repr`` module has been renamed to ``reprlib``.
- The ``statvfs`` module has been removed. - The ``statvfs`` module has been removed.

View File

@ -1952,7 +1952,8 @@ xdrlib Implements (a subset of) Sun XDR (eXternal Data
xmllib A parser for XML, using the derived class as static DTD. xmllib A parser for XML, using the derived class as static DTD.
xml.dom Classes for processing XML using the Document Object Model. xml.dom Classes for processing XML using the Document Object Model.
xml.sax Classes for processing XML using the SAX API. xml.sax Classes for processing XML using the SAX API.
xmlrpclib Support for remote procedure calls using XML. xmlrpc.client Support for remote procedure calls using XML.
xmlrpc.server Create XMLRPC servers.
zipfile Read & write PK zipped files. zipfile Read & write PK zipped files.