diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index f86e784288029c..5138afc2bbe47b 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -524,8 +524,8 @@ Opening network connections When a server's IPv4 path and protocol are working, but the server's IPv6 path and protocol are not working, a dual-stack client application experiences significant connection delay compared to an - IPv4-only client. This is undesirable because it causes the dual- - stack client to have a worse user experience. This document + IPv4-only client. This is undesirable because it causes the + dual-stack client to have a worse user experience. This document specifies requirements for algorithms that reduce this user-visible delay and provides an algorithm. diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 169f7196a74ec6..bbbf6920ddec88 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1588,7 +1588,7 @@ These are not used in annotations. They are building blocks for creating generic methods, not their type signatures. For example, :class:`ssl.SSLObject` is a class, therefore it passes an :func:`issubclass` check against :data:`Callable`. However, the - :meth:`ssl.SSLObject.__init__` method exists only to raise a + ``ssl.SSLObject.__init__`` method exists only to raise a :exc:`TypeError` with a more informative message, therefore making it impossible to call (instantiate) :class:`ssl.SSLObject`. diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 052b7bca28ddd3..00e77749e25e77 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -11,7 +11,7 @@ __all__ = ["version", "bootstrap"] _PACKAGE_NAMES = ('setuptools', 'pip') _SETUPTOOLS_VERSION = "65.5.0" -_PIP_VERSION = "23.0" +_PIP_VERSION = "23.0.1" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), ("pip", _PIP_VERSION, "py3"), diff --git a/Lib/ensurepip/_bundled/pip-23.0-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-23.0.1-py3-none-any.whl similarity index 93% rename from Lib/ensurepip/_bundled/pip-23.0-py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-23.0.1-py3-none-any.whl index bb9aebf474cfe9..a855dc40e8630d 100644 Binary files a/Lib/ensurepip/_bundled/pip-23.0-py3-none-any.whl and b/Lib/ensurepip/_bundled/pip-23.0.1-py3-none-any.whl differ diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index f46f21da8da351..b711ffb9a3ecd5 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -40,6 +40,12 @@ def test_float(self): self.assertEqual(float(True), 1.0) self.assertIsNot(float(True), True) + def test_complex(self): + self.assertEqual(complex(False), 0j) + self.assertEqual(complex(False), False) + self.assertEqual(complex(True), 1+0j) + self.assertEqual(complex(True), True) + def test_math(self): self.assertEqual(+False, 0) self.assertIsNot(+False, False) diff --git a/Lib/test/test_zipfile/_context.py b/Lib/test/test_zipfile/_context.py deleted file mode 100644 index 348798aa08d452..00000000000000 --- a/Lib/test/test_zipfile/_context.py +++ /dev/null @@ -1,30 +0,0 @@ -import contextlib -import time - - -class DeadlineExceeded(Exception): - pass - - -class TimedContext(contextlib.ContextDecorator): - """ - A context that will raise DeadlineExceeded if the - max duration is reached during the execution. - - >>> TimedContext(1)(time.sleep)(.1) - >>> TimedContext(0)(time.sleep)(.1) - Traceback (most recent call last): - ... - tests._context.DeadlineExceeded: (..., 0) - """ - - def __init__(self, max_duration: int): - self.max_duration = max_duration - - def __enter__(self): - self.start = time.monotonic() - - def __exit__(self, *err): - duration = time.monotonic() - self.start - if duration > self.max_duration: - raise DeadlineExceeded(duration, self.max_duration) diff --git a/Lib/test/test_zipfile/_func_timeout_compat.py b/Lib/test/test_zipfile/_func_timeout_compat.py deleted file mode 100644 index b1f2b2698a8538..00000000000000 --- a/Lib/test/test_zipfile/_func_timeout_compat.py +++ /dev/null @@ -1,8 +0,0 @@ -try: - from func_timeout import func_set_timeout as set_timeout -except ImportError: # pragma: no cover - # provide a fallback that doesn't actually time out - from ._context import TimedContext as set_timeout - - -__all__ = ['set_timeout'] diff --git a/Lib/test/test_zipfile/_itertools.py b/Lib/test/test_zipfile/_itertools.py index 74f01fe5ba3de2..f735dd21733006 100644 --- a/Lib/test/test_zipfile/_itertools.py +++ b/Lib/test/test_zipfile/_itertools.py @@ -1,4 +1,6 @@ import itertools +from collections import deque +from itertools import islice # from jaraco.itertools 6.3.0 @@ -39,3 +41,39 @@ def always_iterable(obj, base_type=(str, bytes)): return iter(obj) except TypeError: return iter((obj,)) + + +# from more_itertools v9.0.0 +def consume(iterator, n=None): + """Advance *iterable* by *n* steps. If *n* is ``None``, consume it + entirely. + Efficiently exhausts an iterator without returning values. Defaults to + consuming the whole iterator, but an optional second argument may be + provided to limit consumption. + >>> i = (x for x in range(10)) + >>> next(i) + 0 + >>> consume(i, 3) + >>> next(i) + 4 + >>> consume(i) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + If the iterator has fewer items remaining than the provided limit, the + whole iterator will be consumed. + >>> i = (x for x in range(3)) + >>> consume(i, 5) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + """ + # Use functions that consume iterators at C speed. + if n is None: + # feed the entire iterator into a zero-length deque + deque(iterator, maxlen=0) + else: + # advance to the empty slice starting at position n + next(islice(iterator, n, n), None) diff --git a/Lib/test/test_zipfile/_support.py b/Lib/test/test_zipfile/_support.py new file mode 100644 index 00000000000000..1afdf3b3a773d7 --- /dev/null +++ b/Lib/test/test_zipfile/_support.py @@ -0,0 +1,9 @@ +import importlib +import unittest + + +def import_or_skip(name): + try: + return importlib.import_module(name) + except ImportError: # pragma: no cover + raise unittest.SkipTest(f'Unable to import {name}') diff --git a/Lib/test/test_zipfile/test_complexity.py b/Lib/test/test_zipfile/test_complexity.py new file mode 100644 index 00000000000000..3432dc39e56c4e --- /dev/null +++ b/Lib/test/test_zipfile/test_complexity.py @@ -0,0 +1,24 @@ +import unittest +import string +import zipfile + +from ._functools import compose +from ._itertools import consume + +from ._support import import_or_skip + + +big_o = import_or_skip('big_o') + + +class TestComplexity(unittest.TestCase): + def test_implied_dirs_performance(self): + best, others = big_o.big_o( + compose(consume, zipfile.CompleteDirs._implied_dirs), + lambda size: [ + '/'.join(string.ascii_lowercase + str(n)) for n in range(size) + ], + max_n=1000, + min_n=1, + ) + assert best <= big_o.complexities.Linear diff --git a/Lib/test/test_zipfile/test_path.py b/Lib/test/test_zipfile/test_path.py index 53cbef15a17dc6..aff91e53995875 100644 --- a/Lib/test/test_zipfile/test_path.py +++ b/Lib/test/test_zipfile/test_path.py @@ -3,7 +3,6 @@ import contextlib import pathlib import pickle -import string import sys import unittest import zipfile @@ -12,7 +11,6 @@ from ._itertools import Counter from ._test_params import parameterize, Invoked -from ._func_timeout_compat import set_timeout from test.support.os_helper import temp_dir @@ -22,9 +20,6 @@ class itertools: Counter = Counter -consume = tuple - - def add_dirs(zf): """ Given a writable zip file zf, inject directory entries for @@ -330,12 +325,6 @@ def test_joinpath_constant_time(self): # Check the file iterated all items assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES - # timeout disabled due to #102209 - # @set_timeout(3) - def test_implied_dirs_performance(self): - data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] - zipfile.CompleteDirs._implied_dirs(data) - @pass_alpharep def test_read_does_not_close(self, alpharep): alpharep = self.zipfile_ondisk(alpharep) @@ -513,7 +502,7 @@ def test_pickle(self, alpharep, path_type, subpath): saved_1 = pickle.dumps(zipfile.Path(zipfile_ondisk, at=subpath)) restored_1 = pickle.loads(saved_1) first, *rest = restored_1.iterdir() - assert first.read_text().startswith('content of ') + assert first.read_text(encoding='utf-8').startswith('content of ') @pass_alpharep def test_extract_orig_with_implied_dirs(self, alpharep): @@ -525,3 +514,12 @@ def test_extract_orig_with_implied_dirs(self, alpharep): # wrap the zipfile for its side effect zipfile.Path(zf) zf.extractall(source_path.parent) + + @pass_alpharep + def test_getinfo_missing(self, alpharep): + """ + Validate behavior of getinfo on original zipfile after wrapping. + """ + zipfile.Path(alpharep) + with self.assertRaises(KeyError): + alpharep.getinfo('does-not-exist') diff --git a/Lib/zipfile/_path.py b/Lib/zipfile/_path.py index c2c804e96d6b6c..fd49a3ea91db59 100644 --- a/Lib/zipfile/_path.py +++ b/Lib/zipfile/_path.py @@ -86,6 +86,11 @@ class CompleteDirs(InitializedState, zipfile.ZipFile): """ A ZipFile subclass that ensures that implied directories are always included in the namelist. + + >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt'])) + ['foo/', 'foo/bar/'] + >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt', 'foo/bar/'])) + ['foo/'] """ @staticmethod @@ -215,7 +220,7 @@ class Path: Read text: - >>> c.read_text() + >>> c.read_text(encoding='utf-8') 'content of c' existence: diff --git a/Misc/NEWS.d/next/Library/2023-02-17-18-44-27.gh-issue-101997.A6_blD.rst b/Misc/NEWS.d/next/Library/2023-02-17-18-44-27.gh-issue-101997.A6_blD.rst new file mode 100644 index 00000000000000..f9dfd46d1ed430 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-17-18-44-27.gh-issue-101997.A6_blD.rst @@ -0,0 +1 @@ +Upgrade pip wheel bundled with ensurepip (pip 23.0.1) diff --git a/Objects/listobject.c b/Objects/listobject.c index 494b40178f9f27..1a210e77d55c29 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3451,16 +3451,24 @@ listiter_reduce_general(void *_it, int forward) /* the objects are not the same, index is of different types! */ if (forward) { PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); + if (!iter) { + return NULL; + } _PyListIterObject *it = (_PyListIterObject *)_it; if (it->it_seq) { return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } + Py_DECREF(iter); } else { PyObject *reversed = _PyEval_GetBuiltin(&_Py_ID(reversed)); + if (!reversed) { + return NULL; + } listreviterobject *it = (listreviterobject *)_it; if (it->it_seq) { return Py_BuildValue("N(O)n", reversed, it->it_seq, it->it_index); } + Py_DECREF(reversed); } /* empty iterator, create an empty list */ list = PyList_New(0); diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 6403e359c70714..2f4c3d3793efd6 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -14794,8 +14794,10 @@ unicodeiter_reduce(unicodeiterobject *it, PyObject *Py_UNUSED(ignored)) return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } else { PyObject *u = unicode_new_empty(); - if (u == NULL) + if (u == NULL) { + Py_DECREF(iter); return NULL; + } return Py_BuildValue("N(N)", iter, u); } }