diff options
-rw-r--r-- | Doc/library/basehttpserver.rst | 9 | ||||
-rw-r--r-- | Doc/library/difflib.rst | 113 | ||||
-rw-r--r-- | Doc/library/dis.rst | 2 | ||||
-rw-r--r-- | Doc/library/signal.rst | 15 | ||||
-rw-r--r-- | Doc/library/simplexmlrpcserver.rst | 10 | ||||
-rw-r--r-- | Lib/BaseHTTPServer.py | 11 | ||||
-rw-r--r-- | Lib/curses/__init__.py | 4 | ||||
-rw-r--r-- | Lib/test/test_logging.py | 39 | ||||
-rw-r--r-- | Lib/test/test_signal.py | 48 | ||||
-rw-r--r-- | Modules/signalmodule.c | 33 |
10 files changed, 248 insertions, 36 deletions
diff --git a/Doc/library/basehttpserver.rst b/Doc/library/basehttpserver.rst index 0f65b26..9ac3aaf 100644 --- a/Doc/library/basehttpserver.rst +++ b/Doc/library/basehttpserver.rst @@ -122,6 +122,15 @@ to a handler. Code to create and run the server looks like this:: class variable. +.. attribute:: BaseHTTPRequestHandler.error_content_type + + Specifies the Content-Type HTTP header of error responses sent to the client. + The default value is ``'text/html'``. + + .. versionadded:: 2.6 + Previously, the content type was always ``'text/html'``. + + .. attribute:: BaseHTTPRequestHandler.protocol_version This specifies the HTTP protocol version used in responses. If set to diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst index 7e61aa9..66f64e5 100644 --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -144,7 +144,27 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. expressed in the format returned by :func:`time.ctime`. If not specified, the strings default to blanks. - :file:`Tools/scripts/diff.py` is a command-line front-end for this function. + :: + + >>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] + >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] + >>> for line in context_diff(s1, s2, fromfile='before.py', tofile='after.py'): + ... sys.stdout.write(line) + *** before.py + --- after.py + *************** + *** 1,4 **** + ! bacon + ! eggs + ! ham + guido + --- 1,4 ---- + ! python + ! eggy + ! hamster + guido + + See :ref:`difflib-interface` for a more detailed example. .. function:: get_close_matches(word, possibilities[, n][, cutoff]) @@ -259,7 +279,24 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. expressed in the format returned by :func:`time.ctime`. If not specified, the strings default to blanks. - :file:`Tools/scripts/diff.py` is a command-line front-end for this function. + :: + + >>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] + >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] + >>> for line in unified_diff(s1, s2, fromfile='before.py', tofile='after.py'): + ... sys.stdout.write(line) + --- before.py + +++ after.py + @@ -1,4 +1,4 @@ + -bacon + -eggs + -ham + +python + +eggy + +hamster + guido + + See :ref:`difflib-interface` for a more detailed example. .. function:: IS_LINE_JUNK(line) @@ -635,3 +672,75 @@ As a single multi-line string it looks like this:: ? ++++ ^ ^ + 5. Flat is better than nested. + +.. _difflib-interface: + +A command-line interface to difflib +----------------------------------- + +This example shows how to use difflib to create a ``diff``-like utility. +It is also contained in the Python source distribution, as +:file:`Tools/scripts/diff.py`. + +:: + + """ Command line interface to difflib.py providing diffs in four formats: + + * ndiff: lists every line and highlights interline changes. + * context: highlights clusters of changes in a before/after format. + * unified: highlights clusters of changes in an inline format. + * html: generates side by side comparison with change highlights. + + """ + + import sys, os, time, difflib, optparse + + def main(): + # Configure the option parser + usage = "usage: %prog [options] fromfile tofile" + parser = optparse.OptionParser(usage) + parser.add_option("-c", action="store_true", default=False, + help='Produce a context format diff (default)') + parser.add_option("-u", action="store_true", default=False, + help='Produce a unified format diff') + hlp = 'Produce HTML side by side diff (can use -c and -l in conjunction)' + parser.add_option("-m", action="store_true", default=False, help=hlp) + parser.add_option("-n", action="store_true", default=False, + help='Produce a ndiff format diff') + parser.add_option("-l", "--lines", type="int", default=3, + help='Set number of context lines (default 3)') + (options, args) = parser.parse_args() + + if len(args) == 0: + parser.print_help() + sys.exit(1) + if len(args) != 2: + parser.error("need to specify both a fromfile and tofile") + + n = options.lines + fromfile, tofile = args # as specified in the usage string + + # we're passing these as arguments to the diff function + fromdate = time.ctime(os.stat(fromfile).st_mtime) + todate = time.ctime(os.stat(tofile).st_mtime) + fromlines = open(fromfile, 'U').readlines() + tolines = open(tofile, 'U').readlines() + + if options.u: + diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile, + fromdate, todate, n=n) + elif options.n: + diff = difflib.ndiff(fromlines, tolines) + elif options.m: + diff = difflib.HtmlDiff().make_file(fromlines, tolines, fromfile, + tofile, context=options.c, + numlines=n) + else: + diff = difflib.context_diff(fromlines, tolines, fromfile, tofile, + fromdate, todate, n=n) + + # we're using writelines because diff is a generator + sys.stdout.writelines(diff) + + if __name__ == '__main__': + main() diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 78a7d5b..5b09847 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -437,7 +437,7 @@ the more significant byte last. .. opcode:: STORE_NAME (namei) Implements ``name = TOS``. *namei* is the index of *name* in the attribute - :attr:`co_names` of the code object. The compiler tries to use ``STORE_LOCAL`` + :attr:`co_names` of the code object. The compiler tries to use ``STORE_FAST`` or ``STORE_GLOBAL`` if possible. diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst index 920752b..cf6f8f8 100644 --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -124,6 +124,21 @@ The :mod:`signal` module defines the following functions: exception to be raised. + +.. function:: siginterrupt(signalnum, flag) + + Change system call restart behaviour: if *flag* is :const:`False`, system calls + will be restarted when interrupted by signal *signalnum*, else system calls will + be interrupted. Returns nothing. Availability: Unix, Mac (see the man page + :manpage:`siginterrupt(3)` for further information). + + Note that installing a signal handler with :func:`signal` will reset the restart + behaviour to interruptible by implicitly calling siginterrupt with a true *flag* + value for the given signal. + + .. versionadded:: 2.6 + + .. function:: signal(signalnum, handler) Set the handler for signal *signalnum* to the function *handler*. *handler* can diff --git a/Doc/library/simplexmlrpcserver.rst b/Doc/library/simplexmlrpcserver.rst index 557b525..c736e2a 100644 --- a/Doc/library/simplexmlrpcserver.rst +++ b/Doc/library/simplexmlrpcserver.rst @@ -101,7 +101,7 @@ alone XML-RPC servers. Registers the XML-RPC multicall function system.multicall. -.. attribute:: SimpleXMLRPCServer.rpc_paths +.. attribute:: SimpleXMLRPCRequestHandler.rpc_paths An attribute value that must be a tuple listing valid path portions of the URL for receiving XML-RPC requests. Requests posted to other paths will result in a @@ -116,9 +116,15 @@ SimpleXMLRPCServer Example Server code:: from SimpleXMLRPCServer import SimpleXMLRPCServer + from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler + + # Restrict to a particular path. + class RequestHandler(SimpleXMLRPCRequestHandler): + rpc_paths = ('/RPC2',) # Create server - server = SimpleXMLRPCServer(("localhost", 8000)) + server = SimpleXMLRPCServer(("localhost", 8000), + requestHandler=RequestHandler) server.register_introspection_functions() # Register pow() function; this will use the value of diff --git a/Lib/BaseHTTPServer.py b/Lib/BaseHTTPServer.py index f8a1138..fbb8108 100644 --- a/Lib/BaseHTTPServer.py +++ b/Lib/BaseHTTPServer.py @@ -77,7 +77,7 @@ import socket # For gethostbyaddr() import mimetools import SocketServer -# Default error message +# Default error message template DEFAULT_ERROR_MESSAGE = """\ <head> <title>Error response</title> @@ -90,6 +90,8 @@ DEFAULT_ERROR_MESSAGE = """\ </body> """ +DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8" + def _quote_html(html): return html.replace("&", "&").replace("<", "<").replace(">", ">") @@ -217,6 +219,9 @@ class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler): # where each string is of the form name[/version]. server_version = "BaseHTTP/" + __version__ + error_message_format = DEFAULT_ERROR_MESSAGE + error_content_type = DEFAULT_ERROR_CONTENT_TYPE + def parse_request(self): """Parse a request (internal). @@ -356,14 +361,12 @@ class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler): content = (self.error_message_format % {'code': code, 'message': _quote_html(message), 'explain': explain}) self.send_response(code, message) - self.send_header("Content-Type", "text/html;charset=utf-8") + self.send_header("Content-Type", self.error_content_type) self.send_header('Connection', 'close') self.end_headers() if self.command != 'HEAD' and code >= 200 and code not in (204, 304): self.wfile.write(content.encode('UTF-8', 'replace')) - error_message_format = DEFAULT_ERROR_MESSAGE - def send_response(self, code, message=None): """Send the response header and log the response code. diff --git a/Lib/curses/__init__.py b/Lib/curses/__init__.py index aba540b..c3f2f25 100644 --- a/Lib/curses/__init__.py +++ b/Lib/curses/__init__.py @@ -14,6 +14,7 @@ __revision__ = "$Id$" from _curses import * from curses.wrapper import wrapper +import os as _os # Some constants, most notably the ACS_* ones, are only added to the C # _curses module's dictionary after initscr() is called. (Some @@ -25,6 +26,9 @@ from curses.wrapper import wrapper def initscr(): import _curses, curses + # we call setupterm() here because it raises an error + # instead of calling exit() in error cases. + setupterm(term=_os.environ.get("TERM", "unknown")) stdscr = _curses.initscr() for key, value in _curses.__dict__.items(): if key[0:4] == 'ACS_' or key in ('LINES', 'COLS'): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index a31ad37..66aa9fa 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1828,8 +1828,7 @@ Test Main >>> import logging, logging.handlers, logging.config >>> from test import test_logging -XXX: The test is unstable! -#>>> test_logging.test_main_inner() +>>> test_logging.test_main_inner() ERR -> CRITICAL: Message 0 (via logrecv.tcp.ERR) ERR -> ERROR: Message 1 (via logrecv.tcp.ERR) INF -> CRITICAL: Message 2 (via logrecv.tcp.INF) @@ -2010,7 +2009,7 @@ class LogRecordSocketReceiver(ThreadingTCPServer): port=logging.handlers.DEFAULT_TCP_LOGGING_PORT, handler=LogRecordStreamHandler): ThreadingTCPServer.__init__(self, (host, port), handler) - self.abort = 0 + self.abort = False self.timeout = 1 def serve_until_stopped(self): @@ -2019,11 +2018,11 @@ class LogRecordSocketReceiver(ThreadingTCPServer): self.timeout) if rd: self.handle_request() + socketDataProcessed.set() # close the listen socket self.server_close() def process_request(self, request, client_address): - #import threading t = threading.Thread(target = self.finish_request, args = (request, client_address)) t.start() @@ -2108,28 +2107,18 @@ def test_main_inner(): rootLogger = logging.getLogger("") rootLogger.setLevel(logging.DEBUG) - # Find an unused port number - port = logging.handlers.DEFAULT_TCP_LOGGING_PORT - while port < logging.handlers.DEFAULT_TCP_LOGGING_PORT+100: - try: - tcpserver = LogRecordSocketReceiver(port=port) - except socket.error: - port += 1 - else: - break - else: - raise ImportError("Could not find unused port") - - - #Set up a handler such that all events are sent via a socket to the log - #receiver (logrecv). - #The handler will only be added to the rootLogger for some of the tests + tcpserver = LogRecordSocketReceiver(port=0) + port = tcpserver.socket.getsockname()[1] + + # Set up a handler such that all events are sent via a socket to the log + # receiver (logrecv). + # The handler will only be added to the rootLogger for some of the tests shdlr = logging.handlers.SocketHandler('localhost', port) rootLogger.addHandler(shdlr) - #Configure the logger for logrecv so events do not propagate beyond it. - #The sockLogger output is buffered in memory until the end of the test, - #and printed at the end. + # Configure the logger for logrecv so events do not propagate beyond it. + # The sockLogger output is buffered in memory until the end of the test, + # and printed at the end. sockOut = io.StringIO() sockLogger = logging.getLogger("logrecv") sockLogger.setLevel(logging.DEBUG) @@ -2158,9 +2147,9 @@ def test_main_inner(): finally: #wait for TCP receiver to terminate -# socketDataProcessed.wait() + socketDataProcessed.wait() # ensure the server dies - tcpserver.abort = 1 + tcpserver.abort = True for thread in threads: thread.join(2.0) print(sockOut.getvalue()) diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 80a0bb2..03e8101 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -1,7 +1,7 @@ import unittest from test import test_support import signal -import os, sys, time +import os, sys, time, errno class HandlerBCalled(Exception): pass @@ -211,6 +211,50 @@ class WakeupSignalTests(unittest.TestCase): os.close(self.write) signal.signal(signal.SIGALRM, self.alrm) +class SiginterruptTest(unittest.TestCase): + signum = signal.SIGUSR1 + def readpipe_interrupted(self, cb): + r, w = os.pipe() + ppid = os.getpid() + pid = os.fork() + + oldhandler = signal.signal(self.signum, lambda x,y: None) + cb() + if pid==0: + # child code: sleep, kill, sleep. and then exit, + # which closes the pipe from which the parent process reads + try: + time.sleep(0.2) + os.kill(ppid, self.signum) + time.sleep(0.2) + finally: + os._exit(0) + + try: + os.close(w) + + try: + d=os.read(r, 1) + return False + except OSError as err: + if err.errno != errno.EINTR: + raise + return True + finally: + signal.signal(self.signum, oldhandler) + os.waitpid(pid, 0) + + def test_without_siginterrupt(self): + i=self.readpipe_interrupted(lambda: None) + self.assertEquals(i, True) + + def test_siginterrupt_on(self): + i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 1)) + self.assertEquals(i, True) + + def test_siginterrupt_off(self): + i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 0)) + self.assertEquals(i, False) def test_main(): if sys.platform[:3] in ('win', 'os2'): @@ -218,7 +262,7 @@ def test_main(): sys.platform) test_support.run_unittest(BasicSignalTests, InterProcessSignalTests, - WakeupSignalTests) + WakeupSignalTests, SiginterruptTest) if __name__ == "__main__": diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index fbb7b4f..18f1b52 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -272,6 +272,36 @@ SIG_DFL -- if the default action for the signal is in effect\n\ None -- if an unknown handler is in effect\n\ anything else -- the callable Python object used as a handler"); +#ifdef HAVE_SIGINTERRUPT +PyDoc_STRVAR(siginterrupt_doc, +"siginterrupt(sig, flag) -> None\n\ +change system call restart behaviour: if flag is False, system calls\n\ +will be restarted when interrupted by signal sig, else system calls\n\ +will be interrupted."); + +static PyObject * +signal_siginterrupt(PyObject *self, PyObject *args) +{ + int sig_num; + int flag; + + if (!PyArg_ParseTuple(args, "ii:siginterrupt", &sig_num, &flag)) + return NULL; + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, + "signal number out of range"); + return NULL; + } + if (siginterrupt(sig_num, flag)<0) { + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +#endif static PyObject * signal_set_wakeup_fd(PyObject *self, PyObject *args) @@ -325,6 +355,9 @@ static PyMethodDef signal_methods[] = { {"signal", signal_signal, METH_VARARGS, signal_doc}, {"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc}, {"set_wakeup_fd", signal_set_wakeup_fd, METH_VARARGS, set_wakeup_fd_doc}, +#ifdef HAVE_SIGINTERRUPT + {"siginterrupt", signal_siginterrupt, METH_VARARGS, siginterrupt_doc}, +#endif #ifdef HAVE_PAUSE {"pause", (PyCFunction)signal_pause, METH_NOARGS,pause_doc}, |