~ubuntu-branches/ubuntu/karmic/pypy/karmic

« back to all changes in this revision

Viewing changes to lib-python/2.4.1/DocXMLRPCServer.py

  • Committer: Bazaar Package Importer
  • Author(s): Alexandre Fayolle
  • Date: 2007-04-13 09:33:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070413093309-yoojh4jcoocu2krz
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Self documenting XML-RPC Server.
 
2
 
 
3
This module can be used to create XML-RPC servers that
 
4
serve pydoc-style documentation in response to HTTP
 
5
GET requests. This documentation is dynamically generated
 
6
based on the functions and methods registered with the
 
7
server.
 
8
 
 
9
This module is built upon the pydoc and SimpleXMLRPCServer
 
10
modules.
 
11
"""
 
12
 
 
13
import pydoc
 
14
import inspect
 
15
import types
 
16
import re
 
17
import sys
 
18
 
 
19
from SimpleXMLRPCServer import (SimpleXMLRPCServer,
 
20
            SimpleXMLRPCRequestHandler,
 
21
            CGIXMLRPCRequestHandler,
 
22
            resolve_dotted_attribute)
 
23
 
 
24
class ServerHTMLDoc(pydoc.HTMLDoc):
 
25
    """Class used to generate pydoc HTML document for a server"""
 
26
 
 
27
    def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
 
28
        """Mark up some plain text, given a context of symbols to look for.
 
29
        Each context dictionary maps object names to anchor names."""
 
30
        escape = escape or self.escape
 
31
        results = []
 
32
        here = 0
 
33
 
 
34
        # XXX Note that this regular expressions does not allow for the
 
35
        # hyperlinking of arbitrary strings being used as method
 
36
        # names. Only methods with names consisting of word characters
 
37
        # and '.'s are hyperlinked.
 
38
        pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
 
39
                                r'RFC[- ]?(\d+)|'
 
40
                                r'PEP[- ]?(\d+)|'
 
41
                                r'(self\.)?((?:\w|\.)+))\b')
 
42
        while 1:
 
43
            match = pattern.search(text, here)
 
44
            if not match: break
 
45
            start, end = match.span()
 
46
            results.append(escape(text[here:start]))
 
47
 
 
48
            all, scheme, rfc, pep, selfdot, name = match.groups()
 
49
            if scheme:
 
50
                url = escape(all).replace('"', '"')
 
51
                results.append('<a href="%s">%s</a>' % (url, url))
 
52
            elif rfc:
 
53
                url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
 
54
                results.append('<a href="%s">%s</a>' % (url, escape(all)))
 
55
            elif pep:
 
56
                url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
 
57
                results.append('<a href="%s">%s</a>' % (url, escape(all)))
 
58
            elif text[end:end+1] == '(':
 
59
                results.append(self.namelink(name, methods, funcs, classes))
 
60
            elif selfdot:
 
61
                results.append('self.<strong>%s</strong>' % name)
 
62
            else:
 
63
                results.append(self.namelink(name, classes))
 
64
            here = end
 
65
        results.append(escape(text[here:]))
 
66
        return ''.join(results)
 
67
 
 
68
    def docroutine(self, object, name=None, mod=None,
 
69
                   funcs={}, classes={}, methods={}, cl=None):
 
70
        """Produce HTML documentation for a function or method object."""
 
71
 
 
72
        anchor = (cl and cl.__name__ or '') + '-' + name
 
73
        note = ''
 
74
 
 
75
        title = '<a name="%s"><strong>%s</strong></a>' % (anchor, name)
 
76
 
 
77
        if inspect.ismethod(object):
 
78
            args, varargs, varkw, defaults = inspect.getargspec(object.im_func)
 
79
            # exclude the argument bound to the instance, it will be
 
80
            # confusing to the non-Python user
 
81
            argspec = inspect.formatargspec (
 
82
                    args[1:],
 
83
                    varargs,
 
84
                    varkw,
 
85
                    defaults,
 
86
                    formatvalue=self.formatvalue
 
87
                )
 
88
        elif inspect.isfunction(object):
 
89
            args, varargs, varkw, defaults = inspect.getargspec(object)
 
90
            argspec = inspect.formatargspec(
 
91
                args, varargs, varkw, defaults, formatvalue=self.formatvalue)
 
92
        else:
 
93
            argspec = '(...)'
 
94
 
 
95
        if isinstance(object, types.TupleType):
 
96
            argspec = object[0] or argspec
 
97
            docstring = object[1] or ""
 
98
        else:
 
99
            docstring = pydoc.getdoc(object)
 
100
 
 
101
        decl = title + argspec + (note and self.grey(
 
102
               '<font face="helvetica, arial">%s</font>' % note))
 
103
 
 
104
        doc = self.markup(
 
105
            docstring, self.preformat, funcs, classes, methods)
 
106
        doc = doc and '<dd><tt>%s</tt></dd>' % doc
 
107
        return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
 
108
 
 
109
    def docserver(self, server_name, package_documentation, methods):
 
110
        """Produce HTML documentation for an XML-RPC server."""
 
111
 
 
112
        fdict = {}
 
113
        for key, value in methods.items():
 
114
            fdict[key] = '#-' + key
 
115
            fdict[value] = fdict[key]
 
116
 
 
117
        head = '<big><big><strong>%s</strong></big></big>' % server_name
 
118
        result = self.heading(head, '#ffffff', '#7799ee')
 
119
 
 
120
        doc = self.markup(package_documentation, self.preformat, fdict)
 
121
        doc = doc and '<tt>%s</tt>' % doc
 
122
        result = result + '<p>%s</p>\n' % doc
 
123
 
 
124
        contents = []
 
125
        method_items = methods.items()
 
126
        method_items.sort()
 
127
        for key, value in method_items:
 
128
            contents.append(self.docroutine(value, key, funcs=fdict))
 
129
        result = result + self.bigsection(
 
130
            'Methods', '#ffffff', '#eeaa77', pydoc.join(contents))
 
131
 
 
132
        return result
 
133
 
 
134
class XMLRPCDocGenerator:
 
135
    """Generates documentation for an XML-RPC server.
 
136
 
 
137
    This class is designed as mix-in and should not
 
138
    be constructed directly.
 
139
    """
 
140
 
 
141
    def __init__(self):
 
142
        # setup variables used for HTML documentation
 
143
        self.server_name = 'XML-RPC Server Documentation'
 
144
        self.server_documentation = \
 
145
            "This server exports the following methods through the XML-RPC "\
 
146
            "protocol."
 
147
        self.server_title = 'XML-RPC Server Documentation'
 
148
 
 
149
    def set_server_title(self, server_title):
 
150
        """Set the HTML title of the generated server documentation"""
 
151
 
 
152
        self.server_title = server_title
 
153
 
 
154
    def set_server_name(self, server_name):
 
155
        """Set the name of the generated HTML server documentation"""
 
156
 
 
157
        self.server_name = server_name
 
158
 
 
159
    def set_server_documentation(self, server_documentation):
 
160
        """Set the documentation string for the entire server."""
 
161
 
 
162
        self.server_documentation = server_documentation
 
163
 
 
164
    def generate_html_documentation(self):
 
165
        """generate_html_documentation() => html documentation for the server
 
166
 
 
167
        Generates HTML documentation for the server using introspection for
 
168
        installed functions and instances that do not implement the
 
169
        _dispatch method. Alternatively, instances can choose to implement
 
170
        the _get_method_argstring(method_name) method to provide the
 
171
        argument string used in the documentation and the
 
172
        _methodHelp(method_name) method to provide the help text used
 
173
        in the documentation."""
 
174
 
 
175
        methods = {}
 
176
 
 
177
        for method_name in self.system_listMethods():
 
178
            if self.funcs.has_key(method_name):
 
179
                method = self.funcs[method_name]
 
180
            elif self.instance is not None:
 
181
                method_info = [None, None] # argspec, documentation
 
182
                if hasattr(self.instance, '_get_method_argstring'):
 
183
                    method_info[0] = self.instance._get_method_argstring(method_name)
 
184
                if hasattr(self.instance, '_methodHelp'):
 
185
                    method_info[1] = self.instance._methodHelp(method_name)
 
186
 
 
187
                method_info = tuple(method_info)
 
188
                if method_info != (None, None):
 
189
                    method = method_info
 
190
                elif not hasattr(self.instance, '_dispatch'):
 
191
                    try:
 
192
                        method = resolve_dotted_attribute(
 
193
                                    self.instance,
 
194
                                    method_name
 
195
                                    )
 
196
                    except AttributeError:
 
197
                        method = method_info
 
198
                else:
 
199
                    method = method_info
 
200
            else:
 
201
                assert 0, "Could not find method in self.functions and no "\
 
202
                          "instance installed"
 
203
 
 
204
            methods[method_name] = method
 
205
 
 
206
        documenter = ServerHTMLDoc()
 
207
        documentation = documenter.docserver(
 
208
                                self.server_name,
 
209
                                self.server_documentation,
 
210
                                methods
 
211
                            )
 
212
 
 
213
        return documenter.page(self.server_title, documentation)
 
214
 
 
215
class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
 
216
    """XML-RPC and documentation request handler class.
 
217
 
 
218
    Handles all HTTP POST requests and attempts to decode them as
 
219
    XML-RPC requests.
 
220
 
 
221
    Handles all HTTP GET requests and interprets them as requests
 
222
    for documentation.
 
223
    """
 
224
 
 
225
    def do_GET(self):
 
226
        """Handles the HTTP GET request.
 
227
 
 
228
        Interpret all HTTP GET requests as requests for server
 
229
        documentation.
 
230
        """
 
231
 
 
232
        response = self.server.generate_html_documentation()
 
233
        self.send_response(200)
 
234
        self.send_header("Content-type", "text/html")
 
235
        self.send_header("Content-length", str(len(response)))
 
236
        self.end_headers()
 
237
        self.wfile.write(response)
 
238
 
 
239
        # shut down the connection
 
240
        self.wfile.flush()
 
241
        self.connection.shutdown(1)
 
242
 
 
243
class DocXMLRPCServer(  SimpleXMLRPCServer,
 
244
                        XMLRPCDocGenerator):
 
245
    """XML-RPC and HTML documentation server.
 
246
 
 
247
    Adds the ability to serve server documentation to the capabilities
 
248
    of SimpleXMLRPCServer.
 
249
    """
 
250
 
 
251
    def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
 
252
                 logRequests=1):
 
253
        SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests)
 
254
        XMLRPCDocGenerator.__init__(self)
 
255
 
 
256
class DocCGIXMLRPCRequestHandler(   CGIXMLRPCRequestHandler,
 
257
                                    XMLRPCDocGenerator):
 
258
    """Handler for XML-RPC data and documentation requests passed through
 
259
    CGI"""
 
260
 
 
261
    def handle_get(self):
 
262
        """Handles the HTTP GET request.
 
263
 
 
264
        Interpret all HTTP GET requests as requests for server
 
265
        documentation.
 
266
        """
 
267
 
 
268
        response = self.generate_html_documentation()
 
269
 
 
270
        print 'Content-Type: text/html'
 
271
        print 'Content-Length: %d' % len(response)
 
272
        print
 
273
        sys.stdout.write(response)
 
274
 
 
275
    def __init__(self):
 
276
        CGIXMLRPCRequestHandler.__init__(self)
 
277
        XMLRPCDocGenerator.__init__(self)
 
278
 
 
279
if __name__ == '__main__':
 
280
    def deg_to_rad(deg):
 
281
        """deg_to_rad(90) => 1.5707963267948966
 
282
 
 
283
        Converts an angle in degrees to an angle in radians"""
 
284
        import math
 
285
        return deg * math.pi / 180
 
286
 
 
287
    server = DocXMLRPCServer(("localhost", 8000))
 
288
 
 
289
    server.set_server_title("Math Server")
 
290
    server.set_server_name("Math XML-RPC Server")
 
291
    server.set_server_documentation("""This server supports various mathematical functions.
 
292
 
 
293
You can use it from Python as follows:
 
294
 
 
295
>>> from xmlrpclib import ServerProxy
 
296
>>> s = ServerProxy("http://localhost:8000")
 
297
>>> s.deg_to_rad(90.0)
 
298
1.5707963267948966""")
 
299
 
 
300
    server.register_function(deg_to_rad)
 
301
    server.register_introspection_functions()
 
302
 
 
303
    server.serve_forever()