mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-28 09:47:43 +00:00
6f0a339c9b
- Sar.Quantower.Tests.cs: add missing opening quote on string literal (line 48) - Exports.cs: rename Correlation.Batch → Correl.Batch (CS0103) - Ad.Validation.Tests.cs: fix Ooples OutputValues key "Ad" → "Adl"
118 lines
4.0 KiB
Python
118 lines
4.0 KiB
Python
"""test_signatures.py — Verify function signatures have no reserved keywords or duplicates."""
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import inspect
|
|
import keyword
|
|
import pytest
|
|
|
|
# All category modules with public indicator functions
|
|
CATEGORY_MODULES = [
|
|
"quantalib.channels",
|
|
"quantalib.core",
|
|
"quantalib.cycles",
|
|
"quantalib.dynamics",
|
|
"quantalib.errors",
|
|
"quantalib.filters",
|
|
"quantalib.momentum",
|
|
"quantalib.numerics",
|
|
"quantalib.oscillators",
|
|
"quantalib.reversals",
|
|
"quantalib.statistics",
|
|
"quantalib.trends_fir",
|
|
"quantalib.trends_iir",
|
|
"quantalib.volatility",
|
|
"quantalib.volume",
|
|
]
|
|
|
|
|
|
def _get_public_functions():
|
|
"""Yield (module_name, func_name, func) for all public functions."""
|
|
for modname in CATEGORY_MODULES:
|
|
mod = importlib.import_module(modname)
|
|
all_names = getattr(mod, "__all__", [])
|
|
for name in all_names:
|
|
fn = getattr(mod, name, None)
|
|
if fn is not None and callable(fn):
|
|
yield modname, name, fn
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def all_functions():
|
|
return list(_get_public_functions())
|
|
|
|
|
|
class TestNoReservedKeywords:
|
|
"""No function parameter should use a Python reserved keyword."""
|
|
|
|
def test_no_reserved_keyword_params(self, all_functions) -> None:
|
|
violations = []
|
|
for modname, fname, fn in all_functions:
|
|
sig = inspect.signature(fn)
|
|
for pname in sig.parameters:
|
|
if keyword.iskeyword(pname):
|
|
violations.append(f"{modname}.{fname}(... {pname} ...)")
|
|
assert violations == [], (
|
|
f"Reserved keyword used as parameter name:\n"
|
|
+ "\n".join(f" - {v}" for v in violations)
|
|
)
|
|
|
|
|
|
class TestNoDuplicateParams:
|
|
"""No function should have duplicate parameter names (caught at parse time,
|
|
but this validates post-fix)."""
|
|
|
|
def test_no_duplicate_params(self, all_functions) -> None:
|
|
violations = []
|
|
for modname, fname, fn in all_functions:
|
|
sig = inspect.signature(fn)
|
|
params = list(sig.parameters.keys())
|
|
if len(params) != len(set(params)):
|
|
seen = set()
|
|
dupes = [p for p in params if p in seen or seen.add(p)] # type: ignore[func-returns-value]
|
|
violations.append(f"{modname}.{fname}: duplicates={dupes}")
|
|
assert violations == [], (
|
|
f"Duplicate parameter names found:\n"
|
|
+ "\n".join(f" - {v}" for v in violations)
|
|
)
|
|
|
|
|
|
class TestNoBuiltinShadowing:
|
|
"""Public function names should not shadow critical Python builtins."""
|
|
|
|
CRITICAL_BUILTINS = {"super", "type", "id", "input", "print", "open", "list", "dict", "set", "map", "filter"}
|
|
|
|
def test_no_builtin_function_names(self, all_functions) -> None:
|
|
violations = []
|
|
for modname, fname, fn in all_functions:
|
|
if fname in self.CRITICAL_BUILTINS:
|
|
violations.append(f"{modname}.{fname}")
|
|
assert violations == [], (
|
|
f"Function names shadow Python builtins:\n"
|
|
+ "\n".join(f" - {v}" for v in violations)
|
|
)
|
|
|
|
|
|
class TestVolumeIndicatorsHaveVolumeParam:
|
|
"""Volume indicators that use _ptr(volume) must have volume in their signature."""
|
|
|
|
VOLUME_REQUIRED = [
|
|
"ad", "adosc", "iii", "kvo", "va", "vwad", "vwap", "wad",
|
|
"obv", "pvt", "pvr", "vf", "nvi", "pvi", "tvi", "pvd",
|
|
"vwma", "evwma", "efi", "aobv", "mfi", "cmf", "eom", "pvo",
|
|
]
|
|
|
|
def test_volume_funcs_have_volume_param(self) -> None:
|
|
import quantalib.volume as vol
|
|
violations = []
|
|
for fname in self.VOLUME_REQUIRED:
|
|
fn = getattr(vol, fname, None)
|
|
if fn is None:
|
|
continue
|
|
sig = inspect.signature(fn)
|
|
if "volume" not in sig.parameters:
|
|
violations.append(fname)
|
|
assert violations == [], (
|
|
f"Volume functions missing 'volume' parameter: {violations}"
|
|
)
|