From c71c90dceeaee57014e7291f15c3560a89bade7f Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Tue, 31 Mar 2026 09:48:16 -0300 Subject: [PATCH 1/4] update best practices example --- .github/dependabot.yml | 15 ++++++++ .github/workflows/deploy-docs.yml | 17 ++++++--- .github/workflows/pypi.yml | 34 +++++++++++------- .github/workflows/tests.yml | 15 +++++--- .isort.cfg | 2 -- .pre-commit-config.yaml | 48 +++++++++++++++++--------- README.md | 1 - docs/source/conf.py | 6 ++-- docs/source/how2package4ioos.md | 1 - ioos_pkg_skeleton/__init__.py | 4 +-- ioos_pkg_skeleton/ioos_pkg_skeleton.py | 14 +++----- notebooks/tutorial.ipynb | 1 - pyproject.toml | 34 +++++++++--------- ruff.toml | 45 ++++++++++++++++++++++++ tests/test_ioos_pkg_skeleton.py | 8 +++-- 15 files changed, 167 insertions(+), 78 deletions(-) delete mode 100644 .isort.cfg create mode 100644 ruff.toml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 563dd9b..441d5c7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,18 @@ updates: interval: "daily" labels: - "Bot" + groups: + github-actions: + patterns: + - '*' + cooldown: + default-days: 7 + + - package-ecosystem: "pre-commit" + directory: "/" + schedule: + interval: "daily" + labels: + - "Bot" + cooldown: + default-days: 7 diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 393bbbb..5927378 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,5 +1,8 @@ name: Documentation +# no permissions by default +permissions: {} + on: pull_request: push: @@ -12,15 +15,21 @@ on: jobs: build-docs: runs-on: ubuntu-latest + permissions: + contents: write + defaults: + run: + shell: bash -l {0} steps: - name: checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - name: Setup Micromamba - uses: mamba-org/setup-micromamba@v2 + uses: mamba-org/setup-micromamba@add3a49764cedee8ee24e82dfde87f5bc2914462 # v2.0.7 with: environment-name: TEST init-shell: bash @@ -31,12 +40,10 @@ jobs: --channel conda-forge - name: Install package - shell: bash -l {0} run: | python -m pip install -e . --no-deps --force-reinstall - name: Build documentation - shell: bash -l {0} run: | set -e jupyter nbconvert --to notebook --execute notebooks/tutorial.ipynb --output=tutorial-output.ipynb @@ -47,7 +54,7 @@ jobs: - name: Deploy if: success() && github.event_name == 'release' - uses: peaceiris/actions-gh-pages@v4 + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: docs/build/html diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index cde1813..d74c041 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -9,45 +9,55 @@ on: types: - published +defaults: + run: + shell: bash + jobs: - packages: + pypi-publish: + name: Upload release to PyPI runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/ioos-pkg-skeleton/ + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + # Should be enough for setuptools-scm + fetch-depth: 100 + persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.x" - name: Get tags run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - shell: bash - name: Install build tools run: | python -m pip install --upgrade pip build - shell: bash - - name: Build binary wheel + - name: Build sdist and binary wheel run: python -m build --sdist --wheel . --outdir dist - name: CheckFiles run: | ls dist - shell: bash + python -m pip install --upgrade check-manifest + check-manifest --verbose - name: Test wheels run: | cd dist && python -m pip install *.whl python -m pip install --upgrade twine python -m twine check * - shell: bash - name: Publish a Python distribution to PyPI if: success() && github.event_name == 'release' - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_PASSWORD }} + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 22ac63e..942c9af 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,5 +1,8 @@ name: Tests +# no permissions by default +permissions: {} + on: pull_request: push: @@ -10,17 +13,21 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: [ "3.10", "3.14"] os: [windows-latest, ubuntu-latest, macos-latest] fail-fast: false + defaults: + run: + shell: bash -l {0} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - name: Setup Micromamba ${{ matrix.python-version }} - uses: mamba-org/setup-micromamba@v2 + uses: mamba-org/setup-micromamba@add3a49764cedee8ee24e82dfde87f5bc2914462 # v2.0.7 with: environment-name: TEST init-shell: bash @@ -31,11 +38,9 @@ jobs: --channel conda-forge - name: Install package - shell: bash -l {0} run: | python -m pip install -e . --no-deps --force-reinstall - name: Tests - shell: bash -l {0} run: | python -m pytest -n 2 -rxs --cov=ioos_pkg_skeleton tests diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 8b66005..0000000 --- a/.isort.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[settings] -known_third_party = numpy,pytest,requests,setuptools diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de59026..473444f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v6.0.0 hooks: - id: trailing-whitespace exclude: tests/data @@ -15,51 +15,67 @@ repos: files: requirements-dev.txt - repo: https://github.com/econchick/interrogate - rev: 1.5.0 + rev: 1.7.0 hooks: - id: interrogate exclude: ^(docs|setup.py|tests) args: [--config=pyproject.toml] - repo: https://github.com/keewis/blackdoc - rev: v0.3.8 + rev: v0.4.6 hooks: - id: blackdoc -- repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.282 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.8 hooks: - id: ruff - -- repo: https://github.com/psf/black - rev: 23.7.0 - hooks: - - id: black - language_version: python3 + args: ["--fix", "--show-fixes"] + - id: ruff-format - repo: https://github.com/codespell-project/codespell - rev: v2.2.5 + rev: v2.4.2 hooks: - id: codespell args: - --quiet-level=2 - repo: https://github.com/asottile/add-trailing-comma - rev: v3.0.1 + rev: v4.0.0 hooks: - id: add-trailing-comma - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.13.0" + rev: "v2.21.0" hooks: - id: pyproject-fmt - repo: https://github.com/aio-libs/sort-all - rev: "v1.2.0" + rev: "v1.3.0" hooks: - id: sort-all types: [file, python] +- repo: https://github.com/nbQA-dev/nbQA + rev: 1.9.1 + hooks: + - id: nbqa-check-ast + - id: nbqa-black + - id: nbqa-ruff + args: [ + --fix, + --config=ruff.toml, + ] + +- repo: https://github.com/bdice/nb-strip-paths + rev: v0.1.0 + hooks: + - id: nb-strip-paths + +- repo: https://github.com/woodruffw/zizmor-pre-commit + rev: v1.23.1 + hooks: + - id: zizmor ci: autofix_commit_msg: | @@ -67,7 +83,5 @@ ci: for more information, see https://pre-commit.ci autofix_prs: false - autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate' - autoupdate_schedule: monthly skip: [] submodules: false diff --git a/README.md b/README.md index 162ce9a..bb40677 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ pip install ioos_pkg_skeleton ```python from ioos_pkg_skeleton import ioos_pkg_skeleton - ioos_pkg_skeleton.meaning_of_life_url() ``` diff --git a/docs/source/conf.py b/docs/source/conf.py index e2cb40b..4c72b50 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -58,11 +58,11 @@ # |version| and |release|, also used in various other places throughout the # built documents. # -from ioos_pkg_skeleton import __version__ as VERSION # noqa +from ioos_pkg_skeleton import __version__ -version = VERSION +version = __version__ # The full version, including alpha/beta/rc tags. -release = VERSION +release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/how2package4ioos.md b/docs/source/how2package4ioos.md index 2e197f6..b172b9f 100644 --- a/docs/source/how2package4ioos.md +++ b/docs/source/how2package4ioos.md @@ -89,7 +89,6 @@ setup( "tag_regex": r"^(?Pv)?(?P[^\+]+)(?P.*)?$", } ) - ``` We recommend the use of `setuptools-scm`. diff --git a/ioos_pkg_skeleton/__init__.py b/ioos_pkg_skeleton/__init__.py index 6a6b763..b4bc27e 100644 --- a/ioos_pkg_skeleton/__init__.py +++ b/ioos_pkg_skeleton/__init__.py @@ -1,5 +1,5 @@ -""" -ioos_pkg_skeleton is not a real package, just a set of best practices examples. +"""ioos_pkg_skeleton is not a real package, +just a set of best practices examples. """ from ioos_pkg_skeleton.ioos_pkg_skeleton import ( diff --git a/ioos_pkg_skeleton/ioos_pkg_skeleton.py b/ioos_pkg_skeleton/ioos_pkg_skeleton.py index bee766b..a00b060 100644 --- a/ioos_pkg_skeleton/ioos_pkg_skeleton.py +++ b/ioos_pkg_skeleton/ioos_pkg_skeleton.py @@ -1,8 +1,4 @@ -""" -ioos_pkg_skeleton - -My awesome ioos_pkg_skeleton -""" +"""My awesome ioos_pkg_skeleton.""" import numpy as np import requests @@ -15,11 +11,9 @@ def meaning_of_life(n: int) -> np.ndarray: def meaning_of_life_url() -> str: - """ - Fetch the meaning of life from https://en.wikipedia.org/wiki/Main_Page. - """ - url = "https://en.wikipedia.org/api/rest_v1/page/summary/Monty_Python's_The_Meaning_of_Life" # noqa - r = requests.get(url) + """Fetch the meaning of life from https://en.wikipedia.org/wiki/Main_Page.""" + url = "https://en.wikipedia.org/api/rest_v1/page/summary/Monty_Python's_The_Meaning_of_Life" + r = requests.get(url, timeout=10) r.raise_for_status() j = r.json() return j["extract"] diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index eebce44..18b9ae2 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -15,7 +15,6 @@ "source": [ "import ioos_pkg_skeleton\n", "\n", - "\n", "ioos_pkg_skeleton.meaning_of_life(3)" ] }, diff --git a/pyproject.toml b/pyproject.toml index f9de5f6..ec1fc86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ build-backend = "setuptools.build_meta" requires = [ "setuptools>=42", - "setuptools_scm[toml]>=3.4", + "setuptools-scm[toml]>=3.4", "wheel", ] @@ -10,9 +10,9 @@ requires = [ name = "ioos-pkg-skeleton" description = "MY AWESOME MODULE" readme = "README.md" -license = {file = "LICENSE.txt"} +license = { file = "LICENSE.txt" } authors = [ - {name = "AUTHOR NAME", email = "AUTHOR@EMAIL.COM"}, + { name = "AUTHOR NAME", email = "AUTHOR@EMAIL.COM" }, ] requires-python = ">=3.8" classifiers = [ @@ -21,6 +21,9 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dynamic = [ "version", @@ -29,13 +32,12 @@ dependencies = [ "numpy", "requests", ] -[project.urls] -documentation = "https://ioos.github.io/ioos-python-package-skeleton" -homepage = "https://github.com/ioos/ioos-python-package-skeleton" -repository = "https://github.com/ioos/ioos-python-package-skeleton" +urls.documentation = "https://ioos.github.io/ioos-python-package-skeleton" +urls.homepage = "https://github.com/ioos/ioos-python-package-skeleton" +urls.repository = "https://github.com/ioos/ioos-python-package-skeleton" [tool.setuptools] -packages = ["ioos_pkg_skeleton"] +packages = [ "ioos_pkg_skeleton" ] zip-safe = false include-package-data = true @@ -44,16 +46,14 @@ write_to = "ioos_pkg_skeleton/_version.py" write_to_template = "__version__ = '{version}'" [tool.ruff] -select = [ - "F", # flakes - "I", # import sorting - "U", # upgrade -] target-version = "py311" line-length = 79 - -[tool.ruff.per-file-ignores] -"docs/conf.py" = ["E402"] +per-file-ignores."docs/conf.py" = [ "E402" ] +select = [ + "F", # flakes + "I", # import sorting + "U", # upgrade +] [tool.interrogate] ignore-init-method = true @@ -63,7 +63,7 @@ ignore-semiprivate = false ignore-private = false ignore-module = false fail-under = 95 -exclude = ["setup.py", "docs", "tests"] +exclude = [ "setup.py", "docs", "tests" ] verbose = 1 quiet = false color = true diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..36ac759 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,45 @@ +line-length = 79 + +lint.select = ["ALL"] + +lint.ignore = [ + "COM812", # missing-trailing-comma + "D203", # 1 blank line required before class docstring + "D205", # 1 blank line required between summary line and description + "D213", # incompatible. Ignoring `multi-line-summary-second-line` + "TRY003", # Avoid specifying long messages outside the exception class +] + +[lint.extend-per-file-ignores] +"docs/source/conf.py" = [ + "A001", # builtin-variable-shadowing + "D100", # Missing docstring in public module + "E402", # Module level import not at top of file + "ERA001", # Found commented-out code + "ERA001", # Found commented-out code + "EXE001", # Shebang is present but file is not executable +] +"test_*.py" = [ + "ANN001", # Missing type annotation for function argument + "ANN201", # Missing return type annotation for public function + "ANN202", # Missing return type annotation for private function + "INP001", # File is part of an implicit namespace package + "PD901", # Avoid using the generic variable name `df` for DataFrames + "S101", # Use of assert detected +] +# nbqa-ruff acts on converted .py so we cannot glob .ipynb :-/ +# https://github.com/nbQA-dev/nbQA/issues/823 +"notebooks/*" = [ + "ANN001", # Missing type annotation for function argument + "ANN201", # Missing return type annotation for public function + "B018", # Found useless expression. Either assign it to a variable or remove it + "D100", # Missing docstring in public module + "D103", # Missing docstring in public function + "E402", # Module level import not at top of file + "FBT003", # Boolean positional value in function call + "INP001", # File is part of an implicit namespace package + "PD901", # Avoid using the generic variable name `df` for DataFrames + "T201", # `print` found" +] +[lint.pycodestyle] +max-doc-length = 180 diff --git a/tests/test_ioos_pkg_skeleton.py b/tests/test_ioos_pkg_skeleton.py index 2d729fa..c96bbc9 100644 --- a/tests/test_ioos_pkg_skeleton.py +++ b/tests/test_ioos_pkg_skeleton.py @@ -1,3 +1,5 @@ +"""Test module.""" + import numpy as np import pytest @@ -6,6 +8,7 @@ @pytest.mark.web def test_meaning_of_life_url(): + """Test meaning_of_life_url function.""" ret = meaning_of_life_url() assert isinstance(ret, str) @@ -14,7 +17,8 @@ def test_meaning_of_life_url(): def test_meaning_of_life(): - n = 2 + """Test meaning_of_life function.""" + answer, n = 2, 42 ret = meaning_of_life(n) assert isinstance(ret, np.ndarray) - assert np.unique(ret) == 42 + assert np.unique(ret) == answer From 9816a5e946768d71f0a19ce373c74c4bbfc04b9a Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Tue, 31 Mar 2026 09:53:32 -0300 Subject: [PATCH 2/4] fix manifest --- MANIFEST.in | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 381c29c..7bced70 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,7 @@ include *.txt -include README.md -include pyproject.toml +include *.md +include *.toml -# CHANGE PKG NAME HERE graft ioos_pkg_skeleton prune docs @@ -10,9 +9,9 @@ prune tests prune notebooks prune *.egg-info +exclude .gitignore + global-exclude *.nc +global-exclude *.yaml +global-exclude *.yml -exclude *.yml -exclude *.enc -exclude .gitignore -exclude .isort.cfg From 823bf1d742a71abe8612ea88cea7db0d61f22aea Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Tue, 31 Mar 2026 10:10:45 -0300 Subject: [PATCH 3/4] fix tests --- .pre-commit-config.yaml | 2 +- MANIFEST.in | 1 - ioos_pkg_skeleton/ioos_pkg_skeleton.py | 3 ++- tests/test_ioos_pkg_skeleton.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 473444f..a3946e4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,7 +26,7 @@ repos: hooks: - id: blackdoc -- repo: https://github.com/astral-sh/ruff-pre-commit +- repo: https://github.com/charliermarsh/ruff-pre-commit rev: v0.15.8 hooks: - id: ruff diff --git a/MANIFEST.in b/MANIFEST.in index 7bced70..735b3e6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -14,4 +14,3 @@ exclude .gitignore global-exclude *.nc global-exclude *.yaml global-exclude *.yml - diff --git a/ioos_pkg_skeleton/ioos_pkg_skeleton.py b/ioos_pkg_skeleton/ioos_pkg_skeleton.py index a00b060..af52f1a 100644 --- a/ioos_pkg_skeleton/ioos_pkg_skeleton.py +++ b/ioos_pkg_skeleton/ioos_pkg_skeleton.py @@ -12,8 +12,9 @@ def meaning_of_life(n: int) -> np.ndarray: def meaning_of_life_url() -> str: """Fetch the meaning of life from https://en.wikipedia.org/wiki/Main_Page.""" + headers = {"User-Agent": "IOOS-SKELETON/0.0 ; ioos-skeleton@example.org)"} url = "https://en.wikipedia.org/api/rest_v1/page/summary/Monty_Python's_The_Meaning_of_Life" - r = requests.get(url, timeout=10) + r = requests.get(url, headers=headers, timeout=10) r.raise_for_status() j = r.json() return j["extract"] diff --git a/tests/test_ioos_pkg_skeleton.py b/tests/test_ioos_pkg_skeleton.py index c96bbc9..08733b4 100644 --- a/tests/test_ioos_pkg_skeleton.py +++ b/tests/test_ioos_pkg_skeleton.py @@ -18,7 +18,7 @@ def test_meaning_of_life_url(): def test_meaning_of_life(): """Test meaning_of_life function.""" - answer, n = 2, 42 + n, answer = 2, 42 ret = meaning_of_life(n) assert isinstance(ret, np.ndarray) assert np.unique(ret) == answer From 43ff99839c115c464932ead32a5bd8c596b16096 Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Tue, 31 Mar 2026 10:13:29 -0300 Subject: [PATCH 4/4] fix manifest --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 735b3e6..d2278a8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -10,6 +10,7 @@ prune notebooks prune *.egg-info exclude .gitignore +exclude ioos_pkg_skeleton/_version.py global-exclude *.nc global-exclude *.yaml