Compare commits
No commits in common. "3e88ed94382c8b11f781c87b31743ee972f7b973" and "ee4147ef99bcdba02e2234c0a7c66e2b86e71363" have entirely different histories.
3e88ed9438
...
ee4147ef99
@ -1,10 +1,3 @@
|
|||||||
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
|
||||||
@ -32,64 +25,15 @@ class BuildError(MasqueError):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PortError(MasqueError):
|
class PortError(MasqueError):
|
||||||
"""
|
"""
|
||||||
Exception raised by port-related functions
|
Exception raised by builder-related functions
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OneShotError(MasqueError):
|
class OneShotError(MasqueError):
|
||||||
"""
|
"""
|
||||||
Exception raised when a function decorated with `@oneshot` is called more than once
|
Exception raised when a function decorated with `@oneshot` is called more than once
|
||||||
"""
|
"""
|
||||||
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))
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
SVG file format readers and writers
|
SVG file format readers and writers
|
||||||
"""
|
"""
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
import logging
|
import warnings
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import ArrayLike
|
from numpy.typing import ArrayLike
|
||||||
@ -12,9 +12,6 @@ from .utils import mangle_name
|
|||||||
from .. import Pattern
|
from .. import Pattern
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def writefile(
|
def writefile(
|
||||||
library: Mapping[str, Pattern],
|
library: Mapping[str, Pattern],
|
||||||
top: str,
|
top: str,
|
||||||
@ -53,7 +50,7 @@ def writefile(
|
|||||||
bounds = pattern.get_bounds(library=library)
|
bounds = pattern.get_bounds(library=library)
|
||||||
if bounds is None:
|
if bounds is None:
|
||||||
bounds_min, bounds_max = numpy.array([[-1, -1], [1, 1]])
|
bounds_min, bounds_max = numpy.array([[-1, -1], [1, 1]])
|
||||||
logger.warning('Pattern had no bounds (empty?); setting arbitrary viewbox', stacklevel=1)
|
warnings.warn('Pattern had no bounds (empty?); setting arbitrary viewbox', stacklevel=1)
|
||||||
else:
|
else:
|
||||||
bounds_min, bounds_max = bounds
|
bounds_min, bounds_max = bounds
|
||||||
|
|
||||||
@ -120,7 +117,7 @@ def writefile_inverted(
|
|||||||
bounds = pattern.get_bounds(library=library)
|
bounds = pattern.get_bounds(library=library)
|
||||||
if bounds is None:
|
if bounds is None:
|
||||||
bounds_min, bounds_max = numpy.array([[-1, -1], [1, 1]])
|
bounds_min, bounds_max = numpy.array([[-1, -1], [1, 1]])
|
||||||
logger.warning('Pattern had no bounds (empty?); setting arbitrary viewbox', stacklevel=1)
|
warnings.warn('Pattern had no bounds (empty?); setting arbitrary viewbox', stacklevel=1)
|
||||||
else:
|
else:
|
||||||
bounds_min, bounds_max = bounds
|
bounds_min, bounds_max = bounds
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
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
|
||||||
@ -12,7 +14,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, format_stacktrace
|
from .error import PortError
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -303,11 +305,11 @@ class PortList(metaclass=ABCMeta):
|
|||||||
|
|
||||||
if type_conflicts.any():
|
if type_conflicts.any():
|
||||||
msg = 'Ports have conflicting types:\n'
|
msg = 'Ports have conflicting types:\n'
|
||||||
for nn, (kk, vv) in enumerate(connections.items()):
|
for nn, (k, v) in enumerate(connections.items()):
|
||||||
if type_conflicts[nn]:
|
if type_conflicts[nn]:
|
||||||
msg += f'{kk} | {a_types[nn]}:{b_types[nn]} | {vv}\n'
|
msg += f'{k} | {a_types[nn]}:{b_types[nn]} | {v}\n'
|
||||||
msg += '\nStack trace:\n' + format_stacktrace()
|
msg = ''.join(traceback.format_stack()) + '\n' + msg
|
||||||
logger.warning(msg)
|
warnings.warn(msg, stacklevel=2)
|
||||||
|
|
||||||
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])
|
||||||
@ -324,17 +326,17 @@ class PortList(metaclass=ABCMeta):
|
|||||||
if not numpy.allclose(rotations, 0):
|
if not numpy.allclose(rotations, 0):
|
||||||
rot_deg = numpy.rad2deg(rotations)
|
rot_deg = numpy.rad2deg(rotations)
|
||||||
msg = 'Port orientations do not match:\n'
|
msg = 'Port orientations do not match:\n'
|
||||||
for nn, (kk, vv) in enumerate(connections.items()):
|
for nn, (k, v) in enumerate(connections.items()):
|
||||||
if not numpy.isclose(rot_deg[nn], 0):
|
if not numpy.isclose(rot_deg[nn], 0):
|
||||||
msg += f'{kk} | {rot_deg[nn]:g} | {vv}\n'
|
msg += f'{k} | {rot_deg[nn]:g} | {v}\n'
|
||||||
raise PortError(msg)
|
raise PortError(msg)
|
||||||
|
|
||||||
translations = a_offsets - b_offsets
|
translations = a_offsets - b_offsets
|
||||||
if not numpy.allclose(translations, 0):
|
if not numpy.allclose(translations, 0):
|
||||||
msg = 'Port translations do not match:\n'
|
msg = 'Port translations do not match:\n'
|
||||||
for nn, (kk, vv) in enumerate(connections.items()):
|
for nn, (k, v) in enumerate(connections.items()):
|
||||||
if not numpy.allclose(translations[nn], 0):
|
if not numpy.allclose(translations[nn], 0):
|
||||||
msg += f'{kk} | {translations[nn]} | {vv}\n'
|
msg += f'{k} | {translations[nn]} | {v}\n'
|
||||||
raise PortError(msg)
|
raise PortError(msg)
|
||||||
|
|
||||||
for pp in chain(a_names, b_names):
|
for pp in chain(a_names, b_names):
|
||||||
@ -404,7 +406,7 @@ class PortList(metaclass=ABCMeta):
|
|||||||
|
|
||||||
map_out_counts = Counter(map_out.values())
|
map_out_counts = Counter(map_out.values())
|
||||||
map_out_counts[None] = 0
|
map_out_counts[None] = 0
|
||||||
conflicts_out = {kk for kk, vv in map_out_counts.items() if vv > 1}
|
conflicts_out = {k for k, v in map_out_counts.items() if v > 1}
|
||||||
if conflicts_out:
|
if conflicts_out:
|
||||||
raise PortError(f'Duplicate targets in `map_out`: {conflicts_out}')
|
raise PortError(f'Duplicate targets in `map_out`: {conflicts_out}')
|
||||||
|
|
||||||
@ -436,7 +438,7 @@ class PortList(metaclass=ABCMeta):
|
|||||||
`set_rotation` must remain `None`.
|
`set_rotation` must remain `None`.
|
||||||
ok_connections: Set of "allowed" ptype combinations. Identical
|
ok_connections: Set of "allowed" ptype combinations. Identical
|
||||||
ptypes are always allowed to connect, as is `'unk'` with
|
ptypes are always allowed to connect, as is `'unk'` with
|
||||||
any other ptypte. Non-allowed ptype connections will log a
|
any other ptypte. Non-allowed ptype connections will emit a
|
||||||
warning. Order is ignored, i.e. `(a, b)` is equivalent to
|
warning. Order is ignored, i.e. `(a, b)` is equivalent to
|
||||||
`(b, a)`.
|
`(b, a)`.
|
||||||
|
|
||||||
@ -487,7 +489,7 @@ class PortList(metaclass=ABCMeta):
|
|||||||
`set_rotation` must remain `None`.
|
`set_rotation` must remain `None`.
|
||||||
ok_connections: Set of "allowed" ptype combinations. Identical
|
ok_connections: Set of "allowed" ptype combinations. Identical
|
||||||
ptypes are always allowed to connect, as is `'unk'` with
|
ptypes are always allowed to connect, as is `'unk'` with
|
||||||
any other ptypte. Non-allowed ptype connections will log a
|
any other ptypte. Non-allowed ptype connections will emit a
|
||||||
warning. Order is ignored, i.e. `(a, b)` is equivalent to
|
warning. Order is ignored, i.e. `(a, b)` is equivalent to
|
||||||
`(b, a)`.
|
`(b, a)`.
|
||||||
|
|
||||||
@ -518,11 +520,11 @@ class PortList(metaclass=ABCMeta):
|
|||||||
for st, ot in zip(s_types, o_types, strict=True)])
|
for st, ot in zip(s_types, o_types, strict=True)])
|
||||||
if type_conflicts.any():
|
if type_conflicts.any():
|
||||||
msg = 'Ports have conflicting types:\n'
|
msg = 'Ports have conflicting types:\n'
|
||||||
for nn, (kk, vv) in enumerate(map_in.items()):
|
for nn, (k, v) in enumerate(map_in.items()):
|
||||||
if type_conflicts[nn]:
|
if type_conflicts[nn]:
|
||||||
msg += f'{kk} | {s_types[nn]}:{o_types[nn]} | {vv}\n'
|
msg += f'{k} | {s_types[nn]}:{o_types[nn]} | {v}\n'
|
||||||
msg += '\nStack trace:\n' + format_stacktrace()
|
msg = ''.join(traceback.format_stack()) + '\n' + msg
|
||||||
logger.warning(msg)
|
warnings.warn(msg, stacklevel=2)
|
||||||
|
|
||||||
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():
|
||||||
@ -544,11 +546,8 @@ class PortList(metaclass=ABCMeta):
|
|||||||
translations = s_offsets - o_offsets
|
translations = s_offsets - o_offsets
|
||||||
if not numpy.allclose(translations[:1], translations):
|
if not numpy.allclose(translations[:1], translations):
|
||||||
msg = 'Port translations do not match:\n'
|
msg = 'Port translations do not match:\n'
|
||||||
common_translation = numpy.min(translations, axis=0)
|
|
||||||
msg += f'Common: {common_translation} \n'
|
|
||||||
msg += 'Deltas:\n'
|
|
||||||
for nn, (kk, vv) in enumerate(map_in.items()):
|
for nn, (kk, vv) in enumerate(map_in.items()):
|
||||||
msg += f'{kk} | {translations[nn] - common_translation} | {vv}\n'
|
msg += f'{kk} | {translations[nn]} | {vv}\n'
|
||||||
raise PortError(msg)
|
raise PortError(msg)
|
||||||
|
|
||||||
return translations[0], rotations[0], o_offsets[0]
|
return translations[0], rotations[0], o_offsets[0]
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from typing import Self
|
from typing import Self, Any
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user