Coverage for python/felis/browser/schemas.py: 100%
30 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-29 01:46 -0700
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-29 01:46 -0700
1"""Lookups in schemas shared by the browser's pages."""
3# This file is part of felis.
4#
5# Developed for the LSST Data Management System.
6# This product includes software developed by the LSST Project
7# (https://www.lsst.org).
8# See the COPYRIGHT file at the top-level directory of this distribution
9# for details of code ownership.
10#
11# This program is free software: you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation, either version 3 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program. If not, see <https://www.gnu.org/licenses/>.
24from __future__ import annotations
26import re
27from collections.abc import Iterable
28from typing import TypeVar
30from lsst.utils.iteration import ensure_iterable
32from felis.datamodel import BaseObject, Column, ForeignKeyConstraint, Schema, SchemaVersion, Table
34_Named = TypeVar("_Named", bound=BaseObject)
35"""A schema object of any kind, sorted by name in the sidebar data."""
38def _resolve_column(schema: Schema, ref: str) -> Column:
39 """Return the column a primary key, index or constraint refers to.
41 Raises `ValueError` if ``ref`` isn't a column of the schema. This is the
42 only place the browser looks up columns by ID; switch it to the name-based
43 lookup from DM-51789 once that is merged, as ID lookups may be removed.
44 """
45 try:
46 return schema.find_object_by_id(ref, Column)
47 except (KeyError, TypeError) as e:
48 raise ValueError(
49 f"Column reference '{ref}' in schema '{schema.name}' is not a column of the schema"
50 ) from e
53def _primary_key_columns(schema: Schema, table: Table) -> list[Column]:
54 """Return the columns of a table's primary key, one reference or a list."""
55 return [_resolve_column(schema, ref) for ref in ensure_iterable(table.primary_key or [])]
58def _foreign_keys(table: Table) -> list[ForeignKeyConstraint]:
59 return [constraint for constraint in table.constraints if isinstance(constraint, ForeignKeyConstraint)]
62def _schema_version(schema: Schema) -> str | None:
63 if isinstance(schema.version, SchemaVersion):
64 return schema.version.current
65 return schema.version
68def _version_order(version: str | None) -> str:
69 """Return a sort key for a version: numbers zero-padded, so 3.8.1 sorts
70 before 10.0.0.
71 """
72 if version is None:
73 return ""
74 # Splitting on a captured group keeps the numbers as separate parts.
75 parts = re.split(r"(\d+)", version)
76 return "".join(part.zfill(10) if part.isdigit() else part for part in parts)
79def _name_sort_key(obj: BaseObject) -> str:
80 return obj.name.lower()
83def _sorted_by_name(objects: Iterable[_Named]) -> list[_Named]:
84 return sorted(objects, key=_name_sort_key)