[Python-Dev] bpo-36829: Add sys.unraisablehook()

Victor Stinner vstinner at redhat.com
Thu May 16 09:47:22 EDT 2019


Le jeu. 16 mai 2019 à 12:57, Serhiy Storchaka <storchaka at gmail.com> a écrit :
> > For unraisable hook, it's not hard to imagine other useful parameters
> > that can be passed to the hook to provide more context about the
> > exception. For example, right now we only pass one object. But there
> > are cases where a second object would totally makes sense.
>
> If you have plans for adding new details in future, I propose to add a
> 6th parameter "context" or "extra" (always None currently). It is as
> extensible as packing all arguments into a single structure, but you do
> not need to introduce the structure type and create its instance until
> you need to pass additional info.

In my implementation, UnraisableHookArgs is a C "structseq" type. In
short, it's a tuple. make_unraisable_hook_args() basically builds a
tuple of 4 items and uses PyTuple_SET_ITEM() to set the items.
_PyErr_WriteUnraisableDefaultHook() uses PyStructSequence_GET_ITEM()
to get items.

The code pack and unpack UnraisableHookArgs is simple and reliable.

An "unraisable exception" doesn't mean that Python is collapsing. It
only means that the code is unable to report the exception to the
caller: there is no reason why allocating a 4-tuple or calling a
simple Python function (sys.unraisablehook) would fail.

--

I dislike the compromise of having an API in 2 parts: positional
parameters for some parameters, and a structure for some other
parameters. I prefer to pack all arguments into a single structure.

--

IMHO it's readable to get attributes from an object in a Python hook:
it doesn't surprised me, OOP is common in Python :-) Simplified
example of a pure Python reimplementation of the default hook:

def myhook(unraisable):
    if unraisable.obj is not None:
        print(f"Exception ignored in: {unraisable.obj!r}")

    if unraisable.exc_tb is not None:
        traceback.print_tb(unraisable.exc_tb)

    print(f"{unraisable.exc_type.__name__}: {unraisable.exc_value}")

Compared to positional arguments:

def myhook(exc_type, exc_value, exc_tb, obj, extras):
    if obj is not None:
        print(f"Exception ignored in: {obj!r}")

    if exc_tb is not None:
        print_tb(exc_tb, file=file)

    print(f"{exc_type.__name__}: {exc_value}")

Again, the warnings module uses a similar WarningsMsg structure and
I'm not shocked by the implementation. Simplified code from
Lib/warnings.py:

def _formatwarnmsg_impl(msg):
    category = msg.category.__name__
    s =  f"{msg.filename}:{msg.lineno}: {category}: {msg.message}\n"

    if msg.line is None:
        line = linecache.getline(msg.filename, msg.lineno)
    else:
        line = msg.line
    if line:
        s += "  %s\n" % line.strip()

    if msg.source is not None:
        ...
    return s

Victor
-- 
Night gathers, and now my watch begins. It shall not end until my death.


More information about the Python-Dev mailing list