From aab820c6e1dedc41e1033b68ccf91401037f0d2d Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 4 Dec 2020 11:06:11 +0100 Subject: [PATCH 1/3] Switch to flit (#478) This reverts commit f8ed61f19c2890cc400acc436f485326474d33e2. --- .gitignore | 1 + .travis.yml | 5 ++- MANIFEST.in | 2 -- anndata/__init__.py | 36 ++++++++++---------- anndata/_metadata.py | 35 ++++++++++++++++++- docs/requirements.txt | 11 ------ pyproject.toml | 79 ++++++++++++++++++++++++++++++++++++++++--- requirements.txt | 8 ----- setup.py | 74 ---------------------------------------- 9 files changed, 131 insertions(+), 120 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 docs/requirements.txt delete mode 100644 requirements.txt delete mode 100644 setup.py diff --git a/.gitignore b/.gitignore index b49f525e3..a2960544c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ __pycache__/ /build/ /dist/ /*.egg-info/ +/setup.py # Tests and coverage /.pytest_cache/ diff --git a/.travis.yml b/.travis.yml index 2d6a8af16..0e1ac1739 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,17 +8,16 @@ matrix: include: - name: static analysis python: "3.7" - install: pip install .[dev,doc] + install: pip install .[dev] script: - black . --check --diff - - python setup.py check --restructuredtext --strict - rst2html.py --halt=2 README.rst >/dev/null after_success: skip python: - "3.6" - "3.7" install: -- pip install -e .[dev,test] +- pip install .[dev,test] script: - pytest --cov=. --cov-report=xml after_success: diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index f398a1841..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -exclude .coveragerc .gitattributes .gitignore .readthedocs.yml .travis.yml -prune docs diff --git a/anndata/__init__.py b/anndata/__init__.py index 79bd2f08e..48108e180 100644 --- a/anndata/__init__.py +++ b/anndata/__init__.py @@ -1,21 +1,23 @@ """Annotated multivariate observation data.""" -from ._metadata import __version__, __author__, __email__ +from ._metadata import __version__, __author__, __email__, within_flit -from ._core.anndata import AnnData, ImplicitModificationWarning -from ._core.merge import concat -from ._core.raw import Raw -from ._io import ( - read_h5ad, - read_loom, - read_hdf, - read_excel, - read_umi_tools, - read_csv, - read_text, - read_mtx, - read_zarr, -) +if not within_flit(): + del within_flit + from ._core.anndata import AnnData, ImplicitModificationWarning + from ._core.merge import concat + from ._core.raw import Raw + from ._io import ( + read_h5ad, + read_loom, + read_hdf, + read_excel, + read_umi_tools, + read_csv, + read_text, + read_mtx, + read_zarr, + ) -# backwards compat / shortcut for default format -from ._io import read_h5ad as read + # backwards compat / shortcut for default format + from ._io import read_h5ad as read diff --git a/anndata/_metadata.py b/anndata/_metadata.py index e12fdd819..45b61170e 100644 --- a/anndata/_metadata.py +++ b/anndata/_metadata.py @@ -1,14 +1,34 @@ +import traceback from pathlib import Path here = Path(__file__).parent + +def refresh_entry_points(): + """\ + Under some circumstances, (e.g. when installing a PEP 517 package via pip), + pkg_resources.working_set.entries is stale. This tries to fix that. + See https://github.com/pypa/setuptools_scm/issues/513 + """ + try: + import sys + import pkg_resources + + ws: pkg_resources.WorkingSet = pkg_resources.working_set + for entry in sys.path: + ws.add_entry(entry) + except Exception: + pass + + try: from setuptools_scm import get_version import pytoml proj = pytoml.loads((here.parent / "pyproject.toml").read_text()) - metadata = proj["tool"]["anndata"] + metadata = proj["tool"]["flit"]["metadata"] + refresh_entry_points() __version__ = get_version(root="..", relative_to=__file__) __author__ = metadata["author"] __email__ = metadata["author-email"] @@ -22,3 +42,16 @@ __version__ = meta["Version"] __author__ = meta["Author"] __email__ = meta["Author-email"] + + +def within_flit(): + """\ + Checks if we are being imported by flit. + This is necessary so flit can import __version__ without all depedencies installed. + There are a few options to make this hack unnecessary, see: + https://github.com/takluyver/flit/issues/253#issuecomment-737870438 + """ + for frame in traceback.extract_stack(): + if frame.name == "get_docstring_and_version_via_import": + return True + return False diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 4f6ae74a2..000000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Anndata dependencies --r ../requirements.txt - -# stuff necessary for docs generation -setuptools_scm -typing_extensions; python_version < '3.8' -# Sphinx 2 has nicer looking sections -sphinx>=2.0.1 -sphinx-autodoc-typehints>=1.11.0 -scanpydoc>=0.4.2 -sphinx_issues diff --git a/pyproject.toml b/pyproject.toml index 389ab9412..de27e52ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,83 @@ [build-system] -requires = ['setuptools', 'setuptools_scm', 'wheel', 'pytoml'] -build-backend = 'setuptools.build_meta' +build-backend = 'flit_core.buildapi' +requires = [ + 'flit_core >=3.1,<4', + 'setuptools_scm', + 'pytoml', + 'importlib_metadata>=0.7; python_version < "3.8"', +] -# uses the format of tool.flit.metadata because we’ll move to it anyway -[tool.anndata] +[tool.flit.metadata] +module = 'anndata' author = 'Philipp Angerer, Alex Wolf, Isaac Virshup, Sergei Rybakov' # We don’t need all emails, the main authors are sufficient. author-email = 'phil.angerer@gmail.com, f.alex.wolf@gmx.de' +description-file = 'README.rst' +home-page = 'http://github.com/theislab/anndata' +urls = { Documentation = 'https://anndata.readthedocs.io/' } +classifiers = [ + 'License :: OSI Approved :: BSD License', + 'Environment :: Console', + 'Framework :: Jupyter', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'Natural Language :: English', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Topic :: Scientific/Engineering :: Bio-Informatics', + 'Topic :: Scientific/Engineering :: Visualization', +] +requires-python = '>=3.6' +requires = [ + # pandas 1.1 breaks tests, https://github.com/pandas-dev/pandas/issues/35446 + 'pandas>=1.0,!=1.1', + 'numpy>=1.15', + 'scipy~=1.0', + 'h5py', + 'natsort', + 'packaging>=20', + 'xlrd<2.0', # xlsx format not support anymore from v2.0, see pandas/issues/38524 + # for getting the stable version + 'importlib_metadata>=0.7; python_version < "3.8"', +] + +[tool.flit.metadata.requires-extra] +dev = [ + # dev version generation + 'setuptools_scm', + 'pytoml', + # static checking + 'black>=20.8b1', + 'docutils', +] +doc = [ + # Sphinx 2 has nicer looking sections + 'sphinx>=2.0.1', + 'sphinx-rtd-theme', + 'sphinx-autodoc-typehints>=1.11.0', + 'sphinx_issues', + 'scanpydoc>=0.5', + 'typing_extensions; python_version < "3.8"', +] +test = [ + 'loompy>=3.0.5', + 'pytest>=6.0', + 'pytest-cov>=2.10', + 'zarr', + 'matplotlib', + 'sklearn', + 'xlrd', + 'joblib', + 'boltons', + 'scanpy', +] + +[tool.flit.sdist] +exclude = ['anndata/tests'] [tool.coverage.run] source = ['anndata'] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index cfefe070f..000000000 --- a/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -pandas>=1.0,!=1.1 # pandas 1.1 breaks tests, https://github.com/pandas-dev/pandas/issues/35446 -numpy>=1.15 -scipy~=1.0 -h5py -natsort -importlib_metadata>=0.7; python_version < '3.8' -packaging>=20 -xlrd<2.0 # xlsx format not support anymore from v2.0, see pandas/issues/38524 diff --git a/setup.py b/setup.py deleted file mode 100644 index 2e277b0c6..000000000 --- a/setup.py +++ /dev/null @@ -1,74 +0,0 @@ -import sys - -if sys.version_info < (3, 6): - sys.exit("anndata requires Python >= 3.6") -from pathlib import Path - -from setuptools import setup, find_namespace_packages - -try: - import pytoml -except ImportError: - sys.exit("Please use `pip install .` or install pytoml first.") - -proj = pytoml.loads(Path("pyproject.toml").read_text()) -metadata = proj["tool"]["anndata"] - -setup( - name="anndata", - use_scm_version=True, - setup_requires=["setuptools_scm"], - description="Annotated Data.", - long_description=Path("README.rst").read_text("utf-8"), - url="http://github.com/theislab/anndata", - author=metadata["author"], - author_email=metadata["author-email"], - license="BSD-3-Clause", - install_requires=[ - l.strip() for l in Path("requirements.txt").read_text("utf-8").splitlines() - ], - extras_require=dict( - dev=["setuptools_scm", "pytoml", "black>=20.8b1"], - doc=[ - # Sphinx 2 has nicer looking sections - "sphinx>=2.0.1", - "sphinx-rtd-theme", - "sphinx-autodoc-typehints>=1.11.0", - "sphinx_issues", - "scanpydoc>=0.5", - 'typing_extensions; python_version < "3.8"', - ], - test=[ - "loompy>=3.0.5", - "pytest>=4.6", - "pytest-cov>=2.10", - "docutils", # for rst2html.py - "zarr", - "matplotlib", - "sklearn", - "xlrd<2", - "joblib", - "boltons", - "scanpy", - ], - ), - python_requires=">=3.6", - packages=find_namespace_packages(include=["anndata", "anndata.*"]), - include_package_data=True, - zip_safe=False, - classifiers=[ - "Environment :: Console", - "Framework :: Jupyter", - "Intended Audience :: Developers", - "Intended Audience :: Science/Research", - "Natural Language :: English", - "Operating System :: MacOS :: MacOS X", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Topic :: Scientific/Engineering :: Bio-Informatics", - "Topic :: Scientific/Engineering :: Visualization", - ], -) From 46aeee676254fca3749c2f5c65dad50f8a630256 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Mon, 18 Jan 2021 14:39:36 +0100 Subject: [PATCH 2/3] update deps to fix tests --- pyproject.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index de27e52ae..c8b2c0812 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,9 +33,8 @@ classifiers = [ ] requires-python = '>=3.6' requires = [ - # pandas 1.1 breaks tests, https://github.com/pandas-dev/pandas/issues/35446 - 'pandas>=1.0,!=1.1', - 'numpy>=1.15', + 'pandas>=1.1.1', # pandas <1.1.1 has pandas/issues/35446 + 'numpy>=1.16.5', # required by pandas 1.x 'scipy~=1.0', 'h5py', 'natsort', From 4d2520faed86a50c0a1fb36b27715434cb0badfb Mon Sep 17 00:00:00 2001 From: Philipp A Date: Mon, 1 Mar 2021 09:52:49 +0100 Subject: [PATCH 3/3] Add setup.py, leaving it ignored by flit --- .gitignore | 1 - pyproject.toml | 5 +++- setup.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 setup.py diff --git a/.gitignore b/.gitignore index a2960544c..b49f525e3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ __pycache__/ /build/ /dist/ /*.egg-info/ -/setup.py # Tests and coverage /.pytest_cache/ diff --git a/pyproject.toml b/pyproject.toml index c8b2c0812..73c349d9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,10 @@ test = [ ] [tool.flit.sdist] -exclude = ['anndata/tests'] +exclude = [ + 'anndata/tests', + 'setup.py', +] [tool.coverage.run] source = ['anndata'] diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..244084d7a --- /dev/null +++ b/setup.py @@ -0,0 +1,73 @@ +"""Temporary setuptools bridge +Don't use this except if you have a deadline or you encounter a bug. +""" +import re +import sys +from warnings import warn + +import setuptools +from pathlib import Path + +from flit_core import common, config +from setuptools_scm.integration import find_files + + +field_map = dict( + description="summary", + long_description="description", + long_description_content_type="description_content_type", + python_requires="requires_python", + url="home_page", + **{ + n: n + for n in ["name", "version", "author", "author_email", "license", "classifiers"] + }, +) + + +def setup_args(config_path=Path("pyproject.toml")): + cfg = config.read_flit_config(config_path) + module = common.Module(cfg.module, config_path.parent) + metadata = common.make_metadata(module, cfg) + kwargs = {} + for st_field, metadata_field in field_map.items(): + val = getattr(metadata, metadata_field, None) + if val is not None: + kwargs[st_field] = val + else: + msg = f"{metadata_field} not found in {dir(metadata)}" + assert metadata_field in {"license"}, msg + kwargs["packages"] = setuptools.find_packages(include=[metadata.name + "*"]) + if metadata.requires_dist: + kwargs["install_requires"] = [ + req for req in metadata.requires_dist if "extra ==" not in req + ] + if cfg.reqs_by_extra: + kwargs["extras_require"] = cfg.reqs_by_extra + scripts = cfg.entrypoints.get("console_scripts") + if scripts is not None: + kwargs["entry_points"] = dict( + console_scipts=[" = ".join(ep) for ep in scripts.items()] + ) + kwargs["include_package_data"] = True + kwargs["package_data"] = { + module.name: [ + re.escape(f[len(module.name) + 1 :]) for f in find_files(module.path) + ] + } + return kwargs + + +if __name__ == "__main__": + if "develop" in sys.argv: + msg = ( + "Please use `flit install -s` or `flit install --pth-file` " + "instead of `pip install -e`/`python setup.py develop` " + "once https://github.com/pypa/pip/issues/9670 is fixed." + ) + elif "install" in sys.argv: + msg = 'Please use `pip install "$d"` instead of `python "$d/setup.py" install`' + else: + msg = "Please use `pip ...` or `flit ...` instead of `python setup.py ...`" + warn(msg, FutureWarning) + setuptools.setup(**setup_args())