Skip to content

Errors

Everything py2tosc raises on its own behalf inherits from Py2ToscError, so a caller that wants to treat any library failure alike can write one except.

Errors caused by passing a bad argument are deliberately left on the builtins. A ValueError for an unparseable colour or a TypeError for the wrong kind of message says exactly what it means already, and wrapping those would make ordinary Python harder to read without telling the caller anything new.

try:
    doc = py2tosc.load(path)
except py2tosc.FormatError as exc:
    print(f"{path}: not a layout ({exc})")

One failure is separated out because its remedy is: a file declaring a schema newer than the installed release reads is a SchemaError, where the description is fine and the reader is old. A generator can ask before it writes instead -- py2tosc.ui_json.supports(3) and py2tosc.ui_json.SCHEMAS answer the same question without a file to raise about.

Py2ToscError

Bases: Exception

Base class for every error py2tosc defines.

Catching this catches anything the library treats as its own failure, without also catching the ValueError a caller gets for handing a function a bad argument.

Source code in src/py2tosc/errors.py
class Py2ToscError(Exception):
    """Base class for every error py2tosc defines.

    Catching this catches anything the library treats as its own failure,
    without also catching the `ValueError` a caller gets for handing a
    function a bad argument.
    """

FormatError

Bases: Py2ToscError, ValueError

Raised when input cannot be read as a TouchOSC layout.

Reading a layout can fail in three unrelated ways -- the bytes are not XML, a .tosc stream will not decompress, or the XML parses but is not a lexml root holding one node -- and each of those used to reach the caller as a different type from a different module. None of them was a py2tosc type, so no single except clause could express "that file is not a layout", which is the only distinction most callers want.

It also inherits ValueError, which is what load and loads have always documented themselves as raising. Code written against that contract keeps working, and code that wants the narrower type can ask for it.

Source code in src/py2tosc/errors.py
class FormatError(Py2ToscError, ValueError):
    """Raised when input cannot be read as a TouchOSC layout.

    Reading a layout can fail in three unrelated ways -- the bytes are not
    XML, a `.tosc` stream will not decompress, or the XML parses but is not a
    `lexml` root holding one node -- and each of those used to reach the
    caller as a different type from a different module. None of them was a
    py2tosc type, so no single `except` clause could express "that file is not
    a layout", which is the only distinction most callers want.

    It also inherits `ValueError`, which is what `load` and `loads` have always
    documented themselves as raising. Code written against that contract keeps
    working, and code that wants the narrower type can ask for it.
    """

SchemaError

Bases: FormatError

Raised when a JSON dialect is handed a schema newer than it reads.

Both dialects carry a schema number, and both refuse one above their own rather than guess at what a key they have never seen was meant to do. That failure is a FormatError like any other, but its remedy is the only one of them that has nothing to do with the file: the description is fine and the reader is old. Separating it lets a caller say so.

SCHEMAS and supports on either dialect answer the same question before a file is written, which is the better place for a generator to ask it. Catching this is for the callers that would rather try.

Source code in src/py2tosc/errors.py
class SchemaError(FormatError):
    """Raised when a JSON dialect is handed a schema newer than it reads.

    Both dialects carry a schema number, and both refuse one above their own
    rather than guess at what a key they have never seen was meant to do. That
    failure is a `FormatError` like any other, but its remedy is the only one
    of them that has nothing to do with the file: the description is fine and
    the reader is old. Separating it lets a caller say so.

    `SCHEMAS` and `supports` on either dialect answer the same question before
    a file is written, which is the better place for a generator to ask it.
    Catching this is for the callers that would rather try.
    """

ValidationError

Bases: Py2ToscError

Raised by save(validate=True) when a layout has errors.

Attributes:

Name Type Description
issues

Every finding, not only the errors, so a caller catching this can report the warnings too.

Source code in src/py2tosc/validate.py
class ValidationError(Py2ToscError):
    """Raised by `save(validate=True)` when a layout has errors.

    Attributes:
        issues: Every finding, not only the errors, so a caller catching this
            can report the warnings too.
    """

    def __init__(self, issues: list[Issue]):
        self.issues = issues
        errors = [i for i in issues if i.level == ERROR]
        super().__init__(
            f"{len(errors)} error(s) in the layout:\n"
            + "\n".join(f"  {i}" for i in errors)
        )