Skip to content

Commit 5920b9f

Browse files
authored
Merge branch 'main' into fstring-fix-last-expr-buffer-use-after-free
2 parents 74be33b + dc3f975 commit 5920b9f

File tree

9 files changed

+141
-9
lines changed

9 files changed

+141
-9
lines changed

Doc/library/typing.rst

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ annotations. These include:
9898
*Introducing* :data:`LiteralString`
9999
* :pep:`681`: Data Class Transforms
100100
*Introducing* the :func:`@dataclass_transform<dataclass_transform>` decorator
101+
* :pep:`692`: Using ``TypedDict`` for more precise ``**kwargs`` typing
102+
*Introducing* a new way of typing ``**kwargs`` with :data:`Unpack` and
103+
:data:`TypedDict`
101104
* :pep:`698`: Adding an override decorator to typing
102105
*Introducing* the :func:`@override<override>` decorator
103106

@@ -1417,8 +1420,10 @@ These are not used in annotations. They are building blocks for creating generic
14171420
tup: tuple[Unpack[Ts]]
14181421

14191422
In fact, ``Unpack`` can be used interchangeably with ``*`` in the context
1420-
of types. You might see ``Unpack`` being used explicitly in older versions
1421-
of Python, where ``*`` couldn't be used in certain places::
1423+
of :class:`typing.TypeVarTuple <TypeVarTuple>` and
1424+
:class:`builtins.tuple <tuple>` types. You might see ``Unpack`` being used
1425+
explicitly in older versions of Python, where ``*`` couldn't be used in
1426+
certain places::
14221427

14231428
# In older versions of Python, TypeVarTuple and Unpack
14241429
# are located in the `typing_extensions` backports package.
@@ -1428,6 +1433,21 @@ These are not used in annotations. They are building blocks for creating generic
14281433
tup: tuple[*Ts] # Syntax error on Python <= 3.10!
14291434
tup: tuple[Unpack[Ts]] # Semantically equivalent, and backwards-compatible
14301435

1436+
``Unpack`` can also be used along with :class:`typing.TypedDict` for typing
1437+
``**kwargs`` in a function signature::
1438+
1439+
from typing import TypedDict, Unpack
1440+
1441+
class Movie(TypedDict):
1442+
name: str
1443+
year: int
1444+
1445+
# This function expects two keyword arguments - `name` of type `str`
1446+
# and `year` of type `int`.
1447+
def foo(**kwargs: Unpack[Movie]): ...
1448+
1449+
See :pep:`692` for more details on using ``Unpack`` for ``**kwargs`` typing.
1450+
14311451
.. versionadded:: 3.11
14321452

14331453
.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False)

Doc/library/urllib.request.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ The :mod:`urllib.request` module defines the following functions:
2828

2929
.. function:: urlopen(url, data=None[, timeout], *, cafile=None, capath=None, cadefault=False, context=None)
3030

31-
Open the URL *url*, which can be either a string or a
32-
:class:`Request` object.
31+
Open *url*, which can be either a string containing a valid, properly
32+
encoded URL, or a :class:`Request` object.
3333

3434
*data* must be an object specifying additional data to be sent to the
3535
server, or ``None`` if no such data is needed. See :class:`Request`
@@ -192,7 +192,7 @@ The following classes are provided:
192192

193193
This class is an abstraction of a URL request.
194194

195-
*url* should be a string containing a valid URL.
195+
*url* should be a string containing a valid, properly encoded URL.
196196

197197
*data* must be an object specifying additional data to send to the
198198
server, or ``None`` if no such data is needed. Currently HTTP

Doc/whatsnew/3.12.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ Summary -- Release highlights
6666
6767
.. PEP-sized items next.
6868
69+
New typing features:
70+
71+
* :ref:`whatsnew312-pep692`
72+
6973
Important deprecations, removals or restrictions:
7074

7175
* :pep:`623`, Remove wstr from Unicode
@@ -145,6 +149,36 @@ New Features
145149
In Python 3.14, the default will switch to ``'data'``.
146150
(Contributed by Petr Viktorin in :pep:`706`.)
147151

152+
New Features Related to Type Hints
153+
==================================
154+
155+
This section covers major changes affecting :pep:`484` type hints and
156+
the :mod:`typing` module.
157+
158+
.. _whatsnew312-pep692:
159+
160+
PEP 692: Using ``TypedDict`` for more precise ``**kwargs`` typing
161+
-----------------------------------------------------------------
162+
163+
Typing ``**kwargs`` in a function signature as introduced by :pep:`484` allowed
164+
for valid annotations only in cases where all of the ``**kwargs`` were of the
165+
same type.
166+
167+
This PEP specifies a more precise way of typing ``**kwargs`` by relying on
168+
typed dictionaries::
169+
170+
from typing import TypedDict, Unpack
171+
172+
class Movie(TypedDict):
173+
name: str
174+
year: int
175+
176+
def foo(**kwargs: Unpack[Movie]): ...
177+
178+
See :pep:`692` for more details.
179+
180+
(PEP written by Franek Magiera)
181+
148182

149183
Other Language Changes
150184
======================

Lib/curses/textpad.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ def do_command(self, ch):
102102
self._insert_printable_char(ch)
103103
elif ch == curses.ascii.SOH: # ^a
104104
self.win.move(y, 0)
105-
elif ch in (curses.ascii.STX,curses.KEY_LEFT, curses.ascii.BS,curses.KEY_BACKSPACE):
105+
elif ch in (curses.ascii.STX,curses.KEY_LEFT,
106+
curses.ascii.BS,
107+
curses.KEY_BACKSPACE,
108+
curses.ascii.DEL):
106109
if x > 0:
107110
self.win.move(y, x-1)
108111
elif y == 0:
@@ -111,7 +114,7 @@ def do_command(self, ch):
111114
self.win.move(y-1, self._end_of_line(y-1))
112115
else:
113116
self.win.move(y-1, self.maxx)
114-
if ch in (curses.ascii.BS, curses.KEY_BACKSPACE):
117+
if ch in (curses.ascii.BS, curses.KEY_BACKSPACE, curses.ascii.DEL):
115118
self.win.delch()
116119
elif ch == curses.ascii.EOT: # ^d
117120
self.win.delch()

Lib/test/test_curses.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
import tempfile
77
import unittest
8+
from unittest.mock import MagicMock
89

910
from test.support import (requires, verbose, SaveSignals, cpython_only,
1011
check_disallow_instantiation)
@@ -1319,5 +1320,75 @@ def lorem_ipsum(win):
13191320
for y, line in enumerate(text[:maxy]):
13201321
win.addstr(y, 0, line[:maxx - (y == maxy - 1)])
13211322

1323+
1324+
class TextboxTest(unittest.TestCase):
1325+
def setUp(self):
1326+
self.mock_win = MagicMock(spec=curses.window)
1327+
self.mock_win.getyx.return_value = (1, 1)
1328+
self.mock_win.getmaxyx.return_value = (10, 20)
1329+
self.textbox = curses.textpad.Textbox(self.mock_win)
1330+
1331+
def test_init(self):
1332+
"""Test textbox initialization."""
1333+
self.mock_win.reset_mock()
1334+
tb = curses.textpad.Textbox(self.mock_win)
1335+
self.mock_win.getmaxyx.assert_called_once_with()
1336+
self.mock_win.keypad.assert_called_once_with(1)
1337+
self.assertEqual(tb.insert_mode, False)
1338+
self.assertEqual(tb.stripspaces, 1)
1339+
self.assertIsNone(tb.lastcmd)
1340+
self.mock_win.reset_mock()
1341+
1342+
def test_insert(self):
1343+
"""Test inserting a printable character."""
1344+
self.mock_win.reset_mock()
1345+
self.textbox.do_command(ord('a'))
1346+
self.mock_win.addch.assert_called_with(ord('a'))
1347+
self.textbox.do_command(ord('b'))
1348+
self.mock_win.addch.assert_called_with(ord('b'))
1349+
self.textbox.do_command(ord('c'))
1350+
self.mock_win.addch.assert_called_with(ord('c'))
1351+
self.mock_win.reset_mock()
1352+
1353+
def test_delete(self):
1354+
"""Test deleting a character."""
1355+
self.mock_win.reset_mock()
1356+
self.textbox.do_command(curses.ascii.BS)
1357+
self.textbox.do_command(curses.KEY_BACKSPACE)
1358+
self.textbox.do_command(curses.ascii.DEL)
1359+
assert self.mock_win.delch.call_count == 3
1360+
self.mock_win.reset_mock()
1361+
1362+
def test_move_left(self):
1363+
"""Test moving the cursor left."""
1364+
self.mock_win.reset_mock()
1365+
self.textbox.do_command(curses.KEY_LEFT)
1366+
self.mock_win.move.assert_called_with(1, 0)
1367+
self.textbox.do_command(curses.KEY_RIGHT)
1368+
self.mock_win.move.assert_called_with(1, 2)
1369+
self.mock_win.reset_mock()
1370+
1371+
def test_move_left(self):
1372+
"""Test moving the cursor left."""
1373+
self.mock_win.reset_mock()
1374+
self.textbox.do_command(curses.KEY_RIGHT)
1375+
self.mock_win.move.assert_called_with(1, 2)
1376+
self.mock_win.reset_mock()
1377+
1378+
def test_move_up(self):
1379+
"""Test moving the cursor left."""
1380+
self.mock_win.reset_mock()
1381+
self.textbox.do_command(curses.KEY_UP)
1382+
self.mock_win.move.assert_called_with(0, 1)
1383+
self.mock_win.reset_mock()
1384+
1385+
def test_move_down(self):
1386+
"""Test moving the cursor left."""
1387+
self.mock_win.reset_mock()
1388+
self.textbox.do_command(curses.KEY_DOWN)
1389+
self.mock_win.move.assert_called_with(2, 1)
1390+
self.mock_win.reset_mock()
1391+
1392+
13221393
if __name__ == '__main__':
13231394
unittest.main()

Lib/uuid.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ def _get_command_stdout(command, *args):
401401
# over locally administered ones since the former are globally unique, but
402402
# we'll return the first of the latter found if that's all the machine has.
403403
#
404-
# See https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local
404+
# See https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local_(U/L_bit)
405405

406406
def _is_universal(mac):
407407
return not (mac & (1 << 41))
@@ -615,7 +615,7 @@ def _random_getnode():
615615
# significant bit of the first octet". This works out to be the 41st bit
616616
# counting from 1 being the least significant bit, or 1<<40.
617617
#
618-
# See https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
618+
# See https://en.wikipedia.org/w/index.php?title=MAC_address&oldid=1128764812#Universal_vs._local_(U/L_bit)
619619
import random
620620
return random.getrandbits(48) | (1 << 40)
621621

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Clarifying documentation about the url parameter to urllib.request.urlopen and urllib.request.Requst needing to be encoded properly.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Mention the new way of typing ``**kwargs`` with ``Unpack`` and ``TypedDict``
2+
introduced in :pep:`692`.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
update curses textbox to additionally handle backspace using the ``curses.ascii.DEL`` key press.

0 commit comments

Comments
 (0)