From 4f119b33664a5c72fb71540b9bfb2d305b8707b2 Mon Sep 17 00:00:00 2001 From: Alex Rubinsteyn Date: Mon, 19 Oct 2026 17:10:12 -0400 Subject: [PATCH] Require Python 3.11 and test through 3.14 --- .github/workflows/tests.yml | 16 ++++++++++++---- AGENTS.md | 2 +- README.md | 2 +- benchmarks/compare_releases.py | 4 ++-- docs/index.md | 2 +- gtfparse/__init__.py | 2 +- gtfparse/write_gtf.py | 4 ++-- pyproject.toml | 10 +++++----- tasks/todo.md | 31 +++++++++++++++++++++++++++++++ 9 files changed, 56 insertions(+), 17 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d5561bb..268ed03 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,9 +14,9 @@ jobs: strategy: matrix: include: - - python-version: "3.9" - setuptools: "setuptools==77.0.3" - python-version: "3.11" + setuptools: "setuptools==77.0.3" + - python-version: "3.14" setuptools: "setuptools" steps: - uses: actions/checkout@v4 @@ -45,11 +45,19 @@ jobs: fail-fast: true matrix: include: - - python-version: "3.9" + - python-version: "3.11" pandas-version: "minimum" pyarrow-version: "minimum" polars-version: "none" - - python-version: "3.10" + - python-version: "3.12" + pandas-version: "latest" + pyarrow-version: "latest" + polars-version: "none" + - python-version: "3.13" + pandas-version: "latest" + pyarrow-version: "latest" + polars-version: "none" + - python-version: "3.14" pandas-version: "latest" pyarrow-version: "latest" polars-version: "none" diff --git a/AGENTS.md b/AGENTS.md index bbb0c38..99c4d92 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,7 @@ Do not tell the user you are "done" or that changes are "complete" until both pa - Use ruff for linting - Configuration is in `pyproject.toml` under `[tool.ruff]` -- Target Python version: 3.9+ (CI matrix runs 3.9 / 3.10 / 3.11) +- Target Python version: 3.11+ (CI matrix runs 3.11 through 3.14) ## Project Shape diff --git a/README.md b/README.md index d49062c..bd51515 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ into columns. python -m pip install gtfparse ``` -Python 3.9 or later is required. For optional Polars output, install +Python 3.11 or later is required. For optional Polars output, install `gtfparse[polars]`. ## Read an annotation and find genes diff --git a/benchmarks/compare_releases.py b/benchmarks/compare_releases.py index c610a48..00ec639 100644 --- a/benchmarks/compare_releases.py +++ b/benchmarks/compare_releases.py @@ -11,7 +11,7 @@ import statistics import subprocess import sys -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path @@ -120,7 +120,7 @@ def main(): ) result = { "description": "Complete production readers; imports/checksums outside wall time; sequential fresh processes", - "date": datetime.now(timezone.utc).isoformat(), + "date": datetime.now(UTC).isoformat(), "threads": args.threads, "inputs": { path.name: { diff --git a/docs/index.md b/docs/index.md index 027e599..0b5554f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,7 +9,7 @@ attribute values such as gene and transcript IDs expanded into columns. python -m pip install gtfparse ``` -Python 3.9 or later is required. For optional Polars output, install +Python 3.11 or later is required. For optional Polars output, install `gtfparse[polars]`. ## Read an annotation and find genes diff --git a/gtfparse/__init__.py b/gtfparse/__init__.py index a2563b7..00f0dcf 100644 --- a/gtfparse/__init__.py +++ b/gtfparse/__init__.py @@ -24,7 +24,7 @@ ) from .write_gtf import write_gtf -__version__ = "3.0.4" +__version__ = "3.0.5" __all__ = [ "GENCODE_BIOTYPE_ALIASES", diff --git a/gtfparse/write_gtf.py b/gtfparse/write_gtf.py index 899cdf4..d227be8 100644 --- a/gtfparse/write_gtf.py +++ b/gtfparse/write_gtf.py @@ -16,7 +16,7 @@ from collections.abc import Iterable from itertools import repeat from pathlib import Path -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING, Union import pandas as pd @@ -74,7 +74,7 @@ def _lines(df): def write_gtf( df: Union[pd.DataFrame, "polars.DataFrame"], path: Union[str, Path], - header_lines: Optional[Iterable[str]] = None, + header_lines: Iterable[str] | None = None, ) -> None: """ Write a DataFrame of genomic features back out to a GTF file. diff --git a/pyproject.toml b/pyproject.toml index 68b8baf..0440797 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "gtfparse" -requires-python = ">=3.9" +requires-python = ">=3.11" authors = [ {name="Alex Rubinsteyn", email="alex.rubinsteyn@unc.edu" } ] description = "Parsing library for extracting data frames of genomic features from GTF files" license = "Apache-2.0" @@ -16,10 +16,10 @@ classifiers = [ "Intended Audience :: Science/Research", "Programming Language :: Python", "Programming Language :: Python :: 3", - "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", "Topic :: Scientific/Engineering :: Bio-Informatics", ] readme = "README.md" @@ -46,7 +46,7 @@ dependencies = {file = ["requirements.txt"]} packages = ["gtfparse"] [tool.ruff] -target-version = "py39" +target-version = "py311" line-length = 100 src = ["gtfparse", "tests"] exclude = [ @@ -78,7 +78,7 @@ ignore = [ "B008", # do not perform function calls in argument defaults "B905", # zip() without explicit strict "SIM108", # use ternary operator instead of if-else - "UP007", # use X | Y for type unions (need Python 3.10+) + "UP007", # retain the established Union annotation style "UP031", # %-format strings ] diff --git a/tasks/todo.md b/tasks/todo.md index d924720..fe1536d 100644 --- a/tasks/todo.md +++ b/tasks/todo.md @@ -521,3 +521,34 @@ PR #93: lint passed, all 213 tests passed, all three executable documentation examples passed, and the strict MkDocs build passed. PR #95: merged the refreshed parent; version 3.0.4, lint, 213 tests, and the strict MkDocs build passed. + +# Python 3.11 support floor (#96) + +## Specification + +Require Python 3.11 or newer with no upper bound. Test Python 3.11–3.14 in CI, +retaining the existing pandas, PyArrow, and optional Polars compatibility +combinations. Keep support for both pandas 2 and 3 because this change only +raises the Python floor. Align package metadata, docs, tooling, and repository +guidance; bump the patch version to 3.0.5. Preserve parsing behavior. + +## Plan + +- [x] Read issue, package requirements, CI matrix, repo guidance, and tests. +- [x] Check plan before implementation: retain compatibility jobs on 3.11 and + add 3.12–3.14 jobs with current dependencies; move packaging endpoints to + 3.11 and 3.14. +- [x] Update metadata, docs, CI, target version, and package version. +- [x] Run `./lint.sh` and `./test.sh` in an isolated environment. +- [x] Verify installed metadata/dependencies and tests on Python 3.11 and 3.14; + review the diff. +- [ ] Open PR, pass CI, merge, deploy from clean master, verify publication. + +## Review + +`./lint.sh` passed. `./test.sh -W error::DeprecationWarning` passed all 213 +tests with pandas 3.0.6, PyArrow 26, and Polars 2.0.0 on both Python 3.11.12 +and 3.14.6 (97% coverage). Isolated dependency checks passed at both endpoints. +Installed metadata reports version 3.0.5, `Requires-Python: >=3.11`, and the +existing pandas 2-compatible dependency floor. Ruff's new Python target enabled +two safe Python 3.11 syntax updates; no parsing behavior changed.