From 5f8ced2b959f306b3a1747a1f3daa4274b8c78b8 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Fri, 16 May 2008 00:03:33 +0000 Subject: Merged revisions 62998-63003,63005-63006,63009-63012,63014-63017,63019-63020,63022-63024,63026-63029,63031-63041,63043-63045,63047-63054,63056-63062 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r62998 | andrew.kuchling | 2008-05-10 15:51:55 -0400 (Sat, 10 May 2008) | 7 lines #1858 from Tarek Ziade: Allow multiple repositories in .pypirc; see http://wiki.python.org/moin/EnhancedPyPI for discussion. The patch is slightly revised from Tarek's last patch: I've simplified the PyPIRCCommand.finalize_options() method to not look at sys.argv. Tests still pass. ........ r63000 | alexandre.vassalotti | 2008-05-10 15:59:16 -0400 (Sat, 10 May 2008) | 5 lines Cleaned up io._BytesIO.write(). I am amazed that the old code, for inserting null-bytes, actually worked. Who wrote that thing? Oh, it is me... doh. ........ r63002 | brett.cannon | 2008-05-10 16:52:01 -0400 (Sat, 10 May 2008) | 2 lines Revert r62998 as it broke the build (seems distutils.config is missing). ........ r63014 | andrew.kuchling | 2008-05-10 18:12:38 -0400 (Sat, 10 May 2008) | 1 line #1858: add distutils.config module ........ r63027 | brett.cannon | 2008-05-10 21:09:32 -0400 (Sat, 10 May 2008) | 2 lines Flesh out the 3.0 deprecation to suggest using the ctypes module. ........ r63028 | skip.montanaro | 2008-05-10 22:59:30 -0400 (Sat, 10 May 2008) | 4 lines Copied two versions of the example from the interactive session. Delete one. ........ r63037 | georg.brandl | 2008-05-11 03:02:17 -0400 (Sun, 11 May 2008) | 2 lines reload() takes the module itself. ........ r63038 | alexandre.vassalotti | 2008-05-11 03:06:04 -0400 (Sun, 11 May 2008) | 4 lines Added test framework for handling module renames. Factored the import guard in test_py3kwarn.TestStdlibRemovals into a context manager, namely test_support.CleanImport. ........ r63039 | georg.brandl | 2008-05-11 03:06:05 -0400 (Sun, 11 May 2008) | 2 lines #2742: ``''`` is not converted to NULL in getaddrinfo. ........ r63040 | alexandre.vassalotti | 2008-05-11 03:08:12 -0400 (Sun, 11 May 2008) | 2 lines Fixed typo in a comment of test_support.CleanImport. ........ r63041 | alexandre.vassalotti | 2008-05-11 03:10:25 -0400 (Sun, 11 May 2008) | 2 lines Removed a dead line of code. ........ r63043 | georg.brandl | 2008-05-11 04:47:53 -0400 (Sun, 11 May 2008) | 2 lines #2812: document property.getter/setter/deleter. ........ r63049 | georg.brandl | 2008-05-11 05:06:30 -0400 (Sun, 11 May 2008) | 2 lines #1153769: document PEP 237 changes to string formatting. ........ r63050 | georg.brandl | 2008-05-11 05:11:40 -0400 (Sun, 11 May 2008) | 2 lines #2809: elaborate str.split docstring a bit. ........ r63051 | georg.brandl | 2008-05-11 06:13:59 -0400 (Sun, 11 May 2008) | 2 lines Fix typo. ........ r63052 | georg.brandl | 2008-05-11 06:33:27 -0400 (Sun, 11 May 2008) | 2 lines #2709: clarification. ........ r63053 | georg.brandl | 2008-05-11 06:42:28 -0400 (Sun, 11 May 2008) | 2 lines #2659: add ``break_on_hyphens`` to TextWrapper. ........ r63057 | georg.brandl | 2008-05-11 06:59:39 -0400 (Sun, 11 May 2008) | 2 lines #2741: clarification of value range for address_family. ........ r63058 | georg.brandl | 2008-05-11 07:09:35 -0400 (Sun, 11 May 2008) | 2 lines #2452: timeout is used for all blocking operations. ........ r63059 | andrew.kuchling | 2008-05-11 09:33:56 -0400 (Sun, 11 May 2008) | 2 lines #1792: Improve performance of marshal.dumps() on large objects by increasing the size of the buffer more quickly. ........ r63060 | andrew.kuchling | 2008-05-11 10:00:00 -0400 (Sun, 11 May 2008) | 1 line #1858: re-apply patch for this, adding the missing files ........ r63061 | benjamin.peterson | 2008-05-11 10:13:25 -0400 (Sun, 11 May 2008) | 2 lines Add the "until" command to pdb ........ r63062 | georg.brandl | 2008-05-11 10:17:13 -0400 (Sun, 11 May 2008) | 2 lines Add some sentence endings. ........ --- Doc/distutils/packageindex.rst | 31 ++++++++++- Doc/distutils/uploading.rst | 12 +++-- Doc/library/bdb.rst | 5 ++ Doc/library/ftplib.rst | 18 ++++--- Doc/library/functions.rst | 45 +++++++++++++--- Doc/library/httplib.rst | 16 +++--- Doc/library/pdb.rst | 6 +++ Doc/library/smtplib.rst | 30 ++++++----- Doc/library/socket.rst | 10 ++-- Doc/library/socketserver.rst | 4 +- Doc/library/stdtypes.rst | 13 +++-- Doc/library/telnetlib.rst | 16 +++--- Doc/library/tempfile.rst | 5 -- Doc/library/textwrap.rst | 18 ++++++- Doc/library/tk.rst | 3 +- Doc/library/urllib2.rst | 21 ++++---- Doc/tutorial/modules.rst | 2 +- Lib/bdb.py | 33 ++++++------ Lib/distutils/command/register.py | 95 +++++++++++++++++----------------- Lib/distutils/command/upload.py | 41 +++++---------- Lib/distutils/config.py | 4 +- Lib/distutils/core.py | 1 + Lib/distutils/dist.py | 7 ++- Lib/distutils/tests/test_config.py | 103 +++++++++++++++++++++++++++++++++++++ Lib/distutils/tests/test_dist.py | 46 +++++++++++++++++ Lib/distutils/tests/test_upload.py | 34 ++++++++++++ Lib/io.py | 12 ++--- Lib/pdb.doc | 4 ++ Lib/pdb.py | 13 +++++ Lib/test/test_marshal.py | 8 +++ Lib/test/test_support.py | 33 ++++++++++++ Lib/test/test_textwrap.py | 8 +++ Lib/textwrap.py | 28 ++++++++-- Misc/ACKS | 2 + Misc/NEWS | 6 ++- Objects/stringobject.c | 4 +- Objects/unicodeobject.c | 5 +- Python/marshal.c | 5 +- 38 files changed, 557 insertions(+), 190 deletions(-) create mode 100644 Lib/distutils/tests/test_config.py create mode 100644 Lib/distutils/tests/test_upload.py diff --git a/Doc/distutils/packageindex.rst b/Doc/distutils/packageindex.rst index 8242012..ef81d64 100644 --- a/Doc/distutils/packageindex.rst +++ b/Doc/distutils/packageindex.rst @@ -55,11 +55,40 @@ The .pypirc file The format of the :file:`.pypirc` file is as follows:: - [server-login] + [distutils] + index-servers = + pypi + + [pypi] repository: username: password: *repository* can be omitted and defaults to ``http://www.python.org/pypi``. +If you want to define another server a new section can be created:: + + [distutils] + index-servers = + pypi + other + + [pypi] + repository: + username: + password: + + [other] + repository: http://example.com/pypi + username: + password: + +The command can then be called with the -r option:: + + python setup.py register -r http://example.com/pypi + +Or even with the section name:: + + python setup.py register -r other + diff --git a/Doc/distutils/uploading.rst b/Doc/distutils/uploading.rst index 5be4130..d960656 100644 --- a/Doc/distutils/uploading.rst +++ b/Doc/distutils/uploading.rst @@ -22,14 +22,20 @@ The :command:`upload` command uses the username, password, and repository URL from the :file:`$HOME/.pypirc` file (see section :ref:`pypirc` for more on this file). +You can specify another PyPI server with the :option:`--repository=*url*` option:: + + python setup.py sdist bdist_wininst upload -r http://example.com/pypi + +See section :ref:`pypirc` for more on defining several servers. + You can use the :option:`--sign` option to tell :command:`upload` to sign each uploaded file using GPG (GNU Privacy Guard). The :program:`gpg` program must be available for execution on the system :envvar:`PATH`. You can also specify which key to use for signing using the :option:`--identity=*name*` option. -Other :command:`upload` options include :option:`--repository=*url*` (which -lets you override the repository setting from :file:`$HOME/.pypirc`), and +Other :command:`upload` options include :option:`--repository=*url*` +or :option:`--repository=*section*` where `url` is the url of the server +and `section` the name of the section in :file:`$HOME/.pypirc`, and :option:`--show-response` (which displays the full response text from the PyPI server for help in debugging upload problems). - diff --git a/Doc/library/bdb.rst b/Doc/library/bdb.rst index f04b671..fefb2ad 100644 --- a/Doc/library/bdb.rst +++ b/Doc/library/bdb.rst @@ -207,6 +207,11 @@ The :mod:`bdb` module also defines two classes: Stop when returning from the given frame. + .. method:: set_until(frame) + + Stop when the line with the line no greater than the current one is + reached or when returning from current frame + .. method:: set_trace([frame]) Start debugging from *frame*. If *frame* is not specified, debugging diff --git a/Doc/library/ftplib.rst b/Doc/library/ftplib.rst index 41bdcd8..99aae0d 100644 --- a/Doc/library/ftplib.rst +++ b/Doc/library/ftplib.rst @@ -40,11 +40,12 @@ The module defines the following items: .. class:: FTP([host[, user[, passwd[, acct[, timeout]]]]]) Return a new instance of the :class:`FTP` class. When *host* is given, the - method call ``connect(host)`` is made. When *user* is given, additionally the - method call ``login(user, passwd, acct)`` is made (where *passwd* and *acct* - default to the empty string when not given). The optional *timeout* parameter - specifies a timeout in seconds for the connection attempt (if is not specified, - or passed as None, the global default timeout setting will be used). + method call ``connect(host)`` is made. When *user* is given, additionally + the method call ``login(user, passwd, acct)`` is made (where *passwd* and + *acct* default to the empty string when not given). The optional *timeout* + parameter specifies a timeout in seconds for blocking operations like the + connection attempt (if is not specified, or passed as None, the global + default timeout setting will be used). .. attribute:: all_errors @@ -122,9 +123,10 @@ followed by ``lines`` for the text version or ``binary`` for the binary version. made. The optional *timeout* parameter specifies a timeout in seconds for the - connection attempt. If is not specified, or passed as None, the object timeout - is used (the timeout that you passed when instantiating the class); if the - object timeout is also None, the global default timeout setting will be used. + connection attempt. If is not specified, or passed as None, the object + timeout is used (the timeout that you passed when instantiating the class); + if the object timeout is also None, the global default timeout setting will + be used. .. method:: FTP.getwelcome() diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 129aa3c..9ccc59c 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -848,10 +848,15 @@ are always available. They are listed here in alphabetical order. use is to define a managed attribute x:: class C(object): - def __init__(self): self._x = None - def getx(self): return self._x - def setx(self, value): self._x = value - def delx(self): del self._x + def __init__(self): + self._x = None + + def getx(self): + return self._x + def setx(self, value): + self._x = value + def delx(self): + del self._x x = property(getx, setx, delx, "I'm the 'x' property.") If given, *doc* will be the docstring of the property attribute. Otherwise, the @@ -867,8 +872,36 @@ are always available. They are listed here in alphabetical order. """Get the current voltage.""" return self._voltage - turns the :meth:`voltage` method into a "getter" for a read-only attribute with - the same name. + turns the :meth:`voltage` method into a "getter" for a read-only attribute + with the same name. + + A property object has :attr:`getter`, :attr:`setter`, and :attr:`deleter` + methods usable as decorators that create a copy of the property with the + corresponding accessor function set to the decorated function. This is + best explained with an example:: + + class C(object): + def __init__(self): self._x = None + + @property + def x(self): + """I'm the 'x' property.""" + return self._x + + @x.setter + def x(self, value): + self._x = value + + @x.deleter + def x(self): + del self._x + + This code is exactly equivalent to the first example. Be sure to give the + additional functions the same name as the original property (``x`` in this + case.) + + The returned property also has the attributes ``fget``, ``fset``, and + ``fdel`` corresponding to the constructor arguments. .. XXX does accept objects with __index__ too diff --git a/Doc/library/httplib.rst b/Doc/library/httplib.rst index 03fe681..16b74ee 100644 --- a/Doc/library/httplib.rst +++ b/Doc/library/httplib.rst @@ -27,14 +27,14 @@ The module provides the following classes: .. class:: HTTPConnection(host[, port[, strict[, timeout]]]) An :class:`HTTPConnection` instance represents one transaction with an HTTP - server. It should be instantiated passing it a host and optional port number. - If no port number is passed, the port is extracted from the host string if it - has the form ``host:port``, else the default HTTP port (80) is used. When True, - the optional parameter *strict* causes ``BadStatusLine`` to be raised if the - status line can't be parsed as a valid HTTP/1.0 or 1.1 status line. If the - optional *timeout* parameter is given, connection attempts will timeout after - that many seconds (if it is not given or ``None``, the global default timeout - setting is used). + server. It should be instantiated passing it a host and optional port + number. If no port number is passed, the port is extracted from the host + string if it has the form ``host:port``, else the default HTTP port (80) is + used. When True, the optional parameter *strict* causes ``BadStatusLine`` to + be raised if the status line can't be parsed as a valid HTTP/1.0 or 1.1 + status line. If the optional *timeout* parameter is given, blocking + operations (like connection attempts) will timeout after that many seconds + (if it is not given or ``None``, the global default timeout setting is used). For example, the following calls all create instances that connect to the server at the same host and port:: diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index df8cf6c..ba6c1b5 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -261,6 +261,12 @@ n(ext) inside a called function, while ``next`` executes called functions at (nearly) full speed, only stopping at the next line in the current function.) +unt(il) + Continue execution until the line with the the line number greater than the + current one is reached or when returning from current frame. + + .. versionadded:: 2.6 + r(eturn) Continue execution until the current function returns. diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst index d4dd43f..602edb6 100644 --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -19,13 +19,14 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). .. class:: SMTP([host[, port[, local_hostname[, timeout]]]]) - A :class:`SMTP` instance encapsulates an SMTP connection. It has methods that - support a full repertoire of SMTP and ESMTP operations. If the optional host and - port parameters are given, the SMTP :meth:`connect` method is called with those - parameters during initialization. An :exc:`SMTPConnectError` is raised if the - specified host doesn't respond correctly. The optional *timeout* parameter - specifies a timeout in seconds for the connection attempt (if not specified, or - passed as None, the global default timeout setting will be used). + A :class:`SMTP` instance encapsulates an SMTP connection. It has methods + that support a full repertoire of SMTP and ESMTP operations. If the optional + host and port parameters are given, the SMTP :meth:`connect` method is called + with those parameters during initialization. An :exc:`SMTPConnectError` is + raised if the specified host doesn't respond correctly. The optional + *timeout* parameter specifies a timeout in seconds for blocking operations + like the connection attempt (if not specified, or passed as None, the global + default timeout setting will be used). For normal use, you should only require the initialization/connect, :meth:`sendmail`, and :meth:`quit` methods. An example is included below. @@ -35,13 +36,14 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). A :class:`SMTP_SSL` instance behaves exactly the same as instances of :class:`SMTP`. :class:`SMTP_SSL` should be used for situations where SSL is - required from the beginning of the connection and using :meth:`starttls` is not - appropriate. If *host* is not specified, the local host is used. If *port* is - omitted, the standard SMTP-over-SSL port (465) is used. *keyfile* and *certfile* - are also optional, and can contain a PEM formatted private key and certificate - chain file for the SSL connection. The optional *timeout* parameter specifies a - timeout in seconds for the connection attempt (if not specified, or passed as - None, the global default timeout setting will be used). + required from the beginning of the connection and using :meth:`starttls` is + not appropriate. If *host* is not specified, the local host is used. If + *port* is omitted, the standard SMTP-over-SSL port (465) is used. *keyfile* + and *certfile* are also optional, and can contain a PEM formatted private key + and certificate chain file for the SSL connection. The optional *timeout* + parameter specifies a timeout in seconds for blocking operations like the + connection attempt (if not specified, or passed as None, the global default + timeout setting will be used). .. class:: LMTP([host[, port[, local_hostname]]]) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index d7164da..8fefce6 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -213,9 +213,9 @@ The module :mod:`socket` exports the following constants and functions: service name (like ``'http'``), a numeric port number or ``None``. The rest of the arguments are optional and must be numeric if specified. For - *host* and *port*, by passing either an empty string or ``None``, you can pass - ``NULL`` to the C API. The :func:`getaddrinfo` function returns a list of - 5-tuples with the following structure: + *host* and *port*, by passing ``None``, you can pass ``NULL`` to the C API. + The :func:`getaddrinfo` function returns a list of 5-tuples with the following + structure: ``(family, socktype, proto, canonname, sockaddr)`` @@ -785,7 +785,7 @@ sends traffic to the first one connected successfully. :: import socket import sys - HOST = '' # Symbolic name meaning all available interfaces + HOST = None # Symbolic name meaning all available interfaces PORT = 50007 # Arbitrary non-privileged port s = None for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): @@ -847,7 +847,7 @@ sends traffic to the first one connected successfully. :: The last example shows how to write a very simple network sniffer with raw -sockets on Windows. The example requires administrator priviliges to modify +sockets on Windows. The example requires administrator privileges to modify the interface:: import socket diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst index b7ce818..e24b429 100644 --- a/Doc/library/socketserver.rst +++ b/Doc/library/socketserver.rst @@ -155,7 +155,7 @@ Server Objects .. data:: address_family The family of protocols to which the server's socket belongs. - :const:`socket.AF_INET` and :const:`socket.AF_UNIX` are two possible values. + Common examples are :const:`socket.AF_INET` and :const:`socket.AF_UNIX`. .. data:: RequestHandlerClass @@ -199,7 +199,7 @@ The server classes support the following class variables: .. data:: socket_type The type of socket used by the server; :const:`socket.SOCK_STREAM` and - :const:`socket.SOCK_DGRAM` are two possible values. + :const:`socket.SOCK_DGRAM` are two common values. .. data:: timeout diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index ce6aab3..09549ad 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1114,7 +1114,7 @@ The conversion flag characters are: +---------+---------------------------------------------------------------------+ A length modifier (``h``, ``l``, or ``L``) may be present, but is ignored as it -is not necessary for Python. +is not necessary for Python -- so e.g. ``%ld`` is identical to ``%d``. The conversion types are: @@ -1125,13 +1125,13 @@ The conversion types are: +------------+-----------------------------------------------------+-------+ | ``'i'`` | Signed integer decimal. | | +------------+-----------------------------------------------------+-------+ -| ``'o'`` | Unsigned octal. | \(1) | +| ``'o'`` | Signed octal value. | \(1) | +------------+-----------------------------------------------------+-------+ -| ``'u'`` | Unsigned decimal. | | +| ``'u'`` | Obselete type -- it is identical to ``'d'``. | \(7) | +------------+-----------------------------------------------------+-------+ -| ``'x'`` | Unsigned hexadecimal (lowercase). | \(2) | +| ``'x'`` | Signed hexadecimal (lowercase). | \(2) | +------------+-----------------------------------------------------+-------+ -| ``'X'`` | Unsigned hexadecimal (uppercase). | \(2) | +| ``'X'`` | Signed hexadecimal (uppercase). | \(2) | +------------+-----------------------------------------------------+-------+ | ``'e'`` | Floating point exponential format (lowercase). | \(3) | +------------+-----------------------------------------------------+-------+ @@ -1193,6 +1193,9 @@ Notes: The precision determines the maximal number of characters used. +(7) + See :pep:`237`. + Since Python strings have an explicit length, ``%s`` conversions do not assume that ``'\0'`` is the end of the string. diff --git a/Doc/library/telnetlib.rst b/Doc/library/telnetlib.rst index 4c8ce45..8c8ddbb 100644 --- a/Doc/library/telnetlib.rst +++ b/Doc/library/telnetlib.rst @@ -27,11 +27,12 @@ Character), EL (Erase Line), GA (Go Ahead), SB (Subnegotiation Begin). :class:`Telnet` represents a connection to a Telnet server. The instance is initially not connected by default; the :meth:`open` method must be used to - establish a connection. Alternatively, the host name and optional port number - can be passed to the constructor, to, in which case the connection to the server - will be established before the constructor returns. The optional *timeout* - parameter specifies a timeout in seconds for the connection attempt (if not - specified, or passed as None, the global default timeout setting will be used). + establish a connection. Alternatively, the host name and optional port + number can be passed to the constructor, to, in which case the connection to + the server will be established before the constructor returns. The optional + *timeout* parameter specifies a timeout in seconds for blocking operations + like the connection attempt (if not specified, or passed as None, the global + default timeout setting will be used). Do not reopen an already connected instance. @@ -121,8 +122,9 @@ Telnet Objects Connect to a host. The optional second argument is the port number, which defaults to the standard Telnet port (23). The optional *timeout* parameter - specifies a timeout in seconds for the connection attempt (if not specified, or - passed as None, the global default timeout setting will be used). + specifies a timeout in seconds for blocking operations like the connection + attempt (if not specified, or passed as None, the global default timeout + setting will be used). Do not try to reopen an already connected instance. diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst index 25d5acb..69b05ce 100644 --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -156,11 +156,6 @@ The module defines the following user-callable functions: :func:`NamedTemporaryFile`, passing it the `delete=False` parameter:: >>> f = NamedTemporaryFile(delete=False) - >>> print f.name - >>> f.write("Hello World!\n") - >>> f.close() - >>> os.unlink(f.name) - >>> f = NamedTemporaryFile(delete=False) >>> f ', mode 'w+b' at 0x384698> >>> f.name diff --git a/Doc/library/textwrap.rst b/Doc/library/textwrap.rst index 243e43c..a18a801 100644 --- a/Doc/library/textwrap.rst +++ b/Doc/library/textwrap.rst @@ -39,6 +39,10 @@ instance and calling a single method on it. That instance is not reused, so for applications that wrap/fill many text strings, it will be more efficient for you to create your own :class:`TextWrapper` object. +Text is preferably wrapped on whitespaces and right after the hyphens in +hyphenated words; only then will long words be broken if necessary, unless +:attr:`TextWrapper.break_long_words` is set to false. + An additional utility function, :func:`dedent`, is provided to remove indentation from strings that have unwanted whitespace to the left of the text. @@ -167,10 +171,22 @@ indentation from strings that have unwanted whitespace to the left of the text. than :attr:`width`. (Long words will be put on a line by themselves, in order to minimize the amount by which :attr:`width` is exceeded.) + + .. attribute:: break_on_hyphens + + (default: ``True``) If true, wrapping will occur preferably on whitespaces + and right after hyphens in compound words, as it is customary in English. + If false, only whitespaces will be considered as potentially good places + for line breaks, but you need to set :attr:`break_long_words` to false if + you want truly insecable words. Default behaviour in previous versions + was to always allow breaking hyphenated words. + + .. versionadded:: 2.6 + + :class:`TextWrapper` also provides two public methods, analogous to the module-level convenience functions: - .. method:: wrap(text) Wraps the single paragraph in *text* (a string) so every line is at most diff --git a/Doc/library/tk.rst b/Doc/library/tk.rst index 3e2f100..8451f6d 100644 --- a/Doc/library/tk.rst +++ b/Doc/library/tk.rst @@ -23,7 +23,8 @@ mechanism which allows Python and Tcl to interact. :mod:`Tkinter`'s chief virtues are that it is fast, and that it usually comes bundled with Python. Although it has been used to create some very good -applications, including IDLE, it has weak documentation and an outdated look and +applications, including IDLE, its standard documentation is weak (but there +are some good books and tutorials), and it has an outdated look and feel. For more modern, better documented, and much more extensive GUI libraries, see the :ref:`other-gui-packages` section. diff --git a/Doc/library/urllib2.rst b/Doc/library/urllib2.rst index 7987007..143fe50 100644 --- a/Doc/library/urllib2.rst +++ b/Doc/library/urllib2.rst @@ -26,10 +26,10 @@ The :mod:`urllib2` module defines the following functions: :func:`urllib.urlencode` function takes a mapping or sequence of 2-tuples and returns a string in this format. - The optional *timeout* parameter specifies a timeout in seconds for the - connection attempt (if not specified, or passed as None, the global default - timeout setting will be used). This actually only work for HTTP, HTTPS, FTP and - FTPS connections. + The optional *timeout* parameter specifies a timeout in seconds for blocking + operations like the connection attempt (if not specified, or passed as + ``None``, the global default timeout setting will be used). This actually + only works for HTTP, HTTPS, FTP and FTPS connections. This function returns a file-like object with two additional methods: @@ -399,12 +399,13 @@ OpenerDirector Objects .. method:: OpenerDirector.open(url[, data][, timeout]) Open the given *url* (which can be a request object or a string), optionally - passing the given *data*. Arguments, return values and exceptions raised are the - same as those of :func:`urlopen` (which simply calls the :meth:`open` method on - the currently installed global :class:`OpenerDirector`). The optional *timeout* - parameter specifies a timeout in seconds for the connection attempt (if not - specified, or passed as None, the global default timeout setting will be used; - this actually only work for HTTP, HTTPS, FTP and FTPS connections). + passing the given *data*. Arguments, return values and exceptions raised are + the same as those of :func:`urlopen` (which simply calls the :meth:`open` + method on the currently installed global :class:`OpenerDirector`). The + optional *timeout* parameter specifies a timeout in seconds for blocking + operations like the connection attempt (if not specified, or passed as + ``None``, the global default timeout setting will be used; this actually only + works for HTTP, HTTPS, FTP and FTPS connections). .. method:: OpenerDirector.error(proto[, arg[, ...]]) diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst index 4d06df2..68e0258 100644 --- a/Doc/tutorial/modules.rst +++ b/Doc/tutorial/modules.rst @@ -111,7 +111,7 @@ you have already defined. For efficiency reasons, each module is only imported once per interpreter session. Therefore, if you change your modules, you must restart the interpreter -- or, if it's just one module you want to test interactively, - use :func:`reload`, e.g. ``reload('modulename')``. + use :func:`reload`, e.g. ``reload(modulename)``. .. _tut-modulesasscripts: diff --git a/Lib/bdb.py b/Lib/bdb.py index e55031e..b8a20ec 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -37,9 +37,7 @@ class Bdb: import linecache linecache.checkcache() self.botframe = None - self.stopframe = None - self.returnframe = None - self.quitting = 0 + self._set_stopinfo(None, None) def trace_dispatch(self, frame, event, arg): if self.quitting: @@ -100,7 +98,7 @@ class Bdb: # (CT) stopframe may now also be None, see dispatch_call. # (CT) the former test for None is therefore removed from here. if frame is self.stopframe: - return True + return frame.f_lineno >= self.stoplineno while frame is not None and frame is not self.stopframe: if frame is self.botframe: return True @@ -156,26 +154,31 @@ class Bdb: but only if we are to stop at or just below this level.""" pass + def _set_stopinfo(self, stopframe, returnframe, stoplineno=-1): + self.stopframe = stopframe + self.returnframe = returnframe + self.quitting = 0 + self.stoplineno = stoplineno + # Derived classes and clients can call the following methods # to affect the stepping state. + def set_until(self, frame): #the name "until" is borrowed from gdb + """Stop when the line with the line no greater than the current one is + reached or when returning from current frame""" + self._set_stopinfo(frame, frame, frame.f_lineno+1) + def set_step(self): """Stop after one line of code.""" - self.stopframe = None - self.returnframe = None - self.quitting = 0 + self._set_stopinfo(None,None) def set_next(self, frame): """Stop on the next line in or below the given frame.""" - self.stopframe = frame - self.returnframe = None - self.quitting = 0 + self._set_stopinfo(frame, None) def set_return(self, frame): """Stop when returning from the given frame.""" - self.stopframe = frame.f_back - self.returnframe = frame - self.quitting = 0 + self._set_stopinfo(frame.f_back, frame) def set_trace(self, frame=None): """Start debugging from `frame`. @@ -194,9 +197,7 @@ class Bdb: def set_continue(self): # Don't stop except at breakpoints or when finished - self.stopframe = self.botframe - self.returnframe = None - self.quitting = 0 + self._set_stopinfo(self.botframe, None) if not self.breaks: # no breakpoints; run without debugger overhead sys.settrace(None) diff --git a/Lib/distutils/command/register.py b/Lib/distutils/command/register.py index b6a36f5..89cb2d4 100644 --- a/Lib/distutils/command/register.py +++ b/Lib/distutils/command/register.py @@ -8,42 +8,34 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" import os, string, urllib2, getpass, urlparse -import io, configparser +import io -from distutils.core import Command +from distutils.core import PyPIRCCommand from distutils.errors import * +from distutils import log def raw_input(prompt): sys.stdout.write(prompt) sys.stdout.flush() return sys.stdin.readline() -class register(Command): +class register(PyPIRCCommand): description = ("register the distribution with the Python package index") - - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]"%DEFAULT_REPOSITORY), + user_options = PyPIRCCommand.user_options + [ ('list-classifiers', None, 'list the valid Trove classifiers'), - ('show-response', None, - 'display full response text from server'), ] - boolean_options = ['verify', 'show-response', 'list-classifiers'] + boolean_options = PyPIRCCommand.boolean_options + [ + 'verify', 'list-classifiers'] def initialize_options(self): - self.repository = None - self.show_response = 0 + PyPIRCCommand.initialize_options(self) self.list_classifiers = 0 - def finalize_options(self): - if self.repository is None: - self.repository = self.DEFAULT_REPOSITORY - def run(self): + self.finalize_options() + self._set_config() self.check_metadata() if self.dry_run: self.verify_metadata() @@ -82,6 +74,23 @@ class register(Command): "or (maintainer and maintainer_email) " + "must be supplied") + def _set_config(self): + ''' Reads the configuration file and set attributes. + ''' + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + self.has_config = True + else: + if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): + raise ValueError('%s not found in .pypirc' % self.repository) + if self.repository == 'pypi': + self.repository = self.DEFAULT_REPOSITORY + self.has_config = False + def classifiers(self): ''' Fetch the list of classifiers from the server. ''' @@ -95,6 +104,7 @@ class register(Command): (code, result) = self.post_to_server(self.build_post_data('verify')) print('Server response (%s): %s'%(code, result)) + def send_metadata(self): ''' Send the metadata to the package index server. @@ -104,10 +114,14 @@ class register(Command): First we try to read the username/password from $HOME/.pypirc, which is a ConfigParser-formatted file with a section - [server-login] containing username and password entries (both + [distutils] containing username and password entries (both in clear text). Eg: - [server-login] + [distutils] + index-servers = + pypi + + [pypi] username: fred password: sekrit @@ -119,21 +133,15 @@ class register(Command): 3. set the password to a random string and email the user. ''' - choice = 'x' - username = password = '' - # see if we can short-cut and get the username/password from the # config - config = None - if 'HOME' in os.environ: - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - print('Using PyPI login from %s'%rc) - config = ConfigParser.ConfigParser() - config.read(rc) - username = config.get('server-login', 'username') - password = config.get('server-login', 'password') - choice = '1' + if self.has_config: + choice = '1' + username = self.username + password = self.password + else: + choice = 'x' + username = password = '' # get the user's login info choices = '1 2 3 4'.split() @@ -160,32 +168,24 @@ Your selection [default 1]: ''', end=' ') # set up the authentication auth = urllib2.HTTPPasswordMgr() host = urlparse.urlparse(self.repository)[1] - auth.add_password('pypi', host, username, password) - + auth.add_password(self.realm, host, username, password) # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) print('Server response (%s): %s'%(code, result)) # possibly save the login - if 'HOME' in os.environ and config is None and code == 200: - rc = os.path.join(os.environ['HOME'], '.pypirc') + if not self.has_config and code == 200: print('I can store your PyPI login so future submissions will be faster.') - print('(the login will be stored in %s)'%rc) + print('(the login will be stored in %s)' % self._get_rc_file()) choice = 'X' while choice.lower() not in 'yn': choice = raw_input('Save your login (y/N)?') if not choice: choice = 'n' if choice.lower() == 'y': - f = open(rc, 'w') - f.write('[server-login]\nusername:%s\npassword:%s\n'%( - username, password)) - f.close() - try: - os.chmod(rc, 0o600) - except: - pass + self._store_pypirc(username, password) + elif choice == '2': data = {':action': 'user'} data['name'] = data['password'] = data['email'] = '' @@ -248,7 +248,8 @@ Your selection [default 1]: ''', end=' ') def post_to_server(self, data, auth=None): ''' Post a query to the server, and return a string response. ''' - + self.announce('Registering %s to %s' % (data['name'], + self.repository), log.INFO) # Build up the MIME payload for the urllib2 POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py index 23999ae..603336c 100644 --- a/Lib/distutils/command/upload.py +++ b/Lib/distutils/command/upload.py @@ -3,7 +3,7 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" from distutils.errors import * -from distutils.core import Command +from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log from hashlib import md5 @@ -15,53 +15,38 @@ import httplib import base64 import urlparse -class upload(Command): +class upload(PyPIRCCommand): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server'), + user_options = PyPIRCCommand.user_options + [ ('sign', 's', 'sign files to upload using gpg'), ('identity=', 'i', 'GPG identity used to sign files'), ] - boolean_options = ['show-response', 'sign'] + + boolean_options = PyPIRCCommand.boolean_options + ['sign'] def initialize_options(self): + PyPIRCCommand.initialize_options(self) self.username = '' self.password = '' - self.repository = '' self.show_response = 0 self.sign = False self.identity = None def finalize_options(self): + PyPIRCCommand.finalize_options(self) if self.identity and not self.sign: raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) - if 'HOME' in os.environ: - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - self.announce('Using PyPI login from %s' % rc) - config = ConfigParser.ConfigParser({ - 'username':'', - 'password':'', - 'repository':''}) - config.read(rc) - if not self.repository: - self.repository = config.get('server-login', 'repository') - if not self.username: - self.username = config.get('server-login', 'username') - if not self.password: - self.password = config.get('server-login', 'password') - if not self.repository: - self.repository = self.DEFAULT_REPOSITORY + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] def run(self): if not self.distribution.dist_files: diff --git a/Lib/distutils/config.py b/Lib/distutils/config.py index e9ba402..a625aec 100644 --- a/Lib/distutils/config.py +++ b/Lib/distutils/config.py @@ -49,7 +49,7 @@ class PyPIRCCommand(Command): finally: f.close() try: - os.chmod(rc, 0600) + os.chmod(rc, 0o600) except OSError: # should do something better here pass @@ -58,7 +58,7 @@ class PyPIRCCommand(Command): """Reads the .pypirc file.""" rc = self._get_rc_file() if os.path.exists(rc): - print 'Using PyPI login from %s' % rc + print('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY realm = self.realm or self.DEFAULT_REALM diff --git a/Lib/distutils/core.py b/Lib/distutils/core.py index a4c5e18..6e48920 100644 --- a/Lib/distutils/core.py +++ b/Lib/distutils/core.py @@ -17,6 +17,7 @@ from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. from distutils.dist import Distribution from distutils.cmd import Command +from distutils.config import PyPIRCCommand from distutils.extension import Extension # This is a barebones help message generated displayed when the user diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py index 847eb90..ddde909 100644 --- a/Lib/distutils/dist.py +++ b/Lib/distutils/dist.py @@ -334,10 +334,9 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - if 'HOME' in os.environ: - user_file = os.path.join(os.environ.get('HOME'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" diff --git a/Lib/distutils/tests/test_config.py b/Lib/distutils/tests/test_config.py new file mode 100644 index 0000000..016ba4c --- /dev/null +++ b/Lib/distutils/tests/test_config.py @@ -0,0 +1,103 @@ +"""Tests for distutils.pypirc.pypirc.""" +import sys +import os +import unittest + +from distutils.core import PyPIRCCommand +from distutils.core import Distribution + +from distutils.tests import support + +PYPIRC = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:secret + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + +PYPIRC_OLD = """\ +[server-login] +username:tarek +password:secret +""" + +class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): + + def setUp(self): + """Patches the environment.""" + if 'HOME' in os.environ: + self._old_home = os.environ['HOME'] + else: + self._old_home = None + curdir = os.path.dirname(__file__) + os.environ['HOME'] = curdir + self.rc = os.path.join(curdir, '.pypirc') + self.dist = Distribution() + + class command(PyPIRCCommand): + def __init__(self, dist): + PyPIRCCommand.__init__(self, dist) + def initialize_options(self): + pass + finalize_options = initialize_options + + self._cmd = command + + def tearDown(self): + """Removes the patch.""" + if self._old_home is None: + del os.environ['HOME'] + else: + os.environ['HOME'] = self._old_home + if os.path.exists(self.rc): + os.remove(self.rc) + + def test_server_registration(self): + # This test makes sure PyPIRCCommand knows how to: + # 1. handle several sections in .pypirc + # 2. handle the old format + + # new format + f = open(self.rc, 'w') + try: + f.write(PYPIRC) + finally: + f.close() + + cmd = self._cmd(self.dist) + config = cmd._read_pypirc() + + config = list(sorted(config.items())) + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server1'), ('username', 'me')] + self.assertEquals(config, waited) + + # old format + f = open(self.rc, 'w') + f.write(PYPIRC_OLD) + f.close() + + config = cmd._read_pypirc() + config = list(sorted(config.items())) + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server-login'), ('username', 'tarek')] + self.assertEquals(config, waited) + +def test_suite(): + return unittest.makeSuite(PyPIRCCommandTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/Lib/distutils/tests/test_dist.py b/Lib/distutils/tests/test_dist.py index 91acf45..81459ac 100644 --- a/Lib/distutils/tests/test_dist.py +++ b/Lib/distutils/tests/test_dist.py @@ -55,6 +55,7 @@ class DistributionTestCase(unittest.TestCase): self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_command_packages_cmdline(self): + from distutils.tests.test_dist import test_dist sys.argv.extend(["--command-packages", "foo.bar,distutils.tests", "test_dist", @@ -179,9 +180,54 @@ class MetadataTestCase(unittest.TestCase): dist.metadata.write_pkg_file(sio) return sio.getvalue() + def test_custom_pydistutils(self): + # fixes #2166 + # make sure pydistutils.cfg is found + old = {} + for env in ('HOME', 'HOMEPATH', 'HOMEDRIVE'): + value = os.environ.get(env) + old[env] = value + if value is not None: + del os.environ[env] + + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + + curdir = os.path.dirname(__file__) + user_filename = os.path.join(curdir, user_filename) + f = open(user_filename, 'w') + f.write('.') + f.close() + + try: + dist = distutils.dist.Distribution() + + # linux-style + if sys.platform in ('linux', 'darwin'): + os.environ['HOME'] = curdir + files = dist.find_config_files() + self.assert_(user_filename in files) + + # win32-style + if sys.platform == 'win32': + # home drive should be found + os.environ['HOMEPATH'] = curdir + files = dist.find_config_files() + self.assert_(user_filename in files) + finally: + for key, value in old.items(): + if value is None: + continue + os.environ[key] = value + os.remove(user_filename) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) suite.addTest(unittest.makeSuite(MetadataTestCase)) return suite + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py new file mode 100644 index 0000000..b05ab1f --- /dev/null +++ b/Lib/distutils/tests/test_upload.py @@ -0,0 +1,34 @@ +"""Tests for distutils.command.upload.""" +import sys +import os +import unittest + +from distutils.command.upload import upload +from distutils.core import Distribution + +from distutils.tests import support +from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase + +class uploadTestCase(PyPIRCCommandTestCase): + + def test_finalize_options(self): + + # new format + f = open(self.rc, 'w') + f.write(PYPIRC) + f.close() + + dist = Distribution() + cmd = upload(dist) + cmd.finalize_options() + for attr, waited in (('username', 'me'), ('password', 'secret'), + ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi')): + self.assertEquals(getattr(cmd, attr), waited) + + +def test_suite(): + return unittest.makeSuite(uploadTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/Lib/io.py b/Lib/io.py index d5c8dfe..207e428 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -813,14 +813,14 @@ class _BytesIO(BufferedIOBase): n = len(b) if n == 0: return 0 - newpos = self._pos + n - if newpos > len(self._buffer): + pos = self._pos + if pos > len(self._buffer): # Inserts null bytes between the current end of the file # and the new write position. - padding = b'\x00' * (newpos - len(self._buffer) - n) - self._buffer[self._pos:newpos - n] = padding - self._buffer[self._pos:newpos] = b - self._pos = newpos + padding = b'\x00' * (pos - len(self._buffer)) + self._buffer += padding + self._buffer[pos:pos + n] = b + self._pos += n return n def seek(self, pos, whence=0): diff --git a/Lib/pdb.doc b/Lib/pdb.doc index c513954..0d32800 100644 --- a/Lib/pdb.doc +++ b/Lib/pdb.doc @@ -128,6 +128,10 @@ n(ext) Continue execution until the next line in the current function is reached or it returns. +unt(il) + Continue execution until the line with a number greater than the + current one is reached or until the current frame returns. + r(eturn) Continue execution until the current function returns. diff --git a/Lib/pdb.py b/Lib/pdb.py index d77ea28..7e1edb5 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -611,6 +611,11 @@ class Pdb(bdb.Bdb, cmd.Cmd): self.lineno = None do_d = do_down + def do_until(self, arg): + self.set_until(self.curframe) + return 1 + do_unt = do_until + def do_step(self, arg): self.set_step() return 1 @@ -958,6 +963,14 @@ i.e., the breakpoint is made unconditional.""", file=self.stdout) Execute the current line, stop at the first possible occasion (either in a function that is called or in the current function).""", file=self.stdout) + def help_until(self): + self.help_unt() + + def help_unt(self): + print("""unt(il) +Continue execution until the line with a number greater than the current +one is reached or until the current frame returns""") + def help_next(self): self.help_n() diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 66545e0..838207a 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -199,6 +199,14 @@ class BugsTestCase(unittest.TestCase): subtyp = type('subtyp', (typ,), {}) self.assertRaises(ValueError, marshal.dumps, subtyp()) + # Issue #1792 introduced a change in how marshal increases the size of its + # internal buffer; this test ensures that the new code is exercised. + def test_large_marshal(self): + size = int(1e6) + testString = 'abc' * size + marshal.dumps(testString) + + def test_main(): test_support.run_unittest(IntTestCase, FloatTestCase, diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 27a01ea..c4084bb 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -409,6 +409,39 @@ def catch_warning(module=warnings, record=True): module.showwarning = original_showwarning module.filters = original_filters + +class CleanImport(object): + """Context manager to force import to return a new module reference. + + This is useful for testing module-level behaviours, such as + the emission of a DepreciationWarning on import. + + Use like this: + + with CleanImport("foo"): + __import__("foo") # new reference + """ + + def __init__(self, *module_names): + self.original_modules = sys.modules.copy() + for module_name in module_names: + if module_name in sys.modules: + module = sys.modules[module_name] + # It is possible that module_name is just an alias for + # another module (e.g. stub for modules renamed in 3.x). + # In that case, we also need delete the real module to clear + # the import cache. + if module.__name__ != module_name: + del sys.modules[module.__name__] + del sys.modules[module_name] + + def __enter__(self): + return self + + def __exit__(self, *ignore_exc): + sys.modules.update(self.original_modules) + + class EnvironmentVarGuard(object): """Class to help protect the environment variable properly. Can be used as diff --git a/Lib/test/test_textwrap.py b/Lib/test/test_textwrap.py index dc97d40..1a9761a 100644 --- a/Lib/test/test_textwrap.py +++ b/Lib/test/test_textwrap.py @@ -351,6 +351,14 @@ What a mess! ["Hello", " ", "there", " ", "--", " ", "you", " ", "goof-", "ball,", " ", "use", " ", "the", " ", "-b", " ", "option!"]) + def test_break_on_hyphens(self): + # Ensure that the break_on_hyphens attributes work + text = "yaba daba-doo" + self.check_wrap(text, 10, ["yaba daba-", "doo"], + break_on_hyphens=True) + self.check_wrap(text, 10, ["yaba", "daba-doo"], + break_on_hyphens=False) + def test_bad_width(self): # Ensure that width <= 0 is caught. text = "Whatever, it doesn't matter." diff --git a/Lib/textwrap.py b/Lib/textwrap.py index b5f87efc..6a2021d 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -55,6 +55,10 @@ class TextWrapper: break_long_words (default: true) Break words longer than 'width'. If false, those words will not be broken, and some lines might be longer than 'width'. + break_on_hyphens (default: true) + Allow breaking hyphenated words. If true, wrapping will occur + preferably on whitespaces and right after hyphens part of + compound words. drop_whitespace (default: true) Drop leading and trailing whitespace from lines. """ @@ -75,11 +79,18 @@ class TextWrapper: r'[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|' # hyphenated words r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash - # XXX this is not locale-aware + # This less funky little regex just split on recognized spaces. E.g. + # "Hello there -- you goof-ball, use the -b option!" + # splits into + # Hello/ /there/ /--/ /you/ /goof-ball,/ /use/ /the/ /-b/ /option!/ + wordsep_simple_re = re.compile(r'(\s+)') + + # XXX this is not locale- or charset-aware -- string.lowercase + # is US-ASCII only (and therefore English-only) sentence_end_re = re.compile(r'[a-z]' # lowercase letter r'[\.\!\?]' # sentence-ending punct. r'[\"\']?' # optional end-of-quote - r'\Z') # end of chunk + r'\Z') # end of chunk def __init__(self, @@ -90,7 +101,8 @@ class TextWrapper: replace_whitespace=True, fix_sentence_endings=False, break_long_words=True, - drop_whitespace=True): + drop_whitespace=True, + break_on_hyphens=True): self.width = width self.initial_indent = initial_indent self.subsequent_indent = subsequent_indent @@ -99,6 +111,7 @@ class TextWrapper: self.fix_sentence_endings = fix_sentence_endings self.break_long_words = break_long_words self.drop_whitespace = drop_whitespace + self.break_on_hyphens = break_on_hyphens # -- Private methods ----------------------------------------------- @@ -128,8 +141,15 @@ class TextWrapper: breaks into the following chunks: 'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ', 'use', ' ', 'the', ' ', '-b', ' ', 'option!' + if break_on_hyphens is True, or in: + 'Look,', ' ', 'goof-ball', ' ', '--', ' ', + 'use', ' ', 'the', ' ', '-b', ' ', option!' + otherwise. """ - chunks = self.wordsep_re.split(text) + if self.break_on_hyphens is True: + chunks = self.wordsep_re.split(text) + else: + chunks = self.wordsep_simple_re.split(text) chunks = [c for c in chunks if c] return chunks diff --git a/Misc/ACKS b/Misc/ACKS index 4aa0101..19210a0 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -588,6 +588,7 @@ Nick Russo Hajime Saitou Rich Salz Kevin Samborn +Ilya Sandler Ty Sarna Ben Sayer Michael Scharf @@ -757,3 +758,4 @@ Siebren van der Zee Uwe Zessin Amaury Forgeot d'Arc Peter Åstrand +Tarek ZiadŽ diff --git a/Misc/NEWS b/Misc/NEWS index 31b2678..3208a69 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -28,13 +28,15 @@ Extension Modules Library ------- -- The ConfigParser module has been renamed to configparser. +- #2659: Added ``break_on_hyphens`` option to textwrap TextWrapper class. - Issue #2487: change the semantics of math.ldexp(x, n) when n is too large to fit in a C long. ldexp(x, n) now returns a zero (with suitable sign) if n is large and negative; previously, it raised OverflowError. +- The ConfigParser module has been renamed to configparser. + - Issue 2865: webbrowser.open() works again in a KDE environment. - The multifile module has been removed. @@ -71,6 +73,8 @@ Library - os.path.walk has been removed in favor of os.walk +- pdb gained the "until" command. + Build ----- diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 3aee28e..0cc48c3 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -1134,8 +1134,8 @@ PyDoc_STRVAR(split__doc__, "B.split([sep[, maxsplit]]) -> list of bytes\n\ \n\ Return a list of the sections in B, using sep as the delimiter.\n\ -If sep is not given, B is split on ASCII whitespace characters\n\ -(space, tab, return, newline, formfeed, vertical tab).\n\ +If sep is not specified or is None, B is split on ASCII whitespace\n\ +characters (space, tab, return, newline, formfeed, vertical tab).\n\ If maxsplit is given, at most maxsplit splits are done."); static PyObject * diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 08e832f..9ea9a88 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7729,8 +7729,9 @@ PyDoc_STRVAR(split__doc__, \n\ Return a list of the words in S, using sep as the\n\ delimiter string. If maxsplit is given, at most maxsplit\n\ -splits are done. If sep is not specified or is None,\n\ -any whitespace string is a separator."); +splits are done. If sep is not specified or is None, any\n\ +whitespace string is a separator and leading and trailing\n\ +whitespace is stripped before splitting."); static PyObject* unicode_split(PyUnicodeObject *self, PyObject *args) diff --git a/Python/marshal.c b/Python/marshal.c index 22f84a8..ce828da 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -68,7 +68,10 @@ w_more(int c, WFILE *p) if (p->str == NULL) return; /* An error already occurred */ size = PyString_Size(p->str); - newsize = size + 1024; + newsize = size + size + 1024; + if (newsize > 32*1024*1024) { + newsize = size + 1024*1024; + } if (_PyString_Resize(&p->str, newsize) != 0) { p->ptr = p->end = NULL; } -- cgit v0.12