This commit is contained in:
Brett Cannon 2025-05-07 12:12:01 -07:00
commit 1a137bc320
6 changed files with 61 additions and 14 deletions

View file

@ -30,9 +30,8 @@ def initscr():
fd=_sys.__stdout__.fileno())
stdscr = _curses.initscr()
for key, value in _curses.__dict__.items():
if key[0:4] == 'ACS_' or key in ('LINES', 'COLS'):
if key.startswith('ACS_') or key in ('LINES', 'COLS'):
setattr(curses, key, value)
return stdscr
# This is a similar wrapper for start_color(), which adds the COLORS and
@ -41,12 +40,9 @@ def initscr():
def start_color():
import _curses, curses
retval = _curses.start_color()
if hasattr(_curses, 'COLORS'):
curses.COLORS = _curses.COLORS
if hasattr(_curses, 'COLOR_PAIRS'):
curses.COLOR_PAIRS = _curses.COLOR_PAIRS
return retval
_curses.start_color()
curses.COLORS = _curses.COLORS
curses.COLOR_PAIRS = _curses.COLOR_PAIRS
# Import Python has_key() implementation if _curses doesn't contain has_key()
@ -85,10 +81,11 @@ def wrapper(func, /, *args, **kwds):
# Start color, too. Harmless if the terminal doesn't have
# color; user can test with has_color() later on. The try/catch
# works around a minor bit of over-conscientiousness in the curses
# module -- the error return from C start_color() is ignorable.
# module -- the error return from C start_color() is ignorable,
# unless they are raised by the interpreter due to other issues.
try:
start_color()
except:
except _curses.error:
pass
return func(stdscr, *args, **kwds)

View file

@ -821,6 +821,17 @@ class AST_Tests(unittest.TestCase):
with self.assertRaisesRegex(ValueError, f"identifier field can't represent '{constant}' constant"):
compile(expr, "<test>", "eval")
def test_constant_as_unicode_name(self):
constants = [
("True", b"Tru\xe1\xb5\x89"),
("False", b"Fal\xc5\xbfe"),
("None", b"N\xc2\xbane"),
]
for constant in constants:
with self.assertRaisesRegex(ValueError,
f"identifier field can't represent '{constant[0]}' constant"):
ast.parse(constant[1], mode="eval")
def test_precedence_enum(self):
class _Precedence(enum.IntEnum):
"""Precedence table that originated from python grammar."""

View file

@ -0,0 +1,2 @@
Raise :exc:`ValueError` when constants ``True``, ``False`` or ``None`` are
used as an identifier after NFKC normalization.

View file

@ -549,6 +549,21 @@ _PyPegen_new_identifier(Parser *p, const char *n)
}
id = id2;
}
static const char * const forbidden[] = {
"None",
"True",
"False",
NULL
};
for (int i = 0; forbidden[i] != NULL; i++) {
if (_PyUnicode_EqualToASCIIString(id, forbidden[i])) {
PyErr_Format(PyExc_ValueError,
"identifier field can't represent '%s' constant",
forbidden[i]);
Py_DECREF(id);
goto error;
}
}
PyInterpreterState *interp = _PyInterpreterState_GET();
_PyUnicode_InternImmortal(interp, &id);
if (_PyArena_AddPyObject(p->arena, id) < 0)

View file

@ -1,7 +1,9 @@
[mypy]
files =
Tools/build/compute-changes.py,
Tools/build/generate_sbom.py
Tools/build/generate_sbom.py,
Tools/build/update_file.py
pretty = True
# Make sure Python can still be built
@ -10,6 +12,8 @@ python_version = 3.10
# ...And be strict:
strict = True
strict_bytes = True
local_partial_types = True
extra_checks = True
enable_error_code = ignore-without-code,redundant-expr,truthy-bool,possibly-undefined
warn_unreachable = True

View file

@ -6,14 +6,27 @@ This avoids wholesale rebuilds when a code (re)generation phase does not
actually change the in-tree generated code.
"""
from __future__ import annotations
import contextlib
import os
import os.path
import sys
TYPE_CHECKING = False
if TYPE_CHECKING:
import typing
from collections.abc import Iterator
from io import TextIOWrapper
_Outcome: typing.TypeAlias = typing.Literal['created', 'updated', 'same']
@contextlib.contextmanager
def updating_file_with_tmpfile(filename, tmpfile=None):
def updating_file_with_tmpfile(
filename: str,
tmpfile: str | None = None,
) -> Iterator[tuple[TextIOWrapper, TextIOWrapper]]:
"""A context manager for updating a file via a temp file.
The context manager provides two open files: the source file open
@ -46,13 +59,18 @@ def updating_file_with_tmpfile(filename, tmpfile=None):
update_file_with_tmpfile(filename, tmpfile)
def update_file_with_tmpfile(filename, tmpfile, *, create=False):
def update_file_with_tmpfile(
filename: str,
tmpfile: str,
*,
create: bool = False,
) -> _Outcome:
try:
targetfile = open(filename, 'rb')
except FileNotFoundError:
if not create:
raise # re-raise
outcome = 'created'
outcome: _Outcome = 'created'
os.replace(tmpfile, filename)
else:
with targetfile: