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
14 changes: 8 additions & 6 deletions Doc/reference/compound_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -416,12 +416,14 @@ clauses.
--------------------------

If :keyword:`!finally` is present, it specifies a 'cleanup' handler. The
:keyword:`try` clause is executed, including any :keyword:`except` and
:keyword:`else` clauses. If an exception occurs in any of the clauses and is
not handled, the exception is temporarily saved. The :keyword:`!finally` clause
is executed. If there is a saved exception it is re-raised at the end of the
:keyword:`!finally` clause. If the :keyword:`!finally` clause raises another
exception, the saved exception is set as the context of the new exception.
:keyword:`try` clause is executed, including any :keyword:`except`
and :keyword:`else <except_else>` clauses.
If an exception occurs in any of the clauses and is not handled,
the exception is temporarily saved.
The :keyword:`!finally` clause is executed. If there is a saved exception
it is re-raised at the end of the :keyword:`!finally` clause.
If the :keyword:`!finally` clause raises another exception, the saved exception
is set as the context of the new exception.
If the :keyword:`!finally` clause executes a :keyword:`return`, :keyword:`break`
or :keyword:`continue` statement, the saved exception is discarded. For example,
this function returns 42.
Expand Down
4 changes: 2 additions & 2 deletions Doc/reference/lexical_analysis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -628,10 +628,10 @@ to indicate that an ending quote ends the literal.
STRING: [`stringprefix`] (`stringcontent`)
stringprefix: <("r" | "u" | "b" | "br" | "rb"), case-insensitive>
stringcontent:
| "'" ( !"'" `stringitem`)* "'"
| '"' ( !'"' `stringitem`)* '"'
| "'''" ( !"'''" `longstringitem`)* "'''"
| '"""' ( !'"""' `longstringitem`)* '"""'
| "'" ( !"'" `stringitem`)* "'"
| '"' ( !'"' `stringitem`)* '"'
stringitem: `stringchar` | `stringescapeseq`
stringchar: <any `source_character`, except backslash and newline>
longstringitem: `stringitem` | newline
Expand Down
9 changes: 5 additions & 4 deletions Lib/test/test_io/test_general.py
Original file line number Diff line number Diff line change
Expand Up @@ -5029,12 +5029,12 @@ def write(self, b: bytes):
pass

def test_reader_subclass(self):
self.assertIsSubclass(MyReader, io.Reader[bytes])
self.assertNotIsSubclass(str, io.Reader[bytes])
self.assertIsSubclass(self.MyReader, io.Reader)
self.assertNotIsSubclass(str, io.Reader)

def test_writer_subclass(self):
self.assertIsSubclass(MyWriter, io.Writer[bytes])
self.assertNotIsSubclass(str, io.Writer[bytes])
self.assertIsSubclass(self.MyWriter, io.Writer)
self.assertNotIsSubclass(str, io.Writer)


def load_tests(loader, tests, pattern):
Expand All @@ -5048,6 +5048,7 @@ def load_tests(loader, tests, pattern):
CTextIOWrapperTest, PyTextIOWrapperTest,
CMiscIOTest, PyMiscIOTest,
CSignalsTest, PySignalsTest, TestIOCTypes,
ProtocolsTest,
)

# Put the namespaces of the IO module we are testing and some useful mock
Expand Down
14 changes: 11 additions & 3 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -2281,6 +2281,9 @@ def test__all__(self):
@unittest.skipUnless(hasattr(_thread, 'set_name'), "missing _thread.set_name")
@unittest.skipUnless(hasattr(_thread, '_get_name'), "missing _thread._get_name")
def test_set_name(self):
# Ensure main thread name is restored after test
self.addCleanup(_thread.set_name, _thread._get_name())

# set_name() limit in bytes
truncate = getattr(_thread, "_NAME_MAXLEN", None)
limit = truncate or 100
Expand Down Expand Up @@ -2320,7 +2323,8 @@ def test_set_name(self):
tests.append(os_helper.TESTFN_UNENCODABLE)

if sys.platform.startswith("sunos"):
encoding = "utf-8"
# Use ASCII encoding on Solaris/Illumos/OpenIndiana
encoding = "ascii"
else:
encoding = sys.getfilesystemencoding()

Expand All @@ -2336,7 +2340,7 @@ def work():
if truncate is not None:
encoded = encoded[:truncate]
if sys.platform.startswith("sunos"):
expected = encoded.decode("utf-8", "surrogateescape")
expected = encoded.decode("ascii", "surrogateescape")
else:
expected = os.fsdecode(encoded)
else:
Expand All @@ -2355,7 +2359,11 @@ def work():
if '\0' in expected:
expected = expected.split('\0', 1)[0]

with self.subTest(name=name, expected=expected):
with self.subTest(name=name, expected=expected, thread="main"):
_thread.set_name(name)
self.assertEqual(_thread._get_name(), expected)

with self.subTest(name=name, expected=expected, thread="worker"):
work_name = None
thread = threading.Thread(target=work, name=name)
thread.start()
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ Weilin Du
John DuBois
Paul Dubois
Jacques Ducasse
Jadon Duff
Andrei Dorian Duma
Graham Dumpleton
Quinn Dunkan
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
On Solaris/Illumos platforms, thread names are now encoded as ASCII to avoid errors on systems (e.g. OpenIndiana) that don't support non-ASCII names.
9 changes: 6 additions & 3 deletions Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2523,7 +2523,9 @@ _thread__get_name_impl(PyObject *module)
}

#ifdef __sun
return PyUnicode_DecodeUTF8(name, strlen(name), "surrogateescape");
// gh-138004: Decode Solaris/Illumos (e.g. OpenIndiana) thread names
// from ASCII, since OpenIndiana only supports ASCII names.
return PyUnicode_DecodeASCII(name, strlen(name), "surrogateescape");
#else
return PyUnicode_DecodeFSDefault(name);
#endif
Expand Down Expand Up @@ -2561,8 +2563,9 @@ _thread_set_name_impl(PyObject *module, PyObject *name_obj)
{
#ifndef MS_WINDOWS
#ifdef __sun
// Solaris always uses UTF-8
const char *encoding = "utf-8";
// gh-138004: Encode Solaris/Illumos thread names to ASCII,
// since OpenIndiana does not support non-ASCII names.
const char *encoding = "ascii";
#else
// Encode the thread name to the filesystem encoding using the "replace"
// error handler
Expand Down
26 changes: 24 additions & 2 deletions Tools/c-analyzer/c_parser/parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@
* alt impl using a state machine (& tokenizer or split on delimiters)
"""

import textwrap

from ..info import ParsedItem
from ._info import SourceInfo

Expand Down Expand Up @@ -208,7 +210,27 @@ def _iter_source(lines, *, maxtext=11_000, maxlines=200, showtext=False):
return
# At this point either the file ended prematurely
# or there's "too much" text.
filename, lno, text = srcinfo.filename, srcinfo._start, srcinfo.text
filename, lno_from, lno_to = srcinfo.filename, srcinfo.start, srcinfo.end
text = srcinfo.text
if len(text) > 500:
text = text[:500] + '...'
raise Exception(f'unmatched text ({filename} starting at line {lno}):\n{text}')

if srcinfo.too_much_text(maxtext):
msg = f'''
too much text, try to increase MAX_SIZES[MAXTEXT] in cpython/_parser.py
{filename} starting at line {lno_from} to {lno_to}
has code with length {len(text)} greater than {maxtext}:
{text}
'''
raise RuntimeError(textwrap.dedent(msg))

if srcinfo.too_many_lines(maxlines):
msg = f'''
too many lines, try to increase MAX_SIZES[MAXLINES] in cpython/_parser.py
{filename} starting at line {lno_from} to {lno_to}
has code with number of lines {lno_to - lno_from} greater than {maxlines}:
{text}
'''
raise RuntimeError(textwrap.dedent(msg))

raise RuntimeError(f'unmatched text ({filename} starting at line {lno_from}):\n{text}')
10 changes: 8 additions & 2 deletions Tools/c-analyzer/c_parser/parser/_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,16 @@ def resolve(self, kind, data, name, parent=None):
def done(self):
self._set_ready()

def too_much_text(self, maxtext):
return maxtext and len(self.text) > maxtext

def too_many_lines(self, maxlines):
return maxlines and self.end - self.start > maxlines

def too_much(self, maxtext, maxlines):
if maxtext and len(self.text) > maxtext:
if self.too_much_text(maxtext):
pass
elif maxlines and self.end - self.start > maxlines:
elif self.too_many_lines(maxlines):
pass
else:
return False
Expand Down
2 changes: 1 addition & 1 deletion Tools/c-analyzer/cpython/_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ def clean_lines(text):
_abs('Modules/_testcapimodule.c'): (20_000, 400),
_abs('Modules/expat/expat.h'): (10_000, 400),
_abs('Objects/stringlib/unicode_format.h'): (10_000, 400),
_abs('Objects/typeobject.c'): (35_000, 200),
_abs('Objects/typeobject.c'): (380_000, 13_000),
_abs('Python/compile.c'): (20_000, 500),
_abs('Python/optimizer.c'): (100_000, 5_000),
_abs('Python/parking_lot.c'): (40_000, 1000),
Expand Down
Loading