mirror of https://github.com/python/cpython
Create xmlrpc package. Issue #2886.
This commit is contained in:
parent
7f986acb01
commit
38eceaaf0c
|
@ -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.
|
||||
|
|
@ -42,6 +42,5 @@ is currently supported on most popular platforms. Here is an overview:
|
|||
cgihttpserver.rst
|
||||
cookielib.rst
|
||||
cookie.rst
|
||||
xmlrpclib.rst
|
||||
simplexmlrpcserver.rst
|
||||
docxmlrpcserver.rst
|
||||
xmlrpc.client.rst
|
||||
xmlrpc.server.rst
|
||||
|
|
|
@ -23,4 +23,5 @@ The list of modules described in this chapter is:
|
|||
shelve.rst
|
||||
marshal.rst
|
||||
dbm.rst
|
||||
bsddb.rst
|
||||
sqlite3.rst
|
||||
|
|
|
@ -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.
|
||||
.. moduleauthor:: Fredrik Lundh <fredrik@pythonware.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
|
||||
: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
|
||||
: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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
.. _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::
|
||||
|
||||
import xmlrpclib
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
from xmlrpc.server import SimpleXMLRPCServer
|
||||
|
||||
def is_even(n):
|
||||
return n%2 == 0
|
||||
|
@ -202,9 +183,9 @@ A working example follows. The server code::
|
|||
|
||||
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("100 is even: %s" % str(proxy.is_even(100)))
|
||||
|
||||
|
@ -235,12 +216,12 @@ and :meth:`__repr__` methods.
|
|||
A working example follows. The server code::
|
||||
|
||||
import datetime
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
import xmlrpclib
|
||||
from xmlrpc.server import SimpleXMLRPCServer
|
||||
import xmlrpc.client
|
||||
|
||||
def today():
|
||||
today = datetime.datetime.today()
|
||||
return xmlrpclib.DateTime(today)
|
||||
return xmlrpc.client.DateTime(today)
|
||||
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
print("Listening on port 8000...")
|
||||
|
@ -249,10 +230,10 @@ A working example follows. The server code::
|
|||
|
||||
The client code for the preceding server::
|
||||
|
||||
import xmlrpclib
|
||||
import xmlrpc.client
|
||||
import datetime
|
||||
|
||||
proxy = xmlrpclib.ServerProxy("http://localhost:8000/")
|
||||
proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
|
||||
|
||||
today = proxy.today()
|
||||
# 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
|
||||
XMLRPC::
|
||||
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
import xmlrpclib
|
||||
from xmlrpc.server import SimpleXMLRPCServer
|
||||
import xmlrpc.client
|
||||
|
||||
def python_logo():
|
||||
handle = open("python_logo.jpg")
|
||||
return xmlrpclib.Binary(handle.read())
|
||||
return xmlrpc.client.Binary(handle.read())
|
||||
handle.close()
|
||||
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
|
@ -314,9 +295,9 @@ XMLRPC::
|
|||
|
||||
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.write(proxy.python_logo().data)
|
||||
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
|
||||
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
|
||||
# complex number
|
||||
|
@ -357,12 +338,12 @@ returning a complex type object. The server code::
|
|||
|
||||
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:
|
||||
proxy.add(2, 5)
|
||||
except xmlrpclib.Fault, err:
|
||||
except xmlrpc.client.Fault, err:
|
||||
print("A fault occured")
|
||||
print("Fault code: %d" % err.faultCode)
|
||||
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`
|
||||
by providing an invalid URI::
|
||||
|
||||
import xmlrpclib
|
||||
import xmlrpc.client
|
||||
|
||||
# create a ServerProxy with an invalid URI
|
||||
proxy = xmlrpclib.ServerProxy("http://invalidaddress/")
|
||||
proxy = xmlrpc.client.ServerProxy("http://invalidaddress/")
|
||||
|
||||
try:
|
||||
proxy.some_method()
|
||||
except xmlrpclib.ProtocolError, err:
|
||||
except xmlrpc.client.ProtocolError, err:
|
||||
print("A protocol error occured")
|
||||
print("URL: %s" % err.url)
|
||||
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 ::
|
||||
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
from xmlrpc.server import SimpleXMLRPCServer
|
||||
|
||||
def add(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::
|
||||
|
||||
import xmlrpclib
|
||||
import xmlrpc.client
|
||||
|
||||
proxy = xmlrpclib.ServerProxy("http://localhost:8000/")
|
||||
multicall = xmlrpclib.MultiCall(proxy)
|
||||
proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
|
||||
multicall = xmlrpc.client.MultiCall(proxy)
|
||||
multicall.add(7,3)
|
||||
multicall.subtract(7,3)
|
||||
multicall.multiply(7,3)
|
||||
|
@ -477,13 +458,6 @@ The client code for the preceding server::
|
|||
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]]]])
|
||||
|
||||
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)
|
||||
from xmlrpclib import ServerProxy, Error
|
||||
from xmlrpc.client import ServerProxy, Error
|
||||
|
||||
# server = ServerProxy("http://localhost:8000") # local server
|
||||
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):
|
||||
self.proxy = proxy
|
||||
def make_connection(self, host):
|
||||
|
@ -548,7 +522,7 @@ transport. The following example shows how:
|
|||
|
||||
p = ProxiedTransport()
|
||||
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())
|
||||
|
||||
|
|
@ -1,15 +1,14 @@
|
|||
:mod:`xmlrpc.server` --- Basic XML-RPC servers
|
||||
==============================================
|
||||
|
||||
:mod:`SimpleXMLRPCServer` --- Basic XML-RPC server
|
||||
==================================================
|
||||
|
||||
.. module:: SimpleXMLRPCServer
|
||||
:synopsis: Basic XML-RPC server implementation.
|
||||
.. module:: xmlrpc.server
|
||||
:synopsis: Basic XML-RPC server implementations.
|
||||
.. moduleauthor:: Brian Quinlan <brianq@activestate.com>
|
||||
.. sectionauthor:: Fred L. Drake, Jr. <fdrake@acm.org>
|
||||
|
||||
|
||||
The :mod:`SimpleXMLRPCServer` module provides a basic server framework for
|
||||
XML-RPC servers written in Python. Servers can either be free standing, using
|
||||
The :mod:`xmlrpc.server` module provides a basic server framework for XML-RPC
|
||||
servers written in Python. Servers can either be free standing, using
|
||||
:class:`SimpleXMLRPCServer`, or embedded in a CGI environment, using
|
||||
: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*
|
||||
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
|
||||
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
|
||||
: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
|
||||
|
@ -33,8 +32,8 @@ XML-RPC servers written in Python. Servers can either be free standing, using
|
|||
.. class:: CGIXMLRPCRequestHandler([allow_none[, encoding]])
|
||||
|
||||
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
|
||||
control the XML-RPC responses that will be returned from the server.
|
||||
*allow_none* and *encoding* parameters are passed on to :mod:`xmlrpc.client`
|
||||
and control the XML-RPC responses that will be returned from the server.
|
||||
|
||||
|
||||
.. class:: SimpleXMLRPCRequestHandler()
|
||||
|
@ -115,8 +114,8 @@ SimpleXMLRPCServer Example
|
|||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Server code::
|
||||
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
|
||||
from xmlrpc.server import SimpleXMLRPCServer
|
||||
from xmlrpc.server import SimpleXMLRPCRequestHandler
|
||||
|
||||
# Restrict to a particular path.
|
||||
class RequestHandler(SimpleXMLRPCRequestHandler):
|
||||
|
@ -150,9 +149,9 @@ Server code::
|
|||
The following client code will call the methods made available by the preceding
|
||||
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.add(2,3)) # Returns 5
|
||||
print(s.mul(5,2)) # Returns 5*2 = 10
|
||||
|
@ -220,3 +219,89 @@ Example::
|
|||
handler.register_instance(MyFuncs())
|
||||
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.
|
|
@ -570,7 +570,7 @@ The :mod:`uu` module contains the following notice::
|
|||
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
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@ Batteries Included
|
|||
Python has a "batteries included" philosophy. This is best seen through the
|
||||
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
|
||||
names, no direct knowledge or handling of XML is needed.
|
||||
|
||||
|
|
|
@ -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('"', '"')
|
||||
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)
|
|
@ -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()
|
|
@ -1,10 +1,9 @@
|
|||
from DocXMLRPCServer import DocXMLRPCServer
|
||||
from xmlrpc.server import DocXMLRPCServer
|
||||
import httplib
|
||||
from test import support
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
import xmlrpclib
|
||||
|
||||
PORT = None
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ import datetime
|
|||
import sys
|
||||
import time
|
||||
import unittest
|
||||
import xmlrpclib
|
||||
import SimpleXMLRPCServer
|
||||
import xmlrpc.client as xmlrpclib
|
||||
import xmlrpc.server
|
||||
import threading
|
||||
import mimetools
|
||||
import httplib
|
||||
|
@ -160,9 +160,9 @@ class FaultTestCase(unittest.TestCase):
|
|||
# this will raise AttirebuteError because code don't want us to use
|
||||
# private methods
|
||||
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):
|
||||
def test_default(self):
|
||||
|
@ -249,7 +249,7 @@ def http_server(evt, numrequests):
|
|||
'''This is my function'''
|
||||
return True
|
||||
|
||||
class MyXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
|
||||
class MyXMLRPCServer(xmlrpc.server.SimpleXMLRPCServer):
|
||||
def get_request(self):
|
||||
# Ensure the socket is always non-blocking. On Linux, socket
|
||||
# attributes are not inherited like they are on *BSD and Windows.
|
||||
|
@ -306,7 +306,7 @@ def is_unavailable_exception(e):
|
|||
class SimpleServerTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# enable traceback reporting
|
||||
SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True
|
||||
xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = True
|
||||
|
||||
self.evt = threading.Event()
|
||||
# start server thread to handle requests
|
||||
|
@ -326,7 +326,7 @@ class SimpleServerTestCase(unittest.TestCase):
|
|||
raise RuntimeError("timeout reached, test has failed")
|
||||
|
||||
# disable traceback reporting
|
||||
SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = False
|
||||
xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = False
|
||||
|
||||
def test_simple1(self):
|
||||
try:
|
||||
|
@ -443,9 +443,9 @@ class SimpleServerTestCase(unittest.TestCase):
|
|||
def test_dotted_attribute(self):
|
||||
# Raises an AttributeError because private methods are not allowed.
|
||||
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.
|
||||
# This avoids waiting for the socket timeout.
|
||||
self.test_simple1()
|
||||
|
@ -475,17 +475,17 @@ class FailingServerTestCase(unittest.TestCase):
|
|||
# wait on the server thread to terminate
|
||||
self.evt.wait()
|
||||
# reset flag
|
||||
SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = False
|
||||
xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = False
|
||||
# reset message class
|
||||
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = mimetools.Message
|
||||
xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = mimetools.Message
|
||||
|
||||
def test_basic(self):
|
||||
# check that flag is false by default
|
||||
flagval = SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header
|
||||
flagval = xmlrpc.server.SimpleXMLRPCServer._send_traceback_header
|
||||
self.assertEqual(flagval, False)
|
||||
|
||||
# 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
|
||||
try:
|
||||
|
@ -499,7 +499,7 @@ class FailingServerTestCase(unittest.TestCase):
|
|||
|
||||
def test_fail_no_info(self):
|
||||
# use the broken message class
|
||||
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass
|
||||
xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass
|
||||
|
||||
try:
|
||||
p = xmlrpclib.ServerProxy('http://localhost:%d' % PORT)
|
||||
|
@ -515,11 +515,11 @@ class FailingServerTestCase(unittest.TestCase):
|
|||
|
||||
def test_fail_with_info(self):
|
||||
# use the broken message class
|
||||
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass
|
||||
xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass
|
||||
|
||||
# Check that errors in the server send back exception/traceback
|
||||
# info when flag is set
|
||||
SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True
|
||||
xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = True
|
||||
|
||||
try:
|
||||
p = xmlrpclib.ServerProxy('http://localhost:%d' % PORT)
|
||||
|
@ -536,7 +536,7 @@ class FailingServerTestCase(unittest.TestCase):
|
|||
|
||||
class CGIHandlerTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.cgi = SimpleXMLRPCServer.CGIXMLRPCRequestHandler()
|
||||
self.cgi = xmlrpc.server.CGIXMLRPCRequestHandler()
|
||||
|
||||
def tearDown(self):
|
||||
self.cgi = None
|
||||
|
|
|
@ -6,7 +6,7 @@ import sys
|
|||
import unittest
|
||||
from test import support
|
||||
|
||||
import xmlrpclib
|
||||
import xmlrpclib.client as xmlrpclib
|
||||
|
||||
class CurrentTimeTest(unittest.TestCase):
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
# This directory is a Python package.
|
|
@ -108,7 +108,6 @@ Exported classes:
|
|||
ServerProxy Represents a logical connection to an XML-RPC server
|
||||
|
||||
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
|
||||
localtime integer value to generate a "dateTime.iso8601"
|
||||
XML-RPC value
|
||||
|
@ -127,7 +126,6 @@ Exported constants:
|
|||
|
||||
Exported functions:
|
||||
|
||||
boolean Convert any Python value to an XML-RPC boolean
|
||||
getparser Create instance of the fastest available parser & attach
|
||||
to an unmarshalling object
|
||||
dumps Convert an argument tuple or a Fault instance to an XML-RPC
|
||||
|
@ -147,11 +145,6 @@ try:
|
|||
except ImportError:
|
||||
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):
|
||||
# decode non-ascii string (if possible)
|
||||
if encoding and is8bit(data):
|
||||
|
@ -265,12 +258,7 @@ class Fault(Error):
|
|||
# Special values
|
||||
|
||||
##
|
||||
# Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and
|
||||
# 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.
|
||||
# Backwards compatibility
|
||||
|
||||
boolean = Boolean = bool
|
||||
|
||||
|
@ -451,26 +439,10 @@ def _binary(data):
|
|||
return value
|
||||
|
||||
WRAPPERS = (DateTime, Binary)
|
||||
if not _bool_is_builtin:
|
||||
WRAPPERS = WRAPPERS + (Boolean,)
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 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
|
||||
# XML parser. SGMLOP sources can be downloaded from:
|
||||
|
@ -640,12 +612,11 @@ class Marshaller:
|
|||
write("</int></value>\n")
|
||||
#dispatch[int] = dump_int
|
||||
|
||||
if _bool_is_builtin:
|
||||
def dump_bool(self, value, write):
|
||||
write("<value><boolean>")
|
||||
write(value and "1" or "0")
|
||||
write("</boolean></value>\n")
|
||||
dispatch[bool] = dump_bool
|
||||
def dump_bool(self, value, write):
|
||||
write("<value><boolean>")
|
||||
write(value and "1" or "0")
|
||||
write("</boolean></value>\n")
|
||||
dispatch[bool] = dump_bool
|
||||
|
||||
def dump_long(self, value, write):
|
||||
if value > MAXINT or value < MININT:
|
||||
|
@ -968,6 +939,8 @@ class MultiCall:
|
|||
# --------------------------------------------------------------------
|
||||
# convenience functions
|
||||
|
||||
FastMarshaller = FastParser = FastUnmarshaller = None
|
||||
|
||||
##
|
||||
# Create a parser object, and connect it to an unmarshalling instance.
|
||||
# This function picks the fastest available XML parser.
|
|
@ -1,4 +1,4 @@
|
|||
"""Simple XML-RPC Server.
|
||||
"""XML-RPC Servers.
|
||||
|
||||
This module can be used to create simple XML-RPC servers
|
||||
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
|
||||
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:
|
||||
|
||||
1. Install functions:
|
||||
|
@ -98,12 +104,14 @@ server.handle_request()
|
|||
# Written by Brian Quinlan (brian@sweetapp.com).
|
||||
# Based on code written by Fredrik Lundh.
|
||||
|
||||
import xmlrpclib
|
||||
from xmlrpclib import Fault
|
||||
from xmlrpc.client import Fault, dumps, loads
|
||||
import socketserver
|
||||
import BaseHTTPServer
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import pydoc
|
||||
import inspect
|
||||
import traceback
|
||||
try:
|
||||
import fcntl
|
||||
|
@ -235,7 +243,7 @@ class SimpleXMLRPCDispatcher:
|
|||
"""
|
||||
|
||||
try:
|
||||
params, method = xmlrpclib.loads(data)
|
||||
params, method = loads(data)
|
||||
|
||||
# generate response
|
||||
if dispatch_method is not None:
|
||||
|
@ -244,16 +252,16 @@ class SimpleXMLRPCDispatcher:
|
|||
response = self._dispatch(method, params)
|
||||
# wrap response in a singleton tuple
|
||||
response = (response,)
|
||||
response = xmlrpclib.dumps(response, methodresponse=1,
|
||||
allow_none=self.allow_none, encoding=self.encoding)
|
||||
response = dumps(response, methodresponse=1,
|
||||
allow_none=self.allow_none, encoding=self.encoding)
|
||||
except Fault as fault:
|
||||
response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
|
||||
encoding=self.encoding)
|
||||
response = dumps(fault, allow_none=self.allow_none,
|
||||
encoding=self.encoding)
|
||||
except:
|
||||
# report exception back to server
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
response = xmlrpclib.dumps(
|
||||
xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
|
||||
response = dumps(
|
||||
Fault(1, "%s:%s" % (exc_type, exc_value)),
|
||||
encoding=self.encoding, allow_none=self.allow_none,
|
||||
)
|
||||
|
||||
|
@ -585,6 +593,273 @@ class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
|
|||
|
||||
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('"', '"')
|
||||
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__':
|
||||
print('Running XML-RPC server on port 8000')
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
11
Misc/NEWS
11
Misc/NEWS
|
@ -56,6 +56,17 @@ Extension Modules
|
|||
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 ``statvfs`` module has been removed.
|
||||
|
|
|
@ -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.
|
||||
xml.dom Classes for processing XML using the Document Object Model.
|
||||
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.
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue