Skip to content

Commit 11ff936

Browse files
committed
Extract palace.opds and palace.util into uv workspace packages
Convert the repo into a uv workspace with two new namespace packages under packages/: - palace-util (palace.util): shared exceptions, datetime helpers, and LoggerMixin previously living in palace.manager.{core.exceptions, util.datetime_helpers, util.log}. - palace-opds (palace.opds): the OPDS/RWPM/ODL/LCP/Palace Pydantic models previously living in palace.manager.opds, depending on palace-util for its small set of cross-cutting utilities. palace.manager.{core.exceptions, util.datetime_helpers, util.log} are kept as thin re-export shims so the ~316 callers across the codebase keep working unchanged. palace.manager.util.log keeps log_elapsed_time, elapsed_time_logging, pluralize, and ExtraDataLoggerAdapter (which depend on palace-manager-specific bits). Imports of palace.manager.opds across src/ and tests/ are rewritten to palace.opds. Tests for the moved code move with it; opds tests that use OPDS2FilesFixture only for type annotations now import it under TYPE_CHECKING so the package tests don't need tests.fixtures.files at import time. Tooling updates: - pyproject.toml: workspace + sources, mypy/coverage/pytest paths, test override extended for the new test module names. - tox.ini: pytest now uses testpaths from pyproject.toml. - docker/Dockerfile: copy workspace pyproject.toml files in the cache layer; switch to --no-install-workspace. - .pre-commit-config.yaml: namespace-package check now matches any src/palace/__init__.py, not just the root one. - README.md: new "Repository Layout" section linking to the package READMEs. - CLAUDE.md: project-structure section reflects the workspace and documents the re-export shims.
1 parent 57475b4 commit 11ff936

89 files changed

Lines changed: 594 additions & 337 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ repos:
7676
- id: check-namespace-package
7777
name: Check that palace is a namespace package
7878
language: fail
79-
entry: Please remove src/palace/__init__.py
80-
files: ^src/palace/__init__.py$
79+
entry: Please remove src/palace/__init__.py - palace must remain a PEP 420 namespace.
80+
files: (^|/)src/palace/__init__\.py$
8181

8282
# Exclude test files, since they may be intentionally malformed
8383
exclude: ^tests/files/

CLAUDE.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ through mobile and web applications.
4949

5050
## Project Structure
5151

52-
- `/src/palace/manager` - Main application source code
52+
This repository is a [`uv` workspace](https://docs.astral.sh/uv/concepts/projects/workspaces/). The main
53+
`palace-manager` application is at the repo root; reusable namespace packages live under `/packages`.
54+
55+
- `/src/palace/manager` - Main application source code (`palace-manager`)
5356
- `/api` - REST API endpoints for mobile applications
5457
- `/admin` - Administrative API endpoints for the web dashboard
5558
- `/celery` - Background worker processes and task definitions
@@ -58,18 +61,27 @@ through mobile and web applications.
5861
- `/data_layer` - Pydantic models for content import and validation
5962
- `/feed` - OPDS (Open Publication Distribution System) feed generation
6063
- `/integration` - Third-party service integrations and content provider APIs
61-
- `/opds` - Pydantic models for OPDS feed parsing and validation
6264
- `/scripts` - Legacy CLI utilities (**deprecated - no new code**)
6365
- `/search` - OpenSearch integration and indexing logic
6466
- `/service` - Dependency injection container and service layer
6567
- `/sqlalchemy` - Database models and schema definitions
6668
- `/util` - Reusable utility functions and helpers
67-
- `/tests` - Test files
69+
- `/packages` - Workspace member packages (each is independently buildable/publishable)
70+
- `/palace-util` - Shared utilities under the `palace.util` namespace: exceptions
71+
(`BasePalaceException`, `PalaceValueError`, `PalaceTypeError`), datetime helpers, `LoggerMixin`.
72+
`palace.manager.util.{datetime_helpers,log}` and `palace.manager.core.exceptions` re-export from
73+
here for backward compatibility — new code can import from either path.
74+
- `/palace-opds` - Pydantic models for OPDS 2.0, RWPM, ODL, LCP, and Palace-specific extensions
75+
under the `palace.opds` namespace.
76+
- `/tests` - Test files for `palace-manager`
6877
- `/files` - Test fixture files
6978
- `/fixtures` - pytest fixtures shared across test functions
7079
- `/manager` - pytest test files (should mirror `src/palace/manager` structure)
7180
- `/migration` - Database migration tests
7281
- `/mocks` - Test mocks (**being phased out - avoid in new code**)
82+
- Each workspace package owns its own `tests/` directory under `packages/<name>/tests/`. The full
83+
pytest suite (configured via `testpaths` in `pyproject.toml`) runs them all together so
84+
root-conftest plugins remain available to package tests.
7385

7486
## Architecture Overview
7587

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ Palace Manager is a backend service for digital library systems, maintained by [
1515
- [Patron Blocking Rules — Allowed Functions](docs/FUNCTIONS.md)
1616
Reference for the functions available in patron blocking rule expressions.
1717

18+
## Repository Layout
19+
20+
This repository is a [`uv` workspace](https://docs.astral.sh/uv/concepts/projects/workspaces/). The main
21+
`palace-manager` application lives at the repo root (`src/palace/manager/`). Reusable namespace packages
22+
live under `packages/`:
23+
24+
- [`palace-util`](packages/palace-util/README.md) — shared utilities (exceptions, datetime helpers,
25+
`LoggerMixin`) under the `palace.util` namespace.
26+
- [`palace-opds`](packages/palace-opds/README.md) — Pydantic models for OPDS 2.0, RWPM, ODL, LCP, and
27+
Palace extensions under the `palace.opds` namespace.
28+
1829
## Installation
1930

2031
Docker images created from this code are available at:

docker/Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ COPY --chmod=644 docker/services/logrotate /etc/
1818
RUN mv /etc/cron.daily/logrotate /etc/cron.hourly/logrotate
1919

2020
# Copy our uv files into the image and install our dependencies.
21+
# We copy the workspace-member pyproject.toml files (but not their source) so that
22+
# uv can resolve workspace members without invalidating this layer on source changes.
2123
COPY --chown=palace:palace uv.lock pyproject.toml /var/www/circulation/
22-
RUN uv sync --frozen --no-dev --no-install-project
24+
COPY --chown=palace:palace packages/palace-util/pyproject.toml /var/www/circulation/packages/palace-util/
25+
COPY --chown=palace:palace packages/palace-opds/pyproject.toml /var/www/circulation/packages/palace-opds/
26+
RUN uv sync --frozen --no-dev --no-install-workspace
2327

2428
COPY --chown=palace:palace . /var/www/circulation
2529

packages/palace-opds/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# palace-opds
2+
3+
Pydantic models for OPDS 2.0, RWPM, ODL, LCP, Authentication for OPDS, and Palace Project extensions.
4+
Exposes the `palace.opds` namespace module.
5+
6+
Top-level modules:
7+
8+
- `palace.opds.opds2` — OPDS 2.0 publication and feed models.
9+
- `palace.opds.rwpm` — Readium Web Publication Manifest.
10+
- `palace.opds.authentication` — Authentication for OPDS 1.0.
11+
- `palace.opds.palace` — Palace-specific extensions.
12+
- `palace.opds.a11y` — W3C accessibility metadata.
13+
- `palace.opds.schema_org` — Schema.org vocabulary.
14+
- `palace.opds.types.*` — Shared Pydantic types (`BaseLink`, language, date, currency).
15+
- `palace.opds.odl.*` — ODL 1.0 license metadata and status.
16+
- `palace.opds.lcp.*` — LCP license and loan status documents.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[build-system]
2+
build-backend = "hatchling.build"
3+
requires = ["hatchling"]
4+
5+
[project]
6+
authors = [{name = "The Palace Project", email = "info@thepalaceproject.org"}]
7+
dependencies = [
8+
"palace-util",
9+
"pycountry>=26.2.16,<27",
10+
"pydantic-xml>=2.17.0,<3",
11+
"pydantic[email]>=2.12,<3",
12+
"uritemplate==4.2.0",
13+
]
14+
description = "Pydantic models for OPDS 2.0, RWPM, ODL, LCP, and Palace Project extensions."
15+
license = "Apache-2.0"
16+
name = "palace-opds"
17+
readme = "README.md"
18+
requires-python = ">=3.12,<4"
19+
version = "0"
20+
21+
[project.urls]
22+
Homepage = "https://thepalaceproject.org"
23+
Repository = "https://github.com/ThePalaceProject/circulation"
24+
25+
[tool.hatch.build.targets.wheel]
26+
packages = ["src/palace"]
27+
28+
[tool.uv.sources]
29+
palace-util = {workspace = true}
File renamed without changes.

src/palace/manager/opds/a11y.py renamed to packages/palace-opds/src/palace/opds/a11y.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
from pydantic import Field, SerializerFunctionWrapHandler, model_serializer
1616

17-
from palace.manager.opds.base import BaseOpdsModel
18-
from palace.manager.opds.util import StrOrTuple, drop_if_falsy, obj_or_tuple_to_tuple
17+
from palace.opds.base import BaseOpdsModel
18+
from palace.opds.util import StrOrTuple, drop_if_falsy, obj_or_tuple_to_tuple
1919

2020

2121
class AccessMode(StrEnum):

src/palace/manager/opds/authentication.py renamed to packages/palace-opds/src/palace/opds/authentication.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
from pydantic import BaseModel, Field, field_validator
77

8-
from palace.manager.core.exceptions import PalaceValueError
9-
from palace.manager.opds.rwpm import Link
10-
from palace.manager.opds.types.link import CompactCollection
8+
from palace.opds.rwpm import Link
9+
from palace.opds.types.link import CompactCollection
10+
from palace.util.exceptions import PalaceValueError
1111

1212

1313
class AuthenticationLabels(BaseModel):
File renamed without changes.

0 commit comments

Comments
 (0)