diff -r 3955265b16b6 -r 8bd713a823b5 Doc/library/collections.abc.rst --- a/Doc/library/collections.abc.rst Tue Mar 22 21:04:12 2011 +1000 +++ b/Doc/library/collections.abc.rst Fri Mar 25 08:42:37 2011 +1000 @@ -115,7 +115,7 @@ That assumption is factored-out to an internal classmethod called :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set. If the :class:`Set` mixin is being used in a class with a different - constructor signature, you will need to override :meth:`from_iterable` + constructor signature, you will need to override :meth:`_from_iterable` with a classmethod that can construct new instances from an iterable argument. diff -r 3955265b16b6 -r 8bd713a823b5 Doc/library/collections.rst --- a/Doc/library/collections.rst Tue Mar 22 21:04:12 2011 +1000 +++ b/Doc/library/collections.rst Fri Mar 25 08:42:37 2011 +1000 @@ -1,3 +1,4 @@ + :mod:`collections` --- Container datatypes ========================================== @@ -660,7 +661,7 @@ ... 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 @@ -693,7 +694,9 @@ 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. @@ -707,51 +710,6 @@ >>> # 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)' - - __slots__ = () - - _fields = ('x', 'y') - - def __new__(_cls, x, y): - 'Create a new instance of Point(x, y)' - return _tuple.__new__(_cls, (x, y)) - - @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 - - def __repr__(self): - 'Return a nicely formatted representation string' - return self.__class__.__name__ + '(x=%r, y=%r)' % 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 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 - - def __getnewargs__(self): - 'Return self as a plain tuple. Used by copy and pickle.' - return tuple(self) - - 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 @@ -780,7 +738,7 @@ 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) @@ -818,6 +776,15 @@ >>> 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 @@ -866,7 +833,6 @@ 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: @@ -878,6 +844,7 @@ >>> Account = namedtuple('Account', 'owner balance transaction_count') >>> default_account = Account('', 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 -r 3955265b16b6 -r 8bd713a823b5 Doc/library/site.rst --- a/Doc/library/site.rst Tue Mar 22 21:04:12 2011 +1000 +++ b/Doc/library/site.rst Fri Mar 25 08:42:37 2011 +1000 @@ -13,7 +13,11 @@ .. 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 @@ .. 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 -r 3955265b16b6 -r 8bd713a823b5 Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst Tue Mar 22 21:04:12 2011 +1000 +++ b/Doc/library/stdtypes.rst Fri Mar 25 08:42:37 2011 +1000 @@ -268,46 +268,46 @@ 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 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst Tue Mar 22 21:04:12 2011 +1000 +++ b/Doc/reference/datamodel.rst Fri Mar 25 08:42:37 2011 +1000 @@ -1352,10 +1352,11 @@ ^^^^^^^^^^^^^^^^^^^^^^^^ 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 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Doc/using/cmdline.rst --- a/Doc/using/cmdline.rst Tue Mar 22 21:04:12 2011 +1000 +++ b/Doc/using/cmdline.rst Fri Mar 25 08:42:37 2011 +1000 @@ -239,7 +239,9 @@ .. 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 -r 3955265b16b6 -r 8bd713a823b5 Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst Tue Mar 22 21:04:12 2011 +1000 +++ b/Doc/whatsnew/3.3.rst Fri Mar 25 08:42:37 2011 +1000 @@ -128,3 +128,8 @@ * 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/collections/__init__.py --- a/Lib/collections/__init__.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/collections/__init__.py Fri Mar 25 08:42:37 2011 +1000 @@ -233,10 +233,62 @@ ### 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 @@ # 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) + seen.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) + # 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/collections/abc.py --- a/Lib/collections/abc.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/collections/abc.py Fri Mar 25 08:42:37 2011 +1000 @@ -46,6 +46,8 @@ class Hashable(metaclass=ABCMeta): + __slots__ = () + @abstractmethod def __hash__(self): return 0 @@ -63,6 +65,8 @@ class Iterable(metaclass=ABCMeta): + __slots__ = () + @abstractmethod def __iter__(self): while False: @@ -78,6 +82,8 @@ class Iterator(Iterable): + __slots__ = () + @abstractmethod def __next__(self): raise StopIteration @@ -109,6 +115,8 @@ class Sized(metaclass=ABCMeta): + __slots__ = () + @abstractmethod def __len__(self): return 0 @@ -123,6 +131,8 @@ class Container(metaclass=ABCMeta): + __slots__ = () + @abstractmethod def __contains__(self, x): return False @@ -137,6 +147,8 @@ class Callable(metaclass=ABCMeta): + __slots__ = () + @abstractmethod def __call__(self, *args, **kwds): return False @@ -164,6 +176,8 @@ then the other operations will automatically follow suit. """ + __slots__ = () + def __le__(self, other): if not isinstance(other, Set): return NotImplemented @@ -275,6 +289,8 @@ class MutableSet(Set): + __slots__ = () + @abstractmethod def add(self, value): """Add an element.""" @@ -348,6 +364,8 @@ class Mapping(Sized, Iterable, Container): + __slots__ = () + @abstractmethod def __getitem__(self, key): raise KeyError @@ -451,6 +469,8 @@ class MutableMapping(Mapping): + __slots__ = () + @abstractmethod def __setitem__(self, key, value): raise KeyError @@ -530,6 +550,8 @@ __getitem__, and __len__. """ + __slots__ = () + @abstractmethod def __getitem__(self, index): raise IndexError @@ -575,12 +597,16 @@ XXX Should add all their methods. """ + __slots__ = () + ByteString.register(bytes) ByteString.register(bytearray) class MutableSequence(Sequence): + __slots__ = () + @abstractmethod def __setitem__(self, index, value): raise IndexError diff -r 3955265b16b6 -r 8bd713a823b5 Lib/email/quoprimime.py --- a/Lib/email/quoprimime.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/email/quoprimime.py Fri Mar 25 08:42:37 2011 +1000 @@ -40,6 +40,7 @@ ] import re +import io from string import ascii_letters, digits, hexdigits @@ -135,9 +136,9 @@ 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: @@ -147,6 +148,59 @@ return '=?%s?q?%s?=' % (charset, EMPTYSTRING.join(encoded)) +class _body_accumulator(io.StringIO): + + def __init__(self, maxlinelen, eol, *args, **kw): + super().__init__(*args, **kw) + self.eol = eol + self.maxlinelen = self.room = maxlinelen + + def write_str(self, s): + """Add string s to the accumulated body.""" + self.write(s) + self.room -= len(s) + + def newline(self): + """Write eol, then start new line.""" + self.write_str(self.eol) + self.room = self.maxlinelen + + def write_soft_break(self): + """Write a soft break, then start a new line.""" + self.write_str('=') + self.newline() + + def write_wrapped(self, s, extra_room=0): + """Add a soft line break if needed, then write s.""" + if self.room < len(s) + extra_room: + self.write_soft_break() + self.write_str(s) + + def write_char(self, c, is_last_char): + if not is_last_char: + # Another character follows on this line, so we must leave + # extra room, either for it or a soft break, and whitespace + # need not be quoted. + self.write_wrapped(c, extra_room=1) + elif c not in ' \t': + # For this and remaining cases, no more characters follow, + # so there is no need to reserve extra room (since a hard + # break will immediately follow). + self.write_wrapped(c) + elif self.room >= 3: + # It's a whitespace character at end-of-line, and we have room + # for the three-character quoted encoding. + self.write(quote(c)) + elif self.room == 2: + # There's room for the whitespace character and a soft break. + self.write(c) + self.write_soft_break() + else: + # There's room only for a soft break. The quoted whitespace + # will be the only content on the subsequent line. + self.write_soft_break() + self.write(quote(c)) + def body_encode(body, maxlinelen=76, eol=NL): """Encode with quoted-printable, wrapping at maxlinelen characters. @@ -155,72 +209,43 @@ this to "\\r\\n" if you will be using the result of this function directly in an email. - Each line will be wrapped at, at most, maxlinelen characters (defaults to - 76 characters). Long lines will have the `soft linefeed' quoted-printable - character "=" appended to them, so the decoded text will be identical to - the original text. + Each line will be wrapped at, at most, maxlinelen characters before the + eol string (maxlinelen defaults to 76 characters, the maximum value + permitted by RFC 2045). Long lines will have the 'soft line break' + quoted-printable character "=" appended to them, so the decoded text will + be identical to the original text. + + The minimum maxlinelen is 4 to have room for a quoted character ("=XX") + followed by a soft line break. Smaller values will generate a + ValueError. + """ + + if maxlinelen < 4: + raise ValueError("maxlinelen must be at least 4") if not body: return body - # BAW: We're accumulating the body text by string concatenation. That - # can't be very efficient, but I don't have time now to rewrite it. It - # just feels like this algorithm could be more efficient. - encoded_body = '' - lineno = -1 - # Preserve line endings here so we can check later to see an eol needs to - # be added to the output later. - lines = body.splitlines(1) - for line in lines: - # But strip off line-endings for processing this line. - if line.endswith(CRLF): - line = line[:-2] - elif line[-1] in CRLF: - line = line[:-1] + # The last line may or may not end in eol, but all other lines do. + last_has_eol = (body[-1] in '\r\n') - lineno += 1 - encoded_line = '' - prev = None - linelen = len(line) - # Now we need to examine every character to see if it needs to be - # quopri encoded. BAW: again, string concatenation is inefficient. - for j in range(linelen): - c = line[j] - prev = c + # This accumulator will make it easier to build the encoded body. + encoded_body = _body_accumulator(maxlinelen, eol) + + lines = body.splitlines() + last_line_no = len(lines) - 1 + for line_no, line in enumerate(lines): + last_char_index = len(line) - 1 + for i, c in enumerate(line): if body_check(ord(c)): c = quote(c) - elif j+1 == linelen: - # Check for whitespace at end of line; special case - if c not in ' \t': - encoded_line += c - prev = c - continue - # Check to see to see if the line has reached its maximum length - if len(encoded_line) + len(c) >= maxlinelen: - encoded_body += encoded_line + '=' + eol - encoded_line = '' - encoded_line += c - # Now at end of line.. - if prev and prev in ' \t': - # Special case for whitespace at end of file - if lineno + 1 == len(lines): - prev = quote(prev) - if len(encoded_line) + len(prev) > maxlinelen: - encoded_body += encoded_line + '=' + eol + prev - else: - encoded_body += encoded_line + prev - # Just normal whitespace at end of line - else: - encoded_body += encoded_line + prev + '=' + eol - encoded_line = '' - # Now look at the line we just finished and it has a line ending, we - # need to add eol to the end of the line. - if lines[lineno].endswith(CRLF) or lines[lineno][-1] in CRLF: - encoded_body += encoded_line + eol - else: - encoded_body += encoded_line - encoded_line = '' - return encoded_body + encoded_body.write_char(c, i==last_char_index) + # Add an eol if input line had eol. All input lines have eol except + # possibly the last one. + if line_no < last_line_no or last_has_eol: + encoded_body.newline() + + return encoded_body.getvalue() @@ -268,7 +293,7 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/getpass.py --- a/Lib/getpass.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/getpass.py Fri Mar 25 08:42:37 2011 +1000 @@ -62,7 +62,7 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/importlib/_bootstrap.py Fri Mar 25 08:42:37 2011 +1000 @@ -404,6 +404,7 @@ 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 @@ _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 -r 3955265b16b6 -r 8bd713a823b5 Lib/importlib/test/regrtest.py --- a/Lib/importlib/test/regrtest.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/importlib/test/regrtest.py Fri Mar 25 08:42:37 2011 +1000 @@ -5,13 +5,6 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/random.py --- a/Lib/random.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/random.py Fri Mar 25 08:42:37 2011 +1000 @@ -465,6 +465,12 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/site.py --- a/Lib/site.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/site.py Fri Mar 25 08:42:37 2011 +1000 @@ -508,6 +508,11 @@ 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 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/test/future_test1.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/future_test1.py Fri Mar 25 08:42:37 2011 +1000 @@ -0,0 +1,11 @@ +"""This is a test""" + +# Import the name nested_scopes twice to trigger SF bug #407394 (regression). +from __future__ import nested_scopes, nested_scopes + +def f(x): + def g(y): + return x + y + return g + +result = f(2)(4) diff -r 3955265b16b6 -r 8bd713a823b5 Lib/test/future_test2.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/future_test2.py Fri Mar 25 08:42:37 2011 +1000 @@ -0,0 +1,10 @@ +"""This is a test""" + +from __future__ import nested_scopes; import site + +def f(x): + def g(y): + return x + y + return g + +result = f(2)(4) diff -r 3955265b16b6 -r 8bd713a823b5 Lib/test/regrtest.py --- a/Lib/test/regrtest.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/regrtest.py Fri Mar 25 08:42:37 2011 +1000 @@ -42,6 +42,9 @@ -- specify which special resource intensive tests to run -M/--memlimit LIMIT -- run very large memory-consuming tests + --testdir DIR + -- execute test files in the specified directory (instead + of the Python stdlib test suite) Special runs @@ -265,7 +268,7 @@ 'use=', 'threshold=', 'trace', 'coverdir=', 'nocoverdir', 'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=', 'multiprocess=', 'coverage', 'slaveargs=', 'forever', 'debug', - 'start=', 'nowindows', 'header']) + 'start=', 'nowindows', 'header', 'testdir=']) except getopt.error as msg: usage(msg) @@ -315,7 +318,9 @@ elif o in ('-T', '--coverage'): trace = True elif o in ('-D', '--coverdir'): - coverdir = os.path.join(os.getcwd(), a) + # CWD is replaced with a temporary dir before calling main(), so we + # need join it with the saved CWD so it goes where the user expects. + coverdir = os.path.join(support.SAVEDCWD, a) elif o in ('-N', '--nocoverdir'): coverdir = None elif o in ('-R', '--huntrleaks'): @@ -374,6 +379,13 @@ 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': @@ -386,6 +398,10 @@ print() # Force a newline (just in case) print(json.dumps(result)) sys.exit(0) + elif o == '--testdir': + # CWD is replaced with a temporary dir before calling main(), so we + # join it with the saved CWD so it ends up where the user expects. + testdir = os.path.join(support.SAVEDCWD, a) else: print(("No handler for option {}. Please report this as a bug " "at http://bugs.python.org.").format(o), file=sys.stderr) @@ -460,7 +476,13 @@ print("== ", os.getcwd()) print("Testing with flags:", sys.flags) - alltests = findtests(testdir, stdtests, nottests) + # if testdir is set, then we are not running the python tests suite, so + # don't add default tests to be executed or skipped (pass empty values) + if testdir: + alltests = findtests(testdir, list(), set()) + else: + alltests = findtests(testdir, stdtests, nottests) + selected = tests or args or alltests if single: selected = selected[:1] @@ -535,7 +557,7 @@ 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 +631,9 @@ 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 @@ -708,6 +728,8 @@ sys.exit(len(bad) > 0 or interrupted) +# small set of tests to determine if we have a basically functioning interpreter +# (i.e. if any of these fail, then anything else is likely to follow) STDTESTS = [ 'test_grammar', 'test_opcodes', @@ -720,10 +742,8 @@ 'test_doctest2', ] -NOTTESTS = { - 'test_future1', - 'test_future2', -} +# set of tests that we don't want to be executed when using regrtest +NOTTESTS = set() def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS): """Return a list of all applicable test modules.""" @@ -758,7 +778,8 @@ 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 +788,7 @@ 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 +803,14 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/test/support.py --- a/Lib/test/support.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/support.py Fri Mar 25 08:42:37 2011 +1000 @@ -1389,9 +1389,8 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_binop.py --- a/Lib/test/test_binop.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/test_binop.py Fri Mar 25 08:42:37 2011 +1000 @@ -1,4 +1,6 @@ -"""Tests for binary operators on subtypes of built-in types.""" +"""Tests for binary operators on subtypes of built-in types and other + details of the binop implementation +""" import unittest from test import support @@ -373,6 +375,105 @@ self.assertEqual(op_sequence(le, B, C), ['C.__ge__', 'B.__le__']) self.assertEqual(op_sequence(le, C, B), ['C.__le__', 'B.__ge__']) + def test_issue11477_sequence_concatenation(self): + # Check overloading for A + B and A += B + # when A only implements sq_concat (but not nb_add) + testcase = self + class RHS: + def __init__(self): + self.allow_radd = True + def __iter__(self): + yield "Excellent!" + def __radd__(self, other): + if not self.allow_radd: + testcase.fail("RHS __radd__ called!") + return other + type(other)(self) + lhs = [] + rhs = RHS() + self.assertEqual(lhs.__add__(rhs), NotImplemented) + self.assertEqual(lhs + rhs, ["Excellent!"]) + with self.assertRaises(TypeError): + lhs + 1 + rhs.allow_radd = False + orig_lhs = lhs + lhs += rhs + self.assertIs(lhs, orig_lhs) + self.assertEqual(lhs, ["Excellent!"]) + with self.assertRaises(TypeError): + lhs += 1 + + def test_issue11477_sequence_concatenation_subclass(self): + # Check overloading for A + B and A += B + # when A only implements sq_concat (but not nb_add) + # and B is a subclass of A + testcase = self + # NOTE: We *cannot* create another subclass of list for the LHS + # here, since doing so moves the __add__ implementation from + # sq_concat to nb_add, bypassing the underlying behaviour + # we're trying to exercise. So we craft a test that can work + # with the standard behaviour of list.__add__ + class RHS(list): + def __init__(self): + list.__init__(self, ["Excellent!"]) + def __radd__(self, other): + return [] + lhs = [] + rhs = RHS() + self.assertEqual(lhs.__add__(rhs), ["Excellent!"]) + self.assertEqual(lhs + rhs, []) + orig_lhs = lhs + lhs += rhs + self.assertIs(lhs, orig_lhs) + self.assertEqual(lhs, ["Excellent!"]) + + def test_issue11477_sequence_repetition(self): + # Check overloading for A * B and A *= B + # when A only implements sq_repeat (but not nb_mul) + testcase = self + class Scalar: + def __index__(self): + return 2 + def __mul__(self, other): + return NotImplemented + def __rmul__(self, other): + testcase.fail("RHS __rmul__ called!") + seq = [None] + scalar = Scalar() + self.assertEqual(seq * scalar, [None, None]) + self.assertEqual(scalar * seq, [None, None]) + with self.assertRaises(TypeError): + seq * object() + orig_seq = seq + seq *= scalar + self.assertIs(seq, orig_seq) + self.assertEqual(seq, [None, None]) + with self.assertRaises(TypeError): + seq *= object() + + def test_issue11477_sequence_repetition_subclass(self): + # Check overloading for A * B and A *= B + # when A only implements sq_repeat (but not nb_mul) + # and B is a subclass of A + # This case may seem unlikely, but may arise for something + # like a 0-d matrix/scalar + testcase = self + class Scalar(list): + def __index__(self): + return 2 + def __mul__(self, other): + return NotImplemented + def __rmul__(self, other): + return 3 * other + seq = [None] + scalar = Scalar() + self.assertEqual(seq * scalar, [None, None, None]) + self.assertEqual(scalar * seq, [None, None]) + orig_seq = seq + seq *= scalar + self.assertIs(seq, orig_seq) + self.assertEqual(seq, [None, None]) + + def test_main(): support.run_unittest(RatTestCase, OperationOrderTests) diff -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_collections.py --- a/Lib/test/test_collections.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/test_collections.py Fri Mar 25 08:42:37 2011 +1000 @@ -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 @@ 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,17 @@ pass self.assertEqual(repr(B(1)), 'B(x=1)') + def test_source(self): + # verify that _source can be run through exec() + tmp = namedtuple('NTColor', 'red green blue') + globals().pop('NTColor', None) # remove artifacts from other tests + exec(tmp._source, globals()) + self.assertIn('NTColor', globals()) + c = NTColor(10, 20, 30) + self.assertEqual((c.red, c.green, c.blue), (10, 20, 30)) + self.assertEqual(NTColor._fields, ('red', 'green', 'blue')) + globals().pop('NTColor', None) # clean-up after this test + ################################################################################ ### Abstract Base Classes diff -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_concurrent_futures.py --- a/Lib/test/test_concurrent_futures.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/test_concurrent_futures.py Fri Mar 25 08:42:37 2011 +1000 @@ -9,6 +9,9 @@ # 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 @@ 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 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 @@ 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): @@ -155,7 +180,7 @@ class WaitTests(unittest.TestCase): def test_first_completed(self): future1 = self.executor.submit(mul, 21, 2) - future2 = self.executor.submit(time.sleep, 5) + future2 = self.executor.submit(time.sleep, 1.5) done, not_done = futures.wait( [CANCELLED_FUTURE, future1, future2], @@ -165,7 +190,7 @@ self.assertEqual(set([CANCELLED_FUTURE, future2]), not_done) def test_first_completed_some_already_completed(self): - future1 = self.executor.submit(time.sleep, 2) + future1 = self.executor.submit(time.sleep, 1.5) finished, pending = futures.wait( [CANCELLED_AND_NOTIFIED_FUTURE, SUCCESSFUL_FUTURE, future1], @@ -178,8 +203,8 @@ def test_first_exception(self): future1 = self.executor.submit(mul, 2, 21) - future2 = self.executor.submit(sleep_and_raise, 5) - future3 = self.executor.submit(time.sleep, 10) + future2 = self.executor.submit(sleep_and_raise, 1.5) + future3 = self.executor.submit(time.sleep, 3) finished, pending = futures.wait( [future1, future2, future3], @@ -190,7 +215,7 @@ def test_first_exception_some_already_complete(self): future1 = self.executor.submit(divmod, 21, 0) - future2 = self.executor.submit(time.sleep, 5) + future2 = self.executor.submit(time.sleep, 1.5) finished, pending = futures.wait( [SUCCESSFUL_FUTURE, @@ -235,14 +260,14 @@ def test_timeout(self): future1 = self.executor.submit(mul, 6, 7) - future2 = self.executor.submit(time.sleep, 10) + future2 = self.executor.submit(time.sleep, 3) finished, pending = futures.wait( [CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, SUCCESSFUL_FUTURE, future1, future2], - timeout=5, + timeout=1.5, return_when=futures.ALL_COMPLETED) self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE, @@ -332,8 +357,8 @@ results = [] try: for i in self.executor.map(time.sleep, - [0, 0, 10], - timeout=5): + [0, 0, 3], + timeout=1.5): results.append(i) except futures.TimeoutError: pass diff -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/test_email/test_email.py Fri Mar 25 08:42:37 2011 +1000 @@ -3344,21 +3344,217 @@ 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_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(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_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') + +# XXX: body_encode() expect strings, but uses ord(char) from these strings +# to index into a 256-entry list. For code points above 255, this will fail. +# Should there be a check for 8-bit only ord() values in body, or at least +# a comment about the expected input? + + def test_encode_two_lines_one_space(self): + self._test_encode(' \n \n', '=20\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_at_maxlinelen(self): + self._test_encode('abcd \n1234', 'abcd=\n=20\n1234', maxlinelen=5) + + def test_encode_trailing_space_beyond_maxlinelen(self): + self._test_encode('abcd \n1234', 'abc=\nd=20\n1234', maxlinelen=4) + + def test_encode_whitespace_lines(self): + self._test_encode(' \n' * 5, '=20\n' * 5) + + 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_maxlinelen_too_small(self): + self.assertRaises(ValueError, self._test_encode, '', '', maxlinelen=3) def test_encode(self): eq = self.assertEqual diff -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_future.py --- a/Lib/test/test_future.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/test_future.py Fri Mar 25 08:42:37 2011 +1000 @@ -13,14 +13,14 @@ class FutureTest(unittest.TestCase): def test_future1(self): - support.unload('test_future1') - from test import test_future1 - self.assertEqual(test_future1.result, 6) + support.unload('future_test1') + from test import future_test1 + self.assertEqual(future_test1.result, 6) def test_future2(self): - support.unload('test_future2') - from test import test_future2 - self.assertEqual(test_future2.result, 6) + support.unload('future_test2') + from test import future_test2 + self.assertEqual(future_test2.result, 6) def test_future3(self): support.unload('test_future3') diff -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_future1.py --- a/Lib/test/test_future1.py Tue Mar 22 21:04:12 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -"""This is a test""" - -# Import the name nested_scopes twice to trigger SF bug #407394 (regression). -from __future__ import nested_scopes, nested_scopes - -def f(x): - def g(y): - return x + y - return g - -result = f(2)(4) diff -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_future2.py --- a/Lib/test/test_future2.py Tue Mar 22 21:04:12 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -"""This is a test""" - -from __future__ import nested_scopes; import site - -def f(x): - def g(y): - return x + y - return g - -result = f(2)(4) diff -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_imaplib.py --- a/Lib/test/test_imaplib.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/test_imaplib.py Fri Mar 25 08:42:37 2011 +1000 @@ -219,20 +219,23 @@ 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 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/test_multiprocessing.py Fri Mar 25 08:42:37 2011 +1000 @@ -1100,7 +1100,7 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/test/test_peepholer.py --- a/Lib/test/test_peepholer.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/test/test_peepholer.py Fri Mar 25 08:42:37 2011 +1000 @@ -3,6 +3,7 @@ import sys from io import StringIO import unittest +from math import copysign def disassemble(func): f = StringIO() @@ -207,6 +208,9 @@ 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 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Lib/urllib/request.py --- a/Lib/urllib/request.py Tue Mar 22 21:04:12 2011 +1000 +++ b/Lib/urllib/request.py Fri Mar 25 08:42:37 2011 +1000 @@ -2136,7 +2136,7 @@ # 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 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Misc/ACKS --- a/Misc/ACKS Tue Mar 22 21:04:12 2011 +1000 +++ b/Misc/ACKS Fri Mar 25 08:42:37 2011 +1000 @@ -867,6 +867,7 @@ Frank J. Tobin R Lindsay Todd Bennett Todd +Eugene Toder Matias Torchinsky Sandro Tosi Richard Townsend diff -r 3955265b16b6 -r 8bd713a823b5 Misc/NEWS --- a/Misc/NEWS Tue Mar 22 21:04:12 2011 +1000 +++ b/Misc/NEWS Fri Mar 25 08:42:37 2011 +1000 @@ -10,6 +10,9 @@ 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,10 +84,26 @@ 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 #11628: cmp_to_key generated class should use __slots__ +- Issue #11333: Add __slots__ to collections ABCs. + +- 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. @@ -280,6 +299,8 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Misc/python.man --- a/Misc/python.man Tue Mar 22 21:04:12 2011 +1000 +++ b/Misc/python.man Fri Mar 25 08:42:37 2011 +1000 @@ -169,7 +169,9 @@ .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 -r 3955265b16b6 -r 8bd713a823b5 Objects/abstract.c --- a/Objects/abstract.c Tue Mar 22 21:04:12 2011 +1000 +++ b/Objects/abstract.c Fri Mar 25 08:42:37 2011 +1000 @@ -917,14 +917,36 @@ PyObject * PyNumber_Add(PyObject *v, PyObject *w) { - PyObject *result = binary_op1(v, w, NB_SLOT(nb_add)); - if (result == Py_NotImplemented) { - PySequenceMethods *m = v->ob_type->tp_as_sequence; - Py_DECREF(result); - if (m && m->sq_concat) { - return (*m->sq_concat)(v, w); + PyNumberMethods *m_num = v->ob_type->tp_as_number; + PySequenceMethods *m_seq = v->ob_type->tp_as_sequence; + PyObject *result = NULL; + /* We do a tapdance here so sq_concat is tried on the LH + * operand before __radd__ is tried on the RH operand. + * If the LH implements both nb_add and sq_concat,only nb_add + * is tried. + */ + int try_binop = 1; + int have_add = m_num && m_num->nb_add; + int have_concat = m_seq && m_seq->sq_concat; + if (have_add || !have_concat || + PyType_IsSubtype(w->ob_type, v->ob_type)) { + try_binop = 0; + result = binary_op1(v, w, NB_SLOT(nb_add)); + } + if (try_binop || result == Py_NotImplemented) { + if (!have_add && have_concat) { + Py_XDECREF(result); + result = (*m_seq->sq_concat)(v, w); + try_binop = result == Py_NotImplemented; } - result = binop_type_error(v, w, "+"); + if (try_binop) { + Py_XDECREF(result); + result = binary_op1(v, w, NB_SLOT(nb_add)); + } + if (result == Py_NotImplemented) { + Py_DECREF(result); + result = binop_type_error(v, w, "+"); + } } return result; } @@ -948,20 +970,46 @@ PyObject * PyNumber_Multiply(PyObject *v, PyObject *w) { - PyObject *result = binary_op1(v, w, NB_SLOT(nb_multiply)); - if (result == Py_NotImplemented) { - PySequenceMethods *mv = v->ob_type->tp_as_sequence; - PySequenceMethods *mw = w->ob_type->tp_as_sequence; - Py_DECREF(result); - if (mv && mv->sq_repeat) { - return sequence_repeat(mv->sq_repeat, v, w); + PyNumberMethods *m_num = v->ob_type->tp_as_number; + PySequenceMethods *m_seq = v->ob_type->tp_as_sequence; + PyObject *result = NULL; + /* We do a tapdance here so sq_repeat is tried on the LH + * operand before __rmul__ is tried on the RH operand. + * If the LH implements both nb_multiply and sq_repeat,only + * nb_multiply is tried. + */ + int try_binop = 1; + int have_mul = m_num && m_num->nb_multiply; + int have_repeat = m_seq && m_seq->sq_repeat; + if (have_mul || !have_repeat || + PyType_IsSubtype(w->ob_type, v->ob_type)) { + try_binop = 0; + result = binary_op1(v, w, NB_SLOT(nb_multiply)); + } + if (try_binop || result == Py_NotImplemented) { + if (!have_mul && have_repeat) { + Py_XDECREF(result); + result = sequence_repeat(m_seq->sq_repeat, v, w); + try_binop = result == Py_NotImplemented; } - else if (mw && mw->sq_repeat) { - return sequence_repeat(mw->sq_repeat, w, v); + if (try_binop) { + Py_XDECREF(result); + result = binary_op1(v, w, NB_SLOT(nb_multiply)); } - result = binop_type_error(v, w, "*"); + if (result == Py_NotImplemented) { + PySequenceMethods *m_seq_rhs = w->ob_type->tp_as_sequence; + if (m_seq_rhs && m_seq_rhs->sq_repeat) { + Py_DECREF(result); + result = sequence_repeat(m_seq_rhs->sq_repeat, w, v); + } + } + if (result == Py_NotImplemented) { + Py_DECREF(result); + result = binop_type_error(v, w, "*"); + } } return result; + } PyObject * @@ -1063,20 +1111,41 @@ PyObject * PyNumber_InPlaceAdd(PyObject *v, PyObject *w) { - PyObject *result = binary_iop1(v, w, NB_SLOT(nb_inplace_add), - NB_SLOT(nb_add)); - if (result == Py_NotImplemented) { - PySequenceMethods *m = v->ob_type->tp_as_sequence; - Py_DECREF(result); - if (m != NULL) { - binaryfunc f = NULL; - f = m->sq_inplace_concat; - if (f == NULL) - f = m->sq_concat; - if (f != NULL) - return (*f)(v, w); + PyNumberMethods *m_num = v->ob_type->tp_as_number; + PySequenceMethods *m_seq = v->ob_type->tp_as_sequence; + PyObject *result = NULL; + /* Similar tapdance to PyNumber_Add, but with extra checks to + * handle the in-place versions of the nb_* and sq_* methods + */ + int try_binop = 1; + int have_add = m_num && m_num->nb_add; + int have_inplace_add = m_num && m_num->nb_inplace_add; + int have_concat = m_seq && m_seq->sq_concat; + int have_inplace_concat = m_seq && m_seq->sq_inplace_concat; + if (have_inplace_add || (!have_inplace_concat && !have_concat)) { + try_binop = 0; + result = binary_iop1(v, w, NB_SLOT(nb_inplace_add), + NB_SLOT(nb_add)); + } + if (try_binop || result == Py_NotImplemented) { + if (!have_inplace_add && have_inplace_concat) { + Py_XDECREF(result); + result = (*m_seq->sq_inplace_concat)(v, w); + try_binop = result == Py_NotImplemented; } - result = binop_type_error(v, w, "+="); + if (try_binop) { + Py_XDECREF(result); + result = binary_iop1(v, w, NB_SLOT(nb_inplace_add), + NB_SLOT(nb_add)); + } + if (!have_add && have_concat && result == Py_NotImplemented) { + Py_DECREF(result); + result = (*m_seq->sq_concat)(v, w); + } + if (result == Py_NotImplemented) { + Py_DECREF(result); + result = binop_type_error(v, w, "+="); + } } return result; } @@ -1084,28 +1153,51 @@ PyObject * PyNumber_InPlaceMultiply(PyObject *v, PyObject *w) { - PyObject *result = binary_iop1(v, w, NB_SLOT(nb_inplace_multiply), - NB_SLOT(nb_multiply)); - if (result == Py_NotImplemented) { - ssizeargfunc f = NULL; - PySequenceMethods *mv = v->ob_type->tp_as_sequence; - PySequenceMethods *mw = w->ob_type->tp_as_sequence; - Py_DECREF(result); - if (mv != NULL) { - f = mv->sq_inplace_repeat; - if (f == NULL) - f = mv->sq_repeat; - if (f != NULL) - return sequence_repeat(f, v, w); + PyNumberMethods *m_num = v->ob_type->tp_as_number; + PySequenceMethods *m_seq = v->ob_type->tp_as_sequence; + PyObject *result = NULL; + /* Similar tapdance to PyNumber_Multiply, but with extra checks to + * handle the in-place versions of the nb_* and sq_* methods + */ + int try_binop = 1; + int have_mul = m_num && m_num->nb_multiply; + int have_inplace_mul = m_num && m_num->nb_inplace_multiply; + int have_repeat = m_seq && m_seq->sq_repeat; + int have_inplace_repeat = m_seq && m_seq->sq_inplace_repeat; + if (have_inplace_mul || (!have_inplace_repeat && !have_repeat)) { + try_binop = 0; + result = binary_iop1(v, w, NB_SLOT(nb_inplace_multiply), + NB_SLOT(nb_multiply)); + } + if (try_binop || result == Py_NotImplemented) { + if (!have_inplace_mul && have_inplace_repeat) { + Py_XDECREF(result); + result = sequence_repeat(m_seq->sq_inplace_repeat, v, w); + try_binop = result == Py_NotImplemented; } - else if (mw != NULL) { + if (try_binop) { + Py_XDECREF(result); + result = binary_iop1(v, w, NB_SLOT(nb_inplace_multiply), + NB_SLOT(nb_multiply)); + } + if (!have_mul && have_repeat && result == Py_NotImplemented) { + Py_DECREF(result); + result = sequence_repeat(m_seq->sq_repeat, v, w); + } + if (result == Py_NotImplemented) { /* Note that the right hand operand should not be * mutated in this case so sq_inplace_repeat is not - * used. */ - if (mw->sq_repeat) - return sequence_repeat(mw->sq_repeat, w, v); + * tried. */ + PySequenceMethods *m_seq_rhs = w->ob_type->tp_as_sequence; + if (m_seq_rhs && m_seq_rhs->sq_repeat) { + Py_DECREF(result); + result = sequence_repeat(m_seq_rhs->sq_repeat, w, v); + } } - result = binop_type_error(v, w, "*="); + if (result == Py_NotImplemented) { + Py_DECREF(result); + result = binop_type_error(v, w, "*="); + } } return result; } diff -r 3955265b16b6 -r 8bd713a823b5 Objects/bytearrayobject.c --- a/Objects/bytearrayobject.c Tue Mar 22 21:04:12 2011 +1000 +++ b/Objects/bytearrayobject.c Fri Mar 25 08:42:37 2011 +1000 @@ -228,14 +228,14 @@ { Py_ssize_t size; Py_buffer va, vb; - PyByteArrayObject *result = NULL; + PyObject *result = NULL; va.len = -1; vb.len = -1; if (_getbuffer(a, &va) < 0 || _getbuffer(b, &vb) < 0) { - PyErr_Format(PyExc_TypeError, "can't concat %.100s to %.100s", - Py_TYPE(a)->tp_name, Py_TYPE(b)->tp_name); + result = Py_NotImplemented; + Py_INCREF(result); goto done; } @@ -245,10 +245,11 @@ goto done; } - result = (PyByteArrayObject *) PyByteArray_FromStringAndSize(NULL, size); + result = PyByteArray_FromStringAndSize(NULL, size); if (result != NULL) { - memcpy(result->ob_bytes, va.buf, va.len); - memcpy(result->ob_bytes + va.len, vb.buf, vb.len); + void * data = ((PyByteArrayObject *) result)->ob_bytes; + memcpy(data, va.buf, va.len); + memcpy(data + va.len, vb.buf, vb.len); } done: diff -r 3955265b16b6 -r 8bd713a823b5 Objects/bytesobject.c --- a/Objects/bytesobject.c Tue Mar 22 21:04:12 2011 +1000 +++ b/Objects/bytesobject.c Fri Mar 25 08:42:37 2011 +1000 @@ -41,10 +41,6 @@ #define PyBytesObject_SIZE (offsetof(PyBytesObject, ob_sval) + 1) /* - For both PyBytes_FromString() and PyBytes_FromStringAndSize(), the - parameter `size' denotes number of characters to allocate, not counting any - null terminating character. - For PyBytes_FromString(), the parameter `str' points to a null-terminated string containing exactly `size' bytes. @@ -61,8 +57,8 @@ The PyObject member `op->ob_size', which denotes the number of "extra items" in a variable-size object, will contain the number of bytes - allocated for string data, not counting the null terminating character. It - is therefore equal to the equal to the `size' parameter (for + allocated for string data, not counting the null terminating character. + It is therefore equal to the `size' parameter (for PyBytes_FromStringAndSize()) or the length of the string in the `str' parameter (for PyBytes_FromString()). */ @@ -675,8 +671,8 @@ vb.len = -1; if (_getbuffer(a, &va) < 0 || _getbuffer(b, &vb) < 0) { - PyErr_Format(PyExc_TypeError, "can't concat %.100s to %.100s", - Py_TYPE(a)->tp_name, Py_TYPE(b)->tp_name); + result = Py_NotImplemented; + Py_INCREF(result); goto done; } diff -r 3955265b16b6 -r 8bd713a823b5 Objects/listobject.c --- a/Objects/listobject.c Tue Mar 22 21:04:12 2011 +1000 +++ b/Objects/listobject.c Fri Mar 25 08:42:37 2011 +1000 @@ -469,10 +469,8 @@ PyObject **src, **dest; PyListObject *np; if (!PyList_Check(bb)) { - PyErr_Format(PyExc_TypeError, - "can only concatenate list (not \"%.200s\") to list", - bb->ob_type->tp_name); - return NULL; + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } #define b ((PyListObject *)bb) size = Py_SIZE(a) + Py_SIZE(b); diff -r 3955265b16b6 -r 8bd713a823b5 Objects/setobject.c --- a/Objects/setobject.c Tue Mar 22 21:04:12 2011 +1000 +++ b/Objects/setobject.c Fri Mar 25 08:42:37 2011 +1000 @@ -365,11 +365,12 @@ { 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 @@ { PySetObject *other; PyObject *key; + Py_hash_t hash; register Py_ssize_t i; register setentry *entry; @@ -660,10 +662,11 @@ 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 -r 3955265b16b6 -r 8bd713a823b5 Objects/typeobject.c --- a/Objects/typeobject.c Tue Mar 22 21:04:12 2011 +1000 +++ b/Objects/typeobject.c Fri Mar 25 08:42:37 2011 +1000 @@ -5466,10 +5466,8 @@ SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc, "x.__len__() <==> len(x)"), /* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL. - The logic in abstract.c always falls back to nb_add/nb_multiply in - this case. Defining both the nb_* and the sq_* slots to call the - user-defined methods has unexpected side-effects, as shown by - test_descr.notimplemented() */ + The logic in abstract.c ignores them if nb_add/nb_multiply are + defined anyway. */ SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc, "x.__add__(y) <==> x+y"), SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc, diff -r 3955265b16b6 -r 8bd713a823b5 Objects/unicodeobject.c --- a/Objects/unicodeobject.c Tue Mar 22 21:04:12 2011 +1000 +++ b/Objects/unicodeobject.c Fri Mar 25 08:42:37 2011 +1000 @@ -7444,11 +7444,22 @@ /* Coerce the two arguments */ u = (PyUnicodeObject *)PyUnicode_FromObject(left); - if (u == NULL) - goto onError; + if (u == NULL) { + PyObject *ni = Py_NotImplemented; + /* XXX: PyErr_Matches check??? */ + PyErr_Clear(); + Py_INCREF(ni); + return ni; + } v = (PyUnicodeObject *)PyUnicode_FromObject(right); - if (v == NULL) - goto onError; + if (v == NULL) { + PyObject *ni = Py_NotImplemented; + /* XXX: PyErr_Matches check??? */ + PyErr_Clear(); + Py_DECREF(u); + Py_INCREF(ni); + return ni; + } /* Shortcuts */ if (v == unicode_empty) { diff -r 3955265b16b6 -r 8bd713a823b5 Python/import.c --- a/Python/import.c Tue Mar 22 21:04:12 2011 +1000 +++ b/Python/import.c Fri Mar 25 08:42:37 2011 +1000 @@ -1374,6 +1374,32 @@ 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 @@ #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 -r 3955265b16b6 -r 8bd713a823b5 Python/peephole.c --- a/Python/peephole.c Tue Mar 22 21:04:12 2011 +1000 +++ b/Python/peephole.c Fri Mar 25 08:42:37 2011 +1000 @@ -238,7 +238,7 @@ 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 @@ 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);