Skip to content

Commit d7a5bfa

Browse files
committed
python documentation & debugging
1 parent 5032946 commit d7a5bfa

File tree

175 files changed

+23056
-1025
lines changed

Some content is hidden

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

175 files changed

+23056
-1025
lines changed

PUBLISHING.md

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
# Publishing Cellucid (Beginner Guide)
2+
3+
This guide walks you through publishing the `cellucid` Python package to:
4+
- PyPI (pip install)
5+
- conda-forge (conda install)
6+
- Bioconda (bioinformatics-focused conda channel)
7+
- Read the Docs (documentation hosting)
8+
9+
## 0) What gets published where
10+
11+
- **PyPI**: the canonical Python release artifact (wheel + sdist). Most downstream ecosystems (including conda-forge) pull from PyPI source releases.
12+
- **conda-forge**: a separate “feedstock” repo maintained by conda-forge; it builds conda packages from your PyPI sdist.
13+
- **Bioconda**: a separate `bioconda-recipes` PR; Bioconda packages typically depend on conda-forge for most Python dependencies.
14+
- **Read the Docs**: builds documentation from your repo (usually from a Git tag or branch).
15+
16+
## 0.1) Recommended release model (lowest friction)
17+
18+
This repo already includes GitHub Actions workflows that publish when you push a Git tag:
19+
- **PyPI publish**: `.github/workflows/pypi-publish.yml` (runs on tags like `v0.1.0`)
20+
- **RTD trigger**: `.github/workflows/readthedocs.yml` (runs on `main` and `v*` tags)
21+
22+
If you use this model, you typically do **not** run `twine upload` locally.
23+
24+
## 1) One-time setup
25+
26+
### 1.1 Accounts + access
27+
28+
- Create a **PyPI** account: https://pypi.org/account/register/
29+
- Enable **2FA** on PyPI (recommended/commonly required).
30+
- (Optional but recommended) Create a **TestPyPI** account: https://test.pypi.org/account/register/
31+
- Ensure you have GitHub permissions to create releases/tags in the `cellucid-python` repo.
32+
- If you will use GitHub Actions for PyPI: you also need access to set repo secrets.
33+
34+
### 1.2 Local tools
35+
36+
Create a clean environment (recommended):
37+
38+
```bash
39+
python -m venv .venv
40+
source .venv/bin/activate
41+
python -m pip install -U pip
42+
python -m pip install -U build twine
43+
```
44+
45+
### 1.3 GitHub repo secrets (for automated publishing)
46+
47+
If you want publishing to happen automatically on tag push:
48+
49+
1) Create a PyPI API token (PyPI → Account settings → API tokens).
50+
2) In GitHub repo settings → **Secrets and variables****Actions**:
51+
- Add `PYPI_API_TOKEN`
52+
- Add `READTHEDOCS_TOKEN` (only if you want RTD triggered from GitHub Actions)
53+
54+
## 2) Release checklist (do this every time)
55+
56+
### 2.1 Decide the version
57+
58+
Update `cellucid-python/pyproject.toml`:
59+
- `version = "..."` (PEP 440 format)
60+
61+
Examples:
62+
- stable: `0.1.0`
63+
- prerelease: `0.1.0a1`, `0.1.0b1`, `0.1.0rc1`
64+
65+
### 2.2 Update changelog
66+
67+
Update `cellucid-python/CHANGELOG.md` with:
68+
- what changed
69+
- any breaking changes
70+
- migration notes (if needed)
71+
72+
### 2.3 Run tests locally
73+
74+
From `cellucid-python/`:
75+
76+
```bash
77+
pytest
78+
```
79+
80+
### 2.3.1 Commit and tag (if using GitHub Actions)
81+
82+
From the `cellucid-python/` repo:
83+
84+
```bash
85+
git status
86+
git add -A
87+
git commit -m "Release v<VERSION>"
88+
git tag v<VERSION>
89+
git push origin main
90+
git push origin v<VERSION>
91+
```
92+
93+
### 2.4 Build the artifacts (wheel + sdist)
94+
95+
From `cellucid-python/`:
96+
97+
```bash
98+
rm -rf dist/
99+
python -m build
100+
```
101+
102+
You should now have:
103+
- `dist/cellucid-<version>-py3-none-any.whl`
104+
- `dist/cellucid-<version>.tar.gz`
105+
106+
### 2.5 Sanity-check the built artifacts (recommended)
107+
108+
```bash
109+
python -m twine check dist/*
110+
```
111+
112+
## 3) Publish to PyPI (pip)
113+
114+
### 3.0 Publish via GitHub Actions (recommended)
115+
116+
If `PYPI_API_TOKEN` is configured (Section 1.3):
117+
1) Push a tag like `v0.0.1a3` (Section 2.3.1).
118+
2) Watch the GitHub Actions run: **Actions → Publish to PyPI**.
119+
120+
That workflow builds and uploads both wheel and sdist.
121+
122+
### 3.1 Publish to TestPyPI first (recommended)
123+
124+
```bash
125+
python -m twine upload --repository testpypi dist/*
126+
```
127+
128+
Then test install in a fresh env:
129+
130+
```bash
131+
python -m venv /tmp/cellucid-test
132+
source /tmp/cellucid-test/bin/activate
133+
python -m pip install -U pip
134+
python -m pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ cellucid
135+
python -c "import cellucid; print(cellucid.__version__)"
136+
```
137+
138+
### 3.2 Publish to real PyPI
139+
140+
```bash
141+
python -m twine upload dist/*
142+
```
143+
144+
## 4) Publish to conda-forge (conda)
145+
146+
conda-forge publishing happens in a separate repo called a **feedstock**.
147+
148+
### 4.1 First-time: create a conda-forge feedstock
149+
150+
1) Make sure the new version is on PyPI (Section 3).
151+
2) Open a PR to conda-forge’s staged-recipes:
152+
- https://github.com/conda-forge/staged-recipes
153+
154+
In that PR you add a `recipe/meta.yaml` for `cellucid`.
155+
156+
Common ways to generate a starting recipe:
157+
- `grayskull pypi cellucid` (then edit)
158+
- manually write `meta.yaml` (fine for simple Python packages)
159+
160+
Minimal recipe ingredients you’ll need:
161+
- `package: name/version`
162+
- `source: url` pointing to the PyPI sdist + its `sha256`
163+
- `build: noarch: python`
164+
- `requirements: host/run` matching `pyproject.toml` dependencies
165+
- `test: imports` and/or `pytest` invocation
166+
- `about: license`, `license_file`, `home`, `summary`
167+
168+
**Important (common conda-forge failure):** conda-forge build jobs typically run with **no network access**.
169+
170+
If your recipe uses plain `pip install .` under PEP 517 build isolation, pip may try to download build requirements from PyPI and fail.
171+
172+
In conda recipes, prefer:
173+
174+
```yaml
175+
script: {{ PYTHON }} -m pip install . --no-deps --no-build-isolation -vv
176+
```
177+
178+
and list build requirements in `requirements: host:`.
179+
180+
### 4.1.1 How to get the sdist URL + sha256
181+
182+
In the conda recipe you need the exact PyPI source tarball URL and its SHA-256 hash.
183+
184+
1) Download the sdist:
185+
186+
```bash
187+
python -m pip download --no-binary :all: --no-deps cellucid==<VERSION>
188+
```
189+
190+
2) Compute sha256:
191+
192+
```bash
193+
shasum -a 256 cellucid-<VERSION>.tar.gz
194+
```
195+
196+
3) Use that URL + sha256 in `meta.yaml`.
197+
198+
### 4.1.2 A minimal `meta.yaml` skeleton (for reference)
199+
200+
This is a *starting point* (you’ll likely tweak deps/tests):
201+
202+
```yaml
203+
{% set name = "cellucid" %}
204+
{% set version = "<VERSION>" %}
205+
206+
package:
207+
name: {{ name|lower }}
208+
version: {{ version }}
209+
210+
source:
211+
url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz
212+
sha256: <SHA256>
213+
214+
build:
215+
noarch: python
216+
script: {{ PYTHON }} -m pip install . --no-deps --no-build-isolation -vv
217+
218+
requirements:
219+
host:
220+
- python >=3.10
221+
- pip
222+
- setuptools
223+
run:
224+
- python >=3.10
225+
- numpy >=1.21
226+
- pandas >=1.4
227+
- scipy >=1.7
228+
- tqdm >=4.45
229+
- anndata >=0.8
230+
- ipython >=7.23
231+
- jupyter-server-proxy >=4.1
232+
233+
test:
234+
imports:
235+
- cellucid
236+
237+
about:
238+
home: https://github.com/theislab/cellucid-python
239+
license: BSD-3-Clause
240+
license_file: LICENSE
241+
summary: Interactive Single-Cell Data Visualization
242+
```
243+
244+
### 4.1.3 Common conda-forge CI failure modes (and fixes)
245+
246+
- **PEP 517 build isolation tries to download from PyPI**: use `--no-build-isolation` and list build deps under `requirements: host:`.
247+
- **Tests accidentally start the server / open a browser**: keep conda tests to `python -c "import cellucid"` or a small unit test set; don’t run `cellucid serve` as a recipe test.
248+
- **Prerelease versions (`a1`, `b1`, `rc1`)**: if reviewers push back, publish a stable `0.x.y` first.
249+
250+
After the staged-recipes PR is merged:
251+
- conda-forge creates `cellucid-feedstock`
252+
- CI builds and uploads the package to conda-forge
253+
254+
### 4.2 Updating versions after the feedstock exists
255+
256+
After the feedstock exists, updates are usually automatic:
257+
- conda-forge’s **autotick bot** opens PRs when it detects new PyPI releases.
258+
- you (or maintainers) review/merge the bot PR.
259+
260+
## 5) Publish to Bioconda
261+
262+
Bioconda is a separate ecosystem:
263+
- https://github.com/bioconda/bioconda-recipes
264+
265+
Typical workflow:
266+
1) Ensure the version is on PyPI (Section 3).
267+
2) Ensure runtime dependencies exist on conda-forge/bioconda.
268+
3) Add or update a recipe in `bioconda-recipes` via PR.
269+
270+
For most Python tools, the pragmatic path is:
271+
1) Get the package onto conda-forge first (Section 4).
272+
2) Then make a Bioconda recipe that depends on the conda-forge package.
273+
274+
Bioconda has stricter policies and review expectations than many projects:
275+
- correct license metadata
276+
- correct dependency declarations
277+
- tests that don’t require network access
278+
279+
Note: in many cases you should publish to **conda-forge first**, then Bioconda can depend on the conda-forge package.
280+
281+
## 6) Publish docs on Read the Docs (RTD)
282+
283+
If RTD is already working, you typically only need to:
284+
- ensure the docs build for the new tag/version
285+
- keep dependencies pinned in the RTD config
286+
287+
Common workflow:
288+
1) Create a Git tag (e.g. `v0.1.0`) and push it.
289+
2) In RTD project settings, enable building tags (if desired).
290+
3) Confirm the build succeeds for the new tag.
291+
292+
If you use the GitHub Actions trigger (`.github/workflows/readthedocs.yml`), pushing a tag is enough.
293+
294+
## 7) Troubleshooting (high-signal)
295+
296+
### “Build fails with pyproject.toml validation errors”
297+
298+
- Fix invalid PEP 621 fields in `cellucid-python/pyproject.toml`.
299+
- Re-run `python -m build`.
300+
301+
### “conda-forge/bioconda builds fail but pip works”
302+
303+
- conda builds from sdists in clean environments with stricter dependency resolution.
304+
- ensure your dependencies are correctly declared and available in conda.
305+
- ensure tests do not rely on network.
306+
307+
### “RTD build fails but local docs build works”
308+
309+
- RTD uses a clean environment; add missing doc deps to the RTD config / docs extras.
310+
- pin incompatible Sphinx extensions.
311+
312+
## 8) Quick reference (minimal happy path)
313+
314+
From `cellucid-python/`:
315+
316+
```bash
317+
pytest
318+
python -m build
319+
python -m twine check dist/*
320+
python -m twine upload dist/*
321+
```
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Jupyter hooks screenshots
2+
3+
Store screenshots for `docs/user_guide/python_package/e_jupyter_hooks/` here.
4+
5+
## Naming
6+
7+
- Use lowercase + hyphens.
8+
- Prefix with an ordering number for step-by-step flows: `01_...`, `02_...`, ...
9+
10+
Examples:
11+
- `01_notebook-embedded-viewer.png`
12+
- `02_selection-event-arrives-in-python.png`
13+
- `03_highlight-roundtrip.png`
14+
- `10_ui-unavailable-error-page.png`
15+
- `11_notebook-proxy-required.png`
16+
17+
## Redaction checklist
18+
19+
Before committing:
20+
- Remove local file paths, usernames, emails.
21+
- Remove private dataset names / sample IDs if not intended for publication.
22+
- Remove tokens/URLs that reveal private infrastructure.
23+
24+
## Capture guidance
25+
26+
Follow `cellucid/markdown/DOCUMENTATION_SCREENSHOTS_AND_FIGURES_GUIDE.md` for:
27+
- required metadata blocks (alt text + caption + capture instructions),
28+
- cropping rules,
29+
- callout style conventions.
30+

0 commit comments

Comments
 (0)