[error, ports] Make stack traces more directly reflect teh location of the issue
This commit is contained in:
parent
240007eb7a
commit
dadaf48d35
@ -1,3 +1,10 @@
|
|||||||
|
import traceback
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
|
||||||
|
MASQUE_DIR = str(pathlib.Path(__file__).parent)
|
||||||
|
|
||||||
|
|
||||||
class MasqueError(Exception):
|
class MasqueError(Exception):
|
||||||
"""
|
"""
|
||||||
Parent exception for all Masque-related Exceptions
|
Parent exception for all Masque-related Exceptions
|
||||||
@ -39,3 +46,50 @@ class OneShotError(MasqueError):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, func_name: str) -> None:
|
def __init__(self, func_name: str) -> None:
|
||||||
Exception.__init__(self, f'Function "{func_name}" with @oneshot was called more than once')
|
Exception.__init__(self, f'Function "{func_name}" with @oneshot was called more than once')
|
||||||
|
|
||||||
|
|
||||||
|
def format_stacktrace(
|
||||||
|
stacklevel: int = 1,
|
||||||
|
*,
|
||||||
|
skip_file_prefixes: tuple[str, ...] = (MASQUE_DIR,),
|
||||||
|
low_file_prefixes: tuple[str, ...] = ('<runpy', '<string>'),
|
||||||
|
low_file_suffixes: tuple[str, ...] = ('IPython/utils/py3compat.py', ),
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Utility function for making nicer stack traces (e.g. excluding <frozen runpy> and similar)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
stacklevel: Number of frames to remove from near this function (default is to
|
||||||
|
show caller but not ourselves). Similar to `warnings.warn` and `logging.warning`.
|
||||||
|
skip_file_prefixes: Indicates frames to ignore after counting stack levels; similar
|
||||||
|
to `warnings.warn` *TODO check if this is actually the same effect re:stacklevel*.
|
||||||
|
Forces stacklevel to max(2, stacklevel).
|
||||||
|
Default is to exclude anything within `masque`.
|
||||||
|
low_file_prefixes: Indicates frames to ignore on the other (entry-point) end of the stack,
|
||||||
|
based on prefixes on their filenames.
|
||||||
|
low_file_suffixes: Indicates frames to ignore on the other (entry-point) end of the stack,
|
||||||
|
based on suffixes on their filenames.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted trimmed stack trace
|
||||||
|
"""
|
||||||
|
if skip_file_prefixes:
|
||||||
|
stacklevel = max(2, stacklevel)
|
||||||
|
|
||||||
|
stack = traceback.extract_stack()
|
||||||
|
|
||||||
|
bad_inds = [ii + 1 for ii, frame in enumerate(stack)
|
||||||
|
if frame.filename.startswith(low_file_prefixes) or frame.filename.endswith(low_file_suffixes)]
|
||||||
|
first_ok = max([0] + bad_inds)
|
||||||
|
|
||||||
|
last_ok = -stacklevel - 1
|
||||||
|
while last_ok >= -len(stack) and stack[last_ok].filename.startswith(skip_file_prefixes):
|
||||||
|
last_ok -= 1
|
||||||
|
|
||||||
|
if selected := stack[first_ok:last_ok + 1]:
|
||||||
|
pass
|
||||||
|
elif selected := stack[:-stacklevel]:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
selected = stack
|
||||||
|
return ''.join(traceback.format_list(selected))
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
from typing import overload, Self, NoReturn, Any
|
from typing import overload, Self, NoReturn, Any
|
||||||
from collections.abc import Iterable, KeysView, ValuesView, Mapping
|
from collections.abc import Iterable, KeysView, ValuesView, Mapping
|
||||||
import warnings
|
|
||||||
import traceback
|
|
||||||
import logging
|
import logging
|
||||||
import functools
|
import functools
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
@ -14,7 +12,7 @@ from numpy.typing import ArrayLike, NDArray
|
|||||||
|
|
||||||
from .traits import PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable
|
from .traits import PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable
|
||||||
from .utils import rotate_offsets_around
|
from .utils import rotate_offsets_around
|
||||||
from .error import PortError
|
from .error import PortError, format_stacktrace
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -307,9 +305,9 @@ class PortList(metaclass=ABCMeta):
|
|||||||
msg = 'Ports have conflicting types:\n'
|
msg = 'Ports have conflicting types:\n'
|
||||||
for nn, (kk, vv) in enumerate(connections.items()):
|
for nn, (kk, vv) in enumerate(connections.items()):
|
||||||
if type_conflicts[nn]:
|
if type_conflicts[nn]:
|
||||||
msg = ''.join(traceback.format_stack()) + '\n' + msg
|
|
||||||
warnings.warn(msg, stacklevel=2)
|
|
||||||
msg += f'{kk} | {a_types[nn]}:{b_types[nn]} | {vv}\n'
|
msg += f'{kk} | {a_types[nn]}:{b_types[nn]} | {vv}\n'
|
||||||
|
msg += '\nStack trace:\n' + format_stacktrace()
|
||||||
|
logger.warning(msg)
|
||||||
|
|
||||||
a_offsets = numpy.array([pp.offset for pp in a_ports])
|
a_offsets = numpy.array([pp.offset for pp in a_ports])
|
||||||
b_offsets = numpy.array([pp.offset for pp in b_ports])
|
b_offsets = numpy.array([pp.offset for pp in b_ports])
|
||||||
@ -522,9 +520,9 @@ class PortList(metaclass=ABCMeta):
|
|||||||
msg = 'Ports have conflicting types:\n'
|
msg = 'Ports have conflicting types:\n'
|
||||||
for nn, (kk, vv) in enumerate(map_in.items()):
|
for nn, (kk, vv) in enumerate(map_in.items()):
|
||||||
if type_conflicts[nn]:
|
if type_conflicts[nn]:
|
||||||
msg = ''.join(traceback.format_stack()) + '\n' + msg
|
|
||||||
warnings.warn(msg, stacklevel=2)
|
|
||||||
msg += f'{kk} | {s_types[nn]}:{o_types[nn]} | {vv}\n'
|
msg += f'{kk} | {s_types[nn]}:{o_types[nn]} | {vv}\n'
|
||||||
|
msg += '\nStack trace:\n' + format_stacktrace()
|
||||||
|
logger.warning(msg)
|
||||||
|
|
||||||
rotations = numpy.mod(s_rotations - o_rotations - pi, 2 * pi)
|
rotations = numpy.mod(s_rotations - o_rotations - pi, 2 * pi)
|
||||||
if not has_rot.any():
|
if not has_rot.any():
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user