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
4 changes: 2 additions & 2 deletions Doc/howto/logging-cookbook.rst
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ messages should not. Here's how you can achieve this::
# tell the handler to use this format
console.setFormatter(formatter)
# add the handler to the root logger
logging.getLogger('').addHandler(console)
logging.getLogger().addHandler(console)

# Now, we can log to the root logger, or any other logger. First the root...
logging.info('Jackdaws love my big sphinx of quartz.')
Expand Down Expand Up @@ -650,7 +650,7 @@ the receiving end. A simple way of doing this is attaching a

import logging, logging.handlers

rootLogger = logging.getLogger('')
rootLogger = logging.getLogger()
rootLogger.setLevel(logging.DEBUG)
socketHandler = logging.handlers.SocketHandler('localhost',
logging.handlers.DEFAULT_TCP_LOGGING_PORT)
Expand Down
12 changes: 7 additions & 5 deletions Doc/library/os.path.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,17 @@ the :mod:`glob` module.)

.. function:: commonprefix(list, /)

Return the longest path prefix (taken character-by-character) that is a
prefix of all paths in *list*. If *list* is empty, return the empty string
Return the longest string prefix (taken character-by-character) that is a
prefix of all strings in *list*. If *list* is empty, return the empty string
(``''``).

.. note::
.. warning::

This function may return invalid paths because it works a
character at a time. To obtain a valid path, see
:func:`commonpath`.
character at a time.
If you need a **common path prefix**, then the algorithm
implemented in this function is not secure. Use
:func:`commonpath` for finding a common path prefix.

::

Expand Down
16 changes: 16 additions & 0 deletions Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2163,6 +2163,8 @@ expression support in the :mod:`re` module).

.. doctest::

>>> 'spam, spam, spam'.index('spam')
0
>>> 'spam, spam, spam'.index('eggs')
Traceback (most recent call last):
File "<python-input-0>", line 1, in <module>
Expand Down Expand Up @@ -2546,6 +2548,20 @@ expression support in the :mod:`re` module).

Like :meth:`rfind` but raises :exc:`ValueError` when the substring *sub* is not
found.
For example:

.. doctest::

>>> 'spam, spam, spam'.rindex('spam')
12
>>> 'spam, spam, spam'.rindex('eggs')
Traceback (most recent call last):
File "<stdin-0>", line 1, in <module>
'spam, spam, spam'.rindex('eggs')
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
ValueError: substring not found

See also :meth:`index` and :meth:`find`.


.. method:: str.rjust(width, fillchar=' ', /)
Expand Down
12 changes: 9 additions & 3 deletions Doc/library/tomllib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
.. module:: tomllib
:synopsis: Parse TOML files.

.. versionadded:: 3.11

.. moduleauthor:: Taneli Hukkinen
.. sectionauthor:: Taneli Hukkinen

**Source code:** :source:`Lib/tomllib`

--------------

This module provides an interface for parsing TOML 1.0.0 (Tom's Obvious Minimal
This module provides an interface for parsing TOML 1.1.0 (Tom's Obvious Minimal
Language, `https://toml.io <https://toml.io/en/>`_). This module does not
support writing TOML.

.. versionadded:: 3.11
The module was added with support for TOML 1.0.0.

.. versionchanged:: next
Added TOML 1.1.0 support.
See the :ref:`What's New <whatsnew315-tomllib-1-1-0>` for details.


.. seealso::

The :pypi:`Tomli-W package <tomli-w>`
Expand Down
50 changes: 50 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,56 @@ tkinter
with outdated names.
(Contributed by Serhiy Storchaka in :gh:`143754`)


.. _whatsnew315-tomllib-1-1-0:

tomllib
-------

* The :mod:`tomllib` module now supports TOML 1.1.0.
This is a backwards compatible update, meaning that all valid TOML 1.0.0
documents are parsed the same way.

The changes, according to the `official TOML changelog`_, are:

- Allow newlines and trailing commas in inline tables.

Previously an inline table had to be on a single line and couldn't end
with a trailing comma. This is now relaxed so that the following is valid:

.. syntax highlighting needs TOML 1.1.0 support in Pygments,
see https://github.com/pygments/pygments/issues/3026

.. code-block:: text

tbl = {
key = "a string",
moar-tbl = {
key = 1,
},
}

- Add ``\xHH`` notation to basic strings for codepoints under 255,
and the ``\e`` escape for the escape character:

.. code-block:: text

null = "null byte: \x00; letter a: \x61"
csi = "\e["

- Seconds in datetime and time values are now optional.
The following are now valid:

.. code-block:: text

dt = 2010-02-03 14:15
t = 14:15

(Contributed by Taneli Hukkinen in :gh:`142956`.)

.. _official TOML changelog: https://github.com/toml-lang/toml/blob/main/CHANGELOG.md


types
------

Expand Down
33 changes: 17 additions & 16 deletions Lib/_pyrepl/windows_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,11 @@
from .windows_eventqueue import EventQueue

try:
from ctypes import get_last_error, GetLastError, WinDLL, windll, WinError # type: ignore[attr-defined]
from ctypes import get_last_error, WinDLL, windll, WinError # type: ignore[attr-defined]
except:
# Keep MyPy happy off Windows
from ctypes import CDLL as WinDLL, cdll as windll

def GetLastError() -> int:
return 42

def get_last_error() -> int:
return 42

Expand Down Expand Up @@ -149,16 +146,18 @@ def __init__(

# Save original console modes so we can recover on cleanup.
original_input_mode = DWORD()
GetConsoleMode(InHandle, original_input_mode)
if not GetConsoleMode(InHandle, original_input_mode):
raise WinError(get_last_error())
trace(f'saved original input mode 0x{original_input_mode.value:x}')
self.__original_input_mode = original_input_mode.value

SetConsoleMode(
if not SetConsoleMode(
OutHandle,
ENABLE_WRAP_AT_EOL_OUTPUT
| ENABLE_PROCESSED_OUTPUT
| ENABLE_VIRTUAL_TERMINAL_PROCESSING,
)
):
raise WinError(get_last_error())

self.screen: list[str] = []
self.width = 80
Expand Down Expand Up @@ -301,7 +300,7 @@ def _scroll(
if not ScrollConsoleScreenBuffer(
OutHandle, scroll_rect, None, destination_origin, fill_info
):
raise WinError(GetLastError())
raise WinError(get_last_error())

def _hide_cursor(self):
self.__write("\x1b[?25l")
Expand Down Expand Up @@ -335,7 +334,7 @@ def __write(self, text: str) -> None:
def screen_xy(self) -> tuple[int, int]:
info = CONSOLE_SCREEN_BUFFER_INFO()
if not GetConsoleScreenBufferInfo(OutHandle, info):
raise WinError(GetLastError())
raise WinError(get_last_error())
return info.dwCursorPosition.X, info.dwCursorPosition.Y

def _erase_to_end(self) -> None:
Expand All @@ -350,14 +349,16 @@ def prepare(self) -> None:
self.__offset = 0

if self.__vt_support:
SetConsoleMode(InHandle, self.__original_input_mode | ENABLE_VIRTUAL_TERMINAL_INPUT)
if not SetConsoleMode(InHandle, self.__original_input_mode | ENABLE_VIRTUAL_TERMINAL_INPUT):
raise WinError(get_last_error())
self._enable_bracketed_paste()

def restore(self) -> None:
if self.__vt_support:
# Recover to original mode before running REPL
self._disable_bracketed_paste()
SetConsoleMode(InHandle, self.__original_input_mode)
if not SetConsoleMode(InHandle, self.__original_input_mode):
raise WinError(get_last_error())

def _move_relative(self, x: int, y: int) -> None:
"""Moves relative to the current posxy"""
Expand Down Expand Up @@ -394,7 +395,7 @@ def getheightwidth(self) -> tuple[int, int]:
and width of the terminal window in characters."""
info = CONSOLE_SCREEN_BUFFER_INFO()
if not GetConsoleScreenBufferInfo(OutHandle, info):
raise WinError(GetLastError())
raise WinError(get_last_error())
return (
info.srWindow.Bottom - info.srWindow.Top + 1,
info.srWindow.Right - info.srWindow.Left + 1,
Expand All @@ -403,15 +404,15 @@ def getheightwidth(self) -> tuple[int, int]:
def _getscrollbacksize(self) -> int:
info = CONSOLE_SCREEN_BUFFER_INFO()
if not GetConsoleScreenBufferInfo(OutHandle, info):
raise WinError(GetLastError())
raise WinError(get_last_error())

return info.srWindow.Bottom # type: ignore[no-any-return]

def _read_input(self) -> INPUT_RECORD | None:
rec = INPUT_RECORD()
read = DWORD()
if not ReadConsoleInput(InHandle, rec, 1, read):
raise WinError(GetLastError())
raise WinError(get_last_error())

return rec

Expand All @@ -421,7 +422,7 @@ def _read_input_bulk(
rec = (n * INPUT_RECORD)()
read = DWORD()
if not ReadConsoleInput(InHandle, rec, n, read):
raise WinError(GetLastError())
raise WinError(get_last_error())

return rec, read.value

Expand Down Expand Up @@ -523,7 +524,7 @@ def flushoutput(self) -> None:
def forgetinput(self) -> None:
"""Forget all pending, but not yet processed input."""
if not FlushConsoleInputBuffer(InHandle):
raise WinError(GetLastError())
raise WinError(get_last_error())

def getpending(self) -> Event:
"""Return the characters that have been typed but not yet
Expand Down
17 changes: 16 additions & 1 deletion Lib/test/test_pyrepl/test_windows_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from test.support import force_not_colorized_test_class
from typing import Iterable
from unittest import TestCase
from unittest.mock import MagicMock, call
from unittest.mock import MagicMock, call, patch

from .support import handle_all_events, code_to_events
from .support import prepare_reader as default_prepare_reader
Expand All @@ -30,7 +30,21 @@
pass


def _mock_console_init(self, f_in=0, f_out=1, term="", encoding="utf-8"):
"""Mock __init__ to avoid real Windows API calls in headless environments."""
super(WindowsConsole, self).__init__(f_in, f_out, term, encoding)
self.screen = []
self.width = 80
self.height = 25
self._WindowsConsole__offset = 0
self.posxy = (0, 0)
self._WindowsConsole__vt_support = False
self._WindowsConsole_original_input_mode = 0
self.event_queue = wc.EventQueue('utf-8')


@force_not_colorized_test_class
@patch.object(WindowsConsole, '__init__', _mock_console_init)
class WindowsConsoleTests(TestCase):
def console(self, events, **kwargs) -> Console:
console = WindowsConsole()
Expand Down Expand Up @@ -373,6 +387,7 @@ def test_multiline_ctrl_z(self):
con.restore()


@patch.object(WindowsConsole, '__init__', _mock_console_init)
class WindowsConsoleGetEventTests(TestCase):
# Virtual-Key Codes: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
VK_BACK = 0x08
Expand Down
33 changes: 8 additions & 25 deletions Lib/test/test_tomllib/burntsushi.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,8 @@
import datetime
from typing import Any

# Aliases for converting TOML compliance format [1] to BurntSushi format [2]
# [1] https://github.com/toml-lang/compliance/blob/db7c3211fda30ff9ddb10292f4aeda7e2e10abc4/docs/json-encoding.md # noqa: E501
# [2] https://github.com/BurntSushi/toml-test/blob/4634fdf3a6ecd6aaea5f4cdcd98b2733c2694993/README.md # noqa: E501
_aliases = {
"boolean": "bool",
"offset datetime": "datetime",
"local datetime": "datetime-local",
"local date": "date-local",
"local time": "time-local",
}


def convert(obj): # noqa: C901

def convert(obj):
if isinstance(obj, str):
return {"type": "string", "value": obj}
elif isinstance(obj, bool):
Expand Down Expand Up @@ -53,31 +42,25 @@ def convert(obj): # noqa: C901
def normalize(obj: Any) -> Any:
"""Normalize test objects.

This normalizes primitive values (e.g. floats), and also converts from
TOML compliance format [1] to BurntSushi format [2].

[1] https://github.com/toml-lang/compliance/blob/db7c3211fda30ff9ddb10292f4aeda7e2e10abc4/docs/json-encoding.md # noqa: E501
[2] https://github.com/BurntSushi/toml-test/blob/4634fdf3a6ecd6aaea5f4cdcd98b2733c2694993/README.md # noqa: E501
"""
This normalizes primitive values (e.g. floats)."""
if isinstance(obj, list):
return [normalize(item) for item in obj]
if isinstance(obj, dict):
if "type" in obj and "value" in obj:
type_ = obj["type"]
norm_type = _aliases.get(type_, type_)
value = obj["value"]
if norm_type == "float":
if type_ == "float":
norm_value = _normalize_float_str(value)
elif norm_type in {"datetime", "datetime-local"}:
elif type_ in {"datetime", "datetime-local"}:
norm_value = _normalize_datetime_str(value)
elif norm_type == "time-local":
elif type_ == "time-local":
norm_value = _normalize_localtime_str(value)
else:
norm_value = value

if norm_type == "array":
if type_ == "array":
return [normalize(item) for item in value]
return {"type": norm_type, "value": norm_value}
return {"type": type_, "value": norm_value}
return {k: normalize(v) for k, v in obj.items()}
raise AssertionError("Burntsushi fixtures should be dicts/lists only")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"local-dt": {"type":"datetime-local","value":"1988-10-27t01:01:01"},
"local-dt-no-seconds": {"type":"datetime-local","value":"2025-04-18t20:05:00"},
"zulu-dt": {"type":"datetime","value":"1988-10-27t01:01:01z"}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
local-dt=1988-10-27t01:01:01
local-dt-no-seconds=2025-04-18T20:05
zulu-dt=1988-10-27t01:01:01z
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
{"t":
{"type":"time-local","value":"00:00:00.999999"}}
{"type":"time-local","value":"00:00:00.999999"},
"t2":
{"type":"time-local","value":"00:00:00"}}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
t=00:00:00.99999999999999
t=00:00:00.99999999999999
t2=00:00
Loading
Loading