Skip to content

Commit

Permalink
Start mindeps
Browse files Browse the repository at this point in the history
  • Loading branch information
ivirshup committed Jan 17, 2024
1 parent 4f4b1c3 commit 141eb6a
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 11 deletions.
98 changes: 98 additions & 0 deletions ci/scripts/min-deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!python3
from __future__ import annotations

import argparse
import sys
from pathlib import Path

if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
from packaging.requirements import Requirement
from packaging.version import Version


def min_dep(req: Requirement) -> str:
"""
Given a requirement, return the minimum version specifier.
Example
-------
>>> min_dep(Requirement("numpy>=1.0"))
"numpy==1.0"
"""
req_name = req.name
if req.extras:
req_name = f"{req_name}[{','.join(req.extras)}]"

# TODO: Should this be allowed?
if not req.specifier:
return req_name

min_version = Version("0.0.0.a1")
for spec in req.specifier:
if spec.operator in [">", ">=", "~-"]:
min_version = max(min_version, Version(spec.version))
elif spec.operator == "==":
min_version = Version(spec.version)

# TODO: should this return `~=` or `==`?
return f"{req_name}=={min_version}.*"


def extract_min_deps(
dependencies: list[str],
*,
pyproject
) -> list[str]:
dependencies = dependencies.copy() # We'll be mutating this
requirements: list[Requirement] = []
project_name = pyproject["project"]["name"]

while len(dependencies) > 0:
req = Requirement(dependencies.pop())

# If we are reffering to other optional dependency lists, resolve them
if req.name == project_name:
assert req.extras, f"Project included itself as dependency, without specifying extras: {req}"
for extra in req.extras:
dependencies.extend(pyproject["project"]["optional-dependencies"][extra])
else:
requirements.append(min_dep(req))

return requirements


def main():
# TODO: Allow optional dependencies
parser = argparse.ArgumentParser(
prog="min-deps",
description="""Parse a pyproject.toml file and output a list of minimum dependencies.
Output is directly passable to `pip install`.""",
usage="pip install `python min-deps.py pyproject.toml`",
)
parser.add_argument(
"path", type=Path, help="pyproject.toml to parse minimum dependencies from"
)
parser.add_argument("--extras", type=str, nargs="*", help="extras to install")

args = parser.parse_args()

pyproject = tomllib.loads(args.path.read_text())

project_name = pyproject["project"]["name"]
deps = pyproject["project"]["dependencies"]

for extra in args.extras:
deps.append(f"{project_name}[{extra}]")

min_deps = extract_min_deps(deps, pyproject=pyproject)

print(" ".join(min_deps))


if __name__ == "__main__":
main()
17 changes: 17 additions & 0 deletions ci/scripts/run-min-deps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
mamba env remove -yn scanpy-min-deps-test
mamba create -yn scanpy-min-deps-test "python=3.9"

PACKAGES=`python3 ci/scripts/min-deps.py pyproject.toml --extra dev test`

# conda activate anndata-min-deps-test
# conda run -n anndata-min-deps-test pip install cupy-cuda12x


echo Installing $PACKAGES
conda run -n scanpy-min-deps-test pip install $PACKAGES
conda run -n scanpy-min-deps-test pip install pytest-xdist # cupy-cuda12x
conda run -n scanpy-min-deps-test pip install -e . --no-deps
echo "Starting tests"
conda run -n scanpy-min-deps-test pytest -n auto

conda list -n scanpy-min-deps-tests
22 changes: 11 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,22 @@ classifiers = [
dependencies = [
"anndata>=0.7.4",
# numpy needs a version due to #1320
"numpy>=1.17.0",
"numpy>=1.23",
"matplotlib>=3.6",
"pandas >=2.1.3",
"scipy>=1.4",
"seaborn>=0.13.0",
"h5py>=3",
"pandas >=1.5",
"scipy>=1.8",
"seaborn>=0.13",
"h5py>=3.1",
"tqdm",
"scikit-learn>=0.24",
"statsmodels>=0.10.0rc2",
"statsmodels>=0.10",
"patsy",
"networkx>=2.3",
"natsort",
"joblib",
"numba>=0.41.0",
"numba>=0.53",
"umap-learn>=0.3.10",
"packaging",
"packaging>=20.0",
"session-info",
"legacy-api-wrap>=1.4", # for positional API deprecations
"get-annotations; python_version < '3.10'",
Expand Down Expand Up @@ -133,8 +133,8 @@ dev = [
]
# Algorithms
paga = ["igraph"]
louvain = ["igraph", "louvain>=0.6,!=0.6.2"] # Louvain community detection
leiden = ["igraph>=0.10", "leidenalg>=0.9"] # Leiden community detection
louvain = ["igraph", "louvain>=0.6.0,!=0.6.2"] # Louvain community detection
leiden = ["igraph>=0.10", "leidenalg>=0.9.0"] # Leiden community detection
bbknn = ["bbknn"] # Batch balanced KNN (batch correction)
magic = ["magic-impute>=2.0"] # MAGIC imputation method
skmisc = ["scikit-misc>=0.1.3"] # highly_variable_genes method 'seurat_v3'
Expand All @@ -143,7 +143,7 @@ scanorama = ["scanorama"] # Scanorama dataset integration
scrublet = ["scrublet"] # Doublet detection
# Acceleration
rapids = ["cudf>=0.9", "cuml>=0.9", "cugraph>=0.9"] # GPU accelerated calculation of neighbors
dask = ["dask[array]!=2.17.0"] # Use the Dask parallelization engine
dask = ["dask[array]>=2022.09"] # Use the Dask parallelization engine
dask-ml = ["dask-ml", "scanpy[dask]"] # Dask-ML for sklearn-like API

[tool.hatch.build]
Expand Down

0 comments on commit 141eb6a

Please sign in to comment.