Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/library/bz2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,8 @@ Writing and reading a bzip2-compressed file in binary mode:
... content = f.read()
>>> content == data # Check equality to original object after round-trip
True

.. testcleanup::

import os
os.remove("myfile.bz2")
5 changes: 5 additions & 0 deletions Doc/library/configparser.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ can be customized by end users easily.

import configparser

.. testcleanup::

import os
os.remove("example.ini")


Quick Start
-----------
Expand Down
3 changes: 3 additions & 0 deletions Doc/library/io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,9 @@ I/O Base Classes
to control the number of lines read: no more lines will be read if the
total size (in bytes/characters) of all lines so far exceeds *hint*.

*hint* values of ``0`` or less, as well as ``None``, are treated as no
hint.

Note that it's already possible to iterate on file objects using ``for
line in file: ...`` without calling ``file.readlines()``.

Expand Down
11 changes: 8 additions & 3 deletions Doc/reference/expressions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ A comprehension in an :keyword:`!async def` function may consist of either a
:keyword:`!for` or :keyword:`!async for` clause following the leading
expression, may contain additional :keyword:`!for` or :keyword:`!async for`
clauses, and may also use :keyword:`await` expressions.
If a comprehension contains either :keyword:`!async for` clauses
or :keyword:`!await` expressions it is called an
:dfn:`asynchronous comprehension`. An asynchronous comprehension may
If a comprehension contains either :keyword:`!async for` clauses or
:keyword:`!await` expressions or other asynchronous comprehensions it is called
an :dfn:`asynchronous comprehension`. An asynchronous comprehension may
suspend the execution of the coroutine function in which it appears.
See also :pep:`530`.

Expand All @@ -230,6 +230,11 @@ See also :pep:`530`.
.. versionchanged:: 3.8
``yield`` and ``yield from`` prohibited in the implicitly nested scope.

.. versionchanged:: 3.11
Asynchronous comprehensions are now allowed inside comprehensions in
asynchronous functions. Outer comprehensions implicitly become
asynchronous.


.. _lists:

Expand Down
17 changes: 10 additions & 7 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,19 @@ See :pep:`657` for more details. (Contributed by Pablo Galindo, Batuhan Taskaya
and Ammar Askar in :issue:`43950`.)



Other Language Changes
======================

A :exc:`TypeError` is now raised instead of an :exc:`AttributeError` in
:meth:`contextlib.ExitStack.enter_context` and
:meth:`contextlib.AsyncExitStack.enter_async_context` for objects which do not
support the :term:`context manager` or :term:`asynchronous context manager`
protocols correspondingly.
(Contributed by Serhiy Storchaka in :issue:`44471`.)
* Asynchronous comprehensions are now allowed inside comprehensions in
asynchronous functions. Outer comprehensions implicitly become
asynchronous. (Contributed by Serhiy Storchaka in :issue:`33346`.)

* A :exc:`TypeError` is now raised instead of an :exc:`AttributeError` in
:meth:`contextlib.ExitStack.enter_context` and
:meth:`contextlib.AsyncExitStack.enter_async_context` for objects which do not
support the :term:`context manager` or :term:`asynchronous context manager`
protocols correspondingly.
(Contributed by Serhiy Storchaka in :issue:`44471`.)

* A :exc:`TypeError` is now raised instead of an :exc:`AttributeError` in
:keyword:`with` and :keyword:`async with` statements for objects which do not
Expand Down
2 changes: 1 addition & 1 deletion Lib/configparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ class RawConfigParser(MutableMapping):
# Regular expressions for parsing section headers and options
_SECT_TMPL = r"""
\[ # [
(?P<header>[^]]+) # very permissive!
(?P<header>.+) # very permissive!
\] # ]
"""
_OPT_TMPL = r"""
Expand Down
4 changes: 0 additions & 4 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -2096,10 +2096,6 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True):
"""Private helper to parse content of '__text_signature__'
and return a Signature based on it.
"""
# Lazy import ast because it's relatively heavy and
# it's not used for other than this function.
import ast

Parameter = cls._parameter_cls

clean_signature, self_parameter, last_positional_only = \
Expand Down
1 change: 0 additions & 1 deletion Lib/lib2to3/btm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ def reduce_tree(node, parent=None):
else:
#TODO: handle {min, max} repeaters
raise NotImplementedError
pass

#add children
if details_node and new_node is not None:
Expand Down
1 change: 0 additions & 1 deletion Lib/multiprocessing/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1337,7 +1337,6 @@ def __init__(self, *args, **kwargs):

def __del__(self):
util.debug(f"{self.__class__.__name__}.__del__ by pid {getpid()}")
pass

def get_server(self):
'Better than monkeypatching for now; merge into Server ultimately'
Expand Down
1 change: 1 addition & 0 deletions Lib/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ def _syscmd_ver(system='', release='', version='',
for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
try:
info = subprocess.check_output(cmd,
stdin=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text=True,
shell=True)
Expand Down
1 change: 0 additions & 1 deletion Lib/telnetlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,6 @@ def process_rawq(self):
except EOFError: # raised by self.rawq_getchar()
self.iacseq = b'' # Reset on EOF
self.sb = 0
pass
self.cookedq = self.cookedq + buf[0]
self.sbdataq = self.sbdataq + buf[1]

Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_configparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def basic_test(self, cf):
'Spacey Bar',
'Spacey Bar From The Beginning',
'Types',
'This One Has A ] In It',
]

if self.allow_no_value:
Expand Down Expand Up @@ -130,6 +131,7 @@ def basic_test(self, cf):
eq(cf.get('Types', 'float'), "0.44")
eq(cf.getboolean('Types', 'boolean'), False)
eq(cf.get('Types', '123'), 'strange but acceptable')
eq(cf.get('This One Has A ] In It', 'forks'), 'spoons')
if self.allow_no_value:
eq(cf.get('NoValue', 'option-without-value'), None)

Expand Down Expand Up @@ -320,6 +322,8 @@ def test_basic(self):
float {0[0]} 0.44
boolean {0[0]} NO
123 {0[1]} strange but acceptable
[This One Has A ] In It]
forks {0[0]} spoons
""".format(self.delimiters, self.comment_prefixes)
if self.allow_no_value:
config_string += (
Expand Down Expand Up @@ -394,6 +398,9 @@ def test_basic_from_dict(self):
"boolean": False,
123: "strange but acceptable",
},
"This One Has A ] In It": {
"forks": "spoons"
},
}
if self.allow_no_value:
config.update({
Expand Down
72 changes: 72 additions & 0 deletions Lib/test/test_coroutines.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ def __await__(self):
yield self.value


async def asynciter(iterable):
"""Convert an iterable to an asynchronous iterator."""
for x in iterable:
yield x


def run_async(coro):
assert coro.__class__ in {types.GeneratorType, types.CoroutineType}

Expand Down Expand Up @@ -125,6 +131,11 @@ def bar():
for c in b]
""",

"""async def foo():
def bar():
[[async for i in b] for b in els]
""",

"""async def foo():
def bar():
[i for i in els
Expand Down Expand Up @@ -200,6 +211,13 @@ def bar():
[i for i in els if await i]
""",

"""def bar():
[[i async for i in a] for a in elts]
""",

"""[[i async for i in a] for a in elts]
""",

"""async def foo():
await
""",
Expand Down Expand Up @@ -2011,6 +2029,60 @@ async def f():
run_async(f()),
([], {1: 1, 2: 2, 3: 3}))

def test_nested_comp(self):
async def run_list_inside_list():
return [[i + j async for i in asynciter([1, 2])] for j in [10, 20]]
self.assertEqual(
run_async(run_list_inside_list()),
([], [[11, 12], [21, 22]]))

async def run_set_inside_list():
return [{i + j async for i in asynciter([1, 2])} for j in [10, 20]]
self.assertEqual(
run_async(run_set_inside_list()),
([], [{11, 12}, {21, 22}]))

async def run_list_inside_set():
return {sum([i async for i in asynciter(range(j))]) for j in [3, 5]}
self.assertEqual(
run_async(run_list_inside_set()),
([], {3, 10}))

async def run_dict_inside_dict():
return {j: {i: i + j async for i in asynciter([1, 2])} for j in [10, 20]}
self.assertEqual(
run_async(run_dict_inside_dict()),
([], {10: {1: 11, 2: 12}, 20: {1: 21, 2: 22}}))

async def run_list_inside_gen():
gen = ([i + j async for i in asynciter([1, 2])] for j in [10, 20])
return [x async for x in gen]
self.assertEqual(
run_async(run_list_inside_gen()),
([], [[11, 12], [21, 22]]))

async def run_gen_inside_list():
gens = [(i async for i in asynciter(range(j))) for j in [3, 5]]
return [x for g in gens async for x in g]
self.assertEqual(
run_async(run_gen_inside_list()),
([], [0, 1, 2, 0, 1, 2, 3, 4]))

async def run_gen_inside_gen():
gens = ((i async for i in asynciter(range(j))) for j in [3, 5])
return [x for g in gens async for x in g]
self.assertEqual(
run_async(run_gen_inside_gen()),
([], [0, 1, 2, 0, 1, 2, 3, 4]))

async def run_list_inside_list_inside_list():
return [[[i + j + k async for i in asynciter([1, 2])]
for j in [10, 20]]
for k in [100, 200]]
self.assertEqual(
run_async(run_list_inside_list_inside_list()),
([], [[[111, 112], [121, 122]], [[211, 212], [221, 222]]]))

def test_copy(self):
async def func(): pass
coro = func()
Expand Down
6 changes: 6 additions & 0 deletions Lib/test/test_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,9 +458,15 @@ class testC(csv.excel):
class testUni(csv.excel):
delimiter = "\u039B"

class unspecified():
# A class to pass as dialect but with no dialect attributes.
pass

csv.register_dialect('testC', testC)
try:
self.compare_dialect_123("1,2,3\r\n")
self.compare_dialect_123("1,2,3\r\n", dialect=None)
self.compare_dialect_123("1,2,3\r\n", dialect=unspecified)
self.compare_dialect_123("1\t2\t3\r\n", testA)
self.compare_dialect_123("1:2:3\r\n", dialect=testB())
self.compare_dialect_123("1|2|3\r\n", dialect='testC')
Expand Down
8 changes: 7 additions & 1 deletion Lib/test/test_tcl.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,14 @@ def get_tk_patchlevel():
class TkinterTest(unittest.TestCase):

def testFlattenLen(self):
# flatten(<object with no length>)
# Object without length.
self.assertRaises(TypeError, _tkinter._flatten, True)
# Object with length, but not sequence.
self.assertRaises(TypeError, _tkinter._flatten, {})
# Sequence or set, but not tuple or list.
# (issue44608: there were leaks in the following cases)
self.assertRaises(TypeError, _tkinter._flatten, 'string')
self.assertRaises(TypeError, _tkinter._flatten, {'set'})


class TclTest(unittest.TestCase):
Expand Down
37 changes: 35 additions & 2 deletions Lib/test/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,39 @@ def test_or_types_operator(self):
x.__args__ = [str, int]
(int | str ) == x

def test_instancecheck(self):
x = int | str
self.assertIsInstance(1, x)
self.assertIsInstance(True, x)
self.assertIsInstance('a', x)
self.assertNotIsInstance(None, x)
self.assertTrue(issubclass(int, x))
self.assertTrue(issubclass(bool, x))
self.assertTrue(issubclass(str, x))
self.assertFalse(issubclass(type(None), x))
x = int | None
self.assertIsInstance(None, x)
self.assertTrue(issubclass(type(None), x))
x = int | collections.abc.Mapping
self.assertIsInstance({}, x)
self.assertTrue(issubclass(dict, x))

def test_bad_instancecheck(self):
class BadMeta(type):
def __instancecheck__(cls, inst):
1/0
x = int | BadMeta('A', (), {})
self.assertTrue(isinstance(1, x))
self.assertRaises(ZeroDivisionError, isinstance, [], x)

def test_bad_subclasscheck(self):
class BadMeta(type):
def __subclasscheck__(cls, sub):
1/0
x = int | BadMeta('A', (), {})
self.assertTrue(issubclass(int, x))
self.assertRaises(ZeroDivisionError, issubclass, list, x)

def test_or_type_operator_with_TypeVar(self):
TV = typing.TypeVar('T')
assert TV | str == typing.Union[TV, str]
Expand Down Expand Up @@ -754,9 +787,9 @@ def __eq__(self, other):
for type_ in union_ga:
with self.subTest(f"check isinstance/issubclass is invalid for {type_}"):
with self.assertRaises(TypeError):
isinstance(list, type_)
isinstance(1, type_)
with self.assertRaises(TypeError):
issubclass(list, type_)
issubclass(int, type_)

def test_or_type_operator_with_bad_module(self):
class TypeVar:
Expand Down
6 changes: 3 additions & 3 deletions Mac/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ Building and using a framework-based Python on macOS


1. Why would I want a framework Python instead of a normal static Python?
--------------------------------------------------------------------------
-------------------------------------------------------------------------

The main reason is because you want to create GUI programs in Python. With the
exception of X11/XDarwin-based GUI toolkits all GUI programs need to be run
Expand All @@ -206,7 +206,7 @@ only two places: "/Library/Framework/Python.framework" and
"/Applications/Python <VERSION>" where ``<VERSION>`` can be e.g. "3.8",
"2.7", etc. This simplifies matters for users installing
Python from a binary distribution if they want to get rid of it again. Moreover,
due to the way frameworks work, usera without admin privileges can install a
due to the way frameworks work, users without admin privileges can install a
binary distribution in their home directory without recompilation.

2. How does a framework Python differ from a normal static Python?
Expand Down Expand Up @@ -272,7 +272,7 @@ normal frameworkinstall which installs the Tools directory into
distributions.

What do all these programs do?
===============================
==============================

"IDLE.app" is an integrated development environment for Python: editor,
debugger, etc.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Asynchronous comprehensions are now allowed inside comprehensions in
asynchronous functions. Outer comprehensions implicitly become
asynchronous.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix ``__instancecheck__`` and ``__subclasscheck__`` for the union type.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:mod:`configparser`: using ']' inside a section header will no longer cut the section name short at the ']'
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix memory leak in :func:`_tkinter._flatten` if it is called with a sequence
or set, but not list or tuple.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Avoid consuming standard input in the :mod:`platform` module
Loading