From 05926fd6ae05c2e91d3c93b1437fc5e76f8e00dc Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Wed, 11 Dec 2024 10:48:02 +1300 Subject: [PATCH] chore: drop python 3.8, support 3.13 (#168) Drop Python 3.8 support and add 3.13 to the test matrix. Other changes: - Ruff's UP check automatically fixes some of the Python code for 3.9+ - Clean-up the `setup.py` wrapper file - Some of the CI jobs have used `python-version: 3.x` to specify the latest stable version of Python - Several steps of the CI workflow use conditionals `if: matrix.python != 3.8`, which are removed - Upgrade ReadTheDocs configuration to use ubuntu-24.04 with python 3.13 --- .github/workflows/ci.yml | 13 +++++-------- .github/workflows/release.yml | 4 ++-- .readthedocs.yml | 4 ++-- DEVELOPER.md | 2 +- README.md | 2 +- autotest/test_misc.py | 5 ++--- modflow_devtools/download.py | 22 ++++++++++++---------- modflow_devtools/fixtures.py | 5 +++-- modflow_devtools/latex.py | 3 ++- modflow_devtools/markers.py | 4 ++-- modflow_devtools/misc.py | 10 +++++----- modflow_devtools/ostags.py | 4 ++-- pyproject.toml | 4 ++-- setup.py | 3 --- 14 files changed, 41 insertions(+), 44 deletions(-) delete mode 100755 setup.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 897ffa3..9a2d15e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.x cache: 'pip' cache-dependency-path: pyproject.toml @@ -52,7 +52,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.x - name: Install Python packages run: | @@ -79,7 +79,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-22.04, macos-13, windows-2022 ] - python: [ 3.8, 3.9, "3.10", "3.11", "3.12" ] + python: [ 3.9, "3.10", "3.11", "3.12", "3.13" ] env: GCC_V: 11 steps: @@ -137,7 +137,6 @@ jobs: run: pip install ".[test]" - name: Cache modflow6 examples - if: matrix.python != 3.8 id: cache-examples uses: actions/cache@v3 with: @@ -145,18 +144,16 @@ jobs: key: modflow6-examples-${{ hashFiles('modflow6-examples/data/**') }} - name: Install extra Python packages - if: matrix.python != 3.8 working-directory: modflow6-examples/etc run: | pip install -r requirements.pip.txt pip install -r requirements.usgs.txt - name: Update FloPy packages - if: matrix.python != 3.8 run: python -m flopy.mf6.utils.generate_classes --ref develop --no-backup - name: Build modflow6 example models - if: matrix.python != 3.8 && steps.cache-examples.outputs.cache-hit != 'true' + if: steps.cache-examples.outputs.cache-hit != 'true' working-directory: modflow6-examples/autotest run: pytest -v -n auto test_scripts.py --init @@ -171,7 +168,7 @@ jobs: # only invoke the GH API on one OS and Python version # to avoid rate limits (1000 rqs / hour / repository) # https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits - if: runner.os == 'Linux' && matrix.python == '3.8' + if: runner.os == 'Linux' && matrix.python == '3.9' working-directory: modflow-devtools/autotest env: REPOS_PATH: ${{ github.workspace }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ff71fc..14fd81a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.x cache: 'pip' cache-dependency-path: pyproject.toml @@ -177,7 +177,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.x - name: Install Python dependencies run: | diff --git a/.readthedocs.yml b/.readthedocs.yml index c1325d5..14c49a3 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,8 +1,8 @@ version: 2 build: - os: "ubuntu-22.04" + os: "ubuntu-24.04" tools: - python: "3.8" + python: "3.13" sphinx: configuration: docs/conf.py formats: diff --git a/DEVELOPER.md b/DEVELOPER.md index 499e1b8..144fc96 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -19,7 +19,7 @@ This document provides guidance to set up a development environment and discusse ## Requirements -Python3.8+ is currently required. This project supports several recent versions of Python, loosely following [NEP 29](https://numpy.org/neps/nep-0029-deprecation_policy.html#implementation) and aiming to stay synchronized with [FloPy](https://github.com/modflowpy/flopy). +Python3.9+ is currently required. This project supports several recent versions of Python, loosely following [NEP 29](https://numpy.org/neps/nep-0029-deprecation_policy.html#implementation) and aiming to stay synchronized with [FloPy](https://github.com/modflowpy/flopy). ## Installation diff --git a/README.md b/README.md index 726b0f6..e144867 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Pytest features include: ## Requirements -Python3.8+, dependency-free, but pairs well with `pytest` and select plugins, e.g. +Python3.9+, dependency-free, but pairs well with `pytest` and select plugins, e.g. - [`pytest-dotenv`](https://github.com/quiqua/pytest-dotenv) - [`pytest-xdist`](https://github.com/pytest-dev/pytest-xdist) diff --git a/autotest/test_misc.py b/autotest/test_misc.py index c56f916..3acc060 100644 --- a/autotest/test_misc.py +++ b/autotest/test_misc.py @@ -4,7 +4,6 @@ from os import environ from pathlib import Path from time import sleep -from typing import List import pytest @@ -122,7 +121,7 @@ def test_has_package(): assert not has_package(namefile_path, "wel") -def get_expected_model_dirs(path, pattern="mfsim.nam") -> List[Path]: +def get_expected_model_dirs(path, pattern="mfsim.nam") -> list[Path]: folders = [] for root, dirs, _ in os.walk(path): for d in dirs: @@ -132,7 +131,7 @@ def get_expected_model_dirs(path, pattern="mfsim.nam") -> List[Path]: return sorted(set(folders)) -def get_expected_namefiles(path, pattern="mfsim.nam") -> List[Path]: +def get_expected_namefiles(path, pattern="mfsim.nam") -> list[Path]: folders = [] for root, dirs, _ in os.walk(path): for d in dirs: diff --git a/modflow_devtools/download.py b/modflow_devtools/download.py index aa21495..3bdb5e8 100644 --- a/modflow_devtools/download.py +++ b/modflow_devtools/download.py @@ -6,7 +6,7 @@ import urllib.request from os import PathLike from pathlib import Path -from typing import List, Optional, Union +from typing import Optional, Union from uuid import uuid4 from warnings import warn @@ -39,7 +39,7 @@ def get_request(url, params={}): def get_releases( repo, per_page=30, max_pages=10, retries=3, verbose=False -) -> List[dict]: +) -> list[dict]: """ Get available releases for the given repository. @@ -221,7 +221,7 @@ def get_latest_version(repo, retries=3, verbose=False) -> str: def get_release_assets( repo, tag="latest", simple=False, retries=3, verbose=False -) -> Union[dict, List[dict]]: +) -> Union[dict, list[dict]]: """ Get assets corresponding to the given release. @@ -264,7 +264,7 @@ def get_release_assets( def list_artifacts( repo, name=None, per_page=30, max_pages=10, retries=3, verbose=False -) -> List[dict]: +) -> list[dict]: """ List artifacts for the given repository, optionally filtering by name (exact match). If more artifacts are available than will fit within the given page size, by default @@ -401,9 +401,10 @@ def download_artifact( while True: tries += 1 try: - with urllib.request.urlopen(request) as url_file, zip_path.open( - "wb" - ) as out_file: + with ( + urllib.request.urlopen(request) as url_file, + zip_path.open("wb") as out_file, + ): content = url_file.read() out_file.write(content) break @@ -479,9 +480,10 @@ def download_and_unzip( while True: tries += 1 try: - with urllib.request.urlopen(request) as url_file, file_path.open( - "wb" - ) as out_file: + with ( + urllib.request.urlopen(request) as url_file, + file_path.open("wb") as out_file, + ): content = url_file.read() out_file.write(content) diff --git a/modflow_devtools/fixtures.py b/modflow_devtools/fixtures.py index bd567df..aaa5353 100644 --- a/modflow_devtools/fixtures.py +++ b/modflow_devtools/fixtures.py @@ -1,9 +1,10 @@ from collections import OrderedDict +from collections.abc import Generator from itertools import groupby from os import PathLike, environ from pathlib import Path from shutil import copytree, rmtree -from typing import Dict, Generator, List, Optional +from typing import Optional from modflow_devtools.imports import import_optional_dependency from modflow_devtools.misc import get_namefile_paths, get_packages @@ -313,7 +314,7 @@ def example_path_from_namfile_path(path: PathLike) -> Path: def example_name_from_namfile_path(path: PathLike) -> str: return example_path_from_namfile_path(path).name - def group_examples(namefile_paths) -> Dict[str, List[Path]]: + def group_examples(namefile_paths) -> dict[str, list[Path]]: d = OrderedDict() for name, paths in groupby( namefile_paths, key=example_name_from_namfile_path diff --git a/modflow_devtools/latex.py b/modflow_devtools/latex.py index c848fee..aa4c1c7 100644 --- a/modflow_devtools/latex.py +++ b/modflow_devtools/latex.py @@ -1,6 +1,7 @@ +from collections.abc import Iterable from os import PathLike from pathlib import Path -from typing import Iterable, Optional, Union +from typing import Optional, Union def build_table( diff --git a/modflow_devtools/markers.py b/modflow_devtools/markers.py index c87fcbe..031bd6f 100644 --- a/modflow_devtools/markers.py +++ b/modflow_devtools/markers.py @@ -5,7 +5,7 @@ from os import environ from platform import python_version, system -from typing import Dict, Optional +from typing import Optional from packaging.version import Version @@ -48,7 +48,7 @@ def requires_python(version, bound="lower"): ) -def requires_pkg(*pkgs, name_map: Optional[Dict[str, str]] = None): +def requires_pkg(*pkgs, name_map: Optional[dict[str, str]] = None): missing = {pkg for pkg in pkgs if not has_pkg(pkg, strict=True, name_map=name_map)} return pytest.mark.skipif( missing, diff --git a/modflow_devtools/misc.py b/modflow_devtools/misc.py index c351738..e754022 100644 --- a/modflow_devtools/misc.py +++ b/modflow_devtools/misc.py @@ -12,7 +12,7 @@ from shutil import which from subprocess import run from timeit import timeit -from typing import Dict, List, Optional, Tuple +from typing import Optional from urllib import request from urllib.error import URLError @@ -67,7 +67,7 @@ def get_ostag() -> str: raise ValueError(f"platform {sys.platform!r} not supported") -def get_suffixes(ostag) -> Tuple[str, str]: +def get_suffixes(ostag) -> tuple[str, str]: """ Returns executable and library suffixes for the given OS (as returned by sys.platform) @@ -144,7 +144,7 @@ def get_current_branch() -> str: raise ValueError(f"Could not determine current branch: {stderr}") -def get_packages(namefile_path: PathLike) -> List[str]: +def get_packages(namefile_path: PathLike) -> list[str]: """ Return a list of packages used by the simulation or model defined in the given namefile. The namefile @@ -273,7 +273,7 @@ def get_model_paths( excluded=None, selected=None, packages=None, -) -> List[Path]: +) -> list[Path]: """ Find model directories recursively in the given location. A model directory is any directory containing one or more @@ -378,7 +378,7 @@ def has_exe(exe): def has_pkg( - pkg: str, strict: bool = False, name_map: Optional[Dict[str, str]] = None + pkg: str, strict: bool = False, name_map: Optional[dict[str, str]] = None ) -> bool: """ Determines if the given Python package is installed. diff --git a/modflow_devtools/ostags.py b/modflow_devtools/ostags.py index 07e35ad..284346a 100644 --- a/modflow_devtools/ostags.py +++ b/modflow_devtools/ostags.py @@ -5,7 +5,7 @@ import sys from platform import processor, system -from typing import Optional, Tuple +from typing import Optional _system = system() _processor = processor() @@ -42,7 +42,7 @@ def get_ostag(kind: str = "modflow") -> str: raise ValueError(f"Invalid kind: {kind}") -def get_binary_suffixes(ostag: Optional[str] = None) -> Tuple[str, str]: +def get_binary_suffixes(ostag: Optional[str] = None) -> tuple[str, str]: """ Returns executable and library suffixes for the given OS tag, if provided, otherwise for the current operating system. diff --git a/pyproject.toml b/pyproject.toml index 3829ab3..0bd826d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,14 +32,14 @@ classifiers = [ "License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3", - "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", + "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering :: Hydrology" ] -requires-python = ">=3.8" +requires-python = ">=3.9" dynamic = ["version"] [project.optional-dependencies] diff --git a/setup.py b/setup.py deleted file mode 100755 index e4505ea..0000000 --- a/setup.py +++ /dev/null @@ -1,3 +0,0 @@ -from setuptools import setup - -setup(name="modflow-devtools")