From 4aa6d3e5c13fb29d8aedc006391069486bb86d1a Mon Sep 17 00:00:00 2001 From: Mayank Lavania Date: Mon, 13 Jul 2026 21:28:19 +0530 Subject: [PATCH] fix(py39): restore Python 3.9 import; make talib numpy optional The v0.7.0 release wheel-smoke failed on CPython 3.9: quantwave/__init__.py used a runtime PEP 604 annotation (`str | None` in _PolarsOnlySurface.__init__) without `from __future__ import annotations`, so `import quantwave` raised `TypeError: unsupported operand type(s) for |` on 3.9 (PEP 604 is 3.10+). Add the future import so all annotations are lazy strings. Also make numpy lazy in quantwave.talib (imported inside the wrappers, not at module top), so `import quantwave` and talib.list_functions() work without numpy; calling a talib function still requires numpy (classic array API). Verified on Python 3.9.19: wheel_smoke_test passes without numpy; talib.RSI works with numpy; talib.list_functions()=161. Co-Authored-By: Claude Opus 4.8 --- quantwave-py/python/quantwave/__init__.py | 2 ++ quantwave-py/python/quantwave/talib.py | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/quantwave-py/python/quantwave/__init__.py b/quantwave-py/python/quantwave/__init__.py index 40663c43e..1d0c7948f 100644 --- a/quantwave-py/python/quantwave/__init__.py +++ b/quantwave-py/python/quantwave/__init__.py @@ -33,6 +33,8 @@ ``StreamingError``, and ``InternalError`` (native FFI). See ``quantwave._errors``. """ +from __future__ import annotations # PEP 604 (str | None) annotations on Python 3.9 + from typing import List, Dict, Any import warnings diff --git a/quantwave-py/python/quantwave/talib.py b/quantwave-py/python/quantwave/talib.py index b98a1d16f..53d2a7704 100644 --- a/quantwave-py/python/quantwave/talib.py +++ b/quantwave-py/python/quantwave/talib.py @@ -25,10 +25,12 @@ import inspect from typing import Any, Callable, Dict, List, Optional -import numpy as np - from quantwave._talib_map_generated import TALIB_SLUG_TO_NAME as _TALIB_MAP +# numpy is imported lazily (inside the wrappers) so that `import quantwave` — and the +# rest of quantwave.talib's discovery API (list_functions) — work without numpy +# installed. Calling a talib function does require numpy (classic array in/out). + # Canonical TA-Lib price-input order. Multi-input functions receive their arrays in # this order (subset), matching classic talib (e.g. ATR(high, low, close)). _OHLCV = ["open", "high", "low", "close", "volume"] @@ -148,6 +150,8 @@ def _make_wrapper(talib_name: str, method_name: str) -> Callable[..., Any]: spec = _Signature(method_name) def wrapper(*arrays, **kwargs): + import numpy as np # lazy: classic talib API is numpy-based + if len(arrays) != len(spec.input_roles): raise TypeError( f"{talib_name} expects {len(spec.input_roles)} input array(s) "