Skip to content

Commit 12d826d

Browse files
roed314claude
andcommitted
Add PyPI publish workflow and a helpful error when Sage is missing
Importing lmfdb.website (or lmfdb.app, or running the lmfdb script) without SageMath now raises an ImportError explaining that the website requires Sage and how to install into Sage's python, rather than a bare ModuleNotFoundError, since pip cannot install Sage itself. The publish workflow builds and twine-checks the sdist and wheel, smoke tests the wheel, and uploads via PyPI trusted publishing: version tags publish to PyPI, manual runs publish to TestPyPI as a dry run. It cannot succeed until psycodict is released on PyPI (PyPI rejects git URL dependencies) and trusted publishing is configured; see the comments in the workflow file. Note that package versions must be PEP 440 compliant (numeric release segments), so a letter-coded LMFDB version would need to be mapped to its numeric form for the package. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 718e45f commit 12d826d

3 files changed

Lines changed: 111 additions & 2 deletions

File tree

.github/workflows/publish.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Build the lmfdb package and publish it to PyPI.
2+
#
3+
# Triggers:
4+
# - pushing a tag v<version> (e.g. v1.2.1) publishes to PyPI;
5+
# - running the workflow manually (workflow_dispatch) publishes to
6+
# TestPyPI, as a dry run.
7+
#
8+
# Before this can succeed, one-time setup is needed:
9+
# - psycodict must be released on PyPI and the git dependency in
10+
# pyproject.toml replaced by a normal versioned requirement (PyPI
11+
# rejects packages whose dependencies are git URLs);
12+
# - a "Trusted Publisher" must be configured for the lmfdb project on
13+
# https://pypi.org (and https://test.pypi.org), pointing at this
14+
# repository, this workflow file (publish.yml) and the environment
15+
# names below (no API tokens are needed with trusted publishing);
16+
# - (recommended) create "pypi" and "testpypi" environments in the
17+
# repository settings, restricted to release tags/branches.
18+
#
19+
# Note on version numbers: the package version (pyproject.toml reads it
20+
# from lmfdb/version.py) must be PEP 440 compliant, i.e. numeric release
21+
# segments. A letter-coded LMFDB version like 1.8.ba must be mapped to
22+
# its numeric form (1.8.26, via class_to_int) for the package; the tag
23+
# check below compares against the package version.
24+
25+
name: Publish to PyPI
26+
27+
on:
28+
push:
29+
tags: ["v*"]
30+
workflow_dispatch:
31+
32+
jobs:
33+
build:
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@v4
37+
38+
- uses: actions/setup-python@v5
39+
with:
40+
python-version: "3.12"
41+
42+
- name: Check that the tag matches the package version
43+
if: startsWith(github.ref, 'refs/tags/v')
44+
run: |
45+
package_version=$(python -c "import lmfdb.version; print(lmfdb.version.version)")
46+
if [ "v${package_version}" != "${GITHUB_REF_NAME}" ]; then
47+
echo "Tag ${GITHUB_REF_NAME} does not match lmfdb/version.py (${package_version})"
48+
exit 1
49+
fi
50+
51+
- name: Build sdist and wheel
52+
run: |
53+
python -m pip install build twine
54+
python -m build
55+
twine check dist/*
56+
57+
- name: Smoke test the wheel
58+
run: |
59+
python -m venv smoke
60+
smoke/bin/pip install dist/*.whl
61+
smoke/bin/python -c "import lmfdb, importlib.metadata; from lmfdb import db; assert not db.connected; print('wheel imports cleanly, version', importlib.metadata.version('lmfdb'))"
62+
63+
- uses: actions/upload-artifact@v4
64+
with:
65+
name: dist
66+
path: dist/
67+
68+
publish-testpypi:
69+
if: github.event_name == 'workflow_dispatch'
70+
needs: build
71+
runs-on: ubuntu-latest
72+
environment: testpypi
73+
permissions:
74+
id-token: write
75+
steps:
76+
- uses: actions/download-artifact@v4
77+
with:
78+
name: dist
79+
path: dist/
80+
- uses: pypa/gh-action-pypi-publish@release/v1
81+
with:
82+
repository-url: https://test.pypi.org/legacy/
83+
84+
publish-pypi:
85+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
86+
needs: build
87+
runs-on: ubuntu-latest
88+
environment: pypi
89+
permissions:
90+
id-token: write
91+
steps:
92+
- uses: actions/download-artifact@v4
93+
with:
94+
name: dist
95+
path: dist/
96+
- uses: pypa/gh-action-pypi-publish@release/v1

lmfdb/app.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
url_for,
1717
)
1818
from markupsafe import escape
19+
20+
# imported before sage so that a missing sage produces a helpful error message
21+
from .logger import critical
22+
1923
from sage.env import SAGE_VERSION
2024
from sage.all import cached_function
2125
# acknowledgment page, reads info from lmfdb/CONTRIBUTORS.yaml
2226

23-
from .logger import critical
2427
from .homepage import load_boxes, contribs
2528

2629
from .version import version as _lmfdb_version

lmfdb/logger/start.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,17 @@
22
INFO, WARNING,
33
info, warning)
44

5-
from sage.version import version as sage_version
5+
try:
6+
from sage.version import version as sage_version
7+
except ModuleNotFoundError as e:
8+
if e.name == "sage" or (e.name or "").startswith("sage."):
9+
raise ImportError(
10+
"The LMFDB website requires SageMath, which pip does not install. "
11+
"Install the LMFDB into Sage's Python, e.g. with `sage -pip install lmfdb` "
12+
"(or `sage -pip install -e .` from a checkout). "
13+
"Database access (lmfdb.db) works without Sage."
14+
) from None
15+
raise
616

717
from .utils import LmfdbFormatter
818

0 commit comments

Comments
 (0)