Add "ignore-case-handling" to exc unmasker
Since it turns out there's even case(s) in `trio` core that are guilty (of implementing things like checkpoints in exc handlers), this adds facility for ignoring explicit cases via `inspect.FrameInfo` field matching from the unmasked `exc_ctx` within `maybe_raise_from_masking_exc()`. Impl deats, - use `inspect.getinnerframes()/getmodule()` to extract the equivalent "guilty place in code" which raised the masked error which we'd like to ignore and **not unmask**. - start a `_mask_cases: dict` which describes the entries to ignore by matching against a specific `FrameInfo`'s fields from indexed from `getinnerframes()`. - describe in that table the case i hit with `trio.WouldBlock` being always masked by a `Cancelled` due to way `trio.Lock.acquire()` implements the blocking case in the would-block handler.. - always call into a new `is_expected_masking_case()` predicate (from `maybe_raise_from_masking_exc()`) on matching `exc_ctx` types.log_sys_testing
parent
93aa39db07
commit
e9f3689191
|
@ -22,6 +22,10 @@ from __future__ import annotations
|
|||
from contextlib import (
|
||||
asynccontextmanager as acm,
|
||||
)
|
||||
import inspect
|
||||
from types import (
|
||||
TracebackType,
|
||||
)
|
||||
from typing import (
|
||||
Type,
|
||||
TYPE_CHECKING,
|
||||
|
@ -63,6 +67,66 @@ def find_masked_excs(
|
|||
return None
|
||||
|
||||
|
||||
_mask_cases: dict[
|
||||
Type[Exception], # masked exc type
|
||||
dict[
|
||||
int, # inner-frame index into `inspect.getinnerframes()`
|
||||
# `FrameInfo.function/filename: str`s to match
|
||||
tuple[str, str],
|
||||
],
|
||||
] = {
|
||||
trio.WouldBlock: {
|
||||
# `trio.Lock.acquire()` has a checkpoint inside the
|
||||
# `WouldBlock`-no_wait path's handler..
|
||||
-5: { # "5th frame up" from checkpoint
|
||||
'filename': 'trio/_sync.py',
|
||||
'function': 'acquire',
|
||||
# 'lineno': 605, # matters?
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def is_expected_masking_case(
|
||||
cases: dict,
|
||||
exc_ctx: Exception,
|
||||
exc_match: BaseException,
|
||||
|
||||
) -> bool|inspect.FrameInfo:
|
||||
'''
|
||||
Determine whether the provided masked exception is from a known
|
||||
bug/special/unintentional-`trio`-impl case which we do not wish
|
||||
to unmask.
|
||||
|
||||
Return any guilty `inspect.FrameInfo` ow `False`.
|
||||
|
||||
'''
|
||||
exc_tb: TracebackType = exc_match.__traceback__
|
||||
if cases := _mask_cases.get(type(exc_ctx)):
|
||||
inner: list[inspect.FrameInfo] = inspect.getinnerframes(exc_tb)
|
||||
|
||||
# from tractor.devx.debug import mk_pdb
|
||||
# mk_pdb().set_trace()
|
||||
for iframe, matchon in cases.items():
|
||||
try:
|
||||
masker_frame: inspect.FrameInfo = inner[iframe]
|
||||
except IndexError:
|
||||
continue
|
||||
|
||||
for field, in_field in matchon.items():
|
||||
val = getattr(
|
||||
masker_frame,
|
||||
field,
|
||||
)
|
||||
if in_field not in val:
|
||||
break
|
||||
else:
|
||||
return masker_frame
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
# XXX, relevant discussion @ `trio`-core,
|
||||
# https://github.com/python-trio/trio/issues/455
|
||||
#
|
||||
|
@ -197,6 +261,24 @@ async def maybe_raise_from_masking_exc(
|
|||
raise_unmasked
|
||||
):
|
||||
if len(masked) < 2:
|
||||
# don't unmask already known "special" cases..
|
||||
if (
|
||||
(cases := _mask_cases.get(type(exc_ctx)))
|
||||
and
|
||||
(masker_frame := is_expected_masking_case(
|
||||
cases,
|
||||
exc_ctx,
|
||||
exc_match,
|
||||
))
|
||||
):
|
||||
log.warning(
|
||||
f'Ignoring already-known/non-ideally-valid masker code @\n'
|
||||
f'{masker_frame}\n'
|
||||
f'\n'
|
||||
f'NOT raising {exc_ctx} from masker {exc_match!r}\n'
|
||||
)
|
||||
raise exc_match
|
||||
|
||||
raise exc_ctx from exc_match
|
||||
|
||||
# ??TODO, see above but, possibly unmasking sub-exc
|
||||
|
|
Loading…
Reference in New Issue