Skip to content

skeleton

Tracked-point skeleton: what a detector localizes, independent of any rig.

A Skeleton is the ordered point_names (channel i is point_names[i]), the edges and the left/right point_symmetries, as in SLEAP and DeepLabCut. Its identity is those three, printed as Skeleton.digest: the name, the colors and the default_pose are out of it, so renaming or recoloring a published skeleton does not report a different one, and editing its points in place is what silently mislabels a model trained under the old one. The mirror of the symmetries must map the edges onto themselves, checked at construction, which makes Skeleton.flip_perm an involution.

Skeleton dataclass

An ordered set of points, their edges and their left/right mirror pairs.

Attributes:

Name Type Description
name str

A human label ("fly38"), not the identity (see digest).

point_names tuple[str, ...]

Name per tracked point, in channel order.

edges Int[ndarray, 'E 2']

(n_edges, 2) point-index pairs, in declaration order.

point_symmetries Int[ndarray, 'S 2']

(n_symmetries, 2) mirror pairs, each row and the rows sorted, so the same pairs in any order compare equal; each point in at most one pair.

point_colors tuple[str, ...]

#rrggbb per point, DEFAULT_PALETTE by index where the skeleton names none.

edge_colors tuple[str, ...]

#rrggbb per edge, the average of its endpoints' colors where the skeleton names none.

default_pose Float[ndarray, 'P 3']

A neutral posture, (n_points, 3) millimeters with the body center at the origin, every point declared, or (0, 3) when none is; resolved_pose stands in the synthetic pose then, to draw and to seed labeling.

Source code in src/deeperfly/skeleton.py
@dataclass(frozen=True)
class Skeleton:
    """An ordered set of points, their edges and their left/right mirror pairs.

    Attributes:
        name: A human label (`"fly38"`), not the identity (see [digest][]).
        point_names: Name per tracked point, in channel order.
        edges: `(n_edges, 2)` point-index pairs, in declaration order.
        point_symmetries: `(n_symmetries, 2)` mirror pairs, each row and the rows
            sorted, so the same pairs in any order compare equal; each point in at
            most one pair.
        point_colors: `#rrggbb` per point, `DEFAULT_PALETTE` by index where the
            skeleton names none.
        edge_colors: `#rrggbb` per edge, the average of its endpoints' colors where
            the skeleton names none.
        default_pose: A neutral posture, `(n_points, 3)` millimeters with the body
            center at the origin, every point declared, or `(0, 3)` when none is;
            [resolved_pose][] stands in the synthetic pose then, to draw and to seed
            labeling.
    """

    name: str
    point_names: tuple[str, ...]
    edges: Int[np.ndarray, "E 2"]
    point_symmetries: Int[np.ndarray, "S 2"] = field(
        default_factory=lambda: np.empty((0, 2), np.int64)
    )
    point_colors: tuple[str, ...] = ()
    edge_colors: tuple[str, ...] = ()
    default_pose: Float[np.ndarray, "P 3"] = field(
        default_factory=lambda: np.empty((0, 3))
    )

    def __post_init__(self) -> None:
        """Coerce the arrays, canonicalize the pairs and check every invariant."""
        names = tuple(str(n) for n in self.point_names)
        object.__setattr__(self, "point_names", names)
        dupes = sorted({n for n in names if names.count(n) > 1})
        if dupes:
            raise ValueError(
                f"point_names repeats {dupes}; a point name is a channel and must "
                "be unique"
            )

        edges = np.asarray(self.edges, dtype=np.int64).reshape(-1, 2)
        _check_indices(edges, len(names), "edges")
        object.__setattr__(self, "edges", edges)

        pairs = np.asarray(self.point_symmetries, dtype=np.int64).reshape(-1, 2)
        _check_indices(pairs, len(names), "point_symmetries")
        if pairs.size:
            flat = pairs.reshape(-1)
            repeated = {i for i in flat.tolist() if (flat == i).sum() > 1}
            if repeated:
                raise ValueError(
                    "these points sit in more than one symmetry pair: "
                    f"{sorted(names[i] for i in repeated)}; each point mirrors "
                    "exactly one other"
                )
            pairs = np.sort(pairs, axis=1)
            pairs = pairs[np.lexsort((pairs[:, 1], pairs[:, 0]))]
        object.__setattr__(self, "point_symmetries", pairs)
        _check_automorphism(edges, pairs, names)

        colors = tuple(str(c) for c in self.point_colors)
        if colors and len(colors) != len(names):
            raise ValueError(
                f"point_colors has {len(colors)} entries for {len(names)} points"
            )
        if not colors:
            colors = tuple(
                DEFAULT_PALETTE[i % len(DEFAULT_PALETTE)] for i in range(len(names))
            )
        colors = tuple(_hex(c, names[i]) for i, c in enumerate(colors))
        object.__setattr__(self, "point_colors", colors)

        edge_colors = tuple(str(c) for c in self.edge_colors)
        if edge_colors and len(edge_colors) != edges.shape[0]:
            raise ValueError(
                f"edge_colors has {len(edge_colors)} entries for {edges.shape[0]} edges"
            )
        if not edge_colors:
            edge_colors = tuple(
                _average_hex(colors[int(a)], colors[int(b)]) for a, b in edges
            )
        edge_colors = tuple(
            _hex(c, f"{names[int(a)]}{_EDGE_SEP}{names[int(b)]}")
            for c, (a, b) in zip(edge_colors, edges, strict=True)
        )
        object.__setattr__(self, "edge_colors", edge_colors)

        pose = np.asarray(self.default_pose, dtype=float)
        if pose.size:
            pose = pose.reshape(-1, 3)
            if pose.shape[0] != len(names):
                raise ValueError(
                    f"default_pose has {pose.shape[0]} points for {len(names)} "
                    "point names"
                )
            # The synthetic pose is in another frame and scale, so it cannot fill the
            # gaps of a declared one: declare every point or none.
            missing = [
                n
                for n, ok in zip(names, np.isfinite(pose).all(1), strict=True)
                if not ok
            ]
            if missing:
                raise ValueError(
                    f"default_pose leaves out {', '.join(missing)}; declare every "
                    "point or none"
                )
        else:
            pose = np.empty((0, 3))
        object.__setattr__(self, "default_pose", pose)

    # -- construction --------------------------------------------------------

    @classmethod
    def from_toml(cls, path: str | os.PathLike) -> Skeleton:
        """Build a skeleton from a TOML file holding a `[skeleton]` table."""
        with open(path, "rb") as f:
            return cls.from_toml_text(f.read().decode("utf-8"))

    @classmethod
    def from_toml_text(cls, text: str) -> Skeleton:
        """Build a skeleton from TOML text with a `[skeleton]` table, as the GUI does.

        Raises:
            ValueError: If the text is not valid TOML, has no `[skeleton]` table, or
                breaks an invariant.
        """
        try:
            data = tomllib.loads(text)
        except tomllib.TOMLDecodeError as exc:
            raise ValueError(f"not valid TOML: {exc}") from exc
        if "skeleton" not in data:
            raise ValueError("no [skeleton] table in the TOML")
        return cls.from_spec(data["skeleton"])

    @classmethod
    def from_spec(cls, spec: dict) -> Skeleton:
        """Build a skeleton from a `[skeleton]` table.

        `points` is required and ordered; `edges` and `point_symmetries` are pairs of
        names or indices; `point_colors` maps a name or `*` pattern to a `#rrggbb` and
        `edge_colors` an `"<a>--<b>"` selector to one, both partial at will; and
        `default_pose` maps a name to `[x, y, z]`.

        Raises:
            ValueError: If `points` is missing or repeats a name, a pair names an
                unknown point, the symmetries are not an automorphism of the edges,
                a color key matches nothing, two match one, or a value is no color,
                or `default_pose` leaves a point out.
        """
        if "points" not in spec:
            raise ValueError(
                "[skeleton] declares no 'points'; the points are the ordered "
                "channel contract"
            )
        names = tuple(str(p) for p in spec["points"] or ())
        index = {name: i for i, name in enumerate(names)}
        edges = _pairs(spec.get("edges"), index, "edges")
        point_colors = _resolve_point_colors(spec.get("point_colors") or {}, names)
        return cls(
            name=str(spec.get("name", "skeleton")),
            point_names=names,
            edges=edges,
            point_symmetries=_pairs(
                spec.get("point_symmetries"), index, "point_symmetries"
            ),
            point_colors=point_colors,
            edge_colors=_resolve_edge_colors(
                spec.get("edge_colors") or {}, names, edges, point_colors
            ),
            default_pose=_default_pose(spec.get("default_pose") or {}, names),
        )

    # -- sizes ---------------------------------------------------------------

    @property
    def n_points(self) -> int:
        """The number of tracked points."""
        return len(self.point_names)

    @property
    def n_edges(self) -> int:
        """The number of edges."""
        return int(self.edges.shape[0])

    @property
    def n_symmetries(self) -> int:
        """The number of left/right mirror pairs."""
        return int(self.point_symmetries.shape[0])

    # -- lookups -------------------------------------------------------------

    def index(self, name: str) -> int:
        """`name`'s point index, or `ValueError` naming the skeleton."""
        if name not in self.point_names:
            raise ValueError(f"{name!r} is not a point of skeleton {self.name!r}")
        return self.point_names.index(name)

    # -- symmetry ------------------------------------------------------------

    def partner(self, point: str | int) -> int | None:
        """The index mirroring `point`, `None` if unpaired, `ValueError` if unknown."""
        i = self.index(point) if isinstance(point, str) else int(point)
        for a, b in self.point_symmetries:
            if int(a) == i:
                return int(b)
            if int(b) == i:
                return int(a)
        return None

    def flip_perm(self) -> Int[np.ndarray, "P"]:
        """The point permutation a left-right mirror implies, `(n_points,)`.

        `mirrored[i] = original[flip_perm()[i]]`, the identity at unpaired points; an
        involution, SLEAP's `Skeleton.get_flipped_node_inds()`.
        """
        perm = np.arange(self.n_points, dtype=np.int64)
        pairs = self.point_symmetries
        if pairs.size:
            perm[pairs[:, 0]] = pairs[:, 1]
            perm[pairs[:, 1]] = pairs[:, 0]
        return perm

    # -- pose ----------------------------------------------------------------

    def resolved_pose(self) -> Float[np.ndarray, "P 3"]:
        """A full `(n_points, 3)` posture, always finite, to draw or seed from.

        The declared `default_pose`, or [synthetic_pose][] when none is declared; a
        fresh array either way, the caller's to write into.
        """
        if self.default_pose.size:
            return self.default_pose.copy()
        return self.synthetic_pose()

    def synthetic_pose(self) -> Float[np.ndarray, "P 3"]:
        """A plausible `(n_points, 3)` layout inferred from the topology alone.

        The unpaired points on the x axis, earliest most anterior; each left limb (a
        connected run of paired points) fanned out at its own azimuth across the left
        half-plane, descending 45 degrees a step, and the right side its mirror;
        centered on its centroid.
        """
        n = self.n_points
        pos = np.zeros((n, 3))
        pairs = self.point_symmetries
        paired = {int(i) for i in pairs.reshape(-1)}

        # Midline: the unpaired points, on the x axis, earliest at the most +x.
        midline = [i for i in range(n) if i not in paired]
        offset = (len(midline) - 1) / 2
        for rank, i in enumerate(midline):
            pos[i, 0] = offset - rank

        # Each limb is a chain of the "left" points (the lower index of each pair,
        # by the left-before-right listing convention); its mirror places the right.
        if pairs.size:
            partner = {int(a): int(b) for a, b in pairs}
            chains = _chains({int(a) for a in pairs[:, 0]}, self.edges)
            chains.sort(key=min)
            depression = np.pi / 4  # below the horizontal xy plane
            for rank, chain in enumerate(chains):
                azimuth = (rank + 0.5) / len(chains) * np.pi  # left half-plane
                step = np.array(
                    [
                        np.cos(depression) * np.cos(azimuth),
                        np.cos(depression) * np.sin(azimuth),
                        -np.sin(depression),
                    ]
                )
                for j, p in enumerate(sorted(chain), start=1):
                    pos[p] = j * step
                    pos[partner[p]] = pos[p] * (1, -1, 1)

        return pos - pos.mean(0) if n else pos

    # -- identity ------------------------------------------------------------

    @property
    def digest(self) -> str:
        """8 hex chars over the point names, edges and symmetry pairs.

        The printable identity, never compared: renaming or recoloring a skeleton
        leaves it unchanged.
        """
        payload = json.dumps(
            {
                "points": list(self.point_names),
                "edges": self.edges.tolist(),
                "point_symmetries": self.point_symmetries.tolist(),
            },
            sort_keys=True,
            separators=(",", ":"),
        )
        return hashlib.sha256(payload.encode("utf-8")).hexdigest()[:8]

    @property
    def label(self) -> str:
        """`"<name>@<digest>"` -- what to print when naming a skeleton to a human."""
        return f"{self.name}@{self.digest}"

digest property

digest: str

8 hex chars over the point names, edges and symmetry pairs.

The printable identity, never compared: renaming or recoloring a skeleton leaves it unchanged.

label property

label: str

"<name>@<digest>" -- what to print when naming a skeleton to a human.

n_edges property

n_edges: int

The number of edges.

n_points property

n_points: int

The number of tracked points.

n_symmetries property

n_symmetries: int

The number of left/right mirror pairs.

__post_init__

__post_init__() -> None

Coerce the arrays, canonicalize the pairs and check every invariant.

Source code in src/deeperfly/skeleton.py
def __post_init__(self) -> None:
    """Coerce the arrays, canonicalize the pairs and check every invariant."""
    names = tuple(str(n) for n in self.point_names)
    object.__setattr__(self, "point_names", names)
    dupes = sorted({n for n in names if names.count(n) > 1})
    if dupes:
        raise ValueError(
            f"point_names repeats {dupes}; a point name is a channel and must "
            "be unique"
        )

    edges = np.asarray(self.edges, dtype=np.int64).reshape(-1, 2)
    _check_indices(edges, len(names), "edges")
    object.__setattr__(self, "edges", edges)

    pairs = np.asarray(self.point_symmetries, dtype=np.int64).reshape(-1, 2)
    _check_indices(pairs, len(names), "point_symmetries")
    if pairs.size:
        flat = pairs.reshape(-1)
        repeated = {i for i in flat.tolist() if (flat == i).sum() > 1}
        if repeated:
            raise ValueError(
                "these points sit in more than one symmetry pair: "
                f"{sorted(names[i] for i in repeated)}; each point mirrors "
                "exactly one other"
            )
        pairs = np.sort(pairs, axis=1)
        pairs = pairs[np.lexsort((pairs[:, 1], pairs[:, 0]))]
    object.__setattr__(self, "point_symmetries", pairs)
    _check_automorphism(edges, pairs, names)

    colors = tuple(str(c) for c in self.point_colors)
    if colors and len(colors) != len(names):
        raise ValueError(
            f"point_colors has {len(colors)} entries for {len(names)} points"
        )
    if not colors:
        colors = tuple(
            DEFAULT_PALETTE[i % len(DEFAULT_PALETTE)] for i in range(len(names))
        )
    colors = tuple(_hex(c, names[i]) for i, c in enumerate(colors))
    object.__setattr__(self, "point_colors", colors)

    edge_colors = tuple(str(c) for c in self.edge_colors)
    if edge_colors and len(edge_colors) != edges.shape[0]:
        raise ValueError(
            f"edge_colors has {len(edge_colors)} entries for {edges.shape[0]} edges"
        )
    if not edge_colors:
        edge_colors = tuple(
            _average_hex(colors[int(a)], colors[int(b)]) for a, b in edges
        )
    edge_colors = tuple(
        _hex(c, f"{names[int(a)]}{_EDGE_SEP}{names[int(b)]}")
        for c, (a, b) in zip(edge_colors, edges, strict=True)
    )
    object.__setattr__(self, "edge_colors", edge_colors)

    pose = np.asarray(self.default_pose, dtype=float)
    if pose.size:
        pose = pose.reshape(-1, 3)
        if pose.shape[0] != len(names):
            raise ValueError(
                f"default_pose has {pose.shape[0]} points for {len(names)} "
                "point names"
            )
        # The synthetic pose is in another frame and scale, so it cannot fill the
        # gaps of a declared one: declare every point or none.
        missing = [
            n
            for n, ok in zip(names, np.isfinite(pose).all(1), strict=True)
            if not ok
        ]
        if missing:
            raise ValueError(
                f"default_pose leaves out {', '.join(missing)}; declare every "
                "point or none"
            )
    else:
        pose = np.empty((0, 3))
    object.__setattr__(self, "default_pose", pose)

flip_perm

flip_perm() -> Int[np.ndarray, P]

The point permutation a left-right mirror implies, (n_points,).

mirrored[i] = original[flip_perm()[i]], the identity at unpaired points; an involution, SLEAP's Skeleton.get_flipped_node_inds().

Source code in src/deeperfly/skeleton.py
def flip_perm(self) -> Int[np.ndarray, "P"]:
    """The point permutation a left-right mirror implies, `(n_points,)`.

    `mirrored[i] = original[flip_perm()[i]]`, the identity at unpaired points; an
    involution, SLEAP's `Skeleton.get_flipped_node_inds()`.
    """
    perm = np.arange(self.n_points, dtype=np.int64)
    pairs = self.point_symmetries
    if pairs.size:
        perm[pairs[:, 0]] = pairs[:, 1]
        perm[pairs[:, 1]] = pairs[:, 0]
    return perm

from_spec classmethod

from_spec(spec: dict) -> Skeleton

Build a skeleton from a [skeleton] table.

points is required and ordered; edges and point_symmetries are pairs of names or indices; point_colors maps a name or * pattern to a #rrggbb and edge_colors an "<a>--<b>" selector to one, both partial at will; and default_pose maps a name to [x, y, z].

Raises:

Type Description
ValueError

If points is missing or repeats a name, a pair names an unknown point, the symmetries are not an automorphism of the edges, a color key matches nothing, two match one, or a value is no color, or default_pose leaves a point out.

Source code in src/deeperfly/skeleton.py
@classmethod
def from_spec(cls, spec: dict) -> Skeleton:
    """Build a skeleton from a `[skeleton]` table.

    `points` is required and ordered; `edges` and `point_symmetries` are pairs of
    names or indices; `point_colors` maps a name or `*` pattern to a `#rrggbb` and
    `edge_colors` an `"<a>--<b>"` selector to one, both partial at will; and
    `default_pose` maps a name to `[x, y, z]`.

    Raises:
        ValueError: If `points` is missing or repeats a name, a pair names an
            unknown point, the symmetries are not an automorphism of the edges,
            a color key matches nothing, two match one, or a value is no color,
            or `default_pose` leaves a point out.
    """
    if "points" not in spec:
        raise ValueError(
            "[skeleton] declares no 'points'; the points are the ordered "
            "channel contract"
        )
    names = tuple(str(p) for p in spec["points"] or ())
    index = {name: i for i, name in enumerate(names)}
    edges = _pairs(spec.get("edges"), index, "edges")
    point_colors = _resolve_point_colors(spec.get("point_colors") or {}, names)
    return cls(
        name=str(spec.get("name", "skeleton")),
        point_names=names,
        edges=edges,
        point_symmetries=_pairs(
            spec.get("point_symmetries"), index, "point_symmetries"
        ),
        point_colors=point_colors,
        edge_colors=_resolve_edge_colors(
            spec.get("edge_colors") or {}, names, edges, point_colors
        ),
        default_pose=_default_pose(spec.get("default_pose") or {}, names),
    )

from_toml classmethod

from_toml(path: str | PathLike) -> Skeleton

Build a skeleton from a TOML file holding a [skeleton] table.

Source code in src/deeperfly/skeleton.py
@classmethod
def from_toml(cls, path: str | os.PathLike) -> Skeleton:
    """Build a skeleton from a TOML file holding a `[skeleton]` table."""
    with open(path, "rb") as f:
        return cls.from_toml_text(f.read().decode("utf-8"))

from_toml_text classmethod

from_toml_text(text: str) -> Skeleton

Build a skeleton from TOML text with a [skeleton] table, as the GUI does.

Raises:

Type Description
ValueError

If the text is not valid TOML, has no [skeleton] table, or breaks an invariant.

Source code in src/deeperfly/skeleton.py
@classmethod
def from_toml_text(cls, text: str) -> Skeleton:
    """Build a skeleton from TOML text with a `[skeleton]` table, as the GUI does.

    Raises:
        ValueError: If the text is not valid TOML, has no `[skeleton]` table, or
            breaks an invariant.
    """
    try:
        data = tomllib.loads(text)
    except tomllib.TOMLDecodeError as exc:
        raise ValueError(f"not valid TOML: {exc}") from exc
    if "skeleton" not in data:
        raise ValueError("no [skeleton] table in the TOML")
    return cls.from_spec(data["skeleton"])

index

index(name: str) -> int

name's point index, or ValueError naming the skeleton.

Source code in src/deeperfly/skeleton.py
def index(self, name: str) -> int:
    """`name`'s point index, or `ValueError` naming the skeleton."""
    if name not in self.point_names:
        raise ValueError(f"{name!r} is not a point of skeleton {self.name!r}")
    return self.point_names.index(name)

partner

partner(point: str | int) -> int | None

The index mirroring point, None if unpaired, ValueError if unknown.

Source code in src/deeperfly/skeleton.py
def partner(self, point: str | int) -> int | None:
    """The index mirroring `point`, `None` if unpaired, `ValueError` if unknown."""
    i = self.index(point) if isinstance(point, str) else int(point)
    for a, b in self.point_symmetries:
        if int(a) == i:
            return int(b)
        if int(b) == i:
            return int(a)
    return None

resolved_pose

resolved_pose() -> Float[np.ndarray, 'P 3']

A full (n_points, 3) posture, always finite, to draw or seed from.

The declared default_pose, or synthetic_pose when none is declared; a fresh array either way, the caller's to write into.

Source code in src/deeperfly/skeleton.py
def resolved_pose(self) -> Float[np.ndarray, "P 3"]:
    """A full `(n_points, 3)` posture, always finite, to draw or seed from.

    The declared `default_pose`, or [synthetic_pose][] when none is declared; a
    fresh array either way, the caller's to write into.
    """
    if self.default_pose.size:
        return self.default_pose.copy()
    return self.synthetic_pose()

synthetic_pose

synthetic_pose() -> Float[np.ndarray, 'P 3']

A plausible (n_points, 3) layout inferred from the topology alone.

The unpaired points on the x axis, earliest most anterior; each left limb (a connected run of paired points) fanned out at its own azimuth across the left half-plane, descending 45 degrees a step, and the right side its mirror; centered on its centroid.

Source code in src/deeperfly/skeleton.py
def synthetic_pose(self) -> Float[np.ndarray, "P 3"]:
    """A plausible `(n_points, 3)` layout inferred from the topology alone.

    The unpaired points on the x axis, earliest most anterior; each left limb (a
    connected run of paired points) fanned out at its own azimuth across the left
    half-plane, descending 45 degrees a step, and the right side its mirror;
    centered on its centroid.
    """
    n = self.n_points
    pos = np.zeros((n, 3))
    pairs = self.point_symmetries
    paired = {int(i) for i in pairs.reshape(-1)}

    # Midline: the unpaired points, on the x axis, earliest at the most +x.
    midline = [i for i in range(n) if i not in paired]
    offset = (len(midline) - 1) / 2
    for rank, i in enumerate(midline):
        pos[i, 0] = offset - rank

    # Each limb is a chain of the "left" points (the lower index of each pair,
    # by the left-before-right listing convention); its mirror places the right.
    if pairs.size:
        partner = {int(a): int(b) for a, b in pairs}
        chains = _chains({int(a) for a in pairs[:, 0]}, self.edges)
        chains.sort(key=min)
        depression = np.pi / 4  # below the horizontal xy plane
        for rank, chain in enumerate(chains):
            azimuth = (rank + 0.5) / len(chains) * np.pi  # left half-plane
            step = np.array(
                [
                    np.cos(depression) * np.cos(azimuth),
                    np.cos(depression) * np.sin(azimuth),
                    -np.sin(depression),
                ]
            )
            for j, p in enumerate(sorted(chain), start=1):
                pos[p] = j * step
                pos[partner[p]] = pos[p] * (1, -1, 1)

    return pos - pos.mean(0) if n else pos

edge_lengths

edge_lengths(pts3d: ndarray, edges: ndarray) -> np.ndarray

(..., E) bone lengths of (..., K, 3) poses along (E, 2) edges.

Source code in src/deeperfly/skeleton.py
def edge_lengths(pts3d: np.ndarray, edges: np.ndarray) -> np.ndarray:
    """`(..., E)` bone lengths of `(..., K, 3)` poses along `(E, 2)` edges."""
    edges = np.asarray(edges, dtype=int).reshape(-1, 2)
    return np.linalg.norm(
        pts3d[..., edges[:, 0], :] - pts3d[..., edges[:, 1], :], axis=-1
    )

load_skeleton

load_skeleton(source: str | PathLike) -> Skeleton

Load a skeleton from a .toml path or a packaged preset name.

Source code in src/deeperfly/skeleton.py
def load_skeleton(source: str | os.PathLike) -> Skeleton:
    """Load a skeleton from a `.toml` path or a packaged preset name."""
    return Skeleton.from_toml(resolve_skeleton_source(source))

resolve_skeleton_source

resolve_skeleton_source(skeleton: str | PathLike) -> Path

The file a skeleton reference names: a .toml path, or a preset by name.

Raises:

Type Description
FileNotFoundError

If it is neither, listing the presets.

Source code in src/deeperfly/skeleton.py
def resolve_skeleton_source(skeleton: str | os.PathLike) -> Path:
    """The file a skeleton reference names: a `.toml` path, or a preset by name.

    Raises:
        FileNotFoundError: If it is neither, listing the presets.
    """
    p = Path(skeleton)
    if p.is_file():
        return p
    preset = PRESETS_DIR / f"{skeleton}.toml"
    if preset.is_file():
        return preset
    raise FileNotFoundError(
        f"skeleton {str(skeleton)!r} is neither a file nor a known preset "
        f"(presets: {skeleton_presets()})"
    )

skeleton_presets

skeleton_presets() -> list[str]

The names of the packaged skeleton presets (e.g. ["fly38"]), sorted.

Source code in src/deeperfly/skeleton.py
def skeleton_presets() -> list[str]:
    """The names of the packaged skeleton presets (e.g. `["fly38"]`), sorted."""
    return sorted(p.stem for p in PRESETS_DIR.glob("*.toml"))

skeleton_source_text

skeleton_source_text(source: str | PathLike) -> str

The raw TOML of a .toml path or a preset, to prefill the skeleton editor.

Source code in src/deeperfly/skeleton.py
def skeleton_source_text(source: str | os.PathLike) -> str:
    """The raw TOML of a `.toml` path or a preset, to prefill the skeleton editor."""
    return resolve_skeleton_source(source).read_text()