8000 Merge branch 'main' into fstring-grammar-rebased-after-sprint · python/cpython@3eb7616 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3eb7616

Browse files
authored
Merge branch 'main' into fstring-grammar-rebased-after-sprint
2 parents ef9c43a + 59e0de4 commit 3eb7616

27 files changed

+198
-1812
lines changed

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ Lib/test/test_importlib/resources/data01/* noeol
3232
Lib/test/test_importlib/resources/namespacedata01/* noeol
3333
Lib/test/xmltestdata/* noeol
3434

35+
# Shell scripts should have LF even on Windows because of Cygwin
36+
Lib/venv/scripts/common/activate text eol=lf
37+
3538
# CRLF files
3639
[attr]dos text eol=crlf
3740

.github/CODEOWNERS

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,7 @@ Python/traceback.c @iritkatriel
6161
/Tools/build/parse_html5_entities.py @ezio-melotti
6262

6363
# Import (including importlib).
64-
# Ignoring importlib.h so as to not get flagged on
65-
# all pull requests that change the emitted
66-
# bytecode.
67-
**/*import*.c @brettcannon @encukou @ericsnowcurrently @ncoghlan @warsaw
68-
**/*import*.py @brettcannon @encukou @ericsnowcurrently @ncoghlan @warsaw
64+
**/*import* @brettcannon @encukou @ericsnowcurrently @ncoghlan @warsaw
6965
**/*importlib/resources/* @jaraco @warsaw @FFY00
7066
**/importlib/metadata/* @jaraco @warsaw
7167

Doc/library/csv.rst

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ The :mod:`csv` module defines the following constants:
327327

328328
Instructs :class:`writer` objects to quote all non-numeric fields.
329329

330-
Instructs the reader to convert all non-quoted fields to type *float*.
330+
Instructs :class:`reader` objects to convert all non-quoted fields to type *float*.
331331

332332

333333
.. data:: QUOTE_NONE
@@ -337,7 +337,25 @@ The :mod:`csv` module defines the following constants:
337337
character. If *escapechar* is not set, the writer will raise :exc:`Error` if
338338
any characters that require escaping are encountered.
339339

340-
Instructs :class:`reader` to perform no special processing of quote characters.
340+
Instructs :class:`reader` objects to perform no special processing of quote characters.
341+
342+
.. data:: QUOTE_NOTNULL
343+
344+
Instructs :class:`writer` objects to quote all fields which are not
345+
``None``. This is similar to :data:`QUOTE_ALL`, except that if a
346+
field value is ``None`` an empty (unquoted) string is written.
347+
348+
Instructs :class:`reader` objects to interpret an empty (unquoted) field as None and
349+
to otherwise behave as :data:`QUOTE_ALL`.
350+
351+
.. data:: QUOTE_STRINGS
352+
353+
Instructs :class:`writer` objects to always place quotes around fields
354+
which are strings. This is similar to :data:`QUOTE_NONNUMERIC`, except that if a
355+
field value is ``None`` an empty (unquoted) string is written.
356+
357+
Instructs :class:`reader` objects to interpret an empty (unquoted) string as ``None`` and
358+
to otherwise behave as :data:`QUOTE_NONNUMERIC`.
341359

342360
The :mod:`csv` module defines the following exception:
343361

Doc/whatsnew/3.12.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,13 @@ asyncio
253253
* :func:`asyncio.wait` now accepts generators yielding tasks.
254254
(Contributed by Kumar Aditya in :gh:`78530`.)
255255

256+
csv
257+
---
258+
259+
* Add :data:`~csv.QUOTE_NOTNULL` and :data:`~csv.QUOTE_STRINGS` flags to
260+
provide finer grained control of ``None`` and empty strings by
261+
:class:`~csv.reader` and :class:`~csv.writer` objects.
262+
256263
inspect
257264
-------
258265

Lib/asyncio/selector_events.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,9 @@ def writelines(self, list_of_data):
11761176
return
11771177
self._buffer.extend([memoryview(data) for data in list_of_data])
11781178
self._write_ready()
1179+
# If the entire buffer couldn't be written, register a write handler
1180+
if self._buffer:
1181+
self._loop._add_writer(self._sock_fd, self._write_ready)
11791182

11801183
def can_write_eof(self):
11811184
return True

Lib/csv.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
unregister_dialect, get_dialect, list_dialects, \
1010
field_size_limit, \
1111
QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE, \
12+
QUOTE_STRINGS, QUOTE_NOTNULL, \
1213
__doc__
1314
from _csv import Dialect as _Dialect
1415

1516
from io import StringIO
1617

1718
__all__ = ["QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_NONE",
19+
"QUOTE_STRINGS", "QUOTE_NOTNULL",
1820
"Error", "Dialect", "__doc__", "excel", "excel_tab",
1921
"field_size_limit", "reader", "writer",
2022
"register_dialect", "get_dialect", "list_dialects", "Sniffer",

Lib/test/setup_testcppext.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# gh-91321: Build a basic C++ test extension to check that the Python C API is
22
# compatible with C++ and does not emit C++ compiler warnings.
3+
import os
34
import sys
45
from test import support
56

@@ -25,14 +26,8 @@
2526

2627
def main():
2728
cppflags = list(CPPFLAGS)
28-
if '-std=c++03' in sys.argv:
29-
sys.argv.remove('-std=c++03')
30-
std = 'c++03'
31-
name = '_testcpp03ext'
32-
else:
33-
# Python currently targets C++11
34-
std = 'c++11'
35-
name = '_testcpp11ext'
29+
std = os.environ["CPYTHON_TEST_CPP_STD"]
30+
name = os.environ["CPYTHON_TEST_EXT_NAME"]
3631

3732
cppflags = [*CPPFLAGS, f'-std={std}']
3833

1.04 MB
Binary file not shown.

Lib/test/test_asyncio/test_selector_events.py

Expand all lines: Lib/test/test_asyncio/test_selector_events.py
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,48 @@ def test_write_sendmsg_no_data(self):
747747
self.assertFalse(self.sock.sendmsg.called)
748748
self.assertEqual(list_to_buffer([b'data']), transport._buffer)
749749

750+
@unittest.skipUnless(selector_events._HAS_SENDMSG, 'no sendmsg')
751+
def test_writelines_sendmsg_full(self):
752+
data = memoryview(b'data')
753+
self.sock.sendmsg = mock.Mock()
754+
self.sock.sendmsg.return_value = len(data)
755+
756+
transport = self.socket_transport(sendmsg=True)
757+
transport.writelines([data])
758+
self.assertTrue(self.sock.sendmsg.called)
759+
self.assertFalse(self.loop.writers)
760+
761+
@unittest.skipUnless(selector_events._HAS_SENDMSG, 'no sendmsg')
762+
def test_writelines_sendmsg_partial(self):
763+
data = memoryview(b'data')
764+
self.sock.sendmsg = mock.Mock()
765+
self.sock.sendmsg.return_value = 2
766+
767+
transport = self.socket_transport(sendmsg=True)
768+
transport.writelines([data])
769+
self.assertTrue(self.sock.sendmsg.called)
770+
self.assertTrue(self.loop.writers)
771+
772+
def test_writelines_send_full(self):
773+
data = memoryview(b'data')
774+
self.sock.send.return_value = len(data)
775+
self.sock.send.fileno.return_value = 7
776+
777+
transport = self.socket_transport()
778+
transport.writelines([data])
779+
self.assertTrue(self.sock.send.called)
780+
self.assertFalse(self.loop.writers)
781+
782+
def test_writelines_send_partial(self):
783+
data = memoryview(b'data')
784+
self.sock.send.return_value = 2
785+
self.sock.send.fileno.return_value = 7
786+
787+
transport = self.socket_transport()
788+
transport.writelines([data])
789+
self.assertTrue(self.sock.send.called)
790+
self.assertTrue(self.loop.writers)
791+
750792
@unittest.skipUnless(selector_events._HAS_SENDMSG, 'no sendmsg')
751793
def test_write_sendmsg_full(self):
752794
data = memoryview(b'data')

Lib/test/test_cppext.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# gh-91321: Build a basic C++ test extension to check that the Python C API is
22
# compatible with C++ and does not emit C++ compiler warnings.
33
import os.path
4+
import shutil
45
import sys
56
import unittest
67
import subprocess
@@ -39,6 +40,10 @@ def check_build(self, std_cpp03, extension_name):
3940
self._check_build(std_cpp03, extension_name)
4041

4142
def _check_build(self, std_cpp03, extension_name):
43+
pkg_dir = 'pkg'
44+
os.mkdir(pkg_dir)
45+
shutil.copy(SETUP_TESTCPPEXT, os.path.join(pkg_dir, "setup.py"))
46+
4247
venv_dir = 'env'
4348
verbose = support.verbose
4449

@@ -59,11 +64,15 @@ def _check_build(self, std_cpp03, extension_name):
5964
python = os.path.join(venv_dir, 'bin', python_exe)
6065

6166
def run_cmd(operation, cmd):
67+
env = os.environ.copy()
68+
env['CPYTHON_TEST_CPP_STD'] = 'c++03' if std_cpp03 else 'c++11'
69+
env['CPYTHON_TEST_EXT_NAME'] = extension_name
6270
if verbose:
6371
print('Run:', ' '.join(cmd))
64-
subprocess.run(cmd, check=True)
72+
subprocess.run(cmd, check=True, env=env)
6573
else:
6674
proc = subprocess.run(cmd,
75+
env=env,
6776
stdout=subprocess.PIPE,
6877
stderr=subprocess.STDOUT,
6978
text=True)
@@ -72,16 +81,16 @@ def run_cmd(operation, cmd):
7281
self.fail(
7382
f"{operation} failed with exit code {proc.returncode}")
7483

75-
# Build the C++ extension
7684
cmd = [python, '-X', 'dev',
77-
SETUP_TESTCPPEXT, 'build_ext', '--verbose']
78-
if std_cpp03:
79-
cmd.append('-std=c++03')
80-
run_cmd('Build', cmd)
85+
'-m', 'pip', 'install',
86+
support.findfile('setuptools-67.6.1-py3-none-any.whl'),
87+
support.findfile('wheel-0.40.0-py3-none-any.whl')]
88+
run_cmd('Install build dependencies', cmd)
8189

82-
# Install the C++ extension
90+
# Build and install the C++ extension
8391
cmd = [python, '-X', 'dev',
84 10000 -
SETUP_TESTCPPEXT, 'install']
92+
'-m', 'pip', 'install', '--no-build-isolation',
93+
os.path.abspath(pkg_dir)]
8594
run_cmd('Install', cmd)
8695

8796
# Do a reference run. Until we test that running python

Lib/test/test_csv.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ def test_write_quoting(self):
187187
quoting = csv.QUOTE_ALL)
188188
self._write_test(['a\nb',1], '"a\nb","1"',
189189
quoting = csv.QUOTE_ALL)
190+
self._write_test(['a','',None,1], '"a","",,1',
191+
quoting = csv.QUOTE_STRINGS)
192+
self._write_test(['a','',None,1], '"a","",,"1"',
193+
quoting = csv.QUOTE_NOTNULL)
190194

191195
def test_write_escape(self):
192196
self._write_test(['a',1,'p,q'], 'a,1,"p,q"',

Lib/test/test_unittest/testmock/testhelpers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,24 @@ def __getattr__(self, attribute):
952952
self.assertFalse(hasattr(autospec, '__name__'))
953953

954954

955+
def test_autospec_signature_staticmethod(self):
956+
class Foo:
957+
@staticmethod
958+
def static_method(a, b=10, *, c): pass
959+
960+
mock = create_autospec(Foo.__dict__['static_method'])
961+
self.assertEqual(inspect.signature(Foo.static_method), inspect.signature(mock))
962+
963+
964+
def test_autospec_signature_classmethod(self):
965+
class Foo:
966+
@classmethod
967+
def class_method(cls, a, b=10, *, c): pass
968+
969+
mock = create_autospec(Foo.__dict__['class_method'])
970+
self.assertEqual(inspect.signature(Foo.class_method), inspect.signature(mock))
971+
972+
955973
def test_spec_inspect_signature(self):
956974

957975
def myfunc(x, y): pass

Lib/test/test_unittest/testmock/testpatch.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,36 @@ def test_autospec_classmethod(self):
996996
method.assert_called_once_with()
997997

998998

999+
def test_autospec_staticmethod_signature(self):
1000+
# Patched methods which are decorated with @staticmethod should have the same signature
1001+
class Foo:
1002+
@staticmethod
1003+
def static_method(a, b=10, *, c): pass
1004+
1005+
Foo.static_method(1, 2, c=3)
1006+
1007+
with patch.object(Foo, 'static_method', autospec=True) as method:
1008+
method(1, 2, c=3)
1009+
self.assertRaises(TypeError, method)
1010+
self.assertRaises(TypeError, method, 1)
1011+
self.assertRaises(TypeError, method, 1, 2, 3, c=4)
1012+
1013+
1014+
def test_autospec_classmethod_signature(self):
1015+
# Patched methods which are decorated with @classmethod should have the same signature
1016+
class Foo:
1017+
@classmethod
1018+
def class_method(cls, a, b=10, *, c): pass
1019+
1020+
Foo.class_method(1, 2, c=3)
1021+
1022+
with patch.object(Foo, 'class_method', autospec=True) as method:
1023+
method(1, 2, c=3)
1024+
self.assertRaises(TypeError, method)
1025+
self.assertRaises(TypeError, method, 1)
1026+
self.assertRaises(TypeError, method, 1, 2, 3, c=4)
1027+
1028+
9991029
def test_autospec_with_new(self):
10001030
patcher = patch('%s.function' % __name__, new=3, autospec=True)
10011031
self.assertRaises(TypeError, patcher.start)

Lib/test/test_venv.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,21 @@ def test_zippath_from_non_installed_posix(self):
611611
out, err = check_output(cmd)
612612
self.assertTrue(zip_landmark.encode() in out)
613613

614+
def test_activate_shell_script_has_no_dos_newlines(self):
615+
"""
616+
Test that the `activate` shell script contains no CR LF.
617+
This is relevant for Cygwin, as the Windows build might have
618+
converted line endings accidentally.
619+
"""
620+
venv_dir = pathlib.Path(self.env_dir)
621+
rmtree(venv_dir)
622+
[[scripts_dir], *_] = self.ENV_SUBDIRS
623+
script_path = venv_dir / scripts_dir / "activate"
624+
venv.create(venv_dir)
625+
with open(script_path, 'rb') as script:
626+
for line in script:
627+
self.assertFalse(line.endswith(b'\r\n'), line)
628+
614629
@requireVenvCreate
615630
class EnsurePipTest(BaseTest):
616631
"""Test venv module installation of pip."""
63 KB
Binary file not shown.

Lib/unittest/mock.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ def _get_signature_object(func, as_instance, eat_self):
9898
func = func.__init__
9999
# Skip the `self` argument in __init__
100100
eat_self = True
101+
elif isinstance(func, (classmethod, staticmethod)):
102+
if isinstance(func, classmethod):
103+
# Skip the `cls` argument of a class method
104+
eat_self = True
105+
# Use the original decorated method to extract the correct function signature
106+
func = func.__func__
101107
elif not isinstance(func, FunctionTypes):
102108
# If we really want to model an instance of the passed type,
103109
# __call__ should be looked up, not __init__.

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,7 @@ Hugo van Rossum
15501550
Saskia van Rossum
15511551
Robin Roth
15521552
Clement Rouault
1553+
Tomas Roun
15531554
Donald Wallace Rouse II
15541555
Liam Routt
15551556
Todd Rovito
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :data:`~csv.QUOTE_STRINGS` and :data:`~csv.QUOTE_NOTNULL` to the suite
2+
of :mod:`csv` module quoting styles.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixes :func:`unittest.mock.patch` not enforcing function signatures for methods
2+
decorated with ``@classmethod`` or ``@staticmethod`` when patch is called with
3+
``autospec=True``.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fixed an issue with using :meth:`~asyncio.WriteTransport.writelines` in :mod:`asyncio` to send very
2+
large payloads that exceed the amount of data that can be written in one
3+
call to :meth:`socket.socket.send` or :meth:`socket.socket.sendmsg`,
4+
resulting in the remaining buffer being left unwritten.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix virtual environment :file:`activate` script having incorrect line endings for Cygwin.

0 commit comments

Comments
 (0)
0