diff --git a/Doc/library/pydoc.rst b/Doc/library/pydoc.rst --- a/Doc/library/pydoc.rst +++ b/Doc/library/pydoc.rst @@ -57,12 +57,13 @@ Specifying ``0`` as the port number will :program:`pydoc -g` will start the server and additionally bring up a small :mod:`tkinter`\ -based graphical interface to help you search for -documentation pages. (The ``-g`` option is deprecated.) +documentation pages. The ``-g`` option is deprecated, since the server can +now be controlled directly from HTTP clients. :program:`pydoc -b` will start the server and additionally open a web browser to a module index page. Each served page has a navigation bar at the top where you can *Get* help on an individual item, *Search* all modules with a -keyword in their synopsis line, and goto to the *Module index*, *Topics*, and +keyword in their synopsis line, and go to the *Module index*, *Topics* and *Keywords* pages. When :program:`pydoc` generates documentation, it uses the current environment diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst --- a/Doc/whatsnew/3.2.rst +++ b/Doc/whatsnew/3.2.rst @@ -565,12 +565,6 @@ New, Improved, and Deprecated Modules (Contributed by R. David Murray, :issue:`10321`.) -* The :mod:`pydoc` module now provides a much improved web server interface, - as well as a new command-line option to automatically open a browser - window to display that server. - - (Contributed by Ron Adam; :issue:`2001`.) - * The :mod:`inspect` module has a new function :func:`getgenatorstate` to easily identify the current state of a generator as one of ``GEN_CREATED``, ``GEN_RUNNING``, ``GEN_SUSPENDED`` or ``GEN_CLOSED``. @@ -579,6 +573,12 @@ New, Improved, and Deprecated Modules .. XXX: Mention inspect.getattr_static (Michael Foord) +* The :mod:`pydoc` module now provides a much improved Web server interface, + as well as a new command-line option to automatically open a browser + window to display that server. + + (Contributed by Ron Adam; :issue:`2001`.) + Multi-threading =============== @@ -662,7 +662,7 @@ The :mod:`os` module has two new functio :data:`os.environ`, :func:`os.getenvb` function and :data:`os.supports_bytes_environ` constant. -``'mbcs'`` encoding doesn't ignore the error handler argument anymore. By +``'mbcs'`` encoding doesn't ignore the error handler argument any more. By default (strict mode), it raises an UnicodeDecodeError on undecodable byte sequence and UnicodeEncodeError on unencodable character. To get the ``'mbcs'`` encoding of Python 3.1, use ``'ignore'`` error handler to decode and @@ -726,7 +726,7 @@ require changes to your code: * The :mod:`nntplib` module was reworked extensively, meaning that its APIs are often incompatible with the 3.1 APIs. -* :class:`bytearray` objects cannot be used anymore as filenames: convert them +* :class:`bytearray` objects cannot be used any more as filenames: convert them to :class:`bytes`. * PyArg_Parse*() functions: diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -16,15 +16,16 @@ Run "pydoc -k " to search for a of all available modules. Run "pydoc -p " to start an HTTP server on the given port on the -local machine. Port number 0 can be used to get an arbitrary unused port. +local machine. Port number 0 can be used to get an arbitrary unused port. Run "pydoc -b" to start an HTTP server on an arbitrary unused port and -open a web browser to interactively browse documentation. The -p option +open a Web browser to interactively browse documentation. The -p option can be used with the -b option to explicitly specify the server port. For platforms without a command line, "pydoc -g" starts the HTTP server -and also pops up a little window for controlling it. -(The "-g" option is deprecated.) +and also pops up a little window for controlling it. This option is +deprecated, since the server can now be controlled directly from HTTP +clients. Run "pydoc -w " to write out the HTML documentation for a module to a file named ".html". @@ -56,19 +57,20 @@ Richard Chamberlain, for the first imple # the current directory is changed with os.chdir(), an incorrect # path will be displayed. +import os import sys +import builtins import imp -import os +import io +import inspect +import pkgutil +import platform import re -import inspect -import builtins -import pkgutil import time import warnings -import platform +from collections import deque from reprlib import Repr -from traceback import extract_tb as _extract_tb -from collections import deque +from traceback import extract_tb # --------------------------------------------------------- common routines @@ -301,7 +303,7 @@ def safeimport(path, forceload=0, cache= elif exc is SyntaxError: # A SyntaxError occurred before we could execute the module. raise ErrorDuringImport(value.filename, info) - elif exc is ImportError and _extract_tb(tb)[-1][2]=='safeimport': + elif exc is ImportError and extract_tb(tb)[-1][2]=='safeimport': # The import error occurred directly in this function, # which means there is no such module in the path. return None @@ -528,8 +530,8 @@ class HTMLDoc(Doc): else: text = name return '%s' % (url, text) - - def filelink(self, url, path): + + def filelink(self, url, path): """Make a link to source file.""" return '%s' % (url, path) @@ -1858,18 +1860,18 @@ module "pydoc_data.topics" could not be if more_xrefs: xrefs = (xrefs or '') + ' ' + more_xrefs if xrefs: - import io, formatter + import formatter buffer = io.StringIO() formatter.DumbWriter(buffer).send_flowing_data( 'Related help topics: ' + ', '.join(xrefs.split()) + '\n') self.output.write('\n%s\n' % buffer.getvalue()) def _gettopic(self, topic, more_xrefs=''): - """ Returns unbuffered tuple of (topic, xrefs). + """Return unbuffered tuple of (topic, xrefs). - If an error occurs, topic is the error message, and xrefs is ''. - This function duplicates the showtopic method but returns it's - result directly so it can be formatted for display in an html page. + If an error occurs, topic is the error message, and xrefs is ''. + This function duplicates the showtopic method but returns its + result directly so it can be formatted for display in an html page. """ try: import pydoc_data.topics @@ -1880,14 +1882,14 @@ module "pydoc_data.topics" could not be ''' , '') target = self.topics.get(topic, self.keywords.get(topic)) if not target: - return 'no documentation found for %s' % repr(topic), '' - if type(target) is type(''): + return 'no documentation found for %r' % topic, '' + if isinstance(target, str): return self._gettopic(target, more_xrefs) label, xrefs = target try: doc = pydoc_data.topics.topics[label] except KeyError: - return 'no documentation found for %s' % repr(topic), '' + return 'no documentation found for %r' % topic, '' if more_xrefs: xrefs = (xrefs or '') + ' ' + more_xrefs return doc, xrefs @@ -1973,13 +1975,13 @@ class ModuleScanner: for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror): if self.quit: break - - # XXX Skipping this file is a get-around for a bug + + # XXX Skipping this file is a workaround for a bug # that causes python to crash with a segfault. # http://bugs.python.org/issue9319 # - # TODO: Remove the this once the bug is fixed. - if modname in ["test.badsyntax_pep3120", "badsyntax_pep3120"]: + # TODO Remove this once the bug is fixed. + if modname in {'test.badsyntax_pep3120', 'badsyntax_pep3120'}: continue if key is None: @@ -1997,7 +1999,6 @@ class ModuleScanner: if onerror: onerror(modname) continue - import io desc = source_synopsis(io.StringIO(source)) or '' if hasattr(loader, 'get_filename'): path = loader.get_filename(modname) @@ -2032,14 +2033,14 @@ def apropos(key): else: warnings.filterwarnings('ignore') # ignore problems during import ModuleScanner().run(callback, key, onerror=onerror) -# --------------------------------------------------- web browser interface +# --------------------------------------------------- Web browser interface def serve(port, callback=None, completer=None): import http.server, email.message, select msg = 'the pydoc.serve() function is deprecated' warnings.warn(msg, DeprecationWarning, stacklevel=2) - + class DocHandler(http.server.BaseHTTPRequestHandler): def send_document(self, title, contents): try: @@ -2118,12 +2119,12 @@ pydoc by Ka-Ping Yee <ping@l # ----------------------------------------------------- graphical interface def gui(): - """Graphical interface (starts web server and pops up a control window).""" - - msg = ('the pydoc.gui() function and "pydoc -g" option are deprecated, ' + """Graphical interface (starts Web server and pops up a control window).""" + + msg = ('the pydoc.gui() function and "pydoc -g" option are deprecated\n', 'use "pydoc.browse() function and "pydoc -b" option instead.') warnings.warn(msg, DeprecationWarning, stacklevel=2) - + class GUI: def __init__(self, window, port=7464): self.window = window @@ -2203,15 +2204,8 @@ def gui(): def open(self, event=None, url=None): url = url or self.server.url - try: - import webbrowser - webbrowser.open(url) - except ImportError: # pre-webbrowser.py compatibility - if sys.platform == 'win32': - os.system('start "%s"' % url) - else: - rc = os.system('netscape -remote "openURL(%s)" &' % url) - if rc: os.system('netscape "%s" &' % url) + import webbrowser + webbrowser.open(url) def quit(self, event=None): if self.server: @@ -2302,97 +2296,95 @@ def gui(): root.destroy() except KeyboardInterrupt: pass - - -# --------------------------------------- enhanced web browser interface + + +# --------------------------------------- enhanced Web browser interface def _start_server(urlhandler, port): - """ Start an HTTP server thread on a specific port. - - Start an HTML/text server thread, so HTML or text documents can be - browsed dynamically and interactively with a web browser. - - Example use - =========== + """Start an HTTP server thread on a specific port. - >>> import time - >>> import pydoc + Start an HTML/text server thread, so HTML or text documents can be + browsed dynamically and interactively with a Web browser. Example use: - Define a URL handler. To determine what the client is asking - for, check the URL and content_type. + >>> import time + >>> import pydoc - Then get or generate some text or HTML code and return it. + Define a URL handler. To determine what the client is asking + for, check the URL and content_type. - >>> def my_url_handler(url, content_type): - ... text = 'the URL sent was: (%s, %s)' % (url, content_type) - ... return text + Then get or generate some text or HTML code and return it. - Start server thread on port 0. - If you use port 0, the server will pick a random port number. - You can then use serverthread.port to get the port number. + >>> def my_url_handler(url, content_type): + ... text = 'the URL sent was: (%s, %s)' % (url, content_type) + ... return text - >>> port = 0 - >>> serverthread = pydoc._start_server(my_url_handler, port) + Start server thread on port 0. + If you use port 0, the server will pick a random port number. + You can then use serverthread.port to get the port number. - Check that the server is really started. If it is, open browser - and get first page. Use serverthread.url as the starting page. + >>> port = 0 + >>> serverthread = pydoc._start_server(my_url_handler, port) - >>> if serverthread.serving: - ... import webbrowser + Check that the server is really started. If it is, open browser + and get first page. Use serverthread.url as the starting page. - The next two lines are commented out so a browser doesn't open if - doctest is run on this module. - - #... webbrowser.open(serverthread.url) - #True + >>> if serverthread.serving: + ... import webbrowser - Let the server do it's thing. We just need to monitor its status. - Use time.sleep so the loop doesn't hog the CPU. + The next two lines are commented out so a browser doesn't open if + doctest is run on this module. - >>> starttime = time.time() - >>> timeout = 1 #seconds + #... webbrowser.open(serverthread.url) + #True - This is a short timeout for testing purposes. + Let the server do its thing. We just need to monitor its status. + Use time.sleep so the loop doesn't hog the CPU. - >>> while serverthread.serving: - ... time.sleep(.01) - ... if serverthread.serving and time.time() - starttime > timeout: - ... serverthread.stop() - ... break + >>> starttime = time.time() + >>> timeout = 1 #seconds - Print any errors that may have occurred. + This is a short timeout for testing purposes. - >>> print(serverthread.error) - None + >>> while serverthread.serving: + ... time.sleep(.01) + ... if serverthread.serving and time.time() - starttime > timeout: + ... serverthread.stop() + ... break - """ + Print any errors that may have occurred. + + >>> print(serverthread.error) + None + """ import http.server import email.message import select import threading class DocHandler(http.server.BaseHTTPRequestHandler): - """ Handle server requests from browser. """ + def do_GET(self): - """ Process a request from a HTML browser. - The URL received is in self.path. - Get an HTML page from self.urlhandler and send it. + """Process a request from an HTML browser. + + The URL received is in self.path. + Get an HTML page from self.urlhandler and send it. """ if self.path.endswith('.css'): content_type = 'text/css' else: content_type = 'text/html' self.send_response(200) - self.send_header('Content-Type', content_type + ';chrset=UTF-8') + self.send_header('Content-Type', '%s;charset=UTF-8' % content_type) self.end_headers() - self.wfile.write(bytes(self.urlhandler(self.path, content_type), - 'UTF-8')) + self.wfile.write(self.urlhandler( + self.path, content_type).encode('utf-8')) def log_message(self, *args): # Don't log messages. pass class DocServer(http.server.HTTPServer): + def __init__(self, port, callback): self.host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost' self.address = ('', port) @@ -2412,7 +2404,7 @@ def _start_server(urlhandler, port): self.callback(self) class ServerThread(threading.Thread): - """ Use to start the server as a thread in an application. """ + def __init__(self, urlhandler, port): self.urlhandler = urlhandler self.port = int(port) @@ -2421,7 +2413,7 @@ def _start_server(urlhandler, port): self.error = None def run(self): - """ Start the server. """ + """Start the server.""" try: DocServer.base = http.server.HTTPServer DocServer.handler = DocHandler @@ -2440,11 +2432,11 @@ def _start_server(urlhandler, port): self.url = 'http://%s:%d/' % (self.host, self.port) def stop(self): - """ Stop the server and this thread nicely """ + """Stop the server and this thread nicely""" self.docserver.quit = True self.serving = False self.url = None - + thread = ServerThread(urlhandler, port) thread.start() # Wait until thread.serving is True to make sure we are @@ -2455,41 +2447,39 @@ def _start_server(urlhandler, port): def _url_handler(url, content_type="text/html"): - """ The pydoc url handler for use with the pydoc server. - - If the content_type is 'text/css', the _pydoc.css style - sheet is read and returned if it exits. - - If the content_type is 'text/html', then the result of - get_html_page(url) is returned. - + """The pydoc url handler for use with the pydoc server. + + If the content_type is 'text/css', the _pydoc.css style + sheet is read and returned if it exits. + + If the content_type is 'text/html', then the result of + get_html_page(url) is returned. """ class _HTMLDoc(HTMLDoc): - + def page(self, title, contents): """Format an HTML page.""" css_path = "pydoc_data/_pydoc.css" css_link = ( - """""" - % css_path) + '' % + css_path) return '''\ Python: %s %s%s ''' % (title, css_link, contents) - - def filelink(self, url, path): + + def filelink(self, url, path): return '%s' % (url, path) - - + + html = _HTMLDoc() - def html_navbar(): - version = "%s [%s, %s]" % ( - platform.python_version(), - platform.python_build()[0], - platform.python_compiler()) + def html_navbar(): + version = "%s [%s, %s]" % (platform.python_version(), + platform.python_build()[0], + platform.python_compiler()) return """
Python %s
%s

@@ -2516,33 +2506,40 @@ def _url_handler(url, content_type="text """ % (version, platform.platform(terse=True)) def html_index(): - """ Module Index web page. """ + """Module Index page.""" + def bltinlink(name): return '%s' % (name, name) + heading = html.heading( 'Index of Modules', '#ffffff', '#7799ee') - names = list(filter(lambda x: x != '__main__', - sys.builtin_module_names)) + names = [name for name in sys.builtin_module_names + if name != '__main__'] contents = html.multicolumn(names, bltinlink) - indices = ['

' + html.bigsection( + contents = [heading, '

' + html.bigsection( 'Built-in Modules', '#ffffff', '#ee77aa', contents)] + seen = {} for dir in sys.path: - indices.append(html.index(dir, seen)) - contents = heading + ''.join(indices) + \ - '''

- pydoc by Ka-Ping Yee <ping@lfw.org>''' - return html.page('Index of Modules' ,contents) + contents.append(html.index(dir, seen)) + + contents.append( + '

pydoc by Ka-Ping Yee' + '<ping@lfw.org>') + return html.page('Index of Modules', ''.join(contents)) def html_search(key): - """ Search results page. """ + """Search results page.""" # scan for modules search_result = [] + def callback(path, modname, desc): if modname[-9:] == '.__init__': modname = modname[:-9] + ' (package)' search_result.append((modname, desc and '- ' + desc)) + try: import warnings except ImportError: @@ -2550,9 +2547,11 @@ def _url_handler(url, content_type="text else: warnings.filterwarnings('ignore') # ignore problems during import ModuleScanner().run(callback, key) + # format page def bltinlink(name): return '%s' % (name, name) + results = [] heading = html.heading( 'Search Results', @@ -2564,7 +2563,7 @@ def _url_handler(url, content_type="text return html.page('Search Results', contents) def html_getfile(path): - """ Get and display a source file listing safely. """ + """Get and display a source file listing safely.""" path = os.sep + path.replace('%20', ' ') with open(path, 'r') as fp: lines = html.escape(fp.read()) @@ -2577,36 +2576,38 @@ def _url_handler(url, content_type="text return html.page('getfile %s' % path, contents) def html_topics(): - """ Index of topic texts available. """ + """Index of topic texts available.""" + def bltinlink(name): return '%s' % (name, name) + heading = html.heading( - 'INDEX', + 'INDEX', '#ffffff', '#7799ee') names = sorted(Helper.topics.keys()) - def bltinlink(name): - return '%s' % (name, name) + contents = html.multicolumn(names, bltinlink) contents = heading + html.bigsection( 'Topics', '#ffffff', '#ee77aa', contents) return html.page('Topics', contents) def html_keywords(): - """ Index of keywords. """ + """Index of keywords.""" heading = html.heading( - 'INDEX', - '#ffffff', '#7799ee') + 'INDEX', + '#ffffff', '#7799ee') names = sorted(Helper.keywords.keys()) + def bltinlink(name): return '%s' % (name, name) + contents = html.multicolumn(names, bltinlink) contents = heading + html.bigsection( 'Keywords', '#ffffff', '#ee77aa', contents) return html.page('Keywords', contents) def html_topicpage(topic): - """ Topic or keyword help page. """ - import io + """Topic or keyword help page.""" buf = io.StringIO() htmlhelp = Helper(buf, buf) contents, xrefs = htmlhelp._gettopic(topic) @@ -2615,28 +2616,32 @@ def _url_handler(url, content_type="text else: title = 'TOPIC' heading = html.heading( - '%s' % title, - '#ffffff', '#7799ee') + '%s' % title, + '#ffffff', '#7799ee') contents = '

%s
' % contents contents = html.bigsection(topic , '#ffffff','#ee77aa', contents) - xrefs = sorted(xrefs.split()) + xrefs = sorted(xrefs.split()) + def bltinlink(name): return '%s' % (name, name) + xrefs = html.multicolumn(xrefs, bltinlink) - xrefs = html.section('Related help topics: ', '#ffffff', '#ee77aa', xrefs) - return html.page('%s %s' % (title, topic), heading + contents + xrefs) + xrefs = html.section('Related help topics: ', + '#ffffff', '#ee77aa', xrefs) + return html.page('%s %s' % (title, topic), + ''.join((heading, contents, xrefs))) def html_error(url): heading = html.heading( - 'Error', - '#ffffff', '#ee0000') + 'Error', + '#ffffff', '#ee0000') return heading + url def get_html_page(url): - """ Generate an HTML page for url. """ - if url[-5:] == '.html': + """Generate an HTML page for url.""" + if url.endswith('.html'): url = url[:-5] - if url[:1] == '/': + if url.startswith('/'): url = url[1:] if url.startswith("get?key="): url = url[8:] @@ -2655,7 +2660,7 @@ def _url_handler(url, content_type="text try: contents = html_getfile(url) except IOError: - contents = html_error('could not read file %s' % repr(url)) + contents = html_error('could not read file %r' % url) title = 'Read Error' else: obj = None @@ -2669,8 +2674,8 @@ def _url_handler(url, content_type="text elif url in Helper.keywords or url in Helper.topics: contents = html_topicpage(url) else: - contents = html_error('no Python documentation found for %s' - % repr(url)) + contents = html_error( + 'no Python documentation found for %r' % url) title = 'Error' return html.page(title, html_navbar() + contents) @@ -2678,21 +2683,21 @@ def _url_handler(url, content_type="text url = url[1:] if content_type == 'text/css': path_here = os.path.dirname(os.path.realpath(__file__)) - #try: - with open(os.path.join(path_here, url)) as fp: - return ''.join(fp.readlines()) - #except IOError: - # return 'Error: can not open css file ' + url + try: + with open(os.path.join(path_here, url)) as fp: + return ''.join(fp.readlines()) + except IOError: + return 'Error: can not open css file %r' % url elif content_type == 'text/html': return get_html_page(url) - return 'Error: unknown content type ' + content_type + return 'Error: unknown content type %r' % content_type def browse(port=0, *, open_browser=True): - """ Start the enhanced pydoc web server and open a web browser. - - Use port '0' to start the server on an arbitrary port. - Set open_browser to False to suppress opening a browser. + """Start the enhanced pydoc Web server and open a Web browser. + + Use port '0' to start the server on an arbitrary port. + Set open_browser to False to suppress opening a browser. """ import webbrowser serverthread = _start_server(_url_handler, port) @@ -2700,15 +2705,14 @@ def browse(port=0, *, open_browser=True) print(serverthread.error) return if serverthread.serving: - server_help_msg = ( "Server ready at: %s\n" - "Server commands: [b]rowser, [q]uit" - % serverthread.url ) + server_help_msg = 'Server commands: [b]rowser, [q]uit' if open_browser: webbrowser.open(serverthread.url) try: + print('Server ready at', serverthread.url) print(server_help_msg) while serverthread.serving: - cmd = input('server>') + cmd = input('server> ') cmd = cmd.lower() if cmd == 'q': break @@ -2716,6 +2720,8 @@ def browse(port=0, *, open_browser=True) webbrowser.open(serverthread.url) else: print(server_help_msg) + except (KeyboardInterrupt, EOFError): + print() finally: if serverthread.serving: serverthread.stop() @@ -2750,18 +2756,18 @@ def cli(): if opt == '-g': gui() return - if opt == '-b': + if opt == '-b': start_server = True open_browser = True if opt == '-k': apropos(val) return if opt == '-p': - start_server = True + start_server = True port = val if opt == '-w': writing = True - + if start_server == True: if port == None: port = 0 @@ -2787,7 +2793,7 @@ def cli(): print(value) except (getopt.error, BadUsage): - cmd = os.path.basename(sys.argv[0]) + cmd = os.path.splitext(os.path.basename(sys.argv[0]))[0] print("""pydoc - the Python documentation tool {cmd} ... @@ -2806,12 +2812,12 @@ def cli(): number 0 can be used to get an arbitrary unused port. {cmd} -b - Start an HTTP server on an arbitrary unused port and open a web browser + Start an HTTP server on an arbitrary unused port and open a Web browser to interactively browse documentation. The -p option can be used with the -b option to explicitly specify the server port. - + {cmd} -g - The "-g" option is deprecated. + Deprecated. {cmd} -w ... Write out the HTML documentation for a module to a file in the current @@ -2821,4 +2827,3 @@ def cli(): if __name__ == '__main__': cli() - diff --git a/Lib/pydoc_data/_pydoc.css b/Lib/pydoc_data/_pydoc.css --- a/Lib/pydoc_data/_pydoc.css +++ b/Lib/pydoc_data/_pydoc.css @@ -1,6 +1,6 @@ /* CSS file for pydoc. - Contents of this file subject to change without notice. + Contents of this file are subject to change without notice. */ diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -1,15 +1,15 @@ +import os import sys -import os -import os.path import difflib +import inspect +import pydoc +import re +import string import subprocess -import re -import pydoc -import inspect +import test.support +import time import unittest -import test.support import xml.etree -import time from contextlib import contextmanager from test.support import ( TESTFN, forget, rmtree, EnvironmentVarGuard, reap_children) @@ -220,13 +220,14 @@ def get_pydoc_text(module): output = doc.docmodule(module) - # cleanup the extra text formatting that pydoc preforms + # clean up the extra text formatting that pydoc performs patt = re.compile('\b.') output = patt.sub('', output) return output.strip(), loc def print_diffs(text1, text2): "Prints unified diffs for two texts" + # XXX now obsolete, use unittest built-in support lines1 = text1.splitlines(True) lines2 = text2.splitlines(True) diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected', @@ -234,7 +235,7 @@ def print_diffs(text1, text2): print('\n' + ''.join(diffs)) def get_html_title(text): - _, _, text = text.rpartition("") + _, _, text = text.rpartition("<title>") title, _, _ = text.rpartition("") return title @@ -342,59 +343,55 @@ class TestDescriptions(unittest.TestCase doc = pydoc.render_doc(pydocfodder) self.assertIn("pydocfodder", doc) - def test_classic_class(self): - class C: "Classic class" - c = C() - self.assertEqual(pydoc.describe(C), 'class C') - self.assertEqual(pydoc.describe(c), 'C') - expected = 'C in module %s' % __name__ - self.assertIn(expected, pydoc.render_doc(c)) - def test_class(self): - class C(object): "New-style class" + class C: "New-style class" c = C() self.assertEqual(pydoc.describe(C), 'class C') self.assertEqual(pydoc.describe(c), 'C') expected = 'C in module %s object' % __name__ self.assertIn(expected, pydoc.render_doc(c)) - + class PyDocServerTest(unittest.TestCase): - """ Test pydoc._start_server(). """ - + """Tests for pydoc._start_server""" + def test_server(self): + # Minimal test that starts the server, then stops it. def my_url_handler(url, content_type): text = 'the URL sent was: (%s, %s)' % (url, content_type) return text + serverthread = pydoc._start_server(my_url_handler, port=0) starttime = time.time() - timeout = 1 #seconds + timeout = 1 #seconds + while serverthread.serving: time.sleep(.01) if serverthread.serving and time.time() - starttime > timeout: serverthread.stop() break - self.assertEqual(serverthread.error, None) + + self.assertEqual(serverthread.error, None) class PyDocUrlHandlerTest(unittest.TestCase): - """ Test pydoc._url_handler(). """ - + """Tests for pydoc._url_handler""" + def test_content_type_err(self): err = 'Error: unknown content type ' f = pydoc._url_handler result = f("", "") - self.assertEqual(result, err + "") + self.assertEqual(result, err + "''") result = f("", "foobar") - self.assertEqual(result, err + "foobar") - + self.assertEqual(result, err + "'foobar'") + def test_url_requests(self): # Test for the correct title in the html pages returned. # This tests the different parts of the URL handler without # getting too picky about the exact html. - requests = [ + requests = [ ("", "Python: Index of Modules"), ("get?key=", "Python: Index of Modules"), ("index", "Python: Index of Modules"), @@ -408,24 +405,26 @@ class PyDocUrlHandlerTest(unittest.TestC ("foobar", "Python: Error"), ("getfile?key=foobar", "Python: Read Error"), ] - + for url, title in requests: text = pydoc._url_handler(url, "text/html") result = get_html_title(text) self.assertEqual(result, title) - - import string + path = string.__file__ title = "Python: getfile /" + path url = "getfile?key=" + path - text = pydoc._url_handler(url, "text/html") + text = pydoc._url_handler(url, "text/html") result = get_html_title(text) self.assertEqual(result, title) - - + + def test_main(): - test.support.run_unittest( PyDocDocTest, TestDescriptions, - PyDocServerTest, PyDocUrlHandlerTest ) + test.support.run_unittest(PyDocDocTest, + TestDescriptions, + PyDocServerTest, + PyDocUrlHandlerTest, + ) if __name__ == "__main__": test_main() diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -319,6 +319,7 @@ David Goodger Hans de Graaff Eddy De Greef Duncan Grisby +Eric Groo Dag Gruneau Michael Guravage Lars Gustäbel @@ -458,6 +459,7 @@ Lenny Kneler Pat Knight Greg Kochanski Damon Kohler +Vlad Korolev Joseph Koshy Maksim Kozyarchuk Stefan Krah @@ -537,6 +539,7 @@ David Marek Doug Marien Alex Martelli Anthony Martin +Owen Martin Sébastien Martini Roger Masse Nick Mathewson @@ -734,6 +737,7 @@ Michael Scharf Andreas Schawo Neil Schemenauer David Scherer +Bob Schmertz Gregor Schmid Ralf Schmitt Michael Schneider @@ -821,6 +825,7 @@ Anatoly Techtonik Tobias Thelen James Thomas Robin Thomas +Jeremy Thurgood Eric Tiedemann Tracy Tims Oren Tirosh diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,17 @@ What's New in Python 3.2 Beta 1? Core and Builtins ----------------- +- Issue #10518: Bring back the callable() builtin. + +- Issue #8879. Add os.link support for Windows. + +- Issue #10027. st_nlink was not being set on Windows calls to os.stat or + os.lstat. Patch by Hirokazu Yamamoto. + +- Issue #7094: Added alternate formatting (specified by '#') to + __format__ method of float, complex, and Decimal. This allows more + precise control over when decimal points are displayed. + - Issue #10474: range().count() should return integers. - Issue #10255: Fix reference leak in Py_InitializeEx(). Patch by Neil @@ -32,6 +43,18 @@ Core and Builtins Library ------- +- Issue #2001: New HTML server with enhanced Web page features. Patch by Ron + Adam. + +- Issue #10242: Fixed implementation of unittest.ItemsEqual and gave it + a new more informative name, unittest.CountEqual. + +- Issue #2986: difflib.SequenceMatcher gets a new parameter, autojunk, which + can be set to False to turn off the previously undocumented 'popularity' + heuristic. Patch by Terry Reedy and Eli Bendersky. + +- Issue #9846: zipfile is now correctly closing underlying file objects. + - Issue #10459: Update CJK character names to Unicode 6.0. - Issue #4493: urllib.request adds '/' in front of path components which does not @@ -125,6 +148,11 @@ Build - Issue #10325: Fix two issues in the fallback definitions for PY_ULLONG_MAX and PY_LLONG_MAX that made them unsuitable for use in preprocessor conditionals. +Documentation +------------- + +- Issue #10299: List the built-in functions in a table in functions.rst. + What's New in Python 3.2 Alpha 4? ================================= @@ -245,19 +273,6 @@ Library encoding, instead of the locale encoding. Patch written by Alexander Belopolsky. -- Issue #2001: New html server with enhanced web page features. - * A ``-b`` option to start a enhanced browsing session. - * Allow ``-b`` and ``-p`` options to be used together. - * Specifying port 0, will pick an arbitrary unused socket port. - * A new ``browse()`` function to to start the new server and browser. - * Show python version information in the header. - * A *Get* field which takes the same input as the ``help()`` function. - * A *Search* field which replaces the tkinter search box. - * HTML links to *Module Index*, *Topics*, and *Keywords*. - * Improved source file viewing. - * A ``HTMLDoc.filelink()`` method. - * The ``-g`` option, ``gui()``, and ``serve()`` functions are deprecated. - - Issue #10126: Fix distutils' test_build when Python was built with --enable-shared.