diff options
-rw-r--r-- | Doc/library/collections.rst | 64 | ||||
-rw-r--r-- | Doc/library/site.rst | 13 | ||||
-rw-r--r-- | Doc/library/stdtypes.rst | 86 | ||||
-rw-r--r-- | Doc/reference/datamodel.rst | 11 | ||||
-rw-r--r-- | Doc/using/cmdline.rst | 4 | ||||
-rw-r--r-- | Doc/whatsnew/3.3.rst | 5 | ||||
-rw-r--r-- | Lib/collections/__init__.py | 136 | ||||
-rw-r--r-- | Lib/email/quoprimime.py | 6 | ||||
-rw-r--r-- | Lib/getpass.py | 2 | ||||
-rw-r--r-- | Lib/importlib/_bootstrap.py | 3 | ||||
-rw-r--r-- | Lib/importlib/test/regrtest.py | 7 | ||||
-rw-r--r-- | Lib/random.py | 6 | ||||
-rw-r--r-- | Lib/site.py | 10 | ||||
-rwxr-xr-x | Lib/test/regrtest.py | 28 | ||||
-rw-r--r-- | Lib/test/support.py | 5 | ||||
-rw-r--r-- | Lib/test/test_collections.py | 35 | ||||
-rw-r--r-- | Lib/test/test_concurrent_futures.py | 55 | ||||
-rw-r--r-- | Lib/test/test_email/test_email.py | 207 | ||||
-rw-r--r-- | Lib/test/test_imaplib.py | 28 | ||||
-rw-r--r-- | Lib/test/test_multiprocessing.py | 2 | ||||
-rw-r--r-- | Lib/test/test_peepholer.py | 11 | ||||
-rw-r--r-- | Lib/urllib/request.py | 10 | ||||
-rw-r--r-- | Misc/ACKS | 1 | ||||
-rw-r--r-- | Misc/NEWS | 21 | ||||
-rw-r--r-- | Misc/python.man | 4 | ||||
-rw-r--r-- | Objects/setobject.c | 7 | ||||
-rw-r--r-- | Python/import.c | 27 | ||||
-rw-r--r-- | Python/peephole.c | 6 |
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 @@ -867,6 +867,7 @@ Christian Tismer Frank J. Tobin R Lindsay Todd Bennett Todd +Eugene Toder Matias Torchinsky Sandro Tosi Richard Townsend @@ -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); |