Skip to content

chore(deps): drop Python 3.9, add 3.14, consolidate dependency updates#497

Merged
shcheklein merged 3 commits into
mainfrom
chore/consolidate-deps-updates
Jul 8, 2026
Merged

chore(deps): drop Python 3.9, add 3.14, consolidate dependency updates#497
shcheklein merged 3 commits into
mainfrom
chore/consolidate-deps-updates

Conversation

@shcheklein

@shcheklein shcheklein commented Jul 8, 2026

Copy link
Copy Markdown
Member

Consolidates all open Renovate deps/chore PRs into one change, fixes the CI breakage on main, and moves the supported Python range to 3.10–3.14.

Why main was red

The weekly runs on main have been failing on Python 3.10+ since early June. Root cause: typer β‰₯0.26 vendors click and no longer depends on it, so fresh installs stopped pulling click in β€” while gto imports click directly (gto/cli.py, gto/utils.py, tests/conftest.py) without declaring it. pylint failed with E0401: Unable to import 'click'.

It's worse than lint: gto's CLI machinery subclasses real-click classes (GtoCliMixin(click.Command), @click.pass_context) and mixes them into typer's (now vendored-click) TyperCommand/TyperGroup MROs. With typer 0.26.x even gto --help crashes on startup (TypeError: Command.__init__() got an unexpected keyword argument 'rich_markup_mode') β€” i.e. a fresh pip install gto on Python 3.10+ currently yields a broken CLI. Python 3.9 was unaffected only because it resolves typer 0.23.2 (0.24+ requires Python β‰₯3.10; all pre-0.26 versions depend on real click).

Fix: declare click>=8,<9 explicitly and pin typer>=0.4.1,<0.26. Migrating to vendored-click typer (β‰₯0.26) requires reworking the CLI help/exception machinery against typer._click and should be tracked as a separate effort.

Python support

  • Drop Python 3.9 (EOL October 2025): removed from test matrix, classifiers; requires-python = ">=3.10"
  • Add Python 3.14 to the test matrix and classifiers
  • Remove the CliRunner mix_stderr shim in tests/conftest.py (only needed for the old click that py3.9 resolved)
  • zip(..., strict=False) fixes auto-applied by ruff B905, which activates with the 3.10+ target

Consolidated Renovate updates

PR Update Resolution
#496 actions/checkout v5 β†’ v7 taken as-is
#493 codecov/codecov-action v4 β†’ v7 taken as-is
#484 deploy job Python 3.13 β†’ 3.14 taken, plus 3.14 in test matrix
#487 pylint 3.3.8 β†’ 4.0.6 taken (pylint 4 requires β‰₯3.10 β€” unblocked by the 3.9 drop)
#482 pylint β†’ 3.3.9 superseded by #487
#495 mypy β†’ v2 delivered as mypy==2.2.0 (the py3.9-only <1.11.0 pin is gone with 3.9)
#494 mypy β†’ <1.20.3 superseded by #495

Verification

Ran locally on macOS for Python 3.10, 3.13, and 3.14, each with a fresh venv:

  • pip install -e .[dev] resolves cleanly (click 8.4.2, typer 0.23.2, mypy 2.2.0, pylint 4.0.6, pygit2 1.19.3)
  • pre-commit run --all-files β€” all hooks pass (ruff, ruff-format, mypy 2.2.0, pylint 4.0.6)
  • pytest β€” 165 passed on each
  • gto --help / gto show / gto --version work (broken before on 3.10+ with unpinned typer)
  • python -m build && twine check --strict dist/* passes on 3.14 (deploy job path)

πŸ€– Generated with Claude Code

Consolidates open Renovate PRs and fixes CI breakage on main:

- Drop Python 3.9 (EOL Oct 2025), add Python 3.14 to test matrix and
  classifiers; requires-python >=3.10
- actions/checkout v5 -> v7 (#496)
- codecov/codecov-action v4 -> v7 (#493)
- deploy job Python 3.13 -> 3.14 (#484)
- pylint 3.3.8 -> 4.0.6 (#487, supersedes #482); single pin now that
  3.9 is gone
- mypy 1.17.1 -> 2.2.0 (#495 intent, supersedes #494); the py3.9-only
  <1.11.0 pin is obsolete with 3.9 dropped
- declare click explicitly: typer >=0.24 vendors click and no longer
  pulls it in, so fresh installs on 3.10+ had no click at all while gto
  imports it directly (this is what broke CI on main)
- pin typer <0.24: vendored-click typer is incompatible with gto's
  click-based CLI machinery (subclassing click.Command,
  @click.pass_context); the CLI crashes on startup with 0.24+.
  Migration to newer typer tracked separately
- remove the CliRunner mix_stderr shim in tests (only needed for the
  old click that py3.9 resolved)
- zip(..., strict=False) fixes from ruff B905, enforced now that
  target is 3.10+

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

βœ… All modified and coverable lines are covered by tests.

πŸ“’ Thoughts on this report? Let us know!

Comment thread gto/api.py Outdated
Comment thread tests/test_api.py Outdated
Comment thread pyproject.toml
classifiers = [
"Development Status :: 2 - Pre-Alpha",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@skshetry are we good to drop Python 3.9 now? should not be impacting DVC I think, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified against current DVC main β€” no impact:

  • DVC only uses gto's pure library surface: dvc/repo/artifacts.py imports gto.constants (assert_name_is_valid, fullname_re), gto.exceptions, gto.tag (find, parse_tag), gto.base (sort_versions). None of those modules import click/typer (that's confined to gto/cli.py/gto/utils.py), and none changed in this PR.
  • DVC still supports and tests py3.9 (requires-python = ">=3.9", 3.9 in its matrix) with gto>=1.6.0,<2. Once gto releases with >=3.10, pip on py3.9 simply resolves the last 3.9-compatible gto (1.9.x) β€” installs keep working; 3.9 users just stop receiving gto updates (3.9 has been EOL since Oct 2025).
  • No pin conflicts: DVC envs already carry click via celery (which requires click>=8.1,<9 β€” same range as the new pin), and nothing in DVC's tree uses typer.
  • ⚠️ One requirement: the next gto release must be 1.10.0, not 2.0.0 β€” DVC caps at gto<2, so a major bump would be invisible to DVC until they raise the cap.

Side benefit for DVC docs/users: this PR fixes the gto CLI being broken on fresh 3.10+ installs (unpinned typer resolves to 0.26.x β†’ gto --help crashes).

@skshetry skshetry Jul 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, 3.9 is already EOL'd. I don't plan to drop support in dvc though. But 3.9 users should be able to install older version of GTO.

Comment thread pyproject.toml Outdated
# typer>=0.24 vendors click and is incompatible with gto's click-based
# CLI machinery (subclassing click.Command, @click.pass_context, etc.):
# the CLI crashes on startup. Migration tracked separately.
"typer>=0.4.1,<0.24",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do we use typer for? explain, plan the mitigation strategy

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What typer is used for: the entire CLI (gto/cli.py) is built on it β€” app = Typer(cls=GtoGroup), every command declared via typer Argument/Option, plus typer.Context/typer.Exit/typer.colors. On top of that, gto customizes help/UX by subclassing typer.core.TyperCommand/TyperGroup mixed with real-click classes: GtoCliMixin(click.Command) implements grouped help sections ("Read the registry" / "Modify artifacts"), command aliases, and "Examples:" epilogs; gto_command() wraps every command with @click.pass_context for unified error handling; tests use typer.testing.CliRunner.

Why the pin: typer 0.24+ replaced its click dependency with a vendored copy (typer._click). Two consequences: (1) fresh installs stopped getting click at all while gto imports it directly β€” the current red main; (2) real-click classes mixed into vendored-click TyperCommand/TyperGroup MROs are incompatible β€” with typer 0.26 even gto --help crashes (TypeError: Command.__init__() got an unexpected keyword argument 'rich_markup_mode'). Every fresh pip install gto on 3.10+ has shipped a broken CLI since typer 0.24 started resolving.

Mitigation plan (in preference order):

  1. Migrate to typer-native APIs (preferred): rich_help_panel covers the command grouping, epilog covers the Examples blocks, aliases via registering additional command names; replace the @click.pass_context error-handling wrapper with a ctx: typer.Context param and typer's re-exported exceptions (typer.Exit/Abort/BadParameter); tests drop click.testing.Result for typer.testing's. Then remove both the typer<0.24 and click pins. Scope: essentially one focused PR touching gto/cli.py + tests/conftest.py.
  2. Drop typer for plain click 8.x β€” the custom machinery is click-level already; typer mainly provides the decorator ergonomics.
  3. Rejected: importing from typer._click (private API, breaks on any typer minor).

Happy to open a tracking issue for option 1.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction after checking typer's release notes: the vendoring landed in typer 0.26.0 (2026-05-26), not 0.24 β€” 0.24.0 only dropped Python 3.9. Timeline matches our CI: the weekly run started failing June 8, right after 0.26.0's release.

Relaxed the pin accordingly to typer>=0.4.1,<0.26 in 70bf976 (verified typer 0.25.1 + click 8.4.2: CLI, lints, and the full suite pass).

Typer's stated reasons for vendoring, from the 0.26.0 release notes: simplifies work for both teams, lets typer evolve independently and enables planned features, and avoids dependency conflicts between packages pinning different click versions. The trade-off is exactly what bit us: click-specific integrations (subclassing click.Command, @click.pass_context, click.testing) are no longer supported against typer's runtime β€” hence the migration plan above.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates multiple dependency/CI updates while fixing the CLI breakage caused by newer typer versions (vendored click) and formally updates the supported Python range to 3.10–3.14.

Changes:

  • Drops Python 3.9 support and adds Python 3.14 across metadata and CI.
  • Fixes CLI/runtime dependency resolution by declaring click explicitly and constraining typer to <0.24.
  • Updates CI actions and dev tooling pins (notably pylint v4 and mypy v2), plus ruff-driven zip(..., strict=...) edits.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pyproject.toml Drops 3.9, sets requires-python>=3.10, adds click dependency, constrains typer, updates dev-tool pins.
tests/conftest.py Removes the legacy CliRunner(mix_stderr=...) compatibility shim now that 3.9 is dropped.
tests/test_api.py Adds explicit zip(..., strict=...) argument for Python 3.10+ target.
tests/test_registry.py Adds explicit zip(..., strict=...) argument in registry-state comparisons.
gto/api.py Adds explicit zip(..., strict=...) argument when building the table output rows.
.github/workflows/check-test-release.yml Updates test matrix to 3.10–3.14 and bumps CI actions (checkout@v7, codecov-action@v7), deploy job uses Python 3.14.

πŸ’‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gto/api.py Outdated
Comment thread tests/test_api.py Outdated
Comment thread tests/test_registry.py Outdated
Comment thread tests/test_registry.py Outdated
Per review: fail fast instead of silently truncating. The api.py pair
derives both sides from the same stages list; the test zips are
equality checks where truncation would mask mismatches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
typer 0.24 only dropped py3.9; 0.24/0.25 still depend on real click.
Verified typer 0.25.1 + click 8.4.2: CLI works, lints and full test
suite pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shcheklein shcheklein requested a review from Copilot July 8, 2026 21:44
@shcheklein shcheklein merged commit 21f6a8a into main Jul 8, 2026
16 checks passed
@shcheklein shcheklein deleted the chore/consolidate-deps-updates branch July 8, 2026 21:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

shcheklein added a commit that referenced this pull request Jul 8, 2026
typer >=0.26 vendors click and is incompatible with gto's click-based
CLI machinery (see #497). gto only used typer's signature-based param
declarations; all the custom UX (grouped help, aliases, examples,
error funnel) was already hand-written click code. Re-express command
params as click decorators and remove the typer dependency and pin.

- commands, options, defaults, flags, help texts, exit codes: unchanged
- top-level `gto --help` output is byte-identical
- subcommand help switches from typer's rich boxes to click's plain
  format; argument help is preserved via GtoArgument + an "Arguments"
  help section (click has no native argument help)
- GtoGroup.list_commands keeps definition order (click sorts
  alphabetically by default)
- drop dead option definitions left over from removed commands
  (--rev, --all, --expected, --type, --path, --description, --custom,
  --table, --commit, --push-commit, --branch, arg_version, arg_stage,
  option_version, option_to_version, GTOGroupSection)
- click>=8.2 floor: CliRunner without mix_stderr, kebab-case naming

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
shcheklein added a commit that referenced this pull request Jul 9, 2026
Follow-up to #497 β€” implements the mitigation plan from [the typer
discussion
there](#497 (comment)):
remove typer entirely instead of migrating to its vendored-click API.
Net: **-1 dependency, -1 pin, net -7 lines**, and immunity to typer's
release churn.

## Why dropping typer (not migrating to typer β‰₯0.26)

gto only used typer's thin core β€” declaring command params via function
signatures. Everything that defines the CLI's actual behavior (sectioned
help, command aliases, "Examples:" epilogs, the unified error funnel)
was already hand-written click code. So the migration is mechanical:
re-express ~10 command signatures as click decorators, delete the typer
layer.

## What changed

- Every command's params moved from typer `Argument`/`Option` defaults
to `@click.option`/`@click.argument` decorators. **Flags, defaults, help
texts, callbacks, exit codes: unchanged.**
- `app` is now a `click.Group` (same `gto.cli:app` entry point).
- `typer.Exit`/`typer.colors`/`typer.BadParameter` β†’ click equivalents.
- New `GtoArgument(click.Argument)` carries argument help (click has no
native argument help) and `GtoCliMixin.format_options` renders an
"Arguments" section β€” so `test_commands_args_help` still enforces help
on every param.
- `GtoGroup.list_commands` override keeps definition order (click sorts
alphabetically by default).
- Tests: `typer.testing.CliRunner` β†’ `click.testing.CliRunner`; the
`get_command_from_info` fixture becomes `app.commands.values()`.
- Dead option definitions from long-removed commands dropped (`--rev`,
`--all`, `--expected`, `--type`, `--path`, `--description`, `--custom`,
`--table`, `--commit`, `--push-commit`, `--branch`, `arg_version`,
`arg_stage`, `option_version`, `option_to_version`, `GTOGroupSection`).
- `pyproject.toml`: **typer removed**; `click>=8.2,<9` (8.2 floor for
`CliRunner` without `mix_stderr` and kebab-case command naming).

## User-visible changes

- Top-level `gto --help` output is **byte-identical** to before.
- Subcommand `--help` switches from typer's rich boxes to click's
classic plain format (matching the top-level help style), with an added
"Arguments" section preserving argument descriptions. Everything else β€”
usage lines, option lists, defaults β€” carries over.

## Verification

- `pre-commit run --all-files` green (ruff, mypy 2.2.0, pylint 4.0.6).
- 165/165 tests pass on Python 3.10, 3.13, 3.14 (fresh venvs; typer
confirmed absent).
- End-to-end CLI exercise in a scratch repo: `register`, `assign` (via
`promote` alias), `show` (table + `--json`), `check-ref`, `history
--plain`, `deprecate`; error paths verified β€” GTOException β†’ ❌ message +
exit 1, usage error β†’ exit 2, `-h` works on subcommands.
- Warning count in pytest dropped 27 β†’ 1 (typer's click-compat
deprecation warnings gone).

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants