mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
fix(grpo): import on Python 3.9, the floor 2.1.2 declares (#50)
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 <[email protected]>
This commit is contained in:
committed by
GitHub
co-authored by
Claude Opus 5
parent
07646380eb
commit
53516065bf
@@ -50,3 +50,27 @@ jobs:
|
||||
|
||||
- name: Run tests
|
||||
run: ./scripts/test
|
||||
|
||||
# 2.1.2 declared requires-python >= 3.9 and could not be imported on 3.9, and
|
||||
# the test job above only ever runs .python-version (3.12). Install the package
|
||||
# the way a user does, on the floor and on the newest Python it classifies.
|
||||
import:
|
||||
name: import (Python ${{ matrix.python-version }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ['3.9', '3.13']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install the package
|
||||
run: python -m pip install . pytest pytest-asyncio respx
|
||||
|
||||
- name: Every module imports
|
||||
run: python -m pytest tests/test_imports.py -q
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import time
|
||||
import asyncio
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import copy
|
||||
import json
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Every module in ``hanzoai`` imports on every Python that requires-python admits.
|
||||
|
||||
2.1.2 declared ``requires-python >= 3.9`` and could not be imported on 3.9 at all:
|
||||
``grpo/enhanced_api_model_adapter.py`` annotated a return as ``str | Tuple[...]``,
|
||||
which 3.9 evaluates at class-body time and rejects with ``TypeError``, and
|
||||
``hanzoai/__init__.py`` imports ``grpo`` eagerly. The test job runs only
|
||||
``.python-version`` (3.12), where that line is valid, so nothing could see it.
|
||||
|
||||
The module list comes from the filesystem rather than from importing the package,
|
||||
so a package that fails to import shows up as a failing module instead of an
|
||||
empty list.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def _module_names() -> list[str]:
|
||||
spec = importlib.util.find_spec("hanzoai")
|
||||
assert spec is not None and spec.submodule_search_locations
|
||||
root = Path(next(iter(spec.submodule_search_locations)))
|
||||
names = []
|
||||
for path in sorted(root.rglob("*.py")):
|
||||
parts = path.relative_to(root.parent).with_suffix("").parts
|
||||
if parts[-1] == "__init__":
|
||||
parts = parts[:-1]
|
||||
names.append(".".join(parts))
|
||||
return names
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", _module_names())
|
||||
def test_module_imports(name: str) -> None:
|
||||
try:
|
||||
importlib.import_module(name)
|
||||
except ModuleNotFoundError as exc:
|
||||
# An optional dependency that is not installed says nothing about this
|
||||
# interpreter; a missing hanzoai module does.
|
||||
if exc.name and exc.name.split(".")[0] != "hanzoai":
|
||||
pytest.skip(f"optional dependency {exc.name} is not installed")
|
||||
raise
|
||||
Reference in New Issue
Block a user