Skip to content

Commit

Permalink
Adopt the Scientific Python deprecation schedule: (#3485)
Browse files Browse the repository at this point in the history
  • Loading branch information
flying-sheep authored Mar 4, 2025
1 parent 7baadb7 commit 672675d
Show file tree
Hide file tree
Showing 14 changed files with 38 additions and 86 deletions.
14 changes: 7 additions & 7 deletions .azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ trigger:
- "*.*.x"

variables:
python.version: '3.12'
python.version: '3.13'
PYTEST_ADDOPTS: '-v --color=yes --internet-tests --nunit-xml=test-data/test-results.xml'
TEST_EXTRA: 'test-full'
DEPENDENCIES_VERSION: "latest" # |"pre-release" | "minimum-version"
Expand All @@ -15,16 +15,16 @@ jobs:
vmImage: 'ubuntu-22.04'
strategy:
matrix:
Python3.10:
python.version: '3.10'
Python3.12: {}
Python3.11:
python.version: '3.11'
Python3.13: {}
minimal_dependencies:
TEST_EXTRA: 'test-min'
anndata_dev:
DEPENDENCIES_VERSION: "pre-release"
TEST_TYPE: "coverage"
minimum_versions:
python.version: '3.10'
python.version: '3.11'
DEPENDENCIES_VERSION: "minimum-version"
TEST_TYPE: "coverage"

Expand Down Expand Up @@ -120,8 +120,8 @@ jobs:

- task: UsePythonVersion@0
inputs:
versionSpec: '3.12'
displayName: 'Use Python 3.12'
versionSpec: '3.13'
displayName: 'Use Python 3.13'

- script: |
python -m pip install --upgrade pip
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.12"]
python: ["3.13"]
os: [ubuntu-latest]

env:
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ submodules:
build:
os: ubuntu-24.04
tools:
python: '3.12'
python: '3.13'
jobs:
post_checkout:
# unshallow so version can be derived from tag
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/asv.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@

// The Pythons you'd like to test against. If not provided, defaults
// to the current version of Python used to run `asv`.
// "pythons": ["3.10", "3.12"],
// "pythons": ["3.11", "3.13"],

// The list of conda channel names to be searched for benchmark
// dependency packages in the specified order
Expand Down
15 changes: 5 additions & 10 deletions ci/scripts/min-deps.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
#!/usr/bin/env python3
# /// script
# dependencies = [
# "tomli; python_version < '3.11'",
# "packaging",
# ]
# requires-python = ">=3.11"
# dependencies = [ "packaging" ]
# ///
"""Parse a pyproject.toml file and output a list of minimum dependencies."""

from __future__ import annotations

import argparse
import sys
import tomllib
from collections import deque
from contextlib import ExitStack
from functools import cached_property
from pathlib import Path
from typing import TYPE_CHECKING

if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib

from packaging.requirements import Requirement
from packaging.version import Version

Expand All @@ -48,7 +42,8 @@ def min_dep(req: Requirement) -> Requirement:
spec for spec in req.specifier if spec.operator in {"==", "~=", ">=", ">"}
]
if not filter_specs:
return Requirement(req_name)
# TODO: handle markers
return Requirement(f"{req_name}{req.specifier}")

min_version = Version("0.0.0.a1")
for spec in filter_specs:
Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ def setup(app: Sphinx):
# -- Suppress link warnings ----------------------------------------------------

qualname_overrides = {
"pathlib._local.Path": "pathlib.Path",
"sklearn.neighbors._dist_metrics.DistanceMetric": "sklearn.metrics.DistanceMetric",
"scanpy.plotting._matrixplot.MatrixPlot": "scanpy.pl.MatrixPlot",
"scanpy.plotting._dotplot.DotPlot": "scanpy.pl.DotPlot",
Expand Down
2 changes: 2 additions & 0 deletions docs/release-notes/3485.breaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Adopt the Scientific Python [deprecation schedule](https://scientific-python.org/specs/spec-0000/):
remove Python 3.10 support and add Python 3.13 support, require anndata≥0.9 {smaller}`P Angerer`
4 changes: 2 additions & 2 deletions hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ overrides.matrix.deps.pre-install-commands = [
{ if = [ "min" ], value = "uv run ci/scripts/min-deps.py pyproject.toml --all-extras -o ci/scanpy-min-deps.txt" },
]
overrides.matrix.deps.python = [
{ if = [ "min" ], value = "3.10" },
{ if = [ "stable", "full", "pre" ], value = "3.12" },
{ if = [ "min" ], value = "3.11" },
{ if = [ "stable", "full", "pre" ], value = "3.13" },
]
overrides.matrix.deps.features = [
{ if = [ "full" ], value = "test-full" },
Expand Down
26 changes: 13 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ requires = [ "hatchling", "hatch-vcs" ]
[project]
name = "scanpy"
description = "Single-Cell Analysis in Python."
requires-python = ">=3.10"
requires-python = ">=3.11"
license = "BSD-3-clause"
authors = [
{ name = "Alex Wolf" },
Expand Down Expand Up @@ -39,29 +39,29 @@ classifiers = [
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Scientific/Engineering :: Bio-Informatics",
"Topic :: Scientific/Engineering :: Visualization",
]
dependencies = [
"anndata>=0.8",
"numpy>=1.24",
"anndata>=0.9",
"numpy>=1.25",
"matplotlib>=3.6",
"pandas >=1.5",
"scipy>=1.8",
"pandas >=2.0",
"scipy>=1.11",
"seaborn>=0.13",
"h5py>=3.7",
"h5py>=3.8",
"tqdm",
"scikit-learn>=1.1,<1.6.0",
"scikit-learn>=1.1,<1.6",
"statsmodels>=0.13",
"patsy!=1.0.0", # https://github.com/pydata/patsy/issues/215
"networkx>=2.7",
"networkx>=2.8",
"natsort",
"joblib",
"numba>=0.57",
"umap-learn>=0.5,!=0.5.0",
"numba>=0.58",
"umap-learn>=0.5,!=0.5",
"pynndescent>=0.5",
"packaging>=21.3",
"session-info2",
Expand Down Expand Up @@ -146,10 +146,10 @@ magic = [ "magic-impute>=2.0" ] # MAGIC imputation method
skmisc = [ "scikit-misc>=0.1.3" ] # highly_variable_genes method 'seurat_v3'
harmony = [ "harmonypy" ] # Harmony dataset integration
scanorama = [ "scanorama" ] # Scanorama dataset integration
scrublet = [ "scikit-image" ] # Doublet detection with automatic thresholds
scrublet = [ "scikit-image>=0.20" ] # Doublet detection with automatic thresholds
# Acceleration
rapids = [ "cudf>=0.9", "cuml>=0.9", "cugraph>=0.9" ] # GPU accelerated calculation of neighbors
dask = [ "dask[array]>=2022.09.2,<2024.8.0" ] # Use the Dask parallelization engine
dask = [ "dask[array]>=2023.5.1,<2024.8.0" ] # Use the Dask parallelization engine
dask-ml = [ "dask-ml", "scanpy[dask]" ] # Dask-ML for sklearn-like API

[tool.hatch.build.targets.wheel]
Expand Down
34 changes: 0 additions & 34 deletions src/scanpy/_compat.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
from __future__ import annotations

import os
import sys
import warnings
from dataclasses import dataclass, field
from functools import WRAPPER_ASSIGNMENTS, cache, partial, wraps
from importlib.util import find_spec
from pathlib import Path
from typing import TYPE_CHECKING, Literal, ParamSpec, TypeVar, cast, overload

import numpy as np
Expand Down Expand Up @@ -63,25 +60,6 @@ def fullname(typ: type) -> str:
return f"{module}.{name}"


if sys.version_info >= (3, 11):
from contextlib import chdir
else:
import os
from contextlib import AbstractContextManager

@dataclass
class chdir(AbstractContextManager):
path: Path
_old_cwd: list[Path] = field(default_factory=list)

def __enter__(self) -> None:
self._old_cwd.append(Path.cwd())
os.chdir(self.path)

def __exit__(self, *_excinfo) -> None:
os.chdir(self._old_cwd.pop())


def pkg_metadata(package: str) -> PackageMetadata:
from importlib.metadata import metadata

Expand All @@ -106,18 +84,6 @@ def old_positionals(*old_positionals: str):
return lambda func: func


if sys.version_info >= (3, 11):

def add_note(exc: BaseException, note: str) -> None:
exc.add_note(note)
else:

def add_note(exc: BaseException, note: str) -> None:
if not hasattr(exc, "__notes__"):
exc.__notes__ = []
exc.__notes__.append(note)


if sys.version_info >= (3, 13):
from warnings import deprecated as _deprecated
else:
Expand Down
4 changes: 2 additions & 2 deletions src/scanpy/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import logging
import sys
from datetime import datetime, timedelta, timezone
from datetime import UTC, datetime, timedelta
from functools import partial, update_wrapper
from logging import CRITICAL, DEBUG, ERROR, INFO, WARNING
from typing import TYPE_CHECKING, overload
Expand Down Expand Up @@ -45,7 +45,7 @@ def log(
) -> datetime:
from ._settings import settings

now = datetime.now(timezone.utc)
now = datetime.now(UTC)
time_passed: timedelta = None if time is None else now - time
extra = {
**(extra or {}),
Expand Down
4 changes: 2 additions & 2 deletions src/scanpy/readwrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from matplotlib.image import imread

from . import logging as logg
from ._compat import add_note, deprecated, old_positionals
from ._compat import deprecated, old_positionals
from ._settings import settings
from ._utils import _empty

Expand Down Expand Up @@ -1025,7 +1025,7 @@ def _download(url: str, path: Path):
try:
from certifi import where
except ImportError as e:
add_note(e, f"{msg} Please install `certifi` and try again.")
e.add_note(f"{msg} Please install `certifi` and try again.")
raise
else:
logg.warning(f"{msg} Trying to use certifi.")
Expand Down
3 changes: 1 addition & 2 deletions src/testing/scanpy/_pytest/fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from __future__ import annotations

import warnings
from contextlib import chdir
from typing import TYPE_CHECKING

import numpy as np
Expand Down Expand Up @@ -39,8 +40,6 @@ def float_dtype(request):

@pytest.fixture
def _doctest_env(cache: pytest.Cache, tmp_path: Path) -> Generator[None, None, None]:
from scanpy._compat import chdir

showwarning_orig = warnings.showwarning

def showwarning(message, category, filename, lineno, file=None, line=None): # noqa: PLR0917
Expand Down
11 changes: 0 additions & 11 deletions tests/test_package_structure.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

import importlib
import os
from collections import defaultdict
from inspect import Parameter, signature
from pathlib import Path
Expand Down Expand Up @@ -54,16 +53,6 @@
]


@pytest.fixture
def in_project_dir():
wd_orig = Path.cwd()
os.chdir(proj_dir)
try:
yield proj_dir
finally:
os.chdir(wd_orig)


@pytest.mark.xfail(reason="TODO: unclear if we want this to totally match, let’s see")
def test_descend_classes_and_funcs():
funcs = set(descend_classes_and_funcs(scanpy, "scanpy"))
Expand Down

0 comments on commit 672675d

Please sign in to comment.