Skip to content

[2.1.x] fix(grpo): hanzoai 2.1.2 cannot be imported on Python 3.9 - #50

Merged
zeekay merged 1 commit into
hanzoai:release/2.1.xfrom
Naz-Tac:fix/py39-import-2.1.x
Sep 11, 2026
Merged

zeekay merged 1 commit into
hanzoai:release/2.1.xfrom
Naz-Tac:fix/py39-import-2.1.x

Conversation

@Naz-Tac

@Naz-Tac Naz-Tac commented Sep 11, 2026 •

Copy link
Copy Markdown

Important

This fix targets the 2.1.x line. Please don't merge it into main. On main, grpo no longer exists and requires-python is >=3.12. The diff is one commit on top of 07646380, the commit the 2.1.2 wheel was built from. To ship it, cut release/2.1.x at 07646380 and retarget this PR.

Repro

On Python 3.9.6:

python3 -m venv /tmp/hz39 && /tmp/hz39/bin/pip install hanzoai==2.1.2
/tmp/hz39/bin/python -c "import hanzoai"
  File ".../hanzoai/grpo/enhanced_api_model_adapter.py", line 117, in EnhancedAPIModelAdapter
    ) -> str | Tuple[str, Optional[str]]:
TypeError: unsupported operand type(s) for |: 'type' and '_GenericAlias'

Fix

  • Both grpo modules now have from __future__ import annotations, so 3.9 no longer evaluates their str | Tuple[...] return annotations. The second module, enhanced_semantic_extractor.py, has one such annotation.
  • A new CI job, import, runs pip install . and then tests/test_imports.py, which imports all 318 modules, on 3.9 and 3.13.
    • On 3.9.6, 07646380 gives 313 failed. This branch gives 318 passed, on both 3.9.6 and 3.13.15.
Why 3.9 users get 2.1.2

Every release from 2.2.0 on requires Python >=3.12, so pip on 3.9, 3.10 and 3.11 resolves to 2.1.2. Of those three, only 3.9 breaks.

A 2.1.3 that raised requires-python to >=3.10 would change nothing: pip on 3.9 would still pick 2.1.2. Only a fixed 2.1.3, or yanking 2.1.2, changes what 3.9 gets. Yanking alone would work too, since 2.0.2 imports on 3.9.6, but it sends 3.9 users back to a client from March 2025.

Why this branch starts at 07646380 and not v2.1.2

The PyPI wheel matches 07646380 ("chore: update hanzoai SDK and tests", 2026-01-21) on all 321 files. Tag v2.1.2 (e57aa3f8, 2025-12-09) doesn't: 266 files differ from the wheel, and 49 exist only in the wheel.

Sibling sweep

An AST scan of the whole wheel finds PEP 604 unions that 3.9 evaluates only in these two modules. Every other module that writes X | Y in annotations already has the future import, and no module uses a union outside an annotation (type aliases, isinstance).

PEP 585 generics such as tuple[...] already work on 3.9. Both modules use plain dataclasses, which never evaluate their annotations, so behavior is unchanged.

Test runs
Check 07646380 This branch
tests/test_imports.py on 3.9.6 313 failed, 5 passed¹ 318 passed
pytest tests on 3.12 3137 passed, 2 failed, 6 skipped, 8 errors 3455 passed, 2 failed, 6 skipped, 8 errors
pytest tests on 3.9.6 conftest.py can't import hanzoai 3455 passed, 2 failed, 6 skipped, 8 errors

¹ Those 5 pass only because the failed import hanzoai left their submodules in sys.modules.

The 2 failures and 8 errors appear on both sides and come from outside hanzoai:

  • Tests for the hanzo CLI package need click, which is not a hanzoai dependency.
  • test_workflow.py runs a python command, and this machine has no python on PATH.

I ran pytest directly, because rye isn't installed here, so neither scripts/test nor the nox pydantic-v1 session ran.

ruff 0.14.8 finds nothing in the changed files.

After retargeting, add release/2.1.x to ci.yml's pull_request.branches, or CI won't run on this PR.

🤖 Generated with Claude Code

hanzoai 2.1.2 declares requires-python >= 3.9, yet `import hanzoai` raises
TypeError on 3.9. grpo/enhanced_api_model_adapter.py annotates four returns
as `str | Tuple[str, Optional[str]]`, which 3.9 evaluates at class-body
time, and hanzoai/__init__.py imports grpo eagerly.
grpo/enhanced_semantic_extractor.py has the same shape once, behind it.

Postpone annotation evaluation in both modules. They are the only two in
the package whose PEP 604 unions reach the interpreter. Their dataclasses
never resolve annotations, so nothing else changes.

tests/test_imports.py imports every module under hanzoai. A new CI job
runs it after `pip install .` on 3.9 and 3.13, because the test job only
runs .python-version (3.12), where the annotation is valid.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zeekay
zeekay changed the base branch from main to release/2.1.x September 11, 2026 04:13
@zeekay
zeekay merged commit 3b9693b into hanzoai:release/2.1.x Sep 11, 2026
1 check passed
zeekay added a commit that referenced this pull request Sep 11, 2026
2.1.2 declares requires-python >= 3.9 and `import hanzoai` raises TypeError
there, so the whole line is unusable on the floor it publishes. The fix has
been on this branch since #50 and reaches nobody until a number above 2.1.2
carries it.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
@zeekay

zeekay commented Sep 11, 2026

Copy link
Copy Markdown
Member

Thanks @Naz-Tac, released as hanzoai 2.1.3. import hanzoai works on Python 3.9 again.

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.

2 participants