Skip to content

Commit cd2dfba

Browse files
authored
Merge branch 'main' into http-range-requests-bpo-42643
2 parents a89f588 + a400767 commit cd2dfba

14 files changed

Lines changed: 293 additions & 17 deletions

Doc/library/exceptions.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ The following exceptions are the exceptions that are usually raised.
215215

216216
The object that was accessed for the named attribute.
217217

218+
When possible, :attr:`name` and :attr:`obj` are set automatically.
219+
218220
.. versionchanged:: 3.10
219221
Added the :attr:`name` and :attr:`obj` attributes.
220222

Doc/library/stdtypes.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4876,8 +4876,9 @@ copying.
48764876
Cast a memoryview to a new format or shape. *shape* defaults to
48774877
``[byte_length//new_itemsize]``, which means that the result view
48784878
will be one-dimensional. The return value is a new memoryview, but
4879-
the buffer itself is not copied. Supported casts are 1D -> C-:term:`contiguous`
4880-
and C-contiguous -> 1D.
4879+
the buffer itself is not copied. Supported casts are
4880+
1D -> C-:term:`contiguous`, C-contiguous -> 1D, and
4881+
F-contiguous -> 1D.
48814882

48824883
The destination format is restricted to a single element native format in
48834884
:mod:`struct` syntax. One of the formats must be a byte format
@@ -4964,6 +4965,10 @@ copying.
49644965
.. versionchanged:: 3.5
49654966
The source format is no longer restricted when casting to a byte view.
49664967

4968+
.. versionchanged:: next
4969+
Casting a multi-dimensional F-contiguous view to a one-dimensional
4970+
view is now supported.
4971+
49674972
.. method:: count(value, /)
49684973

49694974
Count the number of occurrences of *value*.

Doc/whatsnew/3.16.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ New features
7575
Other language changes
7676
======================
7777

78+
* :meth:`memoryview.cast` now allows casting a multidimensional
79+
F-contiguous view to a one-dimensional view.
80+
(Contributed by Jaemin Park in :gh:`91484`.)
81+
7882
* :ref:`Frame objects <frame-objects>` now support :mod:`weak references
7983
<weakref>`. This allows associating extra data with active frames,
8084
for example in debuggers, without keeping the frames (and everything

Lib/test/test_buffer.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2877,6 +2877,32 @@ class BEPoint:
28772877
self.assertEqual(m2.strides, (1,))
28782878
self.assertEqual(m2.suboffsets, ())
28792879

2880+
def test_memoryview_cast_f_contiguous_ND_1D(self):
2881+
nd = ndarray(list(range(12)), shape=[3, 4], format='B', flags=ND_FORTRAN)
2882+
m = memoryview(nd)
2883+
self.assertTrue(m.f_contiguous)
2884+
self.assertTrue(m.contiguous)
2885+
2886+
m1 = m.cast('B')
2887+
self.assertEqual(m1.ndim, 1)
2888+
self.assertEqual(m1.shape, (m.nbytes,))
2889+
self.assertEqual(m1.strides, (1,))
2890+
self.assertTrue(m1.c_contiguous)
2891+
self.assertTrue(m1.contiguous)
2892+
self.assertEqual(m1.tobytes(), memoryview(nd).tobytes(order='F'))
2893+
2894+
for fmt in ('B', 'b', 'c', 'H', 'I'):
2895+
size = struct.calcsize(fmt)
2896+
if m.nbytes % size == 0:
2897+
m2 = m.cast(fmt)
2898+
self.assertEqual(m2.ndim, 1)
2899+
self.assertEqual(m2.shape, (m.nbytes // size,))
2900+
self.assertTrue(m2.contiguous)
2901+
2902+
m3 = m[::-1]
2903+
with self.assertRaises(TypeError):
2904+
m3.cast('B')
2905+
28802906
def test_memoryview_tolist(self):
28812907

28822908
# Most tolist() tests are in self.verify() etc.

Lib/test/test_cmd_line.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Most tests are executed with environment variables ignored
33
# See test_cmd_line_script.py for testing of script execution
44

5+
import locale
56
import os
67
import re
78
import subprocess
@@ -369,9 +370,27 @@ def run_no_utf8_mode(arg):
369370
)
370371
test_args = [valid_utf8, invalid_utf8]
371372

372-
for run_cmd in (run_default, run_c_locale, run_utf8_mode,
373-
run_no_utf8_mode):
374-
with self.subTest(run_cmd=run_cmd):
373+
for run_cmd, encoding in (
374+
(run_default, sys.getfilesystemencoding()),
375+
(run_c_locale, None),
376+
(run_utf8_mode, None),
377+
(run_no_utf8_mode, locale.getencoding())
378+
):
379+
with self.subTest(run_cmd=run_cmd.__name__):
380+
# Arbitrary bytes round-trip through surrogateescape only in
381+
# UTF-8 and single-byte encodings, not in a multibyte encoding
382+
# such as EUC-JP.
383+
if encoding is not None:
384+
try:
385+
lossless = len(bytes(range(256)).decode(
386+
encoding, 'surrogateescape')) == 256
387+
except UnicodeError:
388+
lossless = False
389+
else:
390+
lossless = True
391+
if not lossless:
392+
self.skipTest(f'{encoding} cannot losslessly '
393+
f'round-trip arbitrary bytes')
375394
for arg in test_args:
376395
proc = run_cmd(arg)
377396
self.assertEqual(proc.stdout.rstrip(), ascii(arg))

Lib/test/test_decimal.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4147,6 +4147,15 @@ def test_float_operation_default(self):
41474147
@requires_cdecimal
41484148
class CContextFlags(ContextFlags, unittest.TestCase):
41494149
decimal = C
4150+
4151+
def test_signaldict_repr(self):
4152+
Context = self.decimal.Context
4153+
ctx = Context(prec=7)
4154+
mapping = ctx.flags
4155+
del ctx
4156+
with self.assertRaisesRegex(ValueError, 'invalid signal dict'):
4157+
repr(mapping)
4158+
41504159
class PyContextFlags(ContextFlags, unittest.TestCase):
41514160
decimal = P
41524161

Lib/test/test_exceptions.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from codecs import BOM_UTF8
1111
from itertools import product
1212
from textwrap import dedent
13+
from types import ModuleType
1314

1415
from test.support import (captured_stderr, check_impl_detail,
1516
cpython_only, gc_collect,
@@ -2054,6 +2055,129 @@ def blech(self):
20542055
self.assertEqual("bluch", exc.name)
20552056
self.assertEqual(obj, exc.obj)
20562057

2058+
def test_getattr_error_message(self):
2059+
def fqn(type):
2060+
return f'{type.__module__}.{type.__qualname__}'
2061+
2062+
class RaiseWithName:
2063+
def __getattr__(self, name):
2064+
raise AttributeError(name)
2065+
obj = RaiseWithName()
2066+
with self.assertRaises(AttributeError) as cm:
2067+
getattr(obj, "missing1")
2068+
self.assertEqual(str(cm.exception),
2069+
f"'{fqn(RaiseWithName)}' object has no attribute 'missing1'")
2070+
self.assertIs(cm.exception.obj, obj)
2071+
self.assertEqual(cm.exception.name, "missing1")
2072+
2073+
class BareRaise:
2074+
def __getattr__(self, name):
2075+
raise AttributeError
2076+
obj = BareRaise()
2077+
with self.assertRaises(AttributeError) as cm:
2078+
getattr(obj, "missing2")
2079+
self.assertEqual(str(cm.exception),
2080+
f"'{fqn(BareRaise)}' object has no attribute 'missing2'")
2081+
self.assertIs(cm.exception.obj, obj)
2082+
self.assertEqual(cm.exception.name, "missing2")
2083+
2084+
class RaiseCustom:
2085+
def __getattr__(self, name):
2086+
raise AttributeError("custom")
2087+
obj = RaiseCustom()
2088+
with self.assertRaises(AttributeError) as cm:
2089+
getattr(obj, "missing3")
2090+
self.assertEqual(str(cm.exception), "custom")
2091+
self.assertIs(cm.exception.obj, obj)
2092+
self.assertEqual(cm.exception.name, "missing3")
2093+
2094+
def test_class_getattr_error_message(self):
2095+
def fqn(type):
2096+
return f'{type.__module__}.{type.__qualname__}'
2097+
2098+
class MetaclassRaiseWithName(type):
2099+
def __getattr__(self, name):
2100+
raise AttributeError(name)
2101+
cls = MetaclassRaiseWithName("spam", (), {})
2102+
with self.assertRaises(AttributeError) as cm:
2103+
getattr(cls, "missing1")
2104+
self.assertEqual(str(cm.exception),
2105+
f"type object '{fqn(cls)}' has no attribute 'missing1'")
2106+
self.assertIs(cm.exception.obj, cls)
2107+
self.assertEqual(cm.exception.name, "missing1")
2108+
2109+
class MetaclassBareRaise(type):
2110+
def __getattr__(self, name):
2111+
raise AttributeError
2112+
cls = MetaclassBareRaise("eggs", (), {})
2113+
with self.assertRaises(AttributeError) as cm:
2114+
getattr(cls, "missing2")
2115+
self.assertEqual(str(cm.exception),
2116+
f"type object '{fqn(cls)}' has no attribute 'missing2'")
2117+
self.assertIs(cm.exception.obj, cls)
2118+
self.assertEqual(cm.exception.name, "missing2")
2119+
2120+
class MetaclassRaiseCustom(type):
2121+
def __getattr__(self, name):
2122+
raise AttributeError("custom")
2123+
cls = MetaclassRaiseCustom("ham", (), {})
2124+
with self.assertRaises(AttributeError) as cm:
2125+
getattr(cls, "missing3")
2126+
self.assertEqual(str(cm.exception), "custom")
2127+
self.assertIs(cm.exception.obj, cls)
2128+
self.assertEqual(cm.exception.name, "missing3")
2129+
2130+
def test_module_getattr_error_message(self):
2131+
raisewithname_mod = ModuleType("raisewithname")
2132+
def raise_with_name(name):
2133+
raise AttributeError(name)
2134+
raisewithname_mod.__getattr__ = raise_with_name
2135+
with self.assertRaises(AttributeError) as cm:
2136+
getattr(raisewithname_mod, "missing1")
2137+
self.assertEqual(str(cm.exception),
2138+
"module 'raisewithname' has no attribute 'missing1'")
2139+
self.assertIs(cm.exception.obj, raisewithname_mod)
2140+
self.assertEqual(cm.exception.name, "missing1")
2141+
2142+
bareraise_mod = ModuleType("bareraise")
2143+
def bare_raise(name):
2144+
raise AttributeError
2145+
bareraise_mod.__getattr__ = bare_raise
2146+
with self.assertRaises(AttributeError) as cm:
2147+
getattr(bareraise_mod, "missing2")
2148+
self.assertEqual(str(cm.exception),
2149+
"module 'bareraise' has no attribute 'missing2'")
2150+
self.assertIs(cm.exception.obj, bareraise_mod)
2151+
self.assertEqual(cm.exception.name, "missing2")
2152+
2153+
custom_mod = ModuleType("custom")
2154+
def raise_custom(name):
2155+
raise AttributeError("custom")
2156+
custom_mod.__getattr__ = raise_custom
2157+
with self.assertRaises(AttributeError) as cm:
2158+
getattr(custom_mod, "missing3")
2159+
self.assertEqual(str(cm.exception), "custom")
2160+
self.assertIs(cm.exception.obj, custom_mod)
2161+
self.assertEqual(cm.exception.name, "missing3")
2162+
2163+
nameless_mod = ModuleType("forgettable")
2164+
del nameless_mod.__dict__["__name__"]
2165+
nameless_mod.__getattr__ = raise_with_name
2166+
with self.assertRaises(AttributeError) as cm:
2167+
getattr(nameless_mod, "missing4")
2168+
self.assertEqual(str(cm.exception), "module has no attribute 'missing4'")
2169+
self.assertIs(cm.exception.obj, nameless_mod)
2170+
self.assertEqual(cm.exception.name, "missing4")
2171+
2172+
nameless_mod = ModuleType("broken")
2173+
nameless_mod.__dict__["__name__"] = 10j
2174+
nameless_mod.__getattr__ = raise_with_name
2175+
with self.assertRaises(AttributeError) as cm:
2176+
getattr(nameless_mod, "missing4")
2177+
self.assertEqual(str(cm.exception), "module has no attribute 'missing4'")
2178+
self.assertIs(cm.exception.obj, nameless_mod)
2179+
self.assertEqual(cm.exception.name, "missing4")
2180+
20572181
# Note: name suggestion tests live in `test_traceback`.
20582182

20592183

Lib/test/test_peg_generator/test_c_parser.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,17 @@ def setUpClass(cls):
100100

101101
with contextlib.ExitStack() as stack:
102102
python_exe = stack.enter_context(support.setup_venv_with_pip_setuptools("venv"))
103-
platlib_path = subprocess.check_output(
104-
[python_exe, "-c", "import sysconfig; print(sysconfig.get_path('platlib'))"],
105-
text=True,
106-
).strip()
107-
purelib_path = subprocess.check_output(
108-
[python_exe, "-c", "import sysconfig; print(sysconfig.get_path('purelib'))"],
109-
text=True,
110-
).strip()
103+
104+
def get_sysconfig_path(name):
105+
# Force UTF-8 to emit the non-ASCII venv path in any locale.
106+
return subprocess.check_output(
107+
[python_exe, "-X", "utf8", "-c",
108+
f"import sysconfig; print(sysconfig.get_path({name!r}))"],
109+
encoding="utf-8",
110+
).strip()
111+
112+
platlib_path = get_sysconfig_path("platlib")
113+
purelib_path = get_sysconfig_path("purelib")
111114
stack.enter_context(import_helper.DirsOnSysPath(platlib_path, purelib_path))
112115
cls.addClassCleanup(stack.pop_all().close)
113116

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:meth:`memoryview.cast` now allows casting from N-D to 1-D for F-contiguous.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:exc:`AttributeError`: The default error message is now generated from ``name``
2+
and ``obj`` attributes when both are set and the exception was constructed with
3+
no positional arguments, or with a single positional argument equal to ``name``.
4+
Patch by Bartosz Sławecki.

0 commit comments

Comments
 (0)