Stability¶
What this library promises not to break, and what it reserves the right to change. If you are pinning a version or deciding whether to depend on something, this page is the contract.
The short version¶
Covered by the guarantee Not covered
-------------------------------- ----------------------------------------
py2tosc.<name> for every name py2tosc.ui.* -- provisional, see below
in __all__ Anything whose name starts with _
py2tosc.layout, .properties, Anything not in __all__ and not in the
.surface API reference
The API reference, docs/api/ The wording of CLI output
CLI names, flags and exit codes The wording of validation messages
Byte-exact round trips Which layouts validate warns about
Versioning¶
The project follows semantic versioning. Once 1.0 is out, a breaking change to anything in the covered column requires a major bump. Until then, and this is the current state at 0.3.x, a minor bump may break the API -- which is what the number below 1 is for.
Depending on py2tosc is therefore py2tosc>=1,<2 after 1.0, and pinning more
tightly than that before it.
What counts as the public API¶
Exactly two things, and they are required to agree:
- every name in
py2tosc.__all__ - every object the API reference documents
This is not a convention that relies on care. tests/test_api.py asserts that
everything exported is documented, that everything documented is reachable by
attribute access from a bare import py2tosc, and that the reference never
points at a private module. The three used to disagree -- __all__ listed 48
names against 72 documented objects, and py2tosc.surface was documented while
raising AttributeError -- which is what the tests now prevent.
Anything else in the package is internal, whether or not its name begins with
an underscore. py2tosc._geometry is importable and always will be, because
nothing in Python stops you; that is not the same as it being supported.
The py2tosc.ui carve-out¶
py2tosc.ui is provisional. It may change in a minor release, including in
ways that break callers, and it is not covered by the guarantee above.
This is deliberate and the reason is in the layering. control, codec and
messages bind to a file format Hexler defines; they age at the speed of
TouchOSC and there is an external authority to be correct against. ui encodes
opinions about how interfaces should be composed -- that a row is a function
returning a group, that layout is described first and sized later -- and
opinions age faster than a format does. Freezing them three releases after they
were written would be a promise made on thin evidence.
The carve-out is cheap to state because the separation already exists in the
code rather than only in this document. The top-level py2tosc.grid,
py2tosc.pager, py2tosc.group, py2tosc.fader and py2tosc.label are the
py2tosc.control constructors, not their same-named counterparts in ui.
Nothing unstable leaks into the top-level API by accident, so the rule you have
to remember is the whole of it: if you reached it through py2tosc.ui, it is
provisional.
Everything ui builds is an ordinary Control, OscMessage or MidiMessage.
Nothing it produces can reach a file that hand-written code could not. If a
change to ui is ever inconvenient, the documents it built remain valid and
the escape route is to write the dataclasses directly.
Behavioural guarantees¶
An API that keeps its signatures and changes what it writes has broken you just as thoroughly, so three behaviours are covered as well.
Round trips are byte-exact. A file written by the TouchOSC editor, loaded and saved without modification, is reproduced byte for byte. This is the guarantee the project exists to make and it is checked against every editor-written layout in the corpus.
There are two deliberate exceptions, both cases of the file holding a number
that should not be preserved: a frame coordinate of -0, which survives as the
integer 0, and a colour component of -nan(ind), which a Windows build of
TouchOSC wrote and which is repaired to 0. Reproducing either exactly would
mean carrying damage forward. These will not grow without a major bump.
Loading never rejects a layout TouchOSC accepts. The format permits
properties nobody has heard of -- that is what makes custom properties useful
-- so unknown properties are read and written back rather than refused.
validate is advisory and never raises on its own.
Correcting a default to match the editor is a fix, not a break. The point of the defaults is fidelity to what TouchOSC creates, so a default that disagrees with the editor is wrong, and changing it is a bug fix even though it changes what a freshly built control looks like. 0.3.2 corrected four this way. Such changes are always called out in the changelog with the evidence, and they never affect loading: a control read from a file keeps what the file said.
Deprecation¶
Nothing covered is removed without warning. The sequence is:
- The replacement ships, and the old name keeps working.
- The changelog records the deprecation, what replaces it, and the release the removal is scheduled for.
- Where the call site can be reached at runtime, calling the deprecated name
raises a
DeprecationWarningnaming its replacement. - Removal, in a major release and no sooner than one minor release after the warning appears.
The test suite runs with -W error::DeprecationWarning, so a deprecation the
library itself still depends on internally fails the build rather than being
emitted quietly. That is the intended pressure.
The command line¶
py2tosc is installed as a console script, which makes it something people put
in shell pipelines. Covered: the subcommand names, their flags, and their exit
codes.
0 did what was asked
1 the layout was read and validate found an error in it
2 the command could not run: bad command line, or unreadable input
The distinction between 1 and 2 is part of the guarantee, not an accident
of how the failures happen to be written. 1 is a statement about a layout;
2 is a statement about never having got as far as one. A caller that cannot
tell those apart cannot tell a layout it should fix from a path it should fix.
The numbers are the ones grep, diff and mypy use for the same two ideas,
which is the point: an exit code nobody expects is worth no more than no exit
code at all.
Not covered: the wording and layout of what is printed. If you are parsing the
human-readable output, expect it to move. py2tosc validate exiting zero on
warnings and non-zero on errors is covered; the text of any particular warning
is not.
Python and TouchOSC versions¶
Supported Python versions are the ones in the pyproject.toml classifiers, and
every one of them runs in CI on each change. Dropping a Python version happens
only in a minor or major release, never a patch, and only after that version
has reached end of life upstream.
The format is verified against TouchOSC 1.5.2.262, which writes lexml
version 6. Version 3 files written by older releases are read, and their
version is preserved on save so that editing an old layout does not silently
convert it. Support for reading version 3 is covered by the guarantee.
What is not promised¶
Being explicit about the edges, since a guarantee is only as clear as its boundary:
validaterules will change. New rules are added as the corpus teaches us more, and a layout clean today may report a warning later. Rules are advisory by design; onlysave(validate=True)turns errors into a refusal.- Message wording will change. Both validation messages and error strings. Match on the exception type, never on the text.
- Generated Python will change.
to_pythonaims for readable output, not stable output. Regenerating with a new release may produce a different -- and hopefully better -- script from the same document. - Internal module layout will change. Where a function lives inside the
package is not part of the contract; how you reach it through
py2toscis.