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
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
.github/** @ezio-melotti @hugovk @AA-Turner

# pre-commit
.pre-commit-config.yaml @hugovk @AlexWaygood
.pre-commit-config.yaml @hugovk
.ruff.toml @hugovk @AlexWaygood @AA-Turner

# Build system
Expand Down
4 changes: 4 additions & 0 deletions Doc/c-api/long.rst
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
Set *\*value* to a signed C :c:expr:`int32_t` or :c:expr:`int64_t`
representation of *obj*.

If *obj* is not an instance of :c:type:`PyLongObject`, first call its
:meth:`~object.__index__` method (if present) to convert it to a
:c:type:`PyLongObject`.

If the *obj* value is out of range, raise an :exc:`OverflowError`.

Set *\*value* and return ``0`` on success.
Expand Down
44 changes: 30 additions & 14 deletions Doc/library/ctypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -700,14 +700,10 @@ compiler does it. It is possible to override this behavior entirely by specifyi
:attr:`~Structure._layout_` class attribute in the subclass definition; see
the attribute documentation for details.

It is possible to specify the maximum alignment for the fields by setting
the :attr:`~Structure._pack_` class attribute to a positive integer.
This matches what ``#pragma pack(n)`` does in MSVC.

It is also possible to set a minimum alignment for how the subclass itself is packed in the
same way ``#pragma align(n)`` works in MSVC.
This can be achieved by specifying a :attr:`~Structure._align_` class attribute
in the subclass definition.
It is possible to specify the maximum alignment for the fields and/or for the
structure itself by setting the class attributes :attr:`~Structure._pack_`
and/or :attr:`~Structure._align_`, respectively.
See the attribute documentation for details.

:mod:`ctypes` uses the native byte order for Structures and Unions. To build
structures with non-native byte order, you can use one of the
Expand Down Expand Up @@ -2792,11 +2788,18 @@ fields, or any other data types containing pointer type fields.
.. attribute:: _pack_

An optional small integer that allows overriding the alignment of
structure fields in the instance. :attr:`_pack_` must already be defined
when :attr:`_fields_` is assigned, otherwise it will have no effect.
Setting this attribute to 0 is the same as not setting it at all.
structure fields in the instance.

This is only implemented for the MSVC-compatible memory layout
(see :attr:`_layout_`).

This is only implemented for the MSVC-compatible memory layout.
Setting :attr:`!_pack_` to 0 is the same as not setting it at all.
Otherwise, the value must be a positive power of two.
The effect is equivalent to ``#pragma pack(N)`` in C, except
:mod:`ctypes` may allow larger *n* than what the compiler accepts.

:attr:`!_pack_` must already be defined
when :attr:`_fields_` is assigned, otherwise it will have no effect.

.. deprecated-removed:: 3.14 3.19

Expand All @@ -2809,9 +2812,22 @@ fields, or any other data types containing pointer type fields.

.. attribute:: _align_

An optional small integer that allows overriding the alignment of
An optional small integer that allows increasing the alignment of
the structure when being packed or unpacked to/from memory.
Setting this attribute to 0 is the same as not setting it at all.

The value must not be negative.
The effect is equivalent to ``__attribute__((aligned(N)))`` on GCC
or ``#pragma align(N)`` on MSVC, except :mod:`ctypes` may allow
values that the compiler would reject.

:attr:`!_align_` can only *increase* a structure's alignment
requirements. Setting it to 0 or 1 has no effect.

Using values that are not powers of two is discouraged and may lead to
surprising behavior.

:attr:`!_align_` must already be defined
when :attr:`_fields_` is assigned, otherwise it will have no effect.

.. versionadded:: 3.13

Expand Down
7 changes: 6 additions & 1 deletion Doc/library/sys.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2152,11 +2152,16 @@ always available. Unless explicitly noted otherwise, all variables are read-only

The default hook formats :attr:`!err_msg` and :attr:`!object` as:
``f'{err_msg}: {object!r}'``; use "Exception ignored in" error message
if :attr:`!err_msg` is ``None``.
if :attr:`!err_msg` is ``None``. Similar to the :mod:`traceback` module,
this adds color to exceptions by default. This can be disabled using
:ref:`environment variables <using-on-controlling-color>`.

:func:`sys.unraisablehook` can be overridden to control how unraisable
exceptions are handled.

.. versionchanged:: next
Exceptions are now printed with colorful text.

.. seealso::

:func:`excepthook` which handles uncaught exceptions.
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ Other language changes
* Several error messages incorrectly using the term "argument" have been corrected.
(Contributed by Stan Ulbrych in :gh:`133382`.)

* Unraisable exceptions are now highlighted with color by default. This can be
controlled by :ref:`environment variables <using-on-controlling-color>`.
(Contributed by Peter Bierma in :gh:`134170`.)


New modules
===========
Expand Down
1 change: 0 additions & 1 deletion Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,6 @@ def has_no_debug_ranges():
except ImportError:
raise unittest.SkipTest("_testinternalcapi required")
return not _testcapi.config_get('code_debug_ranges')
return not bool(config['code_debug_ranges'])

def requires_debug_ranges(reason='requires co_positions / debug_ranges'):
try:
Expand Down
10 changes: 9 additions & 1 deletion Lib/test/test_capi/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import textwrap

from test import support
from test.support import import_helper
from test.support import import_helper, force_not_colorized
from test.support.os_helper import TESTFN, TESTFN_UNDECODABLE
from test.support.script_helper import assert_python_failure, assert_python_ok
from test.support.testcase import ExceptionIsLikeMixin
Expand Down Expand Up @@ -337,6 +337,10 @@ def test_err_writeunraisable(self):
self.assertIsNone(cm.unraisable.err_msg)
self.assertIsNone(cm.unraisable.object)

@force_not_colorized
def test_err_writeunraisable_lines(self):
writeunraisable = _testcapi.err_writeunraisable

with (support.swap_attr(sys, 'unraisablehook', None),
support.captured_stderr() as stderr):
writeunraisable(CustomError('oops!'), hex)
Expand Down Expand Up @@ -387,6 +391,10 @@ def test_err_formatunraisable(self):
self.assertIsNone(cm.unraisable.err_msg)
self.assertIsNone(cm.unraisable.object)

@force_not_colorized
def test_err_formatunraisable_lines(self):
formatunraisable = _testcapi.err_formatunraisable

with (support.swap_attr(sys, 'unraisablehook', None),
support.captured_stderr() as stderr):
formatunraisable(CustomError('oops!'), b'Error in %R', [])
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ def test_unmached_quote(self):
self.assertRegex(err.decode('ascii', 'ignore'), 'SyntaxError')
self.assertEqual(b'', out)

@force_not_colorized
def test_stdout_flush_at_shutdown(self):
# Issue #5319: if stdout.flush() fails at shutdown, an error should
# be printed out.
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_concurrent_futures/test_shutdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def test_interpreter_shutdown(self):
self.assertFalse(err)
self.assertEqual(out.strip(), b"apple")

@support.force_not_colorized
def test_submit_after_interpreter_shutdown(self):
# Test the atexit hook for shutdown of worker threads and processes
rc, out, err = assert_python_ok('-c', """if 1:
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import unittest
from test import support
from test.support import (
is_apple, is_apple_mobile, os_helper, threading_helper
force_not_colorized, is_apple, is_apple_mobile, os_helper, threading_helper
)
from test.support.script_helper import assert_python_ok, spawn_python
try:
Expand Down Expand Up @@ -353,6 +353,7 @@ def check_signum(signals):

@unittest.skipIf(_testcapi is None, 'need _testcapi')
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
@force_not_colorized
def test_wakeup_write_error(self):
# Issue #16105: write() errors in the C signal handler should not
# pass silently.
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,7 @@ def test_disable_gil_abi(self):


@test.support.cpython_only
@force_not_colorized
class UnraisableHookTest(unittest.TestCase):
def test_original_unraisablehook(self):
_testcapi = import_helper.import_module('_testcapi')
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -2494,6 +2494,7 @@ def test_atexit_called_once(self):

self.assertFalse(err)

@force_not_colorized
def test_atexit_after_shutdown(self):
# The only way to do this is by registering an atexit within
# an atexit, which is intended to raise an exception.
Expand Down
5 changes: 3 additions & 2 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
BUILTIN_EXCEPTION_LIMIT = object()


def _print_exception_bltin(exc, /):
file = sys.stderr if sys.stderr is not None else sys.__stderr__
def _print_exception_bltin(exc, file=None, /):
if file is None:
file = sys.stderr if sys.stderr is not None else sys.__stderr__
colorize = _colorize.can_colorize(file=file)
return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add colorization to :func:`sys.unraisablehook` by default.
27 changes: 26 additions & 1 deletion Python/errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -1444,12 +1444,16 @@ make_unraisable_hook_args(PyThreadState *tstate, PyObject *exc_type,

It can be called to log the exception of a custom sys.unraisablehook.

Do nothing if sys.stderr attribute doesn't exist or is set to None. */
This assumes 'file' is neither NULL nor None.
*/
static int
write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type,
PyObject *exc_value, PyObject *exc_tb,
PyObject *err_msg, PyObject *obj, PyObject *file)
{
assert(file != NULL);
assert(!Py_IsNone(file));

if (obj != NULL && obj != Py_None) {
if (err_msg != NULL && err_msg != Py_None) {
if (PyFile_WriteObject(err_msg, file, Py_PRINT_RAW) < 0) {
Expand Down Expand Up @@ -1484,6 +1488,27 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type,
}
}

// Try printing the exception using the stdlib module.
// If this fails, then we have to use the C implementation.
PyObject *print_exception_fn = PyImport_ImportModuleAttrString("traceback",
"_print_exception_bltin");
if (print_exception_fn != NULL && PyCallable_Check(print_exception_fn)) {
PyObject *args[2] = {exc_value, file};
PyObject *result = PyObject_Vectorcall(print_exception_fn, args, 2, NULL);
int ok = (result != NULL);
Py_DECREF(print_exception_fn);
Py_XDECREF(result);
if (ok) {
// Nothing else to do
return 0;
}
}
else {
Py_XDECREF(print_exception_fn);
}
// traceback module failed, fall back to pure C
_PyErr_Clear(tstate);

if (exc_tb != NULL && exc_tb != Py_None) {
if (PyTraceBack_Print(exc_tb, file) < 0) {
/* continue even if writing the traceback failed */
Expand Down
Loading