From ee16f8d1114651daa83171f209ac12297f15d8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Haitz=20Legarreta=20Gorro=C3=B1o?= Date: Mon, 23 Jan 2023 18:18:36 -0500 Subject: [PATCH] ENH: Modernize package setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modernize package setup. Adopt PEP518 to specify minimum build system requirements for the package using the `pyproject.toml` file. Partially comply with PEP631: - Dependencies are now stored in the `pyproject.toml` file, including packages that are not in PyPI (e.g. source code URLs -GitHub, etc.), so the related requirements files are removed. - Wildcards are not accepted when specifying version values for the dependencies in the `pyproject.toml` file, as they need to be PEP 508-compliant. Add a patch version or leave the minor version the where applicable. - The script `setup.py` is no longer necessary: `pyproject.toml` contains the necessary information for the package set up. - Project-specific `info.py` and `setup_helpers.py` scripts are no longer necessary. - Describing the `flake8` configuration in the `pyproject.toml` is not yet supported, so it is kept in the `setup.cfg` file. Add `torch` as a dependency and remove unnecessary dependencies: `Cython`, `holoviews`, `pillow`, `plotly`, `scikit-image`, `vtk`, `xlrd`, `xlsxwriter`. Keep the development status to `Alpha`. Adapt the GitHub Actions workflow file accordingly. Specifically, - Set the `SKLEARN_ALLOW_DEPRECATED_SKLEARN_PACKAGE_INSTALL` environment variable to `True` as some dependency is listing `scikit-learn` as `sklearn` in its dependencies. Àvoids: ``` × python setup.py egg_info did not run successfully. │ exit code: 1 ╰─> [18 lines of output] The 'sklearn' PyPI package is deprecated, use 'scikit-learn' rather than 'sklearn' for pip commands. ``` Documentation: https://github.com/scikit-learn/sklearn-pypi-package - Comment testing as the package has not tests currently. Avoids: ``` collecting ... collected 0 items Error: Process completed with exit code 5. ``` Adapt the documentation configuration script to read the necessary project information from the `pyproject.toml` file. --- .github/workflows/test_package.yml | 37 ++++---- README.md | 6 ++ doc/conf.py | 24 ++--- pyproject.toml | 118 +++++++++++++++++++++++++ requirements/requirements.txt | 40 --------- requirements/requirements_dmri_git.txt | 1 - setup.py | 55 ------------ setup_helpers.py | 34 ------- tox.ini | 41 +++++++++ 9 files changed, 196 insertions(+), 160 deletions(-) create mode 100644 pyproject.toml delete mode 100644 requirements/requirements.txt delete mode 100644 requirements/requirements_dmri_git.txt delete mode 100644 setup.py delete mode 100644 setup_helpers.py create mode 100644 tox.ini diff --git a/.github/workflows/test_package.yml b/.github/workflows/test_package.yml index 16be7bf..9e2dd23 100644 --- a/.github/workflows/test_package.yml +++ b/.github/workflows/test_package.yml @@ -32,7 +32,7 @@ jobs: - name: Set min. dependencies if: matrix.requires == 'minimal' run: | - python -c "req = open('setup.cfg').read().replace(' >= ', ' == ') ; open('setup.cfg', 'w').write(req)" + python -c "req = open('pyproject.toml').read().replace(' >= ', ' == ') ; open('pyproject.toml', 'w').write(req)" # - name: Cache pip # uses: actions/cache@v2 @@ -47,13 +47,15 @@ jobs: - name: Install dependencies # if: steps.cache.outputs.cache-hit != 'true' run: | + # SKLEARN_ALLOW_DEPRECATED_SKLEARN_PACKAGE_INSTALL required due to + # some dependency listing "scikit-learn" as "sklearn" in its dependencies + export SKLEARN_ALLOW_DEPRECATED_SKLEARN_PACKAGE_INSTALL=True python -m pip install --upgrade --user pip pip install setuptools tox - pip install -e .[testing] - pip install -r requirements/requirements_dmri_git.txt - # Force upgrading numpy to avoid the `scilpy` requested 1.18.* + pip install -e .[test] + # Force upgrading packages to workaround the `scilpy` requested versions pip install --upgrade matplotlib==3.1.0 - pip install --upgrade "numpy>1.18,<1.21" + pip install --upgrade numpy==1.23.0 # Force upgrading packages to avoid numpy-related built-in type deprecation warnings pip install --upgrade dipy==1.4.1 pip install --upgrade fury==0.7.1 @@ -61,7 +63,6 @@ jobs: pip install --upgrade nibabel==3.2.1 pip install --upgrade scikit-learn==0.24.0 pip install --upgrade scipy==1.7.0 - pip install --upgrade nilearn==0.7.0 python --version pip --version pip list @@ -70,20 +71,20 @@ jobs: run: | # tox --sitepackages python -c 'import tractolearn' - coverage run --source tractolearn -m pytest tractolearn -o junit_family=xunit2 -v --doctest-modules --junitxml=junit/test-results-${{ runner.os }}-${{ matrix.python-version }}.xml + # coverage run --source tractolearn -m pytest tractolearn -o junit_family=xunit2 -v --doctest-modules --junitxml=junit/test-results-${{ runner.os }}-${{ matrix.python-version }}.xml - - name: Upload pytest test results - uses: actions/upload-artifact@master - with: - name: pytest-results-${{ runner.os }}-${{ matrix.python-version }} - path: junit/test-results-${{ runner.os }}-${{ matrix.python-version }}.xml - # Use always() to always run this step to publish test results when there are test failures - if: always() + #- name: Upload pytest test results + # uses: actions/upload-artifact@master + # with: + # name: pytest-results-${{ runner.os }}-${{ matrix.python-version }} + # path: junit/test-results-${{ runner.os }}-${{ matrix.python-version }}.xml + # # Use always() to always run this step to publish test results when there are test failures + # if: always() - - name: Statistics - if: success() - run: | - coverage report + #- name: Statistics + # if: success() + # run: | + # coverage report - name: Package Setup # - name: Run tests with tox diff --git a/README.md b/README.md index 7642973..a6d987e 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ NVIDIA RTX 3090 with: ```sh pip install -e . + pip install --upgrade numpy==1.23 +``` + +Torch tested with an NVIDIA RTX 3090 +```sh + pip install torch==1.8.1+cu111 -f https://download.pytorch.org/whl/cu111/torch_stable.html ``` In order to execute experiments reporting diff --git a/doc/conf.py b/doc/conf.py index 87689ca..5f5bac6 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -10,29 +10,29 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -# import os +import os # import sys +import tomli +from importlib.metadata import version from datetime import datetime # sys.path.insert(0, os.path.abspath('.')) -# -- General configuration ----------------------------------------------------- - # Load the release info into a dict by explicit execution -info = {} -with open(os.path.join("..", "setup.cfg")) as f: - exec(f.read(), info) - +with open(os.path.join("..", "pyproject.toml"), "rb") as f: + info = tomli.load(f) # -- Project information ----------------------------------------------------- -project = "tractolearn" -copyright = f"2022-{datetime.now().year}, {info["__author__"]}s <{info["__email__"]}s>" -author = f"{info["__author__"]}s" +project = info["project"]["name"] +_author = info["project"]["authors"][0]["name"] +_email = info["project"]["authors"][1]["email"] +copyright = f"2022-{datetime.now().year}, {_author}s <{_email}s>" +author = f"{_author}s" -version = info["__version__"] +_version = version(project) # The full version, including alpha/beta/rc tags -release = version +release = _version # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2b3573f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,118 @@ +[build-system] +requires = [ + "setuptools >= 66", + "wheel", + "setuptools_scm >= 6.4", +] +build-backend = "setuptools.build_meta" + +[project] +authors = [ + {name = "Jon Haitz Legarreta"}, {email = "jon.haitz.legarreta@gmail.com"} +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Medical Science Apps.", +] +dependencies = [ + # common dependencies + "comet-ml == 3.0.0", + "h5py == 2.10.0", + "matplotlib >= 2.2.0", + "joblib == 1.1.0", + "numpy >= 1.20.0", + "pandas", + "pathos", + "pydot", + "pygame", + "pyyaml", + "seaborn == 0.11.0", + "scikit-learn == 0.22.0", + "scipy == 1.4.0", + "torch == 1.8.1", + "torchsummary == 1.5.1", + "tqdm", + "umap-learn", + # DWI dependencies + "dipy == 1.3.0", + "fury == 0.7.1", + "nibabel == 3.0.0", + # Git dependencies + "scilpy @ git+https://github.com/scilus/scilpy@1.2.2", +] +description = "Tractography learning" +dynamic = ["version"] +keywords = ["DL, dMRI, neuroimaging, tractography"] +maintainers = [ + {name = "Jon Haitz Legarreta"}, {email = "jon.haitz.legarreta@gmail.com"} +] +name = "tractolearn" +readme = "README.md" +requires-python = ">=3.8" + +[project.optional-dependencies] +test = [ + "hypothesis >= 6.8.0", + "pytest == 5.3", + "pytest-cov", + "pytest-pep8", + "pytest-xdist", +] +dev = [ + "black == 22.12", + "flake8 == 3.9.2", + "flake8-docstrings == 1.6.0", + "isort == 5.8.0", + "pre-commit >= 2.9.0", +] + +[project.scripts] +ae_bundle_streamlines = "scripts:ae_bundle_streamlines.main" +ae_find_thresholds = "scripts:ae_find_thresholds.main" +ae_generate_streamlines = "scripts:ae_generate_streamlines.main" +ae_train = "scripts:ae_train.main" + +[options.extras_require] +all = [ + "%(test)s", +] + +[project.urls] +homepage = "https://github.com/scil-vital/tractolearn" +documentation = "https://tractolearn.readthedocs.io/en/latest/" +repository = "https://github.com/scil-vital/tractolearn" + +[tool.black] +line-length = 79 +target-version = ["py38"] +exclude =''' +( + /( + \.eggs # exclude a few common directories in the + | \.git # root of the project + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + )/ + | data # also separately exclude project-specific files + # and folders in the root of the project +) +''' + +[tool.isort] +profile = "black" +line_length = 79 +src_paths = ["tractolearn"] + +[tool.setuptools.packages] +find = {} # Scanning implicit namespaces is active by default + +[tool.setuptools_scm] +write_to = "tractolearn/_version.py" \ No newline at end of file diff --git a/requirements/requirements.txt b/requirements/requirements.txt deleted file mode 100644 index c17cd07..0000000 --- a/requirements/requirements.txt +++ /dev/null @@ -1,40 +0,0 @@ -Pillow==9.0.1 -black==22.12.0 -comet-ml==3.0.0 -cython==0.29.* -dipy==1.3.* -flake8-docstrings==1.6.0 -flake8==3.9.2 -fury -h5py==2.10.* -holoviews -isort==5.8.0 -matplotlib>=2.2.* -nibabel==3.0.* -nilearn -noise -numpy>=1.20.* -pandas -pathos -plotly -pre-commit>=2.9.0 -ptitprince -pybids -pydot -pygame -pytest-cov -pytest-pep8 -pytest-xdist -pytest==5.3.* -pyyaml -scikit-image -scikit-learn==0.22.* -scipy==1.4.* -seaborn==0.11.* -torchsummary==1.5.1 -tqdm -trimeshpy -umap-learn -vtk>=9.1.* -xlrd==1.2.0 -xlsxwriter==1.3.8 diff --git a/requirements/requirements_dmri_git.txt b/requirements/requirements_dmri_git.txt deleted file mode 100644 index a5d8c3b..0000000 --- a/requirements/requirements_dmri_git.txt +++ /dev/null @@ -1 +0,0 @@ -git+https://github.com/scilus/scilpy.git@1.2.2 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 9b0110f..0000000 --- a/setup.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python -"""Installation script for the tractolearn package.""" - -import glob - -# To use a consistent encoding -from codecs import open -from os import path -from os.path import join as pjoin - -from setuptools import find_packages, setup - -from setup_helpers import read_vars_from - - -# Read package information -info = read_vars_from(pjoin("tractolearn", "info.py")) - -this_directory = path.abspath(path.dirname(__file__)) - -readme_path = pjoin(this_directory, "README.md") -with open(readme_path, encoding="utf-8") as f: - long_description = f.read() - -requirements_path = pjoin(this_directory, "requirements/requirements.txt") -with open(requirements_path, encoding="utf-8") as f: - requirements = f.read().splitlines() - -setup( - name=info.NAME, - version=info.VERSION, - description=info.DESCRIPTION, - long_description=long_description, - long_description_content_type="text/x-rst", - url=info.URL, - project_urls={ - "Bug tracker": info.BUG_TRACKER, - "Documentation": info.DOCUMENTATION, - "Source code": info.SOURCE_CODE, - }, - license=info.LICENSE, - author=info.AUTHOR, - author_email=info.AUTHOR_EMAIL, - classifiers=info.CLASSIFIERS, - keywords=info.KEYWORDS, - maintainer=info.MAINTAINER, - provides=info.PROVIDES, - packages=find_packages(exclude=["contrib", "doc", "unit_tests"]), - install_requires=requirements, - requires=info.REQUIRES, - package_data={}, - data_files=[], - entry_points={}, - scripts=glob.glob("scripts/*.py"), -) diff --git a/setup_helpers.py b/setup_helpers.py deleted file mode 100644 index 300f36c..0000000 --- a/setup_helpers.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -"""distutils / setuptools helpers. -""" - - -class Bunch(object): - def __init__(self, info): - for key, name in info.items(): - if key.startswith("__"): - continue - self.__dict__[key] = name - - -def read_vars_from(info_file): - """Read variables from Python text file. - - Parameters - ---------- - info_file : str - Filename of file to read. - - Returns - ------- - info_vars : Bunch instance - Bunch object where variables read from `info_file` appear as - attributes. - """ - - # Use exec for compatibility with Python 3 - info = {} - with open(info_file, "rt") as fobj: - exec(fobj.read(), info) - - return Bunch(info) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..e3ccc90 --- /dev/null +++ b/tox.ini @@ -0,0 +1,41 @@ +[tox] +envlist = py38 +isolated_build = True + +[testenv] +commands = python -m pytest --cov +deps = pytest-cov +extras = test + +[testenv:isort] +skip_install = True +deps = pre-commit +commands = pre-commit run isort --all-files + +[testenv:flake8] +skip_install = True +deps = pre-commit +commands = pre-commit run flake8 --all-files + +[testenv:black] +skip_install = True +deps = pre-commit +commands = pre-commit run black --all-files + +[testenv:import-lint] +skip_install = True +deps = pre-commit +commands = pre-commit run --hook-stage manual import-linter --all-files + +[testenv:package] +isolated_build = True +skip_install = True +deps = + # check_manifest + wheel + # twine + build +commands = + # check-manifest + python -m build + # python -m twine check dist/* \ No newline at end of file