diff --git a/.circleci/config.yml b/.circleci/config.yml index 76745007..d6e27752 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,6 +13,11 @@ jobs: - checkout: path: /tmp/src/templateflow + - run: + name: Generate requirements.txt + command: | + python /tmp/src/templateflow/.maint/update_requirements.py + - restore_cache: keys: - deps-v10-{{ checksum "/tmp/src/templateflow/requirements.txt"}}-{{ epoch }} @@ -168,7 +173,8 @@ jobs: name: Build only this commit command: | export PATH="$HOME/.conda/bin:$PATH" - python setup.py --version + python -m pip install "setuptools_scm>=8" + python -m setuptools_scm make -C docs SPHINXOPTS="-W" BUILDDIR="$HOME/html" CURBRANCH="${CIRCLE_TAG}" html - store_artifacts: path: ~/html diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index a7c5027c..43384d10 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -131,3 +131,11 @@ jobs: find ${TEMPLATEFLOW_HOME} >> /tmp/.install-2.txt diff /tmp/.install.txt /tmp/.install-2.txt exit $? + + flake8: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + - run: pipx run flake8-pyproject templateflow/ diff --git a/.gitignore b/.gitignore index 4fb6d48b..b112ba85 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ # setuptools_scm templateflow/_version.py +# circleci hash checking +requirements.txt +min-requirements.txt + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/update_changes.sh b/.maint/update_changes.sh similarity index 100% rename from update_changes.sh rename to .maint/update_changes.sh diff --git a/.maint/update_requirements.py b/.maint/update_requirements.py new file mode 100755 index 00000000..c3b9fcec --- /dev/null +++ b/.maint/update_requirements.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from copy import copy +from pathlib import Path + +from packaging.requirements import Requirement, SpecifierSet + +try: + from tomllib import loads # Python +3.11 +except ImportError: + from pip._vendor.tomli import loads + +repo_root = Path(__file__).parent.parent +pyproject = repo_root / 'pyproject.toml' +reqs = repo_root / 'requirements.txt' +min_reqs = repo_root / 'min-requirements.txt' + +requirements = [ + Requirement(req) + for req in loads(pyproject.read_text())['project']['dependencies'] +] + +script_name = Path(__file__).relative_to(repo_root) + + +def to_min(req): + if req.specifier: + req = copy(req) + try: + min_spec = [spec for spec in req.specifier if spec.operator in ('>=', '~=')][0] + except IndexError: + return req + min_spec._spec = ('==',) + min_spec._spec[1:] + req.specifier = SpecifierSet(str(min_spec)) + return req + + +lines = [f'# Auto-generated by {script_name}', ''] + +# Write requirements +lines[1:-1] = [str(req) for req in requirements] +reqs.write_text('\n'.join(lines)) + +# Write minimum requirements +lines[1:-1] = [str(to_min(req)) for req in requirements] +min_reqs.write_text('\n'.join(lines)) diff --git a/MANIFEST.in b/MANIFEST.in index a4dce165..71927a82 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,5 +4,6 @@ recursive-exclude docs/ * recursive-exclude .circleci/ * recursive-exclude .github/ * +recursive-exclude .maint/ * -exclude .gitignore .gitattributes .git_archival.txt .travis.yml .zenodo.json codecov.yml update_changes.sh +exclude .gitignore .gitattributes .git_archival.txt .travis.yml .zenodo.json codecov.yml diff --git a/pyproject.toml b/pyproject.toml index 38860932..c716885f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,164 @@ [build-system] requires = [ - "setuptools >= 45", - "setuptools_scm >= 6.2", - "nipreps-versions", + "setuptools>=64", + "setuptools_scm>=8", ] build-backend = "setuptools.build_meta" [tool.setuptools_scm] -write_to = "templateflow/_version.py" -write_to_template = """\ +version_file = "templateflow/_version.py" +version_file_template = """\ \"\"\"Version file, automatically generated by setuptools_scm.\"\"\" __version__ = "{version}" """ fallback_version = "0.0" -version_scheme = "nipreps-calver" +# version_scheme = "nipreps-calver" + +[project] +name = "templateflow" +description = "TemplateFlow Python Client - TemplateFlow is the Zone of neuroimaging templates." +readme = "README.rst" +authors = [{name = "The NiPreps Developers", email = "nipreps@gmail.com"}] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Image Recognition", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +license = {file = "LICENSE"} +requires-python = ">=3.8" +dependencies = [ + "pybids >= 0.15.2", + "importlib_resources >= 5.7; python_version < '3.11'", + "requests", + "tqdm", +] +dynamic = ["version"] + +[project.urls] +Archive = "https://github.com/templateflow/templateflow" +"Bug Tracker" = "https://github.com/templateflow/python-client/issues" +Home = "https://www.templateflow.org" +Documentation = "https://www.templateflow.org/python-client/" +"Source Code" = "https://github.com/templateflow/python-client" + +[project.optional-dependencies] +test = [ + "pytest", + "pytest-xdist", + "pytest-cov == 2.5.1", + "coverage", +] +datalad = [ + "datalad ~= 0.12.0" +] +doc = [ + "nbsphinx", + "packaging", + "pydot>=1.2.3", + "pydotplus", + "sphinx-argparse", + "sphinx ~= 4.0", + "sphinx_rtd_theme >= 0.4.3", + "sphinxcontrib-apidoc", + "sphinx_multiversion", +] +# Aliases +tests = ["templateflow[test]"] +docs = ["templateflow[doc]"] +all = ["templateflow[datalad,doc,test]"] + +# +# Developer tool configurations +# + +[tool.black] +line-length = 99 +skip-string-normalization = true + +[tool.isort] +profile = 'black' + +[tool.flake8] +max-line-length = "99" +doctests = "False" +exclude = "*build/" +ignore = ["W503", "E203"] +per-file-ignores = [ + "**/__init__.py : F401", + "docs/conf.py : E265", +] + +[tool.pytest.ini_options] +norecursedirs = [".git"] +addopts = "-svx --doctest-modules" +doctest_optionflags = "ALLOW_UNICODE NORMALIZE_WHITESPACE ELLIPSIS" +env = "PYTHONHASHSEED=0" +filterwarnings = ["ignore::DeprecationWarning"] +junit_family = "xunit2" + +[tool.coverage.run] +branch = true +concurrency = 'multiprocessing' +omit = [ + '*/tests/*', + '*/conftest.py', + 'templateflow/_version.py' +] + +[tool.coverage.report] +# Regexes for lines to exclude from consideration +exclude_lines = [ + 'raise NotImplementedError', + 'warnings\.warn', +] + +[tool.ruff] +line-length = 99 + +[tool.ruff.lint] +extend-select = [ + "F", + "E", + "W", + "I", + "UP", + "YTT", + "S", + "BLE", + "B", + "A", + # "CPY", + "C4", + "DTZ", + "T10", + # "EM", + "EXE", + "FA", + "ISC", + "ICN", + "PT", + "Q", +] +extend-ignore = [ + "S311", # We are not using random for cryptographic purposes + "ISC001", + "S603", +] + +[tool.ruff.lint.flake8-quotes] +inline-quotes = "single" + +[tool.ruff.lint.extend-per-file-ignores] +"*/test_*.py" = ["S101"] +"fmriprep/utils/debug.py" = ["A002", "T100"] +"docs/conf.py" = ["A001"] +"docs/sphinxext/github_link.py" = ["BLE001"] + +[tool.ruff.format] +quote-style = "single" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index fd8235af..00000000 --- a/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -pybids>=0.15.2 -importlib_resources >= 5.7; python_version < '3.11' -requests -tqdm -pytest -pytest-xdist -pytest-cov==2.5.1 -coverage diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index a2964336..00000000 --- a/setup.cfg +++ /dev/null @@ -1,103 +0,0 @@ -[metadata] -classifiers = - Development Status :: 3 - Alpha - Intended Audience :: Science/Research - Topic :: Scientific/Engineering :: Image Recognition - License :: OSI Approved :: Apache Software License - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 -description = TemplateFlow Python Client - TemplateFlow is the Zone of neuroimaging templates. -license = Apache-2.0 -license_file = LICENSE -long_description = file:README.rst -long_description_content_type = text/x-rst; charset=UTF-8 -maintainer = The NiPreps developers -maintainer_email = nipreps@gmail.com -name = templateflow -project_urls = - Archive = https://github.com/templateflow/templateflow - Bug Tracker = https://github.com/templateflow/python-client/issues - Home = https://www.templateflow.org - Documentation = https://www.templateflow.org/python-client/ - Source Code = https://github.com/templateflow/python-client - -[options] -python_requires = >= 3.8 -setup_requires = - setuptools >= 45 - setuptools_scm >= 6.2 - wheel -install_requires = - pybids >= 0.15.2 - importlib_resources >= 5.7; python_version < '3.11' - requests - tqdm -test_requires = - pytest - pytest-xdist - pytest-cov == 2.5.1 - coverage -packages = find: -zip_safe = true - -[options.package_data] -templateflow = - conf/config.json - conf/templateflow-skel.zip - conf/templateflow-skel.md5 - -[options.packages.find] -exclude = - *.tests - tests.* - *.tests.* - -[options.extras_require] -datalad = - datalad ~= 0.12.0 -doc = - nbsphinx - packaging - pydot>=1.2.3 - pydotplus - sphinx-argparse - sphinx ~= 4.0 - sphinx_rtd_theme >= 0.4.3 - sphinxcontrib-apidoc - sphinx_multiversion -docs = - %(doc)s -test = - pytest - pytest-xdist - pytest-cov == 2.5.1 - coverage -tests = - %(test)s -all = - %(datalad)s - %(doc)s - %(test)s - -[flake8] -max-line-length = 99 -doctests = False -exclude=*build/ -ignore = - W503 - -[tool:pytest] -norecursedirs = .git -addopts = -svx -doctest_optionflags = ALLOW_UNICODE NORMALIZE_WHITESPACE -env = - PYTHONHASHSEED=0 -filterwarnings = - ignore::DeprecationWarning - -[coverage:run] -branch = True - diff --git a/setup.py b/setup.py deleted file mode 100644 index cbd8588f..00000000 --- a/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python -"""Templateflow's PEP518 setup.py shim.""" -from setuptools import setup - -if __name__ == "__main__": - """ Install entry-point """ - setup() diff --git a/templateflow/api.py b/templateflow/api.py index eadb07eb..5b5f668f 100644 --- a/templateflow/api.py +++ b/templateflow/api.py @@ -1,6 +1,5 @@ """TemplateFlow's Python Client.""" import sys -from importlib import import_module from json import loads from pathlib import Path from bids.layout import Query @@ -11,6 +10,7 @@ item for item in dir(TF_LAYOUT) if item.startswith("get_") ) + @requires_layout def ls(template, **kwargs): """ diff --git a/templateflow/tests/test_api.py b/templateflow/tests/test_api.py index de8cd951..bd02b5be 100644 --- a/templateflow/tests/test_api.py +++ b/templateflow/tests/test_api.py @@ -41,7 +41,10 @@ def __str__(self): return self.text def __repr__(self): - return f'@{self.etype}{{{self.citekey}, {", ".join([f"{key} = {val}" for key, val in self.pairs.items()])}}}' + return ( + f'@{self.etype}{{{self.citekey}, ' + f'{", ".join([f"{key} = {val}" for key, val in self.pairs.items()])}}}' + ) def __eq__(self, other): if isinstance(other, Bibtex): diff --git a/templateflow/tests/test_version.py b/templateflow/tests/test_version.py index 30604d73..2972c8c2 100644 --- a/templateflow/tests/test_version.py +++ b/templateflow/tests/test_version.py @@ -1,6 +1,5 @@ """Test _version.py.""" import sys -from collections import namedtuple from importlib.metadata import PackageNotFoundError from importlib import reload import templateflow