Source code for pybosl2.exceptions

# Copyright (c) 2026, pinkfish
#
# Licensed under the BSD 2-Clause License. See the LICENSE file in the project
# root for the full license text.
# SPDX-License-Identifier: BSD-2-Clause
# DocCategory: internal

# Exceptions for the dual-backend (CSG / SDF) solid system. pybosl2 realizes solids through either
# the exact-CSG backend (PythonSCAD, the default) or the F-Rep/signed-distance backend (libfive).
# These errors make the two failure modes of a two-backend world explicit: asking a backend for a
# feature it cannot express (:class:`UnsupportedByBackendError`), and combining solids that live on
# different backends (:class:`CrossBackendError`, which tells you how to convert).
#

from __future__ import annotations

__all__ = ["Bosl2Error", "UnsupportedByBackendError", "CrossBackendError"]


class Bosl2Error(Exception):
    """Base class for pybosl2's own errors."""


[docs] class UnsupportedByBackendError(Bosl2Error): """A feature the active backend cannot express. Raised, rather than silently producing different geometry, when a call needs something the chosen backend has no faithful equivalent for -- e.g. the BOSL2 attachment/anchor system on the ``"sdf"`` backend, or a smooth-blend union on the ``"csg"`` backend. Args: feature: short name of the unsupported operation (e.g. ``"attach"``, ``"smooth_union"``). backend: the active backend that cannot do it (``"csg"`` / ``"sdf"``). hint: optional guidance (an alternative call, or which backend does support it). """ def __init__(self, feature: str, backend: str, hint: str | None = None) -> None: self.feature = feature self.backend = backend self.hint = hint msg = f"{feature!r} is not supported by the {backend!r} backend" if hint: msg += f". {hint}" super().__init__(msg)
[docs] class CrossBackendError(Bosl2Error): """A boolean/transform combined solids from two different backends. Solids must share a backend to be combined. Convert one first: meshing an SDF solid into a CSG polyhedron is exact (``sdf_solid.to_csg()``); voxel-sampling a CSG solid into an SDF is lossy and opt-in (``csg_solid.to_sdf(voxel_size=...)``). Args: left: backend of the left operand. right: backend of the right operand. """ def __init__(self, left: str, right: str) -> None: self.left = left self.right = right super().__init__( f"cannot combine a {left!r}-backend solid with a {right!r}-backend solid -- operands " f"must share a backend. Bring the SDF solid into the CSG world with `.to_csg()` (an " f"exact mesh->polyhedron) and combine there. (Direct CSG->SDF conversion is not supported.)" )