Author: Philip Jenvey <[email protected]>
Branch: vendor/stdlib-3.6
Changeset: r89221:ace7255d9a26
Date: 2016-12-23 22:39 -0800
http://bitbucket.org/pypy/pypy/changeset/ace7255d9a26/
Log: update stdlib to v3.6.0 (41df79263a11)
diff too long, truncating to 2000 out of 129052 lines
diff --git a/lib-python/3/_collections_abc.py b/lib-python/3/_collections_abc.py
--- a/lib-python/3/_collections_abc.py
+++ b/lib-python/3/_collections_abc.py
@@ -9,9 +9,10 @@
from abc import ABCMeta, abstractmethod
import sys
-__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
- "Hashable", "Iterable", "Iterator", "Generator",
- "Sized", "Container", "Callable",
+__all__ = ["Awaitable", "Coroutine",
+ "AsyncIterable", "AsyncIterator", "AsyncGenerator",
+ "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
+ "Sized", "Container", "Callable", "Collection",
"Set", "MutableSet",
"Mapping", "MutableMapping",
"MappingView", "KeysView", "ItemsView", "ValuesView",
@@ -29,8 +30,8 @@
# so that they will pass tests like:
# it = iter(somebytearray)
# assert isinstance(it, Iterable)
-# Note: in other implementations, these types many not be distinct
-# and they make have their own implementation specific types that
+# Note: in other implementations, these types might not be distinct
+# and they may have their own implementation specific types that
# are not included on this list.
bytes_iterator = type(iter(b''))
bytearray_iterator = type(iter(bytearray()))
@@ -41,6 +42,7 @@
list_iterator = type(iter([]))
list_reverseiterator = type(iter(reversed([])))
range_iterator = type(iter(range(0)))
+longrange_iterator = type(iter(range(1 << 1000)))
set_iterator = type(iter(set()))
str_iterator = type(iter(""))
tuple_iterator = type(iter(()))
@@ -58,10 +60,27 @@
coroutine = type(_coro)
_coro.close() # Prevent ResourceWarning
del _coro
+## asynchronous generator ##
+async def _ag(): yield
+_ag = _ag()
+async_generator = type(_ag)
+del _ag
### ONE-TRICK PONIES ###
+def _check_methods(C, *methods):
+ mro = C.__mro__
+ for method in methods:
+ for B in mro:
+ if method in B.__dict__:
+ if B.__dict__[method] is None:
+ return NotImplemented
+ break
+ else:
+ return NotImplemented
+ return True
+
class Hashable(metaclass=ABCMeta):
__slots__ = ()
@@ -73,11 +92,7 @@
@classmethod
def __subclasshook__(cls, C):
if cls is Hashable:
- for B in C.__mro__:
- if "__hash__" in B.__dict__:
- if B.__dict__["__hash__"]:
- return True
- break
+ return _check_methods(C, "__hash__")
return NotImplemented
@@ -92,11 +107,7 @@
@classmethod
def __subclasshook__(cls, C):
if cls is Awaitable:
- for B in C.__mro__:
- if "__await__" in B.__dict__:
- if B.__dict__["__await__"]:
- return True
- break
+ return _check_methods(C, "__await__")
return NotImplemented
@@ -137,14 +148,7 @@
@classmethod
def __subclasshook__(cls, C):
if cls is Coroutine:
- mro = C.__mro__
- for method in ('__await__', 'send', 'throw', 'close'):
- for base in mro:
- if method in base.__dict__:
- break
- else:
- return NotImplemented
- return True
+ return _check_methods(C, '__await__', 'send', 'throw', 'close')
return NotImplemented
@@ -162,8 +166,7 @@
@classmethod
def __subclasshook__(cls, C):
if cls is AsyncIterable:
- if any("__aiter__" in B.__dict__ for B in C.__mro__):
- return True
+ return _check_methods(C, "__aiter__")
return NotImplemented
@@ -182,12 +185,61 @@
@classmethod
def __subclasshook__(cls, C):
if cls is AsyncIterator:
- if (any("__anext__" in B.__dict__ for B in C.__mro__) and
- any("__aiter__" in B.__dict__ for B in C.__mro__)):
- return True
+ return _check_methods(C, "__anext__", "__aiter__")
return NotImplemented
+class AsyncGenerator(AsyncIterator):
+
+ __slots__ = ()
+
+ async def __anext__(self):
+ """Return the next item from the asynchronous generator.
+ When exhausted, raise StopAsyncIteration.
+ """
+ return await self.asend(None)
+
+ @abstractmethod
+ async def asend(self, value):
+ """Send a value into the asynchronous generator.
+ Return next yielded value or raise StopAsyncIteration.
+ """
+ raise StopAsyncIteration
+
+ @abstractmethod
+ async def athrow(self, typ, val=None, tb=None):
+ """Raise an exception in the asynchronous generator.
+ Return next yielded value or raise StopAsyncIteration.
+ """
+ if val is None:
+ if tb is None:
+ raise typ
+ val = typ()
+ if tb is not None:
+ val = val.with_traceback(tb)
+ raise val
+
+ async def aclose(self):
+ """Raise GeneratorExit inside coroutine.
+ """
+ try:
+ await self.athrow(GeneratorExit)
+ except (GeneratorExit, StopAsyncIteration):
+ pass
+ else:
+ raise RuntimeError("asynchronous generator ignored GeneratorExit")
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is AsyncGenerator:
+ return _check_methods(C, '__aiter__', '__anext__',
+ 'asend', 'athrow', 'aclose')
+ return NotImplemented
+
+
+AsyncGenerator.register(async_generator)
+
+
class Iterable(metaclass=ABCMeta):
__slots__ = ()
@@ -200,8 +252,7 @@
@classmethod
def __subclasshook__(cls, C):
if cls is Iterable:
- if any("__iter__" in B.__dict__ for B in C.__mro__):
- return True
+ return _check_methods(C, "__iter__")
return NotImplemented
@@ -220,9 +271,7 @@
@classmethod
def __subclasshook__(cls, C):
if cls is Iterator:
- if (any("__next__" in B.__dict__ for B in C.__mro__) and
- any("__iter__" in B.__dict__ for B in C.__mro__)):
- return True
+ return _check_methods(C, '__iter__', '__next__')
return NotImplemented
Iterator.register(bytes_iterator)
@@ -234,12 +283,29 @@
Iterator.register(list_iterator)
Iterator.register(list_reverseiterator)
Iterator.register(range_iterator)
+Iterator.register(longrange_iterator)
Iterator.register(set_iterator)
Iterator.register(str_iterator)
Iterator.register(tuple_iterator)
Iterator.register(zip_iterator)
+class Reversible(Iterable):
+
+ __slots__ = ()
+
+ @abstractmethod
+ def __reversed__(self):
+ while False:
+ yield None
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Reversible:
+ return _check_methods(C, "__reversed__", "__iter__")
+ return NotImplemented
+
+
class Generator(Iterator):
__slots__ = ()
@@ -283,17 +349,10 @@
@classmethod
def __subclasshook__(cls, C):
if cls is Generator:
- mro = C.__mro__
- for method in ('__iter__', '__next__', 'send', 'throw', 'close'):
- for base in mro:
- if method in base.__dict__:
- break
- else:
- return NotImplemented
- return True
+ return _check_methods(C, '__iter__', '__next__',
+ 'send', 'throw', 'close')
return NotImplemented
-
Generator.register(generator)
@@ -308,8 +367,7 @@
@classmethod
def __subclasshook__(cls, C):
if cls is Sized:
- if any("__len__" in B.__dict__ for B in C.__mro__):
- return True
+ return _check_methods(C, "__len__")
return NotImplemented
@@ -324,10 +382,18 @@
@classmethod
def __subclasshook__(cls, C):
if cls is Container:
- if any("__contains__" in B.__dict__ for B in C.__mro__):
- return True
+ return _check_methods(C, "__contains__")
return NotImplemented
+class Collection(Sized, Iterable, Container):
+
+ __slots__ = ()
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Collection:
+ return _check_methods(C, "__len__", "__iter__", "__contains__")
+ return NotImplemented
class Callable(metaclass=ABCMeta):
@@ -340,15 +406,14 @@
@classmethod
def __subclasshook__(cls, C):
if cls is Callable:
- if any("__call__" in B.__dict__ for B in C.__mro__):
- return True
+ return _check_methods(C, "__call__")
return NotImplemented
### SETS ###
-class Set(Sized, Iterable, Container):
+class Set(Collection):
"""A set is a finite, iterable container.
@@ -573,7 +638,7 @@
### MAPPINGS ###
-class Mapping(Sized, Iterable, Container):
+class Mapping(Collection):
__slots__ = ()
@@ -621,6 +686,8 @@
return NotImplemented
return dict(self.items()) == dict(other.items())
+ __reversed__ = None
+
Mapping.register(mappingproxy)
@@ -670,7 +737,7 @@
except KeyError:
return False
else:
- return v == value
+ return v is value or v == value
def __iter__(self):
for key in self._mapping:
@@ -685,7 +752,8 @@
def __contains__(self, value):
for key in self._mapping:
- if value == self._mapping[key]:
+ v = self._mapping[key]
+ if v is value or v == value:
return True
return False
@@ -794,7 +862,7 @@
### SEQUENCES ###
-class Sequence(Sized, Iterable, Container):
+class Sequence(Reversible, Collection):
"""All the operations on a read-only sequence.
@@ -820,7 +888,7 @@
def __contains__(self, value):
for v in self:
- if v == value:
+ if v is value or v == value:
return True
return False
diff --git a/lib-python/3/_compat_pickle.py b/lib-python/3/_compat_pickle.py
--- a/lib-python/3/_compat_pickle.py
+++ b/lib-python/3/_compat_pickle.py
@@ -242,3 +242,10 @@
for excname in PYTHON3_OSERROR_EXCEPTIONS:
REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'OSError')
+
+PYTHON3_IMPORTERROR_EXCEPTIONS = (
+ 'ModuleNotFoundError',
+)
+
+for excname in PYTHON3_IMPORTERROR_EXCEPTIONS:
+ REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'ImportError')
diff --git a/lib-python/3/_osx_support.py b/lib-python/3/_osx_support.py
--- a/lib-python/3/_osx_support.py
+++ b/lib-python/3/_osx_support.py
@@ -210,7 +210,7 @@
# Do not alter a config var explicitly overridden by env var
if cv in _config_vars and cv not in os.environ:
flags = _config_vars[cv]
- flags = re.sub('-arch\s+\w+\s', ' ', flags, re.ASCII)
+ flags = re.sub(r'-arch\s+\w+\s', ' ', flags, re.ASCII)
flags = re.sub('-isysroot [^ \t]*', ' ', flags)
_save_modified_value(_config_vars, cv, flags)
@@ -232,7 +232,7 @@
if 'CC' in os.environ:
return _config_vars
- if re.search('-arch\s+ppc', _config_vars['CFLAGS']) is not None:
+ if re.search(r'-arch\s+ppc', _config_vars['CFLAGS']) is not None:
# NOTE: Cannot use subprocess here because of bootstrap
# issues when building Python itself
status = os.system(
@@ -251,7 +251,7 @@
for cv in _UNIVERSAL_CONFIG_VARS:
if cv in _config_vars and cv not in os.environ:
flags = _config_vars[cv]
- flags = re.sub('-arch\s+ppc\w*\s', ' ', flags)
+ flags = re.sub(r'-arch\s+ppc\w*\s', ' ', flags)
_save_modified_value(_config_vars, cv, flags)
return _config_vars
@@ -267,7 +267,7 @@
for cv in _UNIVERSAL_CONFIG_VARS:
if cv in _config_vars and '-arch' in _config_vars[cv]:
flags = _config_vars[cv]
- flags = re.sub('-arch\s+\w+\s', ' ', flags)
+ flags = re.sub(r'-arch\s+\w+\s', ' ', flags)
flags = flags + ' ' + arch
_save_modified_value(_config_vars, cv, flags)
@@ -465,7 +465,7 @@
machine = 'fat'
- archs = re.findall('-arch\s+(\S+)', cflags)
+ archs = re.findall(r'-arch\s+(\S+)', cflags)
archs = tuple(sorted(set(archs)))
if len(archs) == 1:
diff --git a/lib-python/3/_pydecimal.py b/lib-python/3/_pydecimal.py
--- a/lib-python/3/_pydecimal.py
+++ b/lib-python/3/_pydecimal.py
@@ -148,7 +148,7 @@
__name__ = 'decimal' # For pickling
__version__ = '1.70' # Highest version of the spec this complies with
# See http://speleotrove.com/decimal/
-__libmpdec_version__ = "2.4.1" # compatible libmpdec version
+__libmpdec_version__ = "2.4.2" # compatible libmpdec version
import math as _math
import numbers as _numbers
@@ -589,7 +589,7 @@
# From a string
# REs insist on real strings, so we can too.
if isinstance(value, str):
- m = _parser(value.strip())
+ m = _parser(value.strip().replace("_", ""))
if m is None:
if context is None:
context = getcontext()
@@ -1010,6 +1010,56 @@
"""
return DecimalTuple(self._sign, tuple(map(int, self._int)), self._exp)
+ def as_integer_ratio(self):
+ """Express a finite Decimal instance in the form n / d.
+
+ Returns a pair (n, d) of integers. When called on an infinity
+ or NaN, raises OverflowError or ValueError respectively.
+
+ >>> Decimal('3.14').as_integer_ratio()
+ (157, 50)
+ >>> Decimal('-123e5').as_integer_ratio()
+ (-12300000, 1)
+ >>> Decimal('0.00').as_integer_ratio()
+ (0, 1)
+
+ """
+ if self._is_special:
+ if self.is_nan():
+ raise ValueError("cannot convert NaN to integer ratio")
+ else:
+ raise OverflowError("cannot convert Infinity to integer ratio")
+
+ if not self:
+ return 0, 1
+
+ # Find n, d in lowest terms such that abs(self) == n / d;
+ # we'll deal with the sign later.
+ n = int(self._int)
+ if self._exp >= 0:
+ # self is an integer.
+ n, d = n * 10**self._exp, 1
+ else:
+ # Find d2, d5 such that abs(self) = n / (2**d2 * 5**d5).
+ d5 = -self._exp
+ while d5 > 0 and n % 5 == 0:
+ n //= 5
+ d5 -= 1
+
+ # (n & -n).bit_length() - 1 counts trailing zeros in binary
+ # representation of n (provided n is nonzero).
+ d2 = -self._exp
+ shift2 = min((n & -n).bit_length() - 1, d2)
+ if shift2:
+ n >>= shift2
+ d2 -= shift2
+
+ d = 5**d5 << d2
+
+ if self._sign:
+ n = -n
+ return n, d
+
def __repr__(self):
"""Represents the number as an instance of Decimal."""
# Invariant: eval(repr(d)) == d
@@ -1068,12 +1118,11 @@
return sign + intpart + fracpart + exp
def to_eng_string(self, context=None):
- """Convert to engineering-type string.
-
- Engineering notation has an exponent which is a multiple of 3, so there
- are up to 3 digits left of the decimal place.
-
- Same rules for when in exponential and when as a value as in __str__.
+ """Convert to a string, using engineering notation if an exponent is
needed.
+
+ Engineering notation has an exponent which is a multiple of 3. This
+ can leave up to 3 digits to the left of the decimal place and may
+ require the addition of either one or two trailing zeros.
"""
return self.__str__(eng=True, context=context)
@@ -4076,7 +4125,7 @@
This will make it round up for that operation.
"""
rounding = self.rounding
- self.rounding= type
+ self.rounding = type
return rounding
def create_decimal(self, num='0'):
@@ -4085,10 +4134,10 @@
This method implements the to-number operation of the
IBM Decimal specification."""
- if isinstance(num, str) and num != num.strip():
+ if isinstance(num, str) and (num != num.strip() or '_' in num):
return self._raise_error(ConversionSyntax,
- "no trailing or leading whitespace is "
- "permitted.")
+ "trailing or leading whitespace and "
+ "underscores are not permitted.")
d = Decimal(num, context=self)
if d._isnan() and len(d._int) > self.prec - self.clamp:
@@ -4107,7 +4156,7 @@
>>> context.create_decimal_from_float(3.1415926535897932)
Traceback (most recent call last):
...
- decimal.Inexact
+ decimal.Inexact: None
"""
d = Decimal.from_float(f) # An exact conversion
@@ -5502,9 +5551,29 @@
return r
def to_eng_string(self, a):
- """Converts a number to a string, using scientific notation.
+ """Convert to a string, using engineering notation if an exponent is
needed.
+
+ Engineering notation has an exponent which is a multiple of 3. This
+ can leave up to 3 digits to the left of the decimal place and may
+ require the addition of either one or two trailing zeros.
The operation is not affected by the context.
+
+ >>> ExtendedContext.to_eng_string(Decimal('123E+1'))
+ '1.23E+3'
+ >>> ExtendedContext.to_eng_string(Decimal('123E+3'))
+ '123E+3'
+ >>> ExtendedContext.to_eng_string(Decimal('123E-10'))
+ '12.3E-9'
+ >>> ExtendedContext.to_eng_string(Decimal('-123E-12'))
+ '-123E-12'
+ >>> ExtendedContext.to_eng_string(Decimal('7E-7'))
+ '700E-9'
+ >>> ExtendedContext.to_eng_string(Decimal('7E+1'))
+ '70'
+ >>> ExtendedContext.to_eng_string(Decimal('0E+1'))
+ '0.00E+3'
+
"""
a = _convert_other(a, raiseit=True)
return a.to_eng_string(context=self)
diff --git a/lib-python/3/_pyio.py b/lib-python/3/_pyio.py
--- a/lib-python/3/_pyio.py
+++ b/lib-python/3/_pyio.py
@@ -6,7 +6,6 @@
import abc
import codecs
import errno
-import array
import stat
import sys
# Import _thread instead of threading to reduce startup cost
@@ -161,6 +160,8 @@
opened in a text mode, and for bytes a BytesIO can be used like a file
opened in a binary mode.
"""
+ if not isinstance(file, int):
+ file = os.fspath(file)
if not isinstance(file, (str, bytes, int)):
raise TypeError("invalid file: %r" % file)
if not isinstance(mode, str):
@@ -182,8 +183,8 @@
text = "t" in modes
binary = "b" in modes
if "U" in modes:
- if creating or writing or appending:
- raise ValueError("can't use U and writing mode at once")
+ if creating or writing or appending or updating:
+ raise ValueError("mode U cannot be combined with 'x', 'w', 'a', or
'+'")
import warnings
warnings.warn("'U' mode is deprecated",
DeprecationWarning, 2)
@@ -1516,7 +1517,7 @@
if self._fd >= 0 and self._closefd and not self.closed:
import warnings
warnings.warn('unclosed file %r' % (self,), ResourceWarning,
- stacklevel=2)
+ stacklevel=2, source=self)
self.close()
def __getstate__(self):
diff --git a/lib-python/3/_strptime.py b/lib-python/3/_strptime.py
--- a/lib-python/3/_strptime.py
+++ b/lib-python/3/_strptime.py
@@ -199,12 +199,15 @@
'f': r"(?P<f>[0-9]{1,6})",
'H': r"(?P<H>2[0-3]|[0-1]\d|\d)",
'I': r"(?P<I>1[0-2]|0[1-9]|[1-9])",
+ 'G': r"(?P<G>\d\d\d\d)",
'j':
r"(?P<j>36[0-6]|3[0-5]\d|[1-2]\d\d|0[1-9]\d|00[1-9]|[1-9]\d|0[1-9]|[1-9])",
'm': r"(?P<m>1[0-2]|0[1-9]|[1-9])",
'M': r"(?P<M>[0-5]\d|\d)",
'S': r"(?P<S>6[0-1]|[0-5]\d|\d)",
'U': r"(?P<U>5[0-3]|[0-4]\d|\d)",
'w': r"(?P<w>[0-6])",
+ 'u': r"(?P<u>[1-7])",
+ 'V': r"(?P<V>5[0-3]|0[1-9]|[1-4]\d|\d)",
# W is set below by using 'U'
'y': r"(?P<y>\d\d)",
#XXX: Does 'Y' need to worry about having less or more than
@@ -299,6 +302,22 @@
return 1 + days_to_week + day_of_week
+def _calc_julian_from_V(iso_year, iso_week, iso_weekday):
+ """Calculate the Julian day based on the ISO 8601 year, week, and weekday.
+ ISO weeks start on Mondays, with week 01 being the week containing 4 Jan.
+ ISO week days range from 1 (Monday) to 7 (Sunday).
+ """
+ correction = datetime_date(iso_year, 1, 4).isoweekday() + 3
+ ordinal = (iso_week * 7) + iso_weekday - correction
+ # ordinal may be negative or 0 now, which means the date is in the previous
+ # calendar year
+ if ordinal < 1:
+ ordinal += datetime_date(iso_year, 1, 1).toordinal()
+ iso_year -= 1
+ ordinal -= datetime_date(iso_year, 1, 1).toordinal()
+ return iso_year, ordinal
+
+
def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a 2-tuple consisting of a time struct and an int containing
the number of microseconds based on the input string and the
@@ -345,15 +364,15 @@
raise ValueError("unconverted data remains: %s" %
data_string[found.end():])
- year = None
+ iso_year = year = None
month = day = 1
hour = minute = second = fraction = 0
tz = -1
tzoffset = None
# Default to -1 to signify that values not known; not critical to have,
# though
- week_of_year = -1
- week_of_year_start = -1
+ iso_week = week_of_year = None
+ week_of_year_start = None
# weekday and julian defaulted to None so as to signal need to calculate
# values
weekday = julian = None
@@ -375,6 +394,8 @@
year += 1900
elif group_key == 'Y':
year = int(found_dict['Y'])
+ elif group_key == 'G':
+ iso_year = int(found_dict['G'])
elif group_key == 'm':
month = int(found_dict['m'])
elif group_key == 'B':
@@ -420,6 +441,9 @@
weekday = 6
else:
weekday -= 1
+ elif group_key == 'u':
+ weekday = int(found_dict['u'])
+ weekday -= 1
elif group_key == 'j':
julian = int(found_dict['j'])
elif group_key in ('U', 'W'):
@@ -430,6 +454,8 @@
else:
# W starts week on Monday.
week_of_year_start = 0
+ elif group_key == 'V':
+ iso_week = int(found_dict['V'])
elif group_key == 'z':
z = found_dict['z']
tzoffset = int(z[1:3]) * 60 + int(z[3:5])
@@ -450,32 +476,61 @@
else:
tz = value
break
+ # Deal with the cases where ambiguities arize
+ # don't assume default values for ISO week/year
+ if year is None and iso_year is not None:
+ if iso_week is None or weekday is None:
+ raise ValueError("ISO year directive '%G' must be used with "
+ "the ISO week directive '%V' and a weekday "
+ "directive ('%A', '%a', '%w', or '%u').")
+ if julian is not None:
+ raise ValueError("Day of the year directive '%j' is not "
+ "compatible with ISO year directive '%G'. "
+ "Use '%Y' instead.")
+ elif week_of_year is None and iso_week is not None:
+ if weekday is None:
+ raise ValueError("ISO week directive '%V' must be used with "
+ "the ISO year directive '%G' and a weekday "
+ "directive ('%A', '%a', '%w', or '%u').")
+ else:
+ raise ValueError("ISO week directive '%V' is incompatible with "
+ "the year directive '%Y'. Use the ISO year '%G' "
+ "instead.")
+
leap_year_fix = False
if year is None and month == 2 and day == 29:
year = 1904 # 1904 is first leap year of 20th century
leap_year_fix = True
elif year is None:
year = 1900
+
+
# If we know the week of the year and what day of that week, we can figure
# out the Julian day of the year.
- if julian is None and week_of_year != -1 and weekday is not None:
- week_starts_Mon = True if week_of_year_start == 0 else False
- julian = _calc_julian_from_U_or_W(year, week_of_year, weekday,
- week_starts_Mon)
- if julian <= 0:
+ if julian is None and weekday is not None:
+ if week_of_year is not None:
+ week_starts_Mon = True if week_of_year_start == 0 else False
+ julian = _calc_julian_from_U_or_W(year, week_of_year, weekday,
+ week_starts_Mon)
+ elif iso_year is not None and iso_week is not None:
+ year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1)
+ if julian is not None and julian <= 0:
year -= 1
yday = 366 if calendar.isleap(year) else 365
julian += yday
- # Cannot pre-calculate datetime_date() since can change in Julian
- # calculation and thus could have different value for the day of the week
- # calculation.
+
if julian is None:
+ # Cannot pre-calculate datetime_date() since can change in Julian
+ # calculation and thus could have different value for the day of
+ # the week calculation.
# Need to add 1 to result since first day of the year is 1, not 0.
julian = datetime_date(year, month, day).toordinal() - \
datetime_date(year, 1, 1).toordinal() + 1
- else: # Assume that if they bothered to include Julian day it will
- # be accurate.
- datetime_result = datetime_date.fromordinal((julian - 1) +
datetime_date(year, 1, 1).toordinal())
+ else: # Assume that if they bothered to include Julian day (or if it was
+ # calculated above with year/week/weekday) it will be accurate.
+ datetime_result = datetime_date.fromordinal(
+ (julian - 1) +
+ datetime_date(year, 1, 1).toordinal())
year = datetime_result.year
month = datetime_result.month
day = datetime_result.day
diff --git a/lib-python/3/aifc.py b/lib-python/3/aifc.py
--- a/lib-python/3/aifc.py
+++ b/lib-python/3/aifc.py
@@ -257,6 +257,15 @@
_aifc_params = namedtuple('_aifc_params',
'nchannels sampwidth framerate nframes comptype
compname')
+_aifc_params.nchannels.__doc__ = 'Number of audio channels (1 for mono, 2 for
stereo)'
+_aifc_params.sampwidth.__doc__ = 'Sample width in bytes'
+_aifc_params.framerate.__doc__ = 'Sampling frequency'
+_aifc_params.nframes.__doc__ = 'Number of audio frames'
+_aifc_params.comptype.__doc__ = 'Compression type ("NONE" for AIFF files)'
+_aifc_params.compname.__doc__ = ("""\
+A human-readable version of the compression type
+('not compressed' for AIFF files)""")
+
class Aifc_read:
# Variables used in this class:
diff --git a/lib-python/3/antigravity.py b/lib-python/3/antigravity.py
--- a/lib-python/3/antigravity.py
+++ b/lib-python/3/antigravity.py
@@ -2,7 +2,7 @@
import webbrowser
import hashlib
-webbrowser.open("http://xkcd.com/353/")
+webbrowser.open("https://xkcd.com/353/")
def geohash(latitude, longitude, datedow):
'''Compute geohash() using the Munroe algorithm.
diff --git a/lib-python/3/argparse.py b/lib-python/3/argparse.py
--- a/lib-python/3/argparse.py
+++ b/lib-python/3/argparse.py
@@ -118,10 +118,16 @@
def __repr__(self):
type_name = type(self).__name__
arg_strings = []
+ star_args = {}
for arg in self._get_args():
arg_strings.append(repr(arg))
for name, value in self._get_kwargs():
- arg_strings.append('%s=%r' % (name, value))
+ if name.isidentifier():
+ arg_strings.append('%s=%r' % (name, value))
+ else:
+ star_args[name] = value
+ if star_args:
+ arg_strings.append('**%s' % repr(star_args))
return '%s(%s)' % (type_name, ', '.join(arg_strings))
def _get_kwargs(self):
@@ -204,8 +210,6 @@
if self.parent is not None:
self.formatter._indent()
join = self.formatter._join_parts
- for func, args in self.items:
- func(*args)
item_help = join([func(*args) for func, args in self.items])
if self.parent is not None:
self.formatter._dedent()
diff --git a/lib-python/3/ast.py b/lib-python/3/ast.py
--- a/lib-python/3/ast.py
+++ b/lib-python/3/ast.py
@@ -35,6 +35,8 @@
return compile(source, filename, mode, PyCF_ONLY_AST)
+_NUM_TYPES = (int, float, complex)
+
def literal_eval(node_or_string):
"""
Safely evaluate an expression node or a string containing a Python
@@ -47,7 +49,9 @@
if isinstance(node_or_string, Expression):
node_or_string = node_or_string.body
def _convert(node):
- if isinstance(node, (Str, Bytes)):
+ if isinstance(node, Constant):
+ return node.value
+ elif isinstance(node, (Str, Bytes)):
return node.s
elif isinstance(node, Num):
return node.n
@@ -62,24 +66,21 @@
in zip(node.keys, node.values))
elif isinstance(node, NameConstant):
return node.value
- elif isinstance(node, UnaryOp) and \
- isinstance(node.op, (UAdd, USub)) and \
- isinstance(node.operand, (Num, UnaryOp, BinOp)):
+ elif isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
operand = _convert(node.operand)
- if isinstance(node.op, UAdd):
- return + operand
- else:
- return - operand
- elif isinstance(node, BinOp) and \
- isinstance(node.op, (Add, Sub)) and \
- isinstance(node.right, (Num, UnaryOp, BinOp)) and \
- isinstance(node.left, (Num, UnaryOp, BinOp)):
+ if isinstance(operand, _NUM_TYPES):
+ if isinstance(node.op, UAdd):
+ return + operand
+ else:
+ return - operand
+ elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
left = _convert(node.left)
right = _convert(node.right)
- if isinstance(node.op, Add):
- return left + right
- else:
- return left - right
+ if isinstance(left, _NUM_TYPES) and isinstance(right, _NUM_TYPES):
+ if isinstance(node.op, Add):
+ return left + right
+ else:
+ return left - right
raise ValueError('malformed node or string: ' + repr(node))
return _convert(node_or_string)
@@ -196,12 +197,19 @@
"""
if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
raise TypeError("%r can't have docstrings" % node.__class__.__name__)
- if node.body and isinstance(node.body[0], Expr) and \
- isinstance(node.body[0].value, Str):
- if clean:
- import inspect
- return inspect.cleandoc(node.body[0].value.s)
- return node.body[0].value.s
+ if not(node.body and isinstance(node.body[0], Expr)):
+ return
+ node = node.body[0].value
+ if isinstance(node, Str):
+ text = node.s
+ elif isinstance(node, Constant) and isinstance(node.value, str):
+ text = node.value
+ else:
+ return
+ if clean:
+ import inspect
+ text = inspect.cleandoc(text)
+ return text
def walk(node):
diff --git a/lib-python/3/asynchat.py b/lib-python/3/asynchat.py
--- a/lib-python/3/asynchat.py
+++ b/lib-python/3/asynchat.py
@@ -285,35 +285,6 @@
return result
-class fifo:
- def __init__(self, list=None):
- import warnings
- warnings.warn('fifo class will be removed in Python 3.6',
- DeprecationWarning, stacklevel=2)
- if not list:
- self.list = deque()
- else:
- self.list = deque(list)
-
- def __len__(self):
- return len(self.list)
-
- def is_empty(self):
- return not self.list
-
- def first(self):
- return self.list[0]
-
- def push(self, data):
- self.list.append(data)
-
- def pop(self):
- if self.list:
- return (1, self.list.popleft())
- else:
- return (0, None)
-
-
# Given 'haystack', see if any prefix of 'needle' is at its end. This
# assumes an exact match has already been checked. Return the number of
# characters matched.
diff --git a/lib-python/3/asyncio/base_events.py
b/lib-python/3/asyncio/base_events.py
--- a/lib-python/3/asyncio/base_events.py
+++ b/lib-python/3/asyncio/base_events.py
@@ -13,7 +13,6 @@
to modify the meaning of the API call itself.
"""
-
import collections
import concurrent.futures
import heapq
@@ -28,6 +27,7 @@
import traceback
import sys
import warnings
+import weakref
from . import compat
from . import coroutines
@@ -41,9 +41,6 @@
__all__ = ['BaseEventLoop']
-# Argument for default thread pool executor creation.
-_MAX_WORKERS = 5
-
# Minimum number of _scheduled timer handles before cleanup of
# cancelled handles is performed.
_MIN_SCHEDULED_TIMER_HANDLES = 100
@@ -60,7 +57,7 @@
def _format_handle(handle):
cb = handle._callback
- if inspect.ismethod(cb) and isinstance(cb.__self__, tasks.Task):
+ if isinstance(getattr(cb, '__self__', None), tasks.Task):
# format the task
return repr(cb.__self__)
else:
@@ -76,12 +73,29 @@
return repr(fd)
-# Linux's sock.type is a bitmask that can include extra info about socket.
-_SOCKET_TYPE_MASK = 0
-if hasattr(socket, 'SOCK_NONBLOCK'):
- _SOCKET_TYPE_MASK |= socket.SOCK_NONBLOCK
-if hasattr(socket, 'SOCK_CLOEXEC'):
- _SOCKET_TYPE_MASK |= socket.SOCK_CLOEXEC
+def _set_reuseport(sock):
+ if not hasattr(socket, 'SO_REUSEPORT'):
+ raise ValueError('reuse_port not supported by socket module')
+ else:
+ try:
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ except OSError:
+ raise ValueError('reuse_port not supported by socket module, '
+ 'SO_REUSEPORT defined but not implemented.')
+
+
+def _is_stream_socket(sock):
+ # Linux's socket.type is a bitmask that can include extra info
+ # about socket, therefore we can't do simple
+ # `sock_type == socket.SOCK_STREAM`.
+ return (sock.type & socket.SOCK_STREAM) == socket.SOCK_STREAM
+
+
+def _is_dgram_socket(sock):
+ # Linux's socket.type is a bitmask that can include extra info
+ # about socket, therefore we can't do simple
+ # `sock_type == socket.SOCK_DGRAM`.
+ return (sock.type & socket.SOCK_DGRAM) == socket.SOCK_DGRAM
def _ipaddr_info(host, port, family, type, proto):
@@ -94,8 +108,12 @@
host is None:
return None
- type &= ~_SOCKET_TYPE_MASK
if type == socket.SOCK_STREAM:
+ # Linux only:
+ # getaddrinfo() can raise when socket.type is a bit mask.
+ # So if socket.type is a bit mask of SOCK_STREAM, and say
+ # SOCK_NONBLOCK, we simply return None, which will trigger
+ # a call to getaddrinfo() letting it process this request.
proto = socket.IPPROTO_TCP
elif type == socket.SOCK_DGRAM:
proto = socket.IPPROTO_UDP
@@ -104,27 +122,21 @@
if port is None:
port = 0
- elif isinstance(port, bytes):
- if port == b'':
- port = 0
- else:
- try:
- port = int(port)
- except ValueError:
- # Might be a service name like b"http".
- port = socket.getservbyname(port.decode('ascii'))
- elif isinstance(port, str):
- if port == '':
- port = 0
- else:
- try:
- port = int(port)
- except ValueError:
- # Might be a service name like "http".
- port = socket.getservbyname(port)
+ elif isinstance(port, bytes) and port == b'':
+ port = 0
+ elif isinstance(port, str) and port == '':
+ port = 0
+ else:
+ # If port's a service name like "http", don't skip getaddrinfo.
+ try:
+ port = int(port)
+ except (TypeError, ValueError):
+ return None
if family == socket.AF_UNSPEC:
- afs = [socket.AF_INET, socket.AF_INET6]
+ afs = [socket.AF_INET]
+ if hasattr(socket, 'AF_INET6'):
+ afs.append(socket.AF_INET6)
else:
afs = [family]
@@ -242,6 +254,17 @@
self._task_factory = None
self._coroutine_wrapper_set = False
+ if hasattr(sys, 'get_asyncgen_hooks'):
+ # Python >= 3.6
+ # A weak set of all asynchronous generators that are
+ # being iterated by the loop.
+ self._asyncgens = weakref.WeakSet()
+ else:
+ self._asyncgens = None
+
+ # Set to True when `loop.shutdown_asyncgens` is called.
+ self._asyncgens_shutdown_called = False
+
def __repr__(self):
return ('<%s running=%s closed=%s debug=%s>'
% (self.__class__.__name__, self.is_running(),
@@ -333,14 +356,67 @@
if self._closed:
raise RuntimeError('Event loop is closed')
+ def _asyncgen_finalizer_hook(self, agen):
+ self._asyncgens.discard(agen)
+ if not self.is_closed():
+ self.create_task(agen.aclose())
+ # Wake up the loop if the finalizer was called from
+ # a different thread.
+ self._write_to_self()
+
+ def _asyncgen_firstiter_hook(self, agen):
+ if self._asyncgens_shutdown_called:
+ warnings.warn(
+ "asynchronous generator {!r} was scheduled after "
+ "loop.shutdown_asyncgens() call".format(agen),
+ ResourceWarning, source=self)
+
+ self._asyncgens.add(agen)
+
+ @coroutine
+ def shutdown_asyncgens(self):
+ """Shutdown all active asynchronous generators."""
+ self._asyncgens_shutdown_called = True
+
+ if self._asyncgens is None or not len(self._asyncgens):
+ # If Python version is <3.6 or we don't have any asynchronous
+ # generators alive.
+ return
+
+ closing_agens = list(self._asyncgens)
+ self._asyncgens.clear()
+
+ shutdown_coro = tasks.gather(
+ *[ag.aclose() for ag in closing_agens],
+ return_exceptions=True,
+ loop=self)
+
+ results = yield from shutdown_coro
+ for result, agen in zip(results, closing_agens):
+ if isinstance(result, Exception):
+ self.call_exception_handler({
+ 'message': 'an error occurred during closing of '
+ 'asynchronous generator {!r}'.format(agen),
+ 'exception': result,
+ 'asyncgen': agen
+ })
+
def run_forever(self):
"""Run until stop() is called."""
self._check_closed()
if self.is_running():
- raise RuntimeError('Event loop is running.')
+ raise RuntimeError('This event loop is already running')
+ if events._get_running_loop() is not None:
+ raise RuntimeError(
+ 'Cannot run the event loop while another loop is running')
self._set_coroutine_wrapper(self._debug)
self._thread_id = threading.get_ident()
+ if self._asyncgens is not None:
+ old_agen_hooks = sys.get_asyncgen_hooks()
+ sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook,
+ finalizer=self._asyncgen_finalizer_hook)
try:
+ events._set_running_loop(self)
while True:
self._run_once()
if self._stopping:
@@ -348,7 +424,10 @@
finally:
self._stopping = False
self._thread_id = None
+ events._set_running_loop(None)
self._set_coroutine_wrapper(False)
+ if self._asyncgens is not None:
+ sys.set_asyncgen_hooks(*old_agen_hooks)
def run_until_complete(self, future):
"""Run until the Future is done.
@@ -363,7 +442,7 @@
"""
self._check_closed()
- new_task = not isinstance(future, futures.Future)
+ new_task = not futures.isfuture(future)
future = tasks.ensure_future(future, loop=self)
if new_task:
# An exception is raised if the future didn't complete, so there
@@ -426,7 +505,8 @@
if compat.PY34:
def __del__(self):
if not self.is_closed():
- warnings.warn("unclosed event loop %r" % self, ResourceWarning)
+ warnings.warn("unclosed event loop %r" % self, ResourceWarning,
+ source=self)
if not self.is_running():
self.close()
@@ -469,12 +549,10 @@
Absolute time corresponds to the event loop's time() method.
"""
- if (coroutines.iscoroutine(callback)
- or coroutines.iscoroutinefunction(callback)):
- raise TypeError("coroutines cannot be used with call_at()")
self._check_closed()
if self._debug:
self._check_thread()
+ self._check_callback(callback, 'call_at')
timer = events.TimerHandle(when, callback, args, self)
if timer._source_traceback:
del timer._source_traceback[-1]
@@ -492,18 +570,27 @@
Any positional arguments after the callback will be passed to
the callback when it is called.
"""
+ self._check_closed()
if self._debug:
self._check_thread()
+ self._check_callback(callback, 'call_soon')
handle = self._call_soon(callback, args)
if handle._source_traceback:
del handle._source_traceback[-1]
return handle
+ def _check_callback(self, callback, method):
+ if (coroutines.iscoroutine(callback) or
+ coroutines.iscoroutinefunction(callback)):
+ raise TypeError(
+ "coroutines cannot be used with {}()".format(method))
+ if not callable(callback):
+ raise TypeError(
+ 'a callable object was expected by {}(), got {!r}'.format(
+ method, callback))
+
+
def _call_soon(self, callback, args):
- if (coroutines.iscoroutine(callback)
- or coroutines.iscoroutinefunction(callback)):
- raise TypeError("coroutines cannot be used with call_soon()")
- self._check_closed()
handle = events.Handle(callback, args, self)
if handle._source_traceback:
del handle._source_traceback[-1]
@@ -529,6 +616,9 @@
def call_soon_threadsafe(self, callback, *args):
"""Like call_soon(), but thread-safe."""
+ self._check_closed()
+ if self._debug:
+ self._check_callback(callback, 'call_soon_threadsafe')
handle = self._call_soon(callback, args)
if handle._source_traceback:
del handle._source_traceback[-1]
@@ -536,22 +626,13 @@
return handle
def run_in_executor(self, executor, func, *args):
- if (coroutines.iscoroutine(func)
- or coroutines.iscoroutinefunction(func)):
- raise TypeError("coroutines cannot be used with run_in_executor()")
self._check_closed()
- if isinstance(func, events.Handle):
- assert not args
- assert not isinstance(func, events.TimerHandle)
- if func._cancelled:
- f = self.create_future()
- f.set_result(None)
- return f
- func, args = func._callback, func._args
+ if self._debug:
+ self._check_callback(func, 'run_in_executor')
if executor is None:
executor = self._default_executor
if executor is None:
- executor = concurrent.futures.ThreadPoolExecutor(_MAX_WORKERS)
+ executor = concurrent.futures.ThreadPoolExecutor()
self._default_executor = executor
return futures.wrap_future(executor.submit(func, *args), loop=self)
@@ -703,11 +784,19 @@
raise OSError('Multiple exceptions: {}'.format(
', '.join(str(exc) for exc in exceptions)))
- elif sock is None:
- raise ValueError(
- 'host and port was not specified and no sock specified')
-
- sock.setblocking(False)
+ else:
+ if sock is None:
+ raise ValueError(
+ 'host and port was not specified and no sock specified')
+ if not _is_stream_socket(sock):
+ # We allow AF_INET, AF_INET6, AF_UNIX as long as they
+ # are SOCK_STREAM.
+ # We support passing AF_UNIX sockets even though we have
+ # a dedicated API for that: create_unix_connection.
+ # Disallowing AF_UNIX in this method, breaks backwards
+ # compatibility.
+ raise ValueError(
+ 'A Stream Socket was expected, got {!r}'.format(sock))
transport, protocol = yield from self._create_connection_transport(
sock, protocol_factory, ssl, server_hostname)
@@ -721,14 +810,17 @@
@coroutine
def _create_connection_transport(self, sock, protocol_factory, ssl,
- server_hostname):
+ server_hostname, server_side=False):
+
+ sock.setblocking(False)
+
protocol = protocol_factory()
waiter = self.create_future()
if ssl:
sslcontext = None if isinstance(ssl, bool) else ssl
transport = self._make_ssl_transport(
sock, protocol, sslcontext, waiter,
- server_side=False, server_hostname=server_hostname)
+ server_side=server_side, server_hostname=server_hostname)
else:
transport = self._make_socket_transport(sock, protocol, waiter)
@@ -748,6 +840,9 @@
allow_broadcast=None, sock=None):
"""Create datagram connection."""
if sock is not None:
+ if not _is_dgram_socket(sock):
+ raise ValueError(
+ 'A UDP Socket was expected, got {!r}'.format(sock))
if (local_addr or remote_addr or
family or proto or flags or
reuse_address or reuse_port or allow_broadcast):
@@ -813,12 +908,7 @@
sock.setsockopt(
socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if reuse_port:
- if not hasattr(socket, 'SO_REUSEPORT'):
- raise ValueError(
- 'reuse_port not supported by socket module')
- else:
- sock.setsockopt(
- socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ _set_reuseport(sock)
if allow_broadcast:
sock.setsockopt(
socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
@@ -941,12 +1031,7 @@
sock.setsockopt(
socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
if reuse_port:
- if not hasattr(socket, 'SO_REUSEPORT'):
- raise ValueError(
- 'reuse_port not supported by socket module')
- else:
- sock.setsockopt(
- socket.SOL_SOCKET, socket.SO_REUSEPORT, True)
+ _set_reuseport(sock)
# Disable IPv4/IPv6 dual stack support (enabled by
# default on Linux) which makes a single socket
# listen on both address families.
@@ -968,18 +1053,44 @@
else:
if sock is None:
raise ValueError('Neither host/port nor sock were specified')
+ if not _is_stream_socket(sock):
+ raise ValueError(
+ 'A Stream Socket was expected, got {!r}'.format(sock))
sockets = [sock]
server = Server(self, sockets)
for sock in sockets:
sock.listen(backlog)
sock.setblocking(False)
- self._start_serving(protocol_factory, sock, ssl, server)
+ self._start_serving(protocol_factory, sock, ssl, server, backlog)
if self._debug:
logger.info("%r is serving", server)
return server
@coroutine
+ def connect_accepted_socket(self, protocol_factory, sock, *, ssl=None):
+ """Handle an accepted connection.
+
+ This is used by servers that accept connections outside of
+ asyncio but that use asyncio to handle connections.
+
+ This method is a coroutine. When completed, the coroutine
+ returns a (transport, protocol) pair.
+ """
+ if not _is_stream_socket(sock):
+ raise ValueError(
+ 'A Stream Socket was expected, got {!r}'.format(sock))
+
+ transport, protocol = yield from self._create_connection_transport(
+ sock, protocol_factory, ssl, '', server_side=True)
+ if self._debug:
+ # Get the socket from the transport because SSL transport closes
+ # the old socket and creates a new SSL socket
+ sock = transport.get_extra_info('socket')
+ logger.debug("%r handled: (%r, %r)", sock, transport, protocol)
+ return transport, protocol
+
+ @coroutine
def connect_read_pipe(self, protocol_factory, pipe):
protocol = protocol_factory()
waiter = self.create_future()
@@ -1048,7 +1159,7 @@
transport = yield from self._make_subprocess_transport(
protocol, cmd, True, stdin, stdout, stderr, bufsize, **kwargs)
if self._debug:
- logger.info('%s: %r' % (debug_log, transport))
+ logger.info('%s: %r', debug_log, transport)
return transport, protocol
@coroutine
@@ -1078,7 +1189,7 @@
protocol, popen_args, False, stdin, stdout, stderr,
bufsize, **kwargs)
if self._debug:
- logger.info('%s: %r' % (debug_log, transport))
+ logger.info('%s: %r', debug_log, transport)
return transport, protocol
def get_exception_handler(self):
@@ -1158,7 +1269,9 @@
- 'handle' (optional): Handle instance;
- 'protocol' (optional): Protocol instance;
- 'transport' (optional): Transport instance;
- - 'socket' (optional): Socket instance.
+ - 'socket' (optional): Socket instance;
+ - 'asyncgen' (optional): Asynchronous generator that caused
+ the exception.
New keys maybe introduced in the future.
diff --git a/lib-python/3/asyncio/base_futures.py
b/lib-python/3/asyncio/base_futures.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/asyncio/base_futures.py
@@ -0,0 +1,71 @@
+__all__ = []
+
+import concurrent.futures._base
+import reprlib
+
+from . import events
+
+Error = concurrent.futures._base.Error
+CancelledError = concurrent.futures.CancelledError
+TimeoutError = concurrent.futures.TimeoutError
+
+
+class InvalidStateError(Error):
+ """The operation is not allowed in this state."""
+
+
+# States for Future.
+_PENDING = 'PENDING'
+_CANCELLED = 'CANCELLED'
+_FINISHED = 'FINISHED'
+
+
+def isfuture(obj):
+ """Check for a Future.
+
+ This returns True when obj is a Future instance or is advertising
+ itself as duck-type compatible by setting _asyncio_future_blocking.
+ See comment in Future for more details.
+ """
+ return (hasattr(obj.__class__, '_asyncio_future_blocking') and
+ obj._asyncio_future_blocking is not None)
+
+
+def _format_callbacks(cb):
+ """helper function for Future.__repr__"""
+ size = len(cb)
+ if not size:
+ cb = ''
+
+ def format_cb(callback):
+ return events._format_callback_source(callback, ())
+
+ if size == 1:
+ cb = format_cb(cb[0])
+ elif size == 2:
+ cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
+ elif size > 2:
+ cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
+ size - 2,
+ format_cb(cb[-1]))
+ return 'cb=[%s]' % cb
+
+
+def _future_repr_info(future):
+ # (Future) -> str
+ """helper function for Future.__repr__"""
+ info = [future._state.lower()]
+ if future._state == _FINISHED:
+ if future._exception is not None:
+ info.append('exception={!r}'.format(future._exception))
+ else:
+ # use reprlib to limit the length of the output, especially
+ # for very long strings
+ result = reprlib.repr(future._result)
+ info.append('result={}'.format(result))
+ if future._callbacks:
+ info.append(_format_callbacks(future._callbacks))
+ if future._source_traceback:
+ frame = future._source_traceback[-1]
+ info.append('created at %s:%s' % (frame[0], frame[1]))
+ return info
diff --git a/lib-python/3/asyncio/base_subprocess.py
b/lib-python/3/asyncio/base_subprocess.py
--- a/lib-python/3/asyncio/base_subprocess.py
+++ b/lib-python/3/asyncio/base_subprocess.py
@@ -3,7 +3,6 @@
import warnings
from . import compat
-from . import futures
from . import protocols
from . import transports
from .coroutines import coroutine
@@ -87,6 +86,12 @@
def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
raise NotImplementedError
+ def set_protocol(self, protocol):
+ self._protocol = protocol
+
+ def get_protocol(self):
+ return self._protocol
+
def is_closing(self):
return self._closed
@@ -122,7 +127,8 @@
if compat.PY34:
def __del__(self):
if not self._closed:
- warnings.warn("unclosed transport %r" % self, ResourceWarning)
+ warnings.warn("unclosed transport %r" % self, ResourceWarning,
+ source=self)
self.close()
def get_pid(self):
diff --git a/lib-python/3/asyncio/base_tasks.py
b/lib-python/3/asyncio/base_tasks.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/asyncio/base_tasks.py
@@ -0,0 +1,76 @@
+import linecache
+import traceback
+
+from . import base_futures
+from . import coroutines
+
+
+def _task_repr_info(task):
+ info = base_futures._future_repr_info(task)
+
+ if task._must_cancel:
+ # replace status
+ info[0] = 'cancelling'
+
+ coro = coroutines._format_coroutine(task._coro)
+ info.insert(1, 'coro=<%s>' % coro)
+
+ if task._fut_waiter is not None:
+ info.insert(2, 'wait_for=%r' % task._fut_waiter)
+ return info
+
+
+def _task_get_stack(task, limit):
+ frames = []
+ try:
+ # 'async def' coroutines
+ f = task._coro.cr_frame
+ except AttributeError:
+ f = task._coro.gi_frame
+ if f is not None:
+ while f is not None:
+ if limit is not None:
+ if limit <= 0:
+ break
+ limit -= 1
+ frames.append(f)
+ f = f.f_back
+ frames.reverse()
+ elif task._exception is not None:
+ tb = task._exception.__traceback__
+ while tb is not None:
+ if limit is not None:
+ if limit <= 0:
+ break
+ limit -= 1
+ frames.append(tb.tb_frame)
+ tb = tb.tb_next
+ return frames
+
+
+def _task_print_stack(task, limit, file):
+ extracted_list = []
+ checked = set()
+ for f in task.get_stack(limit=limit):
+ lineno = f.f_lineno
+ co = f.f_code
+ filename = co.co_filename
+ name = co.co_name
+ if filename not in checked:
+ checked.add(filename)
+ linecache.checkcache(filename)
+ line = linecache.getline(filename, lineno, f.f_globals)
+ extracted_list.append((filename, lineno, name, line))
+ exc = task._exception
+ if not extracted_list:
+ print('No stack for %r' % task, file=file)
+ elif exc is not None:
+ print('Traceback for %r (most recent call last):' % task,
+ file=file)
+ else:
+ print('Stack for %r (most recent call last):' % task,
+ file=file)
+ traceback.print_list(extracted_list, file=file)
+ if exc is not None:
+ for line in traceback.format_exception_only(exc.__class__, exc):
+ print(line, file=file, end='')
diff --git a/lib-python/3/asyncio/coroutines.py
b/lib-python/3/asyncio/coroutines.py
--- a/lib-python/3/asyncio/coroutines.py
+++ b/lib-python/3/asyncio/coroutines.py
@@ -11,7 +11,7 @@
from . import compat
from . import events
-from . import futures
+from . import base_futures
from .log import logger
@@ -33,12 +33,16 @@
try:
_types_coroutine = types.coroutine
+ _types_CoroutineType = types.CoroutineType
except AttributeError:
+ # Python 3.4
_types_coroutine = None
+ _types_CoroutineType = None
try:
_inspect_iscoroutinefunction = inspect.iscoroutinefunction
except AttributeError:
+ # Python 3.4
_inspect_iscoroutinefunction = lambda func: False
try:
@@ -120,8 +124,8 @@
def send(self, value):
return self.gen.send(value)
- def throw(self, exc):
- return self.gen.throw(exc)
+ def throw(self, type, value=None, traceback=None):
+ return self.gen.throw(type, value, traceback)
def close(self):
return self.gen.close()
@@ -204,8 +208,8 @@
@functools.wraps(func)
def coro(*args, **kw):
res = func(*args, **kw)
- if isinstance(res, futures.Future) or inspect.isgenerator(res) or \
- isinstance(res, CoroWrapper):
+ if (base_futures.isfuture(res) or inspect.isgenerator(res) or
+ isinstance(res, CoroWrapper)):
res = yield from res
elif _AwaitableABC is not None:
# If 'func' returns an Awaitable (new in 3.5) we
@@ -238,19 +242,27 @@
w.__qualname__ = getattr(func, '__qualname__', None)
return w
- wrapper._is_coroutine = True # For iscoroutinefunction().
+ wrapper._is_coroutine = _is_coroutine # For iscoroutinefunction().
return wrapper
+# A marker for iscoroutinefunction.
+_is_coroutine = object()
+
+
def iscoroutinefunction(func):
"""Return True if func is a decorated coroutine function."""
- return (getattr(func, '_is_coroutine', False) or
+ return (getattr(func, '_is_coroutine', None) is _is_coroutine or
_inspect_iscoroutinefunction(func))
_COROUTINE_TYPES = (types.GeneratorType, CoroWrapper)
if _CoroutineABC is not None:
_COROUTINE_TYPES += (_CoroutineABC,)
+if _types_CoroutineType is not None:
+ # Prioritize native coroutine check to speed-up
+ # asyncio.iscoroutine.
+ _COROUTINE_TYPES = (_types_CoroutineType,) + _COROUTINE_TYPES
def iscoroutine(obj):
@@ -261,6 +273,29 @@
def _format_coroutine(coro):
assert iscoroutine(coro)
+ if not hasattr(coro, 'cr_code') and not hasattr(coro, 'gi_code'):
+ # Most likely a built-in type or a Cython coroutine.
+
+ # Built-in types might not have __qualname__ or __name__.
+ coro_name = getattr(
+ coro, '__qualname__',
+ getattr(coro, '__name__', type(coro).__name__))
+ coro_name = '{}()'.format(coro_name)
+
+ running = False
+ try:
+ running = coro.cr_running
+ except AttributeError:
+ try:
+ running = coro.gi_running
+ except AttributeError:
+ pass
+
+ if running:
+ return '{} running'.format(coro_name)
+ else:
+ return coro_name
+
coro_name = None
if isinstance(coro, CoroWrapper):
func = coro.func
@@ -271,7 +306,7 @@
func = coro
if coro_name is None:
- coro_name = events._format_callback(func, ())
+ coro_name = events._format_callback(func, (), {})
try:
coro_code = coro.gi_code
diff --git a/lib-python/3/asyncio/events.py b/lib-python/3/asyncio/events.py
--- a/lib-python/3/asyncio/events.py
+++ b/lib-python/3/asyncio/events.py
@@ -6,6 +6,7 @@
'get_event_loop_policy', 'set_event_loop_policy',
'get_event_loop', 'set_event_loop', 'new_event_loop',
'get_child_watcher', 'set_child_watcher',
+ '_set_running_loop', '_get_running_loop',
]
import functools
@@ -35,23 +36,25 @@
return None
-def _format_args(args):
- """Format function arguments.
+def _format_args_and_kwargs(args, kwargs):
+ """Format function arguments and keyword arguments.
Special case for a single parameter: ('hello',) is formatted as ('hello').
"""
# use reprlib to limit the length of the output
- args_repr = reprlib.repr(args)
- if len(args) == 1 and args_repr.endswith(',)'):
- args_repr = args_repr[:-2] + ')'
- return args_repr
+ items = []
+ if args:
+ items.extend(reprlib.repr(arg) for arg in args)
+ if kwargs:
+ items.extend('{}={}'.format(k, reprlib.repr(v))
+ for k, v in kwargs.items())
+ return '(' + ', '.join(items) + ')'
-def _format_callback(func, args, suffix=''):
+def _format_callback(func, args, kwargs, suffix=''):
if isinstance(func, functools.partial):
- if args is not None:
- suffix = _format_args(args) + suffix
- return _format_callback(func.func, func.args, suffix)
+ suffix = _format_args_and_kwargs(args, kwargs) + suffix
+ return _format_callback(func.func, func.args, func.keywords, suffix)
if hasattr(func, '__qualname__'):
func_repr = getattr(func, '__qualname__')
@@ -60,14 +63,13 @@
else:
func_repr = repr(func)
- if args is not None:
- func_repr += _format_args(args)
+ func_repr += _format_args_and_kwargs(args, kwargs)
if suffix:
func_repr += suffix
return func_repr
def _format_callback_source(func, args):
- func_repr = _format_callback(func, args)
+ func_repr = _format_callback(func, args, None)
source = _get_function_source(func)
if source:
func_repr += ' at %s:%s' % source
@@ -81,7 +83,6 @@
'_source_traceback', '_repr', '__weakref__')
def __init__(self, callback, args, loop):
- assert not isinstance(callback, Handle), 'A Handle is not a callback'
self._loop = loop
self._callback = callback
self._args = args
@@ -248,6 +249,10 @@
"""
raise NotImplementedError
+ def shutdown_asyncgens(self):
+ """Shutdown all active asynchronous generators."""
+ raise NotImplementedError
+
# Methods scheduling callbacks. All these return Handles.
def _timer_handle_cancelled(self, handle):
@@ -603,6 +608,30 @@
_lock = threading.Lock()
+# A TLS for the running event loop, used by _get_running_loop.
+class _RunningLoop(threading.local):
+ _loop = None
+_running_loop = _RunningLoop()
+
+
+def _get_running_loop():
+ """Return the running event loop or None.
+
+ This is a low-level function intended to be used by event loops.
+ This function is thread-specific.
+ """
+ return _running_loop._loop
+
+
+def _set_running_loop(loop):
+ """Set the running event loop.
+
+ This is a low-level function intended to be used by event loops.
+ This function is thread-specific.
+ """
+ _running_loop._loop = loop
+
+
def _init_event_loop_policy():
global _event_loop_policy
with _lock:
@@ -628,7 +657,17 @@
def get_event_loop():
- """Equivalent to calling get_event_loop_policy().get_event_loop()."""
+ """Return an asyncio event loop.
+
+ When called from a coroutine or a callback (e.g. scheduled with call_soon
+ or similar API), this function will always return the running event loop.
+
+ If there is no running event loop set, the function will return
+ the result of `get_event_loop_policy().get_event_loop()` call.
+ """
+ current_loop = _get_running_loop()
+ if current_loop is not None:
+ return current_loop
return get_event_loop_policy().get_event_loop()
diff --git a/lib-python/3/asyncio/futures.py b/lib-python/3/asyncio/futures.py
--- a/lib-python/3/asyncio/futures.py
+++ b/lib-python/3/asyncio/futures.py
@@ -1,35 +1,32 @@
"""A Future class similar to the one in PEP 3148."""
-__all__ = ['CancelledError', 'TimeoutError',
- 'InvalidStateError',
- 'Future', 'wrap_future',
- ]
+__all__ = ['CancelledError', 'TimeoutError', 'InvalidStateError',
+ 'Future', 'wrap_future', 'isfuture']
-import concurrent.futures._base
+import concurrent.futures
import logging
-import reprlib
import sys
import traceback
+from . import base_futures
from . import compat
from . import events
-# States for Future.
-_PENDING = 'PENDING'
-_CANCELLED = 'CANCELLED'
-_FINISHED = 'FINISHED'
-Error = concurrent.futures._base.Error
-CancelledError = concurrent.futures.CancelledError
-TimeoutError = concurrent.futures.TimeoutError
+CancelledError = base_futures.CancelledError
+InvalidStateError = base_futures.InvalidStateError
+TimeoutError = base_futures.TimeoutError
+isfuture = base_futures.isfuture
+
+
+_PENDING = base_futures._PENDING
+_CANCELLED = base_futures._CANCELLED
+_FINISHED = base_futures._FINISHED
+
STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
-class InvalidStateError(Error):
- """The operation is not allowed in this state."""
-
-
class _TracebackLogger:
"""Helper to log a traceback upon destruction if not cleared.
@@ -134,7 +131,15 @@
_loop = None
_source_traceback = None
- _blocking = False # proper use of future (yield vs yield from)
+ # This field is used for a dual purpose:
+ # - Its presence is a marker to declare that a class implements
+ # the Future protocol (i.e. is intended to be duck-type compatible).
+ # The value must also be not-None, to enable a subclass to declare
+ # that it is not compatible by setting this to None.
+ # - It is set by __iter__() below so that Task._step() can tell
+ # the difference between `yield from Future()` (correct) vs.
+ # `yield Future()` (incorrect).
+ _asyncio_future_blocking = False
_log_traceback = False # Used for Python 3.4 and later
_tb_logger = None # Used for Python 3.3 only
@@ -154,45 +159,10 @@
if self._loop.get_debug():
self._source_traceback = traceback.extract_stack(sys._getframe(1))
- def __format_callbacks(self):
- cb = self._callbacks
- size = len(cb)
- if not size:
- cb = ''
-
- def format_cb(callback):
- return events._format_callback_source(callback, ())
-
- if size == 1:
- cb = format_cb(cb[0])
- elif size == 2:
- cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
- elif size > 2:
- cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
- size-2,
- format_cb(cb[-1]))
- return 'cb=[%s]' % cb
-
- def _repr_info(self):
- info = [self._state.lower()]
- if self._state == _FINISHED:
- if self._exception is not None:
- info.append('exception={!r}'.format(self._exception))
- else:
- # use reprlib to limit the length of the output, especially
- # for very long strings
- result = reprlib.repr(self._result)
- info.append('result={}'.format(result))
- if self._callbacks:
- info.append(self.__format_callbacks())
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit