summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/collections.rst64
-rw-r--r--Doc/library/site.rst13
-rw-r--r--Doc/library/stdtypes.rst86
-rw-r--r--Doc/reference/datamodel.rst11
-rw-r--r--Doc/using/cmdline.rst4
-rw-r--r--Doc/whatsnew/3.3.rst5
-rw-r--r--Lib/collections/__init__.py136
-rw-r--r--Lib/email/quoprimime.py6
-rw-r--r--Lib/getpass.py2
-rw-r--r--Lib/importlib/_bootstrap.py3
-rw-r--r--Lib/importlib/test/regrtest.py7
-rw-r--r--Lib/random.py6
-rw-r--r--Lib/site.py10
-rwxr-xr-xLib/test/regrtest.py28
-rw-r--r--Lib/test/support.py5
-rw-r--r--Lib/test/test_collections.py35
-rw-r--r--Lib/test/test_concurrent_futures.py55
-rw-r--r--Lib/test/test_email/test_email.py207
-rw-r--r--Lib/test/test_imaplib.py28
-rw-r--r--Lib/test/test_multiprocessing.py2
-rw-r--r--Lib/test/test_peepholer.py11
-rw-r--r--Lib/urllib/request.py10
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS21
-rw-r--r--Misc/python.man4
-rw-r--r--Objects/setobject.c7
-rw-r--r--Python/import.c27
-rw-r--r--Python/peephole.c6
28 files changed, 574 insertions, 226 deletions
diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
index 2cd8c21..e531d40 100644
--- a/Doc/library/collections.rst
+++ b/Doc/library/collections.rst
@@ -661,7 +661,7 @@ Setting the :attr:`default_factory` to :class:`set` makes the
... d[k].add(v)
...
>>> list(d.items())
- [('blue', set([2, 4])), ('red', set([1, 3]))]
+ [('blue', {2, 4}), ('red', {1, 3})]
:func:`namedtuple` Factory Function for Tuples with Named Fields
@@ -694,7 +694,9 @@ they add the ability to access fields by name instead of position index.
converted to ``['abc', '_1', 'ghi', '_3']``, eliminating the keyword
``def`` and the duplicate fieldname ``abc``.
- If *verbose* is true, the class definition is printed just before being built.
+ If *verbose* is true, the class definition is printed after it is
+ built. This option is outdated; instead, it is simpler to print the
+ :attr:`_source` attribute.
Named tuple instances do not have per-instance dictionaries, so they are
lightweight and require no more memory than regular tuples.
@@ -708,51 +710,6 @@ they add the ability to access fields by name instead of position index.
>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
- >>> p = Point(x=10, y=11)
-
- >>> # Example using the verbose option to print the class definition
- >>> Point = namedtuple('Point', 'x y', verbose=True)
- class Point(tuple):
- 'Point(x, y)'
- <BLANKLINE>
- __slots__ = ()
- <BLANKLINE>
- _fields = ('x', 'y')
- <BLANKLINE>
- def __new__(_cls, x, y):
- 'Create a new instance of Point(x, y)'
- return _tuple.__new__(_cls, (x, y))
- <BLANKLINE>
- @classmethod
- def _make(cls, iterable, new=tuple.__new__, len=len):
- 'Make a new Point object from a sequence or iterable'
- result = new(cls, iterable)
- if len(result) != 2:
- raise TypeError('Expected 2 arguments, got %d' % len(result))
- return result
- <BLANKLINE>
- def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + '(x=%r, y=%r)' % self
- <BLANKLINE>
- def _asdict(self):
- 'Return a new OrderedDict which maps field names to their values'
- return OrderedDict(zip(self._fields, self))
- <BLANKLINE>
- def _replace(_self, **kwds):
- 'Return a new Point object replacing specified fields with new values'
- result = _self._make(map(kwds.pop, ('x', 'y'), _self))
- if kwds:
- raise ValueError('Got unexpected field names: %r' % list(kwds.keys()))
- return result
- <BLANKLINE>
- def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return tuple(self)
- <BLANKLINE>
- x = _property(_itemgetter(0), doc='Alias for field number 0')
- y = _property(_itemgetter(1), doc='Alias for field number 1')
-
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)
33
@@ -781,7 +738,7 @@ by the :mod:`csv` or :mod:`sqlite3` modules::
print(emp.name, emp.title)
In addition to the methods inherited from tuples, named tuples support
-three additional methods and one attribute. To prevent conflicts with
+three additional methods and two attributes. To prevent conflicts with
field names, the method and attribute names start with an underscore.
.. classmethod:: somenamedtuple._make(iterable)
@@ -819,6 +776,15 @@ field names, the method and attribute names start with an underscore.
>>> for partnum, record in inventory.items():
... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
+.. attribute:: somenamedtuple._source
+
+ A string with the pure Python source code used to create the named
+ tuple class. The source makes the named tuple self-documenting.
+ It can be printed, executed using :func:`exec`, or saved to a file
+ and imported.
+
+ .. versionadded:: 3.3
+
.. attribute:: somenamedtuple._fields
Tuple of strings listing the field names. Useful for introspection
@@ -867,7 +833,6 @@ a fixed-width print format:
The subclass shown above sets ``__slots__`` to an empty tuple. This helps
keep memory requirements low by preventing the creation of instance dictionaries.
-
Subclassing is not useful for adding new, stored fields. Instead, simply
create a new named tuple type from the :attr:`_fields` attribute:
@@ -879,6 +844,7 @@ customize a prototype instance:
>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account = Account('<owner name>', 0.0, 0)
>>> johns_account = default_account._replace(owner='John')
+ >>> janes_account = default_account._replace(owner='Jane')
Enumerated constants can be implemented with named tuples, but it is simpler
and more efficient to use a simple class declaration:
diff --git a/Doc/library/site.rst b/Doc/library/site.rst
index b77f3cf..4b7a234 100644
--- a/Doc/library/site.rst
+++ b/Doc/library/site.rst
@@ -13,7 +13,11 @@ import can be suppressed using the interpreter's :option:`-S` option.
.. index:: triple: module; search; path
-Importing this module will append site-specific paths to the module search path.
+Importing this module will append site-specific paths to the module search
+path, unless :option:`-S` was used. In that case, this module can be safely
+imported with no automatic modifications to the module search path. To
+explicitly trigger the usual site-specific additions, call the
+:func:`site.main` function.
.. index::
pair: site-python; directory
@@ -114,6 +118,13 @@ empty, and the path manipulations are skipped; however the import of
.. envvar:: PYTHONUSERBASE
+.. function:: main()
+
+ Adds all the standard site-specific directories to the module search
+ path. This function is called automatically when this module is imported,
+ unless the :program:`python` interpreter was started with the :option:`-S`
+ flag.
+
.. function:: addsitedir(sitedir, known_paths=None)
Adds a directory to sys.path and processes its pth files.
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 6fab5c6..2415e39 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -268,46 +268,46 @@ All numeric types (except complex) support the following operations, sorted by
ascending priority (operations in the same box have the same priority; all
numeric operations have a higher priority than comparison operations):
-+---------------------+---------------------------------+-------+--------------------+
-| Operation | Result | Notes | Full documentation |
-+=====================+=================================+=======+====================+
-| ``x + y`` | sum of *x* and *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x - y`` | difference of *x* and *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x * y`` | product of *x* and *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x / y`` | quotient of *x* and *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x // y`` | floored quotient of *x* and | \(1) | |
-| | *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x % y`` | remainder of ``x / y`` | \(2) | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``-x`` | *x* negated | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``+x`` | *x* unchanged | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``abs(x)`` | absolute value or magnitude of | | :func:`abs` |
-| | *x* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``int(x)`` | *x* converted to integer | \(3) | :func:`int` |
-+---------------------+---------------------------------+-------+--------------------+
-| ``float(x)`` | *x* converted to floating point | \(4) | :func:`float` |
-+---------------------+---------------------------------+-------+--------------------+
-| ``complex(re, im)`` | a complex number with real part | | :func:`complex` |
-| | *re*, imaginary part *im*. | | |
-| | *im* defaults to zero. | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``c.conjugate()`` | conjugate of the complex number | | |
-| | *c* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``divmod(x, y)`` | the pair ``(x // y, x % y)`` | \(2) | :func:`divmod` |
-+---------------------+---------------------------------+-------+--------------------+
-| ``pow(x, y)`` | *x* to the power *y* | \(5) | :func:`pow` |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x ** y`` | *x* to the power *y* | \(5) | |
-+---------------------+---------------------------------+-------+--------------------+
++---------------------+---------------------------------+---------+--------------------+
+| Operation | Result | Notes | Full documentation |
++=====================+=================================+=========+====================+
+| ``x + y`` | sum of *x* and *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x - y`` | difference of *x* and *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x * y`` | product of *x* and *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x / y`` | quotient of *x* and *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x // y`` | floored quotient of *x* and | \(1) | |
+| | *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x % y`` | remainder of ``x / y`` | \(2) | |
++---------------------+---------------------------------+---------+--------------------+
+| ``-x`` | *x* negated | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``+x`` | *x* unchanged | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``abs(x)`` | absolute value or magnitude of | | :func:`abs` |
+| | *x* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``int(x)`` | *x* converted to integer | \(3)\(6)| :func:`int` |
++---------------------+---------------------------------+---------+--------------------+
+| ``float(x)`` | *x* converted to floating point | \(4)\(6)| :func:`float` |
++---------------------+---------------------------------+---------+--------------------+
+| ``complex(re, im)`` | a complex number with real part | \(6) | :func:`complex` |
+| | *re*, imaginary part *im*. | | |
+| | *im* defaults to zero. | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``c.conjugate()`` | conjugate of the complex number | | |
+| | *c* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``divmod(x, y)`` | the pair ``(x // y, x % y)`` | \(2) | :func:`divmod` |
++---------------------+---------------------------------+---------+--------------------+
+| ``pow(x, y)`` | *x* to the power *y* | \(5) | :func:`pow` |
++---------------------+---------------------------------+---------+--------------------+
+| ``x ** y`` | *x* to the power *y* | \(5) | |
++---------------------+---------------------------------+---------+--------------------+
.. index::
triple: operations on; numeric; types
@@ -346,6 +346,12 @@ Notes:
Python defines ``pow(0, 0)`` and ``0 ** 0`` to be ``1``, as is common for
programming languages.
+(6)
+ The numeric literals accepted include the digits ``0`` to ``9`` or any
+ Unicode equivalent (code points with the ``Nd`` property).
+
+ See http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedNumericType.txt
+ for a complete list of code points with the ``Nd`` property.
All :class:`numbers.Real` types (:class:`int` and
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index d3fb2c8..129f987 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -1352,10 +1352,11 @@ Implementing Descriptors
^^^^^^^^^^^^^^^^^^^^^^^^
The following methods only apply when an instance of the class containing the
-method (a so-called *descriptor* class) appears in the class dictionary of
-another class, known as the *owner* class. In the examples below, "the
-attribute" refers to the attribute whose name is the key of the property in the
-owner class' :attr:`__dict__`.
+method (a so-called *descriptor* class) appears in an *owner* class (the
+descriptor must be in either the owner's class dictionary or in the class
+dictionary for one of its parents). In the examples below, "the attribute"
+refers to the attribute whose name is the key of the property in the owner
+class' :attr:`__dict__`.
.. method:: object.__get__(self, instance, owner)
@@ -1418,7 +1419,7 @@ Super Binding
If ``a`` is an instance of :class:`super`, then the binding ``super(B,
obj).m()`` searches ``obj.__class__.__mro__`` for the base class ``A``
immediately preceding ``B`` and then invokes the descriptor with the call:
- ``A.__dict__['m'].__get__(obj, A)``.
+ ``A.__dict__['m'].__get__(obj, obj.__class__)``.
For instance bindings, the precedence of descriptor invocation depends on the
which descriptor methods are defined. A descriptor can define any combination
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index d1f47eb..b5a9b32 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -239,7 +239,9 @@ Miscellaneous options
.. cmdoption:: -S
Disable the import of the module :mod:`site` and the site-dependent
- manipulations of :data:`sys.path` that it entails.
+ manipulations of :data:`sys.path` that it entails. Also disable these
+ manipulations if :mod:`site` is explicitly imported later (call
+ :func:`site.main` if you want them to be triggered).
.. cmdoption:: -u
diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst
index d86826c..7f05a84 100644
--- a/Doc/whatsnew/3.3.rst
+++ b/Doc/whatsnew/3.3.rst
@@ -128,3 +128,8 @@ that may require changes to your code:
* Stub
+
+.. Issue #11591: When :program:`python` was started with :option:`-S`,
+ ``import site`` will not add site-specific paths to the module search
+ paths. In previous versions, it did. See changeset for doc changes in
+ various files. Contributed by Carl Meyer with editions by Éric Araujo.
diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py
index c324ce3..122dfb6 100644
--- a/Lib/collections/__init__.py
+++ b/Lib/collections/__init__.py
@@ -233,10 +233,62 @@ class OrderedDict(dict):
### namedtuple
################################################################################
+_class_template = '''\
+from builtins import property as _property, tuple as _tuple
+from operator import itemgetter as _itemgetter
+from collections import OrderedDict
+
+class {typename}(tuple):
+ '{typename}({arg_list})'
+
+ __slots__ = ()
+
+ _fields = {field_names!r}
+
+ def __new__(_cls, {arg_list}):
+ 'Create new instance of {typename}({arg_list})'
+ return _tuple.__new__(_cls, ({arg_list}))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new {typename} object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != {num_fields:d}:
+ raise TypeError('Expected {num_fields:d} arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + '({repr_fmt})' % self
+
+ def _asdict(self):
+ 'Return a new OrderedDict which maps field names to their values'
+ return OrderedDict(zip(self._fields, self))
+
+ def _replace(_self, **kwds):
+ 'Return a new {typename} object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, {field_names!r}, _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % list(kwds))
+ return result
+
+ def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return tuple(self)
+
+{field_defs}
+'''
+
+_repr_template = '{name}=%r'
+
+_field_template = '''\
+ {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}')
+'''
+
def namedtuple(typename, field_names, verbose=False, rename=False):
"""Returns a new subclass of tuple with named fields.
- >>> Point = namedtuple('Point', 'x y')
+ >>> Point = namedtuple('Point', ['x', 'y'])
>>> Point.__doc__ # docstring for the new class
'Point(x, y)'
>>> p = Point(11, y=22) # instantiate with positional args or keywords
@@ -261,79 +313,55 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
# generating informative error messages and preventing template injection attacks.
if isinstance(field_names, str):
field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas
- field_names = tuple(map(str, field_names))
+ field_names = list(map(str, field_names))
if rename:
- names = list(field_names)
seen = set()
- for i, name in enumerate(names):
- if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name)
- or not name or name[0].isdigit() or name.startswith('_')
+ for index, name in enumerate(field_names):
+ if (not all(c.isalnum() or c=='_' for c in name)
+ or _iskeyword(name)
+ or not name
+ or name[0].isdigit()
+ or name.startswith('_')
or name in seen):
- names[i] = '_%d' % i
+ field_names[index] = '_%d' % index
seen.add(name)
- field_names = tuple(names)
- for name in (typename,) + field_names:
+ for name in [typename] + field_names:
if not all(c.isalnum() or c=='_' for c in name):
raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name)
if _iskeyword(name):
raise ValueError('Type names and field names cannot be a keyword: %r' % name)
if name[0].isdigit():
raise ValueError('Type names and field names cannot start with a number: %r' % name)
- seen_names = set()
+ seen = set()
for name in field_names:
if name.startswith('_') and not rename:
raise ValueError('Field names cannot start with an underscore: %r' % name)
- if name in seen_names:
+ if name in seen:
raise ValueError('Encountered duplicate field name: %r' % name)
- seen_names.add(name)
-
- # Create and fill-in the class template
- numfields = len(field_names)
- argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
- reprtxt = ', '.join('%s=%%r' % name for name in field_names)
- template = '''class %(typename)s(tuple):
- '%(typename)s(%(argtxt)s)' \n
- __slots__ = () \n
- _fields = %(field_names)r \n
- def __new__(_cls, %(argtxt)s):
- 'Create new instance of %(typename)s(%(argtxt)s)'
- return _tuple.__new__(_cls, (%(argtxt)s)) \n
- @classmethod
- def _make(cls, iterable, new=tuple.__new__, len=len):
- 'Make a new %(typename)s object from a sequence or iterable'
- result = new(cls, iterable)
- if len(result) != %(numfields)d:
- raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
- return result \n
- def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + '(%(reprtxt)s)' %% self \n
- def _asdict(self):
- 'Return a new OrderedDict which maps field names to their values'
- return OrderedDict(zip(self._fields, self)) \n
- def _replace(_self, **kwds):
- 'Return a new %(typename)s object replacing specified fields with new values'
- result = _self._make(map(kwds.pop, %(field_names)r, _self))
- if kwds:
- raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
- return result \n
- def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return tuple(self) \n\n''' % locals()
- for i, name in enumerate(field_names):
- template += " %s = _property(_itemgetter(%d), doc='Alias for field number %d')\n" % (name, i, i)
- if verbose:
- print(template)
+ seen.add(name)
+
+ # Fill-in the class template
+ class_definition = _class_template.format(
+ typename = typename,
+ field_names = tuple(field_names),
+ num_fields = len(field_names),
+ arg_list = repr(tuple(field_names)).replace("'", "")[1:-1],
+ repr_fmt = ', '.join(_repr_template.format(name=name) for name in field_names),
+ field_defs = '\n'.join(_field_template.format(index=index, name=name)
+ for index, name in enumerate(field_names))
+ )
# Execute the template string in a temporary namespace and
# support tracing utilities by setting a value for frame.f_globals['__name__']
- namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
- OrderedDict=OrderedDict, _property=property, _tuple=tuple)
+ namespace = dict(__name__='namedtuple_%s' % typename)
try:
- exec(template, namespace)
+ exec(class_definition, namespace)
except SyntaxError as e:
- raise SyntaxError(e.msg + ':\n\n' + template)
+ raise SyntaxError(e.msg + ':\n\n' + class_definition)
result = namespace[typename]
+ result._source = class_definition
+ if verbose:
+ print(result._source)
# For pickling to work, the __module__ variable needs to be set to the frame
# where the named tuple is created. Bypass this step in enviroments where
diff --git a/Lib/email/quoprimime.py b/Lib/email/quoprimime.py
index 168dfff..bffad4e 100644
--- a/Lib/email/quoprimime.py
+++ b/Lib/email/quoprimime.py
@@ -135,9 +135,9 @@ def header_encode(header_bytes, charset='iso-8859-1'):
charset names the character set to use in the RFC 2046 header. It
defaults to iso-8859-1.
"""
- # Return empty headers unchanged
+ # Return empty headers as an empty string.
if not header_bytes:
- return str(header_bytes)
+ return ''
# Iterate over every byte, encoding if necessary.
encoded = []
for octet in header_bytes:
@@ -268,7 +268,7 @@ def decode(encoded, eol=NL):
if i == n:
decoded += eol
# Special case if original string did not end with eol
- if not encoded.endswith(eol) and decoded.endswith(eol):
+ if encoded[-1] not in '\r\n' and decoded.endswith(eol):
decoded = decoded[:-1]
return decoded
diff --git a/Lib/getpass.py b/Lib/getpass.py
index ce04566..dc02bd1 100644
--- a/Lib/getpass.py
+++ b/Lib/getpass.py
@@ -62,7 +62,7 @@ def unix_getpass(prompt='Password: ', stream=None):
try:
old = termios.tcgetattr(fd) # a copy to save
new = old[:]
- new[3] &= ~(termios.ECHO|termios.ISIG) # 3 == 'lflags'
+ new[3] &= ~termios.ECHO # 3 == 'lflags'
tcsetattr_flags = termios.TCSAFLUSH
if hasattr(termios, 'TCSASOFT'):
tcsetattr_flags |= termios.TCSASOFT
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index a944bee..18ea85c 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -404,6 +404,7 @@ class SourceLoader(_LoaderBasics):
else:
found = marshal.loads(bytes_data)
if isinstance(found, code_type):
+ imp._fix_co_filename(found, source_path)
return found
else:
msg = "Non-code object in {}"
@@ -758,7 +759,7 @@ class _ImportLockContext:
_IMPLICIT_META_PATH = [BuiltinImporter, FrozenImporter, _DefaultPathFinder]
-_ERR_MSG = 'No module named {}'
+_ERR_MSG = 'No module named {!r}'
def _gcd_import(name, package=None, level=0):
"""Import and return the module based on its name, the package the call is
diff --git a/Lib/importlib/test/regrtest.py b/Lib/importlib/test/regrtest.py
index b103ae7d..dc0eb97 100644
--- a/Lib/importlib/test/regrtest.py
+++ b/Lib/importlib/test/regrtest.py
@@ -5,13 +5,6 @@ invalidates are automatically skipped if the entire test suite is run.
Otherwise all command-line options valid for test.regrtest are also valid for
this script.
-XXX FAILING
- * test_import
- - test_incorrect_code_name
- file name differing between __file__ and co_filename (r68360 on trunk)
- - test_import_by_filename
- exception for trying to import by file name does not match
-
"""
import importlib
import sys
diff --git a/Lib/random.py b/Lib/random.py
index f29803e..3d2af24 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -465,6 +465,12 @@ class Random(_random.Random):
Conditions on the parameters are alpha > 0 and beta > 0.
+ The probability distribution function is:
+
+ x ** (alpha - 1) * math.exp(-x / beta)
+ pdf(x) = --------------------------------------
+ math.gamma(alpha) * beta ** alpha
+
"""
# alpha > 0, beta > 0, mean is alpha*beta, variance is alpha*beta**2
diff --git a/Lib/site.py b/Lib/site.py
index a2c0bec..fcfdbed 100644
--- a/Lib/site.py
+++ b/Lib/site.py
@@ -508,6 +508,11 @@ def execusercustomize():
def main():
+ """Add standard site-specific directories to the module search path.
+
+ This function is called automatically when this module is imported,
+ unless the python interpreter was started with the -S flag.
+ """
global ENABLE_USER_SITE
abs_paths()
@@ -526,7 +531,10 @@ def main():
if ENABLE_USER_SITE:
execusercustomize()
-main()
+# Prevent edition of sys.path when python was started with -S and
+# site is imported later.
+if not sys.flags.no_site:
+ main()
def _script():
help = """\
diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py
index 1e72f80..63268e5 100755
--- a/Lib/test/regrtest.py
+++ b/Lib/test/regrtest.py
@@ -374,6 +374,13 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
forever = True
elif o in ('-j', '--multiprocess'):
use_mp = int(a)
+ if use_mp <= 0:
+ try:
+ import multiprocessing
+ # Use all cores + extras for tests that like to sleep
+ use_mp = 2 + multiprocessing.cpu_count()
+ except (ImportError, NotImplementedError):
+ use_mp = 3
elif o == '--header':
header = True
elif o == '--slaveargs':
@@ -535,7 +542,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
args_tuple = (
(test, verbose, quiet),
dict(huntrleaks=huntrleaks, use_resources=use_resources,
- debug=debug)
+ debug=debug, rerun_failed=verbose3)
)
yield (test, args_tuple)
pending = tests_and_args()
@@ -609,11 +616,9 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
globals=globals(), locals=vars())
else:
try:
- result = runtest(test, verbose, quiet, huntrleaks, debug)
+ result = runtest(test, verbose, quiet, huntrleaks, debug,
+ rerun_failed=verbose3)
accumulate_result(test, result)
- if verbose3 and result[0] == FAILED:
- print("Re-running test {} in verbose mode".format(test))
- runtest(test, True, quiet, huntrleaks, debug)
except KeyboardInterrupt:
interrupted = True
break
@@ -758,7 +763,8 @@ def replace_stdout():
atexit.register(restore_stdout)
def runtest(test, verbose, quiet,
- huntrleaks=False, debug=False, use_resources=None):
+ huntrleaks=False, debug=False, use_resources=None,
+ rerun_failed=False):
"""Run a single test.
test -- the name of the test
@@ -767,6 +773,7 @@ def runtest(test, verbose, quiet,
test_times -- a list of (time, test_name) pairs
huntrleaks -- run multiple times to test for leaks; requires a debug
build; a triple corresponding to -R's three arguments
+ rerun_failed -- if true, re-run in verbose mode when failed
Returns one of the test result constants:
INTERRUPTED KeyboardInterrupt when run under -j
@@ -781,7 +788,14 @@ def runtest(test, verbose, quiet,
if use_resources is not None:
support.use_resources = use_resources
try:
- return runtest_inner(test, verbose, quiet, huntrleaks, debug)
+ result = runtest_inner(test, verbose, quiet, huntrleaks, debug)
+ if result[0] == FAILED and rerun_failed:
+ cleanup_test_droppings(test, verbose)
+ sys.stdout.flush()
+ sys.stderr.flush()
+ print("Re-running test {} in verbose mode".format(test))
+ runtest(test, True, quiet, huntrleaks, debug)
+ return result
finally:
cleanup_test_droppings(test, verbose)
diff --git a/Lib/test/support.py b/Lib/test/support.py
index 66d7f93..52ec232 100644
--- a/Lib/test/support.py
+++ b/Lib/test/support.py
@@ -1389,9 +1389,8 @@ def args_from_interpreter_flags():
v = getattr(sys.flags, flag)
if v > 0:
args.append('-' + opt * v)
- if sys.warnoptions:
- args.append('-W')
- args.extend(sys.warnoptions)
+ for opt in sys.warnoptions:
+ args.append('-W' + opt)
return args
#============================================================
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index d71fb01..4ef27ce 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -1,6 +1,7 @@
"""Unit tests for collections.py."""
import unittest, doctest, operator
+from test.support import TESTFN, forget, unlink
import inspect
from test import support
from collections import namedtuple, Counter, OrderedDict, _count_elements
@@ -127,6 +128,7 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(Point.__module__, __name__)
self.assertEqual(Point.__getitem__, tuple.__getitem__)
self.assertEqual(Point._fields, ('x', 'y'))
+ self.assertIn('class Point(tuple)', Point._source)
self.assertRaises(ValueError, namedtuple, 'abc%', 'efg ghi') # type has non-alpha char
self.assertRaises(ValueError, namedtuple, 'class', 'efg ghi') # type has keyword
@@ -326,6 +328,39 @@ class TestNamedTuple(unittest.TestCase):
pass
self.assertEqual(repr(B(1)), 'B(x=1)')
+ def test_source(self):
+ # verify that _source can be run through exec()
+ tmp = namedtuple('Color', 'red green blue')
+ self.assertNotIn('Color', globals())
+ exec(tmp._source, globals())
+ self.assertIn('Color', globals())
+ c = Color(10, 20, 30)
+ self.assertEqual((c.red, c.green, c.blue), (10, 20, 30))
+ self.assertEqual(Color._fields, ('red', 'green', 'blue'))
+
+ def test_source_importable(self):
+ tmp = namedtuple('Color', 'hue sat val')
+
+ compiled = None
+ source = TESTFN + '.py'
+ with open(source, 'w') as f:
+ print(tmp._source, file=f)
+
+ if TESTFN in sys.modules:
+ del sys.modules[TESTFN]
+ try:
+ mod = __import__(TESTFN)
+ compiled = mod.__file__
+ Color = mod.Color
+ c = Color(10, 20, 30)
+ self.assertEqual((c.hue, c.sat, c.val), (10, 20, 30))
+ self.assertEqual(Color._fields, ('hue', 'sat', 'val'))
+ finally:
+ forget(TESTFN)
+ if compiled:
+ unlink(compiled)
+ unlink(source)
+
################################################################################
### Abstract Base Classes
diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py
index f639eec..2662af7 100644
--- a/Lib/test/test_concurrent_futures.py
+++ b/Lib/test/test_concurrent_futures.py
@@ -9,6 +9,9 @@ test.support.import_module('multiprocessing.synchronize')
# without thread support.
test.support.import_module('threading')
+from test.script_helper import assert_python_ok
+
+import sys
import threading
import time
import unittest
@@ -43,9 +46,30 @@ def sleep_and_raise(t):
time.sleep(t)
raise Exception('this is an exception')
+def sleep_and_print(t, msg):
+ time.sleep(t)
+ print(msg)
+ sys.stdout.flush()
+
class ExecutorMixin:
worker_count = 5
+
+ def setUp(self):
+ self.t1 = time.time()
+ try:
+ self.executor = self.executor_type(max_workers=self.worker_count)
+ except NotImplementedError as e:
+ self.skipTest(str(e))
+ self._prime_executor()
+
+ def tearDown(self):
+ self.executor.shutdown(wait=True)
+ dt = time.time() - self.t1
+ if test.support.verbose:
+ print("%.2fs" % dt, end=' ')
+ self.assertLess(dt, 60, "synchronization issue: test lasted too long")
+
def _prime_executor(self):
# Make sure that the executor is ready to do work before running the
# tests. This should reduce the probability of timeouts in the tests.
@@ -57,24 +81,11 @@ class ExecutorMixin:
class ThreadPoolMixin(ExecutorMixin):
- def setUp(self):
- self.executor = futures.ThreadPoolExecutor(max_workers=5)
- self._prime_executor()
-
- def tearDown(self):
- self.executor.shutdown(wait=True)
+ executor_type = futures.ThreadPoolExecutor
class ProcessPoolMixin(ExecutorMixin):
- def setUp(self):
- try:
- self.executor = futures.ProcessPoolExecutor(max_workers=5)
- except NotImplementedError as e:
- self.skipTest(str(e))
- self._prime_executor()
-
- def tearDown(self):
- self.executor.shutdown(wait=True)
+ executor_type = futures.ProcessPoolExecutor
class ExecutorShutdownTest(unittest.TestCase):
@@ -84,6 +95,20 @@ class ExecutorShutdownTest(unittest.TestCase):
self.executor.submit,
pow, 2, 5)
+ def test_interpreter_shutdown(self):
+ # Test the atexit hook for shutdown of worker threads and processes
+ rc, out, err = assert_python_ok('-c', """if 1:
+ from concurrent.futures import {executor_type}
+ from time import sleep
+ from test.test_concurrent_futures import sleep_and_print
+ t = {executor_type}(5)
+ t.submit(sleep_and_print, 1.0, "apple")
+ """.format(executor_type=self.executor_type.__name__))
+ # Errors in atexit hooks don't change the process exit code, check
+ # stderr manually.
+ self.assertFalse(err)
+ self.assertEqual(out.strip(), b"apple")
+
class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest):
def _prime_executor(self):
diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
index 29dd0fc..bc6a309 100644
--- a/Lib/test/test_email/test_email.py
+++ b/Lib/test/test_email/test_email.py
@@ -3344,21 +3344,200 @@ class TestQuopri(unittest.TestCase):
c = chr(x)
self.assertEqual(quoprimime.unquote(quoprimime.quote(c)), c)
- def test_header_encode(self):
- eq = self.assertEqual
- he = quoprimime.header_encode
- eq(he(b'hello'), '=?iso-8859-1?q?hello?=')
- eq(he(b'hello', charset='iso-8859-2'), '=?iso-8859-2?q?hello?=')
- eq(he(b'hello\nworld'), '=?iso-8859-1?q?hello=0Aworld?=')
- # Test a non-ASCII character
- eq(he(b'hello\xc7there'), '=?iso-8859-1?q?hello=C7there?=')
+ def _test_header_encode(self, header, expected_encoded_header, charset=None):
+ if charset is None:
+ encoded_header = quoprimime.header_encode(header)
+ else:
+ encoded_header = quoprimime.header_encode(header, charset)
+ self.assertEqual(encoded_header, expected_encoded_header)
- def test_decode(self):
- eq = self.assertEqual
- eq(quoprimime.decode(''), '')
- eq(quoprimime.decode('hello'), 'hello')
- eq(quoprimime.decode('hello', 'X'), 'hello')
- eq(quoprimime.decode('hello\nworld', 'X'), 'helloXworld')
+ def test_header_encode_null(self):
+ self._test_header_encode(b'', '')
+
+ def test_header_encode_one_word(self):
+ self._test_header_encode(b'hello', '=?iso-8859-1?q?hello?=')
+
+ def test_header_encode_two_lines(self):
+ self._test_header_encode(b'hello\nworld',
+ '=?iso-8859-1?q?hello=0Aworld?=')
+
+ def test_header_encode_non_ascii(self):
+ self._test_header_encode(b'hello\xc7there',
+ '=?iso-8859-1?q?hello=C7there?=')
+
+ def test_header_encode_alt_charset(self):
+ self._test_header_encode(b'hello', '=?iso-8859-2?q?hello?=',
+ charset='iso-8859-2')
+
+ def _test_header_decode(self, encoded_header, expected_decoded_header):
+ decoded_header = quoprimime.header_decode(encoded_header)
+ self.assertEqual(decoded_header, expected_decoded_header)
+
+ def test_header_decode_null(self):
+ self._test_header_decode('', '')
+
+ def test_header_decode_one_word(self):
+ self._test_header_decode('hello', 'hello')
+
+ def test_header_decode_two_lines(self):
+ self._test_header_decode('hello=0Aworld', 'hello\nworld')
+
+ def test_header_decode_non_ascii(self):
+ self._test_header_decode('hello=C7there', 'hello\xc7there')
+
+ def _test_decode(self, encoded, expected_decoded, eol=None):
+ if eol is None:
+ decoded = quoprimime.decode(encoded)
+ else:
+ decoded = quoprimime.decode(encoded, eol=eol)
+ self.assertEqual(decoded, expected_decoded)
+
+ def test_decode_null_word(self):
+ self._test_decode('', '')
+
+ def test_decode_null_line_null_word(self):
+ self._test_decode('\r\n', '\n')
+
+ def test_decode_one_word(self):
+ self._test_decode('hello', 'hello')
+
+ def test_decode_one_word_eol(self):
+ self._test_decode('hello', 'hello', eol='X')
+
+ def test_decode_one_line(self):
+ self._test_decode('hello\r\n', 'hello\n')
+
+ def test_decode_one_line_lf(self):
+ self._test_decode('hello\n', 'hello\n')
+
+ def test_decode_one_line_cr(self):
+ self._test_decode('hello\r', 'hello\n')
+
+ def test_decode_one_line_nl(self):
+ self._test_decode('hello\n', 'helloX', eol='X')
+
+ def test_decode_one_line_crnl(self):
+ self._test_decode('hello\r\n', 'helloX', eol='X')
+
+ def test_decode_one_line_one_word(self):
+ self._test_decode('hello\r\nworld', 'hello\nworld')
+
+ def test_decode_one_line_one_word_eol(self):
+ self._test_decode('hello\r\nworld', 'helloXworld', eol='X')
+
+ def test_decode_two_lines(self):
+ self._test_decode('hello\r\nworld\r\n', 'hello\nworld\n')
+
+ def test_decode_two_lines_eol(self):
+ self._test_decode('hello\r\nworld\r\n', 'helloXworldX', eol='X')
+
+ def test_decode_one_long_line(self):
+ self._test_decode('Spam' * 250, 'Spam' * 250)
+
+ def test_decode_one_space(self):
+ self._test_decode(' ', '')
+
+ def test_decode_multiple_spaces(self):
+ self._test_decode(' ' * 5, '')
+
+ def test_decode_one_line_trailing_spaces(self):
+ self._test_decode('hello \r\n', 'hello\n')
+
+ def test_decode_two_lines_trailing_spaces(self):
+ self._test_decode('hello \r\nworld \r\n', 'hello\nworld\n')
+
+ def test_decode_quoted_word(self):
+ self._test_decode('=22quoted=20words=22', '"quoted words"')
+
+ def test_decode_uppercase_quoting(self):
+ self._test_decode('ab=CD=EF', 'ab\xcd\xef')
+
+ def test_decode_lowercase_quoting(self):
+ self._test_decode('ab=cd=ef', 'ab\xcd\xef')
+
+ def test_decode_soft_line_break(self):
+ self._test_decode('soft line=\r\nbreak', 'soft linebreak')
+
+ def test_decode_false_quoting(self):
+ self._test_decode('A=1,B=A ==> A+B==2', 'A=1,B=A ==> A+B==2')
+
+ def _test_encode(self, body, expected_encoded_body, maxlinelen=None, eol=None):
+ kwargs = {}
+ if maxlinelen is None:
+ # Use body_encode's default.
+ maxlinelen = 76
+ else:
+ kwargs['maxlinelen'] = maxlinelen
+ if eol is None:
+ # Use body_encode's default.
+ eol = '\n'
+ else:
+ kwargs['eol'] = eol
+ encoded_body = quoprimime.body_encode(body, **kwargs)
+ self.assertEqual(encoded_body, expected_encoded_body)
+ if eol == '\n' or eol == '\r\n':
+ # We know how to split the result back into lines, so maxlinelen
+ # can be checked.
+ for line in encoded_body.splitlines():
+ self.assertLessEqual(len(line), maxlinelen)
+
+ def test_encode_null(self):
+ self._test_encode('', '')
+
+ def test_encode_null_lines(self):
+ self._test_encode('\n\n', '\n\n')
+
+ def test_encode_one_line(self):
+ self._test_encode('hello\n', 'hello\n')
+
+ def test_encode_one_line_crlf(self):
+ self._test_encode('hello\r\n', 'hello\n')
+
+ def test_encode_one_line_eol(self):
+ self._test_encode('hello\n', 'hello\r\n', eol='\r\n')
+
+ def test_encode_one_space(self):
+ self._test_encode(' ', '=20')
+
+ def test_encode_one_line_one_space(self):
+ self._test_encode(' \n', '=20\n')
+
+ def test_encode_one_word_trailing_spaces(self):
+ self._test_encode('hello ', 'hello =20')
+
+ def test_encode_one_line_trailing_spaces(self):
+ self._test_encode('hello \n', 'hello =20\n')
+
+ def test_encode_one_word_trailing_tab(self):
+ self._test_encode('hello \t', 'hello =09')
+
+ def test_encode_one_line_trailing_tab(self):
+ self._test_encode('hello \t\n', 'hello =09\n')
+
+ def test_encode_trailing_space_before_maxlinelen(self):
+ self._test_encode('abcd \n1234', 'abcd =\n\n1234', maxlinelen=6)
+
+ def test_encode_trailing_space_beyond_maxlinelen(self):
+ self._test_encode('abcd \n1234', 'abc=\nd =\n\n1234', maxlinelen=4)
+
+ def test_encode_quoted_equals(self):
+ self._test_encode('a = b', 'a =3D b')
+
+ def test_encode_one_long_string(self):
+ self._test_encode('x' * 100, 'x' * 75 + '=\n' + 'x' * 25)
+
+ def test_encode_one_long_line(self):
+ self._test_encode('x' * 100 + '\n', 'x' * 75 + '=\n' + 'x' * 25 + '\n')
+
+ def test_encode_one_very_long_line(self):
+ self._test_encode('x' * 200 + '\n',
+ 2 * ('x' * 75 + '=\n') + 'x' * 50 + '\n')
+
+ def test_encode_one_long_line(self):
+ self._test_encode('x' * 100 + '\n', 'x' * 75 + '=\n' + 'x' * 25 + '\n')
+
+ def test_encode_shortest_maxlinelen(self):
+ self._test_encode('=' * 5, '=3D=\n' * 4 + '=3D', maxlinelen=4)
def test_encode(self):
eq = self.assertEqual
diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py
index 4ece1bf..8034000 100644
--- a/Lib/test/test_imaplib.py
+++ b/Lib/test/test_imaplib.py
@@ -219,20 +219,23 @@ class RemoteIMAPTest(unittest.TestCase):
def tearDown(self):
if self.server is not None:
- self.server.logout()
+ with transient_internet(self.host):
+ self.server.logout()
def test_logincapa(self):
- for cap in self.server.capabilities:
- self.assertIsInstance(cap, str)
- self.assertTrue('LOGINDISABLED' in self.server.capabilities)
- self.assertTrue('AUTH=ANONYMOUS' in self.server.capabilities)
- rs = self.server.login(self.username, self.password)
- self.assertEqual(rs[0], 'OK')
+ with transient_internet(self.host):
+ for cap in self.server.capabilities:
+ self.assertIsInstance(cap, str)
+ self.assertTrue('LOGINDISABLED' in self.server.capabilities)
+ self.assertTrue('AUTH=ANONYMOUS' in self.server.capabilities)
+ rs = self.server.login(self.username, self.password)
+ self.assertEqual(rs[0], 'OK')
def test_logout(self):
- rs = self.server.logout()
- self.server = None
- self.assertEqual(rs[0], 'BYE')
+ with transient_internet(self.host):
+ rs = self.server.logout()
+ self.server = None
+ self.assertEqual(rs[0], 'BYE')
@unittest.skipUnless(ssl, "SSL not available")
@@ -240,8 +243,9 @@ class RemoteIMAP_STARTTLSTest(RemoteIMAPTest):
def setUp(self):
super().setUp()
- rs = self.server.starttls()
- self.assertEqual(rs[0], 'OK')
+ with transient_internet(self.host):
+ rs = self.server.starttls()
+ self.assertEqual(rs[0], 'OK')
def test_logincapa(self):
for cap in self.server.capabilities:
diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py
index 8a33f5e..06449d2 100644
--- a/Lib/test/test_multiprocessing.py
+++ b/Lib/test/test_multiprocessing.py
@@ -1100,7 +1100,7 @@ class _TestPool(BaseTestCase):
self.pool.terminate()
join = TimingWrapper(self.pool.join)
join()
- self.assertTrue(join.elapsed < 0.5)
+ self.assertLess(join.elapsed, 0.5)
def raising():
raise KeyError("key")
diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py
index f73565e..78de909 100644
--- a/Lib/test/test_peepholer.py
+++ b/Lib/test/test_peepholer.py
@@ -3,6 +3,7 @@ import re
import sys
from io import StringIO
import unittest
+from math import copysign
def disassemble(func):
f = StringIO()
@@ -207,6 +208,9 @@ class TestTranforms(unittest.TestCase):
def test_folding_of_unaryops_on_constants(self):
for line, elem in (
('-0.5', '(-0.5)'), # unary negative
+ ('-0.0', '(-0.0)'), # -0.0
+ ('-(1.0-1.0)','(-0.0)'), # -0.0 after folding
+ ('-0', '(0)'), # -0
('~-2', '(1)'), # unary invert
('+1', '(1)'), # unary positive
):
@@ -214,6 +218,13 @@ class TestTranforms(unittest.TestCase):
self.assertIn(elem, asm, asm)
self.assertNotIn('UNARY_', asm)
+ # Check that -0.0 works after marshaling
+ def negzero():
+ return -(1.0-1.0)
+
+ self.assertNotIn('UNARY_', disassemble(negzero))
+ self.assertTrue(copysign(1.0, negzero()) < 0)
+
# Verify that unfoldables are skipped
for line, elem in (
('-"abc"', "('abc')"), # unary negative
diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py
index a501e37..0aa7a77 100644
--- a/Lib/urllib/request.py
+++ b/Lib/urllib/request.py
@@ -2136,7 +2136,7 @@ class ftpwrapper:
# Try to retrieve as a file
try:
cmd = 'RETR ' + file
- conn = self.ftp.ntransfercmd(cmd)
+ conn, retrlen = self.ftp.ntransfercmd(cmd)
except ftplib.error_perm as reason:
if str(reason)[:3] != '550':
raise URLError('ftp error', reason).with_traceback(
@@ -2157,10 +2157,14 @@ class ftpwrapper:
cmd = 'LIST ' + file
else:
cmd = 'LIST'
- conn = self.ftp.ntransfercmd(cmd)
+ conn, retrlen = self.ftp.ntransfercmd(cmd)
self.busy = 1
+
+ ftpobj = addclosehook(conn.makefile('rb'), self.endtransfer)
+ conn.close()
# Pass back both a suitably decorated object and a retrieval length
- return (addclosehook(conn[0].makefile('rb'), self.endtransfer), conn[1])
+ return (ftpobj, retrlen)
+
def endtransfer(self):
if not self.busy:
return
diff --git a/Misc/ACKS b/Misc/ACKS
index 91bcec8..5801151 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -867,6 +867,7 @@ Christian Tismer
Frank J. Tobin
R Lindsay Todd
Bennett Todd
+Eugene Toder
Matias Torchinsky
Sandro Tosi
Richard Townsend
diff --git a/Misc/NEWS b/Misc/NEWS
index e8a1926..f70998c 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
Core and Builtins
-----------------
+- Issue #11244: Remove an unnecessary peepholer check that was preventing
+ negative zeros from being constant-folded properly.
+
- Issue #11395: io.FileIO().write() clamps the data length to 32,767 bytes on
Windows if the file is a TTY to workaround a Windows bug. The Windows console
returns an error (12: not enough space error) on writing into stdout if
@@ -81,12 +84,26 @@ Core and Builtins
Library
-------
+- Issue #6811: Allow importlib to change a code object's co_filename attribute
+ to match the path to where the source code currently is, not where the code
+ object originally came from.
+
+- Issue #8754: Have importlib use the repr of a module name in error messages.
+
+- Issue #11591: Prevent "import site" from modifying sys.path when python
+ was started with -S.
+
+- collections.namedtuple() now adds a _source attribute to the generated
+ class. This make the source more accessible than the outdated
+ "verbose" option which prints to stdout but doesn't make the source
+ string available.
+
- Issue #11371: Mark getopt error messages as localizable. Patch by Filip
Gruszczyński.
- Issue #11333: Add __slots__ to collections ABCs.
-- Issue #11628: cmp_to_key generated class should use __slots__
+- Issue #11628: cmp_to_key generated class should use __slots__.
- Issue #5537: Fix time2isoz() and time2netscape() functions of
httplib.cookiejar for expiration year greater than 2038 on 32-bit systems.
@@ -282,6 +299,8 @@ Tools/Demos
Tests
-----
+- Issue #11653: fix -W with -j in regrtest.
+
- The email test suite now lives in the Lib/test/test_email package. The test
harness code has also been modernized to allow use of new unittest features.
diff --git a/Misc/python.man b/Misc/python.man
index 2d15d5d..53b77fc 100644
--- a/Misc/python.man
+++ b/Misc/python.man
@@ -169,7 +169,9 @@ Disable the import of the module
.I site
and the site-dependent manipulations of
.I sys.path
-that it entails.
+that it entails. Also disable these manipulations if
+.I site
+is explicitly imported later.
.TP
.B \-u
Force the binary I/O layers of stdin, stdout and stderr to be unbuffered.
diff --git a/Objects/setobject.c b/Objects/setobject.c
index 478b5cb..48edad8 100644
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -365,11 +365,12 @@ set_add_entry(register PySetObject *so, setentry *entry)
{
register Py_ssize_t n_used;
PyObject *key = entry->key;
+ Py_hash_t hash = entry->hash;
assert(so->fill <= so->mask); /* at least one empty slot */
n_used = so->used;
Py_INCREF(key);
- if (set_insert_key(so, key, entry->hash) == -1) {
+ if (set_insert_key(so, key, hash) == -1) {
Py_DECREF(key);
return -1;
}
@@ -639,6 +640,7 @@ set_merge(PySetObject *so, PyObject *otherset)
{
PySetObject *other;
PyObject *key;
+ Py_hash_t hash;
register Py_ssize_t i;
register setentry *entry;
@@ -660,10 +662,11 @@ set_merge(PySetObject *so, PyObject *otherset)
for (i = 0; i <= other->mask; i++) {
entry = &other->table[i];
key = entry->key;
+ hash = entry->hash;
if (key != NULL &&
key != dummy) {
Py_INCREF(key);
- if (set_insert_key(so, key, entry->hash) == -1) {
+ if (set_insert_key(so, key, hash) == -1) {
Py_DECREF(key);
return -1;
}
diff --git a/Python/import.c b/Python/import.c
index 907ccd7..b074b83 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -1374,6 +1374,32 @@ update_compiled_module(PyCodeObject *co, PyObject *newname)
Py_DECREF(oldname);
}
+static PyObject *
+imp_fix_co_filename(PyObject *self, PyObject *args)
+{
+ PyObject *co;
+ PyObject *file_path;
+
+ if (!PyArg_ParseTuple(args, "OO:_fix_co_filename", &co, &file_path))
+ return NULL;
+
+ if (!PyCode_Check(co)) {
+ PyErr_SetString(PyExc_TypeError,
+ "first argument must be a code object");
+ return NULL;
+ }
+
+ if (!PyUnicode_Check(file_path)) {
+ PyErr_SetString(PyExc_TypeError,
+ "second argument must be a string");
+ return NULL;
+ }
+
+ update_compiled_module((PyCodeObject*)co, file_path);
+
+ Py_RETURN_NONE;
+}
+
/* Load a source module from a given file and return its module
object WITH INCREMENTED REFERENCE COUNT. If there's a matching
byte-compiled file, use that instead. */
@@ -3976,6 +4002,7 @@ static PyMethodDef imp_methods[] = {
#endif
{"load_package", imp_load_package, METH_VARARGS},
{"load_source", imp_load_source, METH_VARARGS},
+ {"_fix_co_filename", imp_fix_co_filename, METH_VARARGS},
{NULL, NULL} /* sentinel */
};
diff --git a/Python/peephole.c b/Python/peephole.c
index 4bc65dc..69f6161 100644
--- a/Python/peephole.c
+++ b/Python/peephole.c
@@ -238,7 +238,7 @@ fold_binops_on_constants(unsigned char *codestr, PyObject *consts, PyObject **ob
static int
fold_unaryops_on_constants(unsigned char *codestr, PyObject *consts, PyObject *v)
{
- PyObject *newconst=NULL/*, *v*/;
+ PyObject *newconst;
Py_ssize_t len_consts;
int opcode;
@@ -250,9 +250,7 @@ fold_unaryops_on_constants(unsigned char *codestr, PyObject *consts, PyObject *v
opcode = codestr[3];
switch (opcode) {
case UNARY_NEGATIVE:
- /* Preserve the sign of -0.0 */
- if (PyObject_IsTrue(v) == 1)
- newconst = PyNumber_Negative(v);
+ newconst = PyNumber_Negative(v);
break;
case UNARY_INVERT:
newconst = PyNumber_Invert(v);