From e9f36891919f29dafe7305bf77de3d7f2cacecac Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 4 Sep 2025 14:51:25 -0400 Subject: [PATCH] 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. --- tractor/trionics/_taskc.py | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tractor/trionics/_taskc.py b/tractor/trionics/_taskc.py index b2503434..d299bbb0 100644 --- a/tractor/trionics/_taskc.py +++ b/tractor/trionics/_taskc.py @@ -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