From 4a9031249764d0e3e292a5af1306ad528c2f3dc5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 12 Nov 2025 13:32:11 +0200 Subject: [PATCH 1/3] Add support for Python 3.14 and drop EOL 3.9 --- .github/workflows/ci.yml | 10 +++++----- .pre-commit-config.yaml | 12 ++++++------ docs/environment.yml | 2 +- gcsfs/tests/test_fuse.py | 4 +--- setup.cfg | 3 --- setup.py | 4 ++-- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59943c45..b159fc6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,11 +14,11 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Checkout source - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup conda uses: conda-incubator/setup-miniconda@v3 @@ -47,8 +47,8 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: python-version: "3.11" - - uses: pre-commit/action@v3.0.0 + - uses: pre-commit/action@v3.0.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 486b2dcf..1b83074e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,19 +3,19 @@ exclude: versioneer.py repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v6.0.0 hooks: - id: end-of-file-fixer - id: requirements-txt-fixer - id: trailing-whitespace - - repo: https://github.com/psf/black - rev: 22.10.0 + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 25.11.0 hooks: - id: black args: - - --target-version=py37 + - --target-version=py310 - repo: https://github.com/pycqa/flake8 - rev: 6.0.0 + rev: 7.3.0 hooks: - id: flake8 - repo: https://github.com/asottile/seed-isort-config @@ -23,6 +23,6 @@ repos: hooks: - id: seed-isort-config - repo: https://github.com/pre-commit/mirrors-isort - rev: v5.7.0 + rev: v5.10.1 hooks: - id: isort diff --git a/docs/environment.yml b/docs/environment.yml index 1cfba7b5..a5a0eafc 100644 --- a/docs/environment.yml +++ b/docs/environment.yml @@ -2,7 +2,7 @@ name: s3fs channels: - defaults dependencies: - - python= 3.9 + - python= 3.10 - docutils<0.17 - sphinx - sphinx_rtd_theme diff --git a/gcsfs/tests/test_fuse.py b/gcsfs/tests/test_fuse.py index f1f494cd..7a252b2a 100644 --- a/gcsfs/tests/test_fuse.py +++ b/gcsfs/tests/test_fuse.py @@ -1,6 +1,5 @@ import logging import os -import sys import tempfile import threading import time @@ -26,7 +25,6 @@ def fsspec_fuse_run(): pytest.skip("Error importing fuse.") -@pytest.mark.skipif(sys.version_info < (3, 9), reason="Test fuse causes hang.") @pytest.mark.xfail(reason="Failing test not previously tested.") @pytest.mark.timeout(180) def test_fuse(gcs, fsspec_fuse_run): @@ -40,7 +38,7 @@ def test_fuse(gcs, fsspec_fuse_run): timeout = 20 n = 40 for i in range(n): - logging.debug(f"Attempt # {i+1}/{n} to create lock file.") + logging.debug(f"Attempt # {i + 1}/{n} to create lock file.") try: open(os.path.join(mountpath, "lock"), "w").close() os.remove(os.path.join(mountpath, "lock")) diff --git a/setup.cfg b/setup.cfg index fda24791..b3dff84e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,9 +5,6 @@ versionfile_source = gcsfs/_version.py versionfile_build = gcsfs/_version.py tag_prefix = -[bdist_wheel] -universal=1 - [flake8] exclude = versioneer.py,docs/source/conf.py ignore = diff --git a/setup.py b/setup.py index 0881f007..fddbb51d 100755 --- a/setup.py +++ b/setup.py @@ -18,17 +18,17 @@ "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", - "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", ], keywords=["google-cloud-storage", "gcloud", "file-system"], packages=["gcsfs", "gcsfs.cli"], install_requires=[open("requirements.txt").read().strip().split("\n")], extras_require={"gcsfuse": ["fusepy"], "crc": ["crcmod"]}, - python_requires=">=3.9", + python_requires=">=3.10", long_description_content_type="text/markdown", long_description=open("README.md").read(), zip_safe=False, From 1293d74d85d0c2e86722bad837d17e1fc575f68d Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 12 Nov 2025 13:34:15 +0200 Subject: [PATCH 2/3] ruff check --select UP --fix --- gcsfs/checkers.py | 3 +-- gcsfs/core.py | 2 +- gcsfs/inventory_report.py | 2 +- gcsfs/tests/test_inventory_report.py | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/gcsfs/checkers.py b/gcsfs/checkers.py index abff6351..d97bbb65 100644 --- a/gcsfs/checkers.py +++ b/gcsfs/checkers.py @@ -1,7 +1,6 @@ import base64 from base64 import b64encode from hashlib import md5 -from typing import Optional from .retry import ChecksumError @@ -104,7 +103,7 @@ def validate_http_response(self, r): return self.validate_headers(r.headers) -def get_consistency_checker(consistency: Optional[str]) -> ConsistencyChecker: +def get_consistency_checker(consistency: str | None) -> ConsistencyChecker: if consistency == "size": return SizeChecker() elif consistency == "md5": diff --git a/gcsfs/core.py b/gcsfs/core.py index 18b4eb3e..8fe06625 100644 --- a/gcsfs/core.py +++ b/gcsfs/core.py @@ -146,7 +146,7 @@ def _coalesce_generation(*args): if len(generations) > 1: raise ValueError( "Cannot coalesce generations where more than one are defined," - " {}".format(generations) + f" {generations}" ) elif len(generations) == 0: return None diff --git a/gcsfs/inventory_report.py b/gcsfs/inventory_report.py index 869a6fe0..3e4ed6bd 100644 --- a/gcsfs/inventory_report.py +++ b/gcsfs/inventory_report.py @@ -623,7 +623,7 @@ def _convert_str_to_datetime(str): return datetime.fromisoformat(str.replace("Z", "+00:00")) -class InventoryReportConfig(object): +class InventoryReportConfig: """ Represents the configuration for fetching inventory reports. diff --git a/gcsfs/tests/test_inventory_report.py b/gcsfs/tests/test_inventory_report.py index 36e42270..2c1bc199 100644 --- a/gcsfs/tests/test_inventory_report.py +++ b/gcsfs/tests/test_inventory_report.py @@ -8,7 +8,7 @@ from gcsfs.inventory_report import InventoryReport, InventoryReportConfig -class TestInventoryReport(object): +class TestInventoryReport: """ Unit tests for the inventory report logic, see 'inventory_report.py'. From 3429b6a15b22afe2b6af93d1f3369520af2579c4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 12 Nov 2025 13:35:34 +0200 Subject: [PATCH 3/3] Fix pytest.PytestRemovedIn9Warning: Marks applied to fixtures have no effect See docs: https://docs.pytest.org/en/stable/deprecations.html\#applying-a-mark-to-a-fixture-function --- gcsfs/tests/test_fuse.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gcsfs/tests/test_fuse.py b/gcsfs/tests/test_fuse.py index 7a252b2a..1c0e1c0a 100644 --- a/gcsfs/tests/test_fuse.py +++ b/gcsfs/tests/test_fuse.py @@ -10,7 +10,6 @@ from gcsfs.tests.settings import TEST_BUCKET -@pytest.mark.timeout(180) @pytest.fixture def fsspec_fuse_run(): """Fixture catches other errors on fuse import."""