Skip to content

Commit 90cebff

Browse files
committed
Add demo entrypoint
1 parent e49bf62 commit 90cebff

File tree

3 files changed

+142
-8
lines changed

3 files changed

+142
-8
lines changed

pyproject.toml

+9-2
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,18 @@ classifiers = [
1919
"Programming Language :: Python :: 3.10",
2020
"Programming Language :: Python :: 3.11",
2121
]
22-
requires-python = ">=3.8"
22+
requires-python = ">=3.8,<3.12"
2323
dependencies = [
2424
"anywidget>=0.2.3",
2525
"cev-metrics>=0.1.2",
2626
"ipywidgets>=8.0.0",
2727
"jinja2>=3.0.0",
2828
"jupyter-scatter>=0.14.0",
29-
"pandas>=1.0",
29+
"pandas>=1.0,<2.0",
30+
"numpy>=1.0,<2.0",
31+
"pyarrow",
32+
"pooch>=1.3.0",
33+
"rich>=13.0.0",
3034
]
3135
dynamic = ["version"]
3236

@@ -45,6 +49,9 @@ notebooks = [
4549
"matplotlib",
4650
]
4751

52+
[project.scripts]
53+
cev = "cev._cli:main"
54+
4855
[project.urls]
4956
homepage = "https://github.com/OzetteTech/comparative-embedding-visualization"
5057

src/cev/__init__.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
from importlib.metadata import PackageNotFoundError, version
1+
from cev._version import __version__ # noqa
22

33
import cev.metrics as metrics # noqa
44
import cev.widgets as widgets # noqa
5-
6-
try:
7-
__version__ = version("cev")
8-
except PackageNotFoundError:
9-
__version__ = "uninstalled"

src/cev/_cli.py

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import argparse
2+
import json
3+
import os
4+
import shutil
5+
import sys
6+
import textwrap
7+
import zipfile
8+
from pathlib import Path
9+
10+
import pooch
11+
12+
from cev._version import __version__
13+
14+
_DEV = True
15+
16+
17+
def download_data() -> tuple[Path, Path]:
18+
archive = pooch.retrieve(
19+
url="https://figshare.com/ndownloader/articles/23063615/versions/1",
20+
path=pooch.os_cache("cev"),
21+
fname="data.zip",
22+
known_hash=None,
23+
)
24+
archive = Path(archive)
25+
files = [
26+
"mair-2022-tissue-138-umap.pq",
27+
"mair-2022-tissue-138-ozette.pq",
28+
]
29+
with zipfile.ZipFile(archive, "r") as zip_ref:
30+
for file in files:
31+
zip_ref.extract(file, path=archive.parent)
32+
return (
33+
archive.parent / "mair-2022-tissue-138-umap.pq",
34+
archive.parent / "mair-2022-tissue-138-ozette.pq",
35+
)
36+
37+
38+
def write_notebook(output: Path):
39+
umap_path, ozette_path = download_data()
40+
source = textwrap.dedent(f"""
41+
import pandas as pd
42+
from cev.widgets import Embedding, EmbeddingComparisonWidget
43+
44+
umap_embedding = pd.read_parquet("{umap_path}").pipe(Embedding.from_ozette)
45+
ozette_embedding = pd.read_parquet("{ozette_path}").pipe(Embedding.from_ozette)
46+
47+
EmbeddingComparisonWidget(
48+
umap_embedding,
49+
ozette_embedding,
50+
titles=("Standard UMAP", "Annotation-Transformed UMAP"),
51+
metric="confusion",
52+
selection="synced",
53+
auto_zoom=True,
54+
row_height=320,
55+
)
56+
""").strip()
57+
58+
nb = {
59+
"cells": [
60+
{
61+
"cell_type": "code",
62+
"execution_count": None,
63+
"metadata": {},
64+
"outputs": [],
65+
"source": source,
66+
}
67+
],
68+
"metadata": {
69+
"kernelspec": {
70+
"display_name": "Python 3",
71+
"language": "python",
72+
"name": "python3",
73+
}
74+
},
75+
"nbformat": 4,
76+
"nbformat_minor": 5,
77+
}
78+
with output.open("w") as f:
79+
json.dump(nb, f, indent=2)
80+
81+
82+
def check_uv_available():
83+
if shutil.which("uv") is None:
84+
print("Error: 'uv' command not found.", file=sys.stderr)
85+
print("Please install 'uv' to run `cev demo` entrypoint.", file=sys.stderr)
86+
print(
87+
"For more information, visit: https://github.com/astral-sh/uv",
88+
file=sys.stderr,
89+
)
90+
sys.exit(1)
91+
92+
93+
def run_notebook(notebook_path: Path):
94+
check_uv_available()
95+
command = [
96+
"uvx",
97+
"--python",
98+
"3.11",
99+
"--with",
100+
"." if _DEV else f"cev=={__version__}",
101+
"--with",
102+
"jupyterlab",
103+
"jupyter",
104+
"lab",
105+
str(notebook_path),
106+
]
107+
try:
108+
os.execvp(command[0], command)
109+
except OSError as e:
110+
print(f"Error executing {command[0]}: {e}", file=sys.stderr)
111+
sys.exit(1)
112+
113+
114+
def main():
115+
parser = argparse.ArgumentParser(prog="cev")
116+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
117+
subparsers.add_parser("download", help="Download the demo notebook (and data)")
118+
subparsers.add_parser("demo", help="Run the demo notebook in JupyterLab")
119+
args = parser.parse_args()
120+
121+
notebook_path = Path("cev-demo.ipynb")
122+
if args.command == "download":
123+
write_notebook(notebook_path)
124+
elif args.command == "demo":
125+
write_notebook(notebook_path)
126+
run_notebook(notebook_path)
127+
else:
128+
parser.print_help()
129+
130+
131+
if __name__ == "__main__":
132+
main()

0 commit comments

Comments
 (0)