| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- from xmlrpc.server import DocXMLRPCServer
- import http.client
- import re
- import sys
- import threading
- import unittest
- from test import support
- support.requires_working_socket(module=True)
- def make_request_and_skipIf(condition, reason):
- # If we skip the test, we have to make a request because
- # the server created in setUp blocks expecting one to come in.
- if not condition:
- return lambda func: func
- def decorator(func):
- def make_request_and_skip(self):
- self.client.request("GET", "/")
- self.client.getresponse()
- raise unittest.SkipTest(reason)
- return make_request_and_skip
- return decorator
- def make_server():
- serv = DocXMLRPCServer(("localhost", 0), logRequests=False)
- try:
- # Add some documentation
- serv.set_server_title("DocXMLRPCServer Test Documentation")
- serv.set_server_name("DocXMLRPCServer Test Docs")
- serv.set_server_documentation(
- "This is an XML-RPC server's documentation, but the server "
- "can be used by POSTing to /RPC2. Try self.add, too.")
- # Create and register classes and functions
- class TestClass(object):
- def test_method(self, arg):
- """Test method's docs. This method truly does very little."""
- self.arg = arg
- serv.register_introspection_functions()
- serv.register_instance(TestClass())
- def add(x, y):
- """Add two instances together. This follows PEP008, but has nothing
- to do with RFC1952. Case should matter: pEp008 and rFC1952. Things
- that start with http and ftp should be auto-linked, too:
- http://google.com.
- """
- return x + y
- def annotation(x: int):
- """ Use function annotations. """
- return x
- class ClassWithAnnotation:
- def method_annotation(self, x: bytes):
- return x.decode()
- serv.register_function(add)
- serv.register_function(lambda x, y: x-y)
- serv.register_function(annotation)
- serv.register_instance(ClassWithAnnotation())
- return serv
- except:
- serv.server_close()
- raise
- class DocXMLRPCHTTPGETServer(unittest.TestCase):
- def setUp(self):
- # Enable server feedback
- DocXMLRPCServer._send_traceback_header = True
- self.serv = make_server()
- self.thread = threading.Thread(target=self.serv.serve_forever)
- self.thread.start()
- PORT = self.serv.server_address[1]
- self.client = http.client.HTTPConnection("localhost:%d" % PORT)
- def tearDown(self):
- self.client.close()
- # Disable server feedback
- DocXMLRPCServer._send_traceback_header = False
- self.serv.shutdown()
- self.thread.join()
- self.serv.server_close()
- def test_valid_get_response(self):
- self.client.request("GET", "/")
- response = self.client.getresponse()
- self.assertEqual(response.status, 200)
- self.assertEqual(response.getheader("Content-type"), "text/html; charset=UTF-8")
- # Server raises an exception if we don't start to read the data
- response.read()
- def test_get_css(self):
- self.client.request("GET", "/pydoc.css")
- response = self.client.getresponse()
- self.assertEqual(response.status, 200)
- self.assertEqual(response.getheader("Content-type"), "text/css; charset=UTF-8")
- # Server raises an exception if we don't start to read the data
- response.read()
- def test_invalid_get_response(self):
- self.client.request("GET", "/spam")
- response = self.client.getresponse()
- self.assertEqual(response.status, 404)
- self.assertEqual(response.getheader("Content-type"), "text/plain")
- response.read()
- def test_lambda(self):
- """Test that lambda functionality stays the same. The output produced
- currently is, I suspect invalid because of the unencoded brackets in the
- HTML, "<lambda>".
- The subtraction lambda method is tested.
- """
- self.client.request("GET", "/")
- response = self.client.getresponse()
- self.assertIn((b'<dl><dt><a name="-<lambda>"><strong>'
- b'<lambda></strong></a>(x, y)</dt></dl>'),
- response.read())
- @make_request_and_skipIf(sys.flags.optimize >= 2,
- "Docstrings are omitted with -O2 and above")
- def test_autolinking(self):
- """Test that the server correctly automatically wraps references to
- PEPS and RFCs with links, and that it linkifies text starting with
- http or ftp protocol prefixes.
- The documentation for the "add" method contains the test material.
- """
- self.client.request("GET", "/")
- response = self.client.getresponse().read()
- self.assertIn(
- (b'<dl><dt><a name="-add"><strong>add</strong></a>(x, y)</dt><dd>'
- b'<tt>Add two instances together. This '
- b'follows <a href="https://peps.python.org/pep-0008/">'
- b'PEP008</a>, but has nothing<br>\nto do '
- b'with <a href="https://www.rfc-editor.org/rfc/rfc1952.txt">'
- b'RFC1952</a>. Case should matter: pEp008 '
- b'and rFC1952. Things<br>\nthat start '
- b'with http and ftp should be '
- b'auto-linked, too:<br>\n<a href="http://google.com">'
- b'http://google.com</a>.</tt></dd></dl>'), response)
- @make_request_and_skipIf(sys.flags.optimize >= 2,
- "Docstrings are omitted with -O2 and above")
- def test_system_methods(self):
- """Test the presence of three consecutive system.* methods.
- This also tests their use of parameter type recognition and the
- systems related to that process.
- """
- self.client.request("GET", "/")
- response = self.client.getresponse().read()
- self.assertIn(
- (b'<dl><dt><a name="-system.methodHelp"><strong>system.methodHelp'
- b'</strong></a>(method_name)</dt><dd><tt><a href="#-system.method'
- b'Help">system.methodHelp</a>(\'add\') => "Adds '
- b'two integers together"<br>\n <br>\nReturns a'
- b' string containing documentation for '
- b'the specified method.</tt></dd></dl>\n<dl><dt><a name'
- b'="-system.methodSignature"><strong>system.methodSignature</strong>'
- b'</a>(method_name)</dt><dd><tt><a href="#-system.methodSignature">'
- b'system.methodSignature</a>(\'add\') => [double, '
- b'int, int]<br>\n <br>\nReturns a list '
- b'describing the signature of the method.'
- b' In the<br>\nabove example, the add '
- b'method takes two integers as arguments'
- b'<br>\nand returns a double result.<br>\n '
- b'<br>\nThis server does NOT support system'
- b'.methodSignature.</tt></dd></dl>'), response)
- def test_autolink_dotted_methods(self):
- """Test that selfdot values are made strong automatically in the
- documentation."""
- self.client.request("GET", "/")
- response = self.client.getresponse()
- self.assertIn(b"""Try self.<strong>add</strong>, too.""",
- response.read())
- def test_annotations(self):
- """ Test that annotations works as expected """
- self.client.request("GET", "/")
- response = self.client.getresponse()
- docstring = (b'' if sys.flags.optimize >= 2 else
- b'<dd><tt>Use function annotations.</tt></dd>')
- self.assertIn(
- (b'<dl><dt><a name="-annotation"><strong>annotation</strong></a>'
- b'(x: int)</dt>' + docstring + b'</dl>\n'
- b'<dl><dt><a name="-method_annotation"><strong>'
- b'method_annotation</strong></a>(x: bytes)</dt></dl>'),
- response.read())
- def test_server_title_escape(self):
- # bpo-38243: Ensure that the server title and documentation
- # are escaped for HTML.
- self.serv.set_server_title('test_title<script>')
- self.serv.set_server_documentation('test_documentation<script>')
- self.assertEqual('test_title<script>', self.serv.server_title)
- self.assertEqual('test_documentation<script>',
- self.serv.server_documentation)
- generated = self.serv.generate_html_documentation()
- title = re.search(r'<title>(.+?)</title>', generated).group()
- documentation = re.search(r'<p><tt>(.+?)</tt></p>', generated).group()
- self.assertEqual('<title>Python: test_title<script></title>', title)
- self.assertEqual('<p><tt>test_documentation<script></tt></p>', documentation)
- if __name__ == '__main__':
- unittest.main()
|