Skip to content

fix(helpers): fix type annotation bug: np.arraynp.ndarray in helpers.py (TypeError on Python 3.11+) - #95

Open
nervgh wants to merge 3 commits into
masterfrom
fix/type-anno-bug
Open

fix(helpers): fix type annotation bug: np.arraynp.ndarray in helpers.py (TypeError on Python 3.11+)#95
nervgh wants to merge 3 commits into
masterfrom
fix/type-anno-bug

Conversation

@nervgh

@nervgh nervgh commented Jul 26, 2026

Copy link
Copy Markdown

Fix type annotation bug: np.arraynp.ndarray in helpers.py (TypeError on Python 3.11+)

Description

Two methods in okama/common/helpers/helpers.py use np.array (a function) in PEP 604 union type hints instead of np.ndarray (the actual type). On Python 3.11+ these annotations are evaluated eagerly at import time and crash with TypeError.

Background

In okama/common/helpers/helpers.py, class Frame has two methods annotated with list | np.array:

def get_portfolio_mean_return(cls, weights: list | np.array, ror: pd.DataFrame) -> float:
def get_portfolio_risk(cls, weights: list | np.array, assets_ror: pd.DataFrame) -> float:

np.array is a builtin_function_or_method, not a type. PEP 604's | operator requires both operands to be types. When Python evaluates list | np.array it calls list.__or__(np.array), which receives a non-type and raises:

TypeError: unsupported operand type(s) for |: 'type' and 'builtin_function_or_method'

The bug was introduced in v1.3.0 (May 2023) during a refactoring into common/helpers/. At that time the code used typing.Union[list, np.array]Union accepts any object and does not validate that it's a type, so the bug remained latent. When the codebase migrated to PEP 604 syntax in v1.5.0 (Python 3.10+) and then v2.0.0 (Python 3.11+), the bug became fatal — list | np.array is evaluated eagerly and crashes.

The bug has persisted unreleased through 11 releases up to the current v2.3.0. It went unnoticed because no other module in okama directly triggers import of these Frame methods. It manifests when external code (e.g. okama-mcp server) calls get_portfolio_mean_return or get_portfolio_risk.

Scope

  • Fix two type annotations in okama/common/helpers/helpers.py, replacing np.array with np.ndarray:
    • get_portfolio_mean_return (line 258)
    • get_portfolio_risk (line 386)
  • The function bodies are correct as-is — np.array() calls inside the body are valid runtime invocations and must not be changed.

Related


Patch applied

File: okama/okama/common/helpers/helpers.py

Method Before (bug) After (fix)
get_portfolio_mean_return weights: list | np.array weights: list | np.ndarray
get_portfolio_risk weights: list | np.array weights: list | np.ndarray

Validation:

  • Ruff — All checks passed!
  • Full test suite could not be run (no Poetry environment available), but the change is purely mechanical — swapping a function reference for a type reference in two type annotations, with zero logic changes.

Summary by Sourcery

Enhancements:

  • Update Frame.get_portfolio_mean_return and Frame.get_portfolio_risk signatures to use np.ndarray instead of the np.array factory in union type hints.

@nervgh

nervgh commented Jul 26, 2026

Copy link
Copy Markdown
Author

I came from okama-mcp with the error.

# python --version # Python 3.12.3

# uvx okama-mcp stdio # <-- my command

Installed 91 packages in 84ms
Traceback (most recent call last):
  File "/tmp/.tmpshlVOC/archive-v0/dgrAM591ZcXkBhCg/bin/okama-mcp", line 12, in <module>
    sys.exit(main())
             ^^^^^^
  File "/tmp/.tmpshlVOC/archive-v0/dgrAM591ZcXkBhCg/lib/python3.12/site-packages/okama_mcp/transport.py", line 57, in main
    from okama_mcp.server import mcp
  File "/tmp/.tmpshlVOC/archive-v0/dgrAM591ZcXkBhCg/lib/python3.12/site-packages/okama_mcp/server.py", line 37, in <module>
    register_all(mcp)
  File "/tmp/.tmpshlVOC/archive-v0/dgrAM591ZcXkBhCg/lib/python3.12/site-packages/okama_mcp/tools/__init__.py", line 14, in register_all
    from okama_mcp.tools import (
  File "/tmp/.tmpshlVOC/archive-v0/dgrAM591ZcXkBhCg/lib/python3.12/site-packages/okama_mcp/tools/asset.py", line 13, in <module>
    import okama as ok
  File "/tmp/.tmpshlVOC/archive-v0/dgrAM591ZcXkBhCg/lib/python3.12/site-packages/okama/__init__.py", line 27, in <module>
    from okama.asset_list import AssetList  # noqa: F401
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.tmpshlVOC/archive-v0/dgrAM591ZcXkBhCg/lib/python3.12/site-packages/okama/asset_list.py", line 7, in <module>
    from okama.common.helpers.helpers import check_rolling_window
  File "/tmp/.tmpshlVOC/archive-v0/dgrAM591ZcXkBhCg/lib/python3.12/site-packages/okama/common/helpers/helpers.py", line 225, in <module>
    class Frame:
  File "/tmp/.tmpshlVOC/archive-v0/dgrAM591ZcXkBhCg/lib/python3.12/site-packages/okama/common/helpers/helpers.py", line 258, in Frame
    def get_portfolio_mean_return(cls, weights: list | np.array, ror: pd.DataFrame) -> float:
                                                ~~~~~^~~~~~~~~~
TypeError: unsupported operand type(s) for |: 'type' and 'builtin_function_or_method'

A quick reproduction command with Docker:

docker run --rm astral/uv:python3.12-bookworm-slim bash -c 'uvx okama-mcp stdio'

@nervgh
nervgh marked this pull request as ready for review July 26, 2026 14:23
@nervgh
nervgh requested a review from chilango74 July 26, 2026 14:24

@chilango74 chilango74 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The code fix is correct: I reproduced the import-time TypeError on the current master with Python 3.11, verified that this branch imports successfully and resolves both weight annotations as list | np.ndarray, and ran the full suite (405 passed, 3 skipped) plus Ruff (All checks passed!).

One blocking item remains: CONTRIBUTING.md requires every contribution to be accompanied by unit tests, but this PR changes production code without a regression test. Please add a focused test, preferably in tests/test_helpers.py, that evaluates the weight annotations of both Frame.get_portfolio_mean_return and Frame.get_portfolio_risk (for example via typing.get_type_hints) and verifies list | np.ndarray. Such a test fails on the current master even under Python 3.14, where annotation evaluation is deferred, and protects this exact regression.

@chilango74

Copy link
Copy Markdown
Collaborator

@nervgh, thank you for tracing this back to okama-mcp and providing a precise reproduction.

I reproduced the failure on the current master with Python 3.11 and confirmed that your change fixes the import and both annotations. I also ran the full test suite (405 passed, 3 skipped) and Ruff (All checks passed!).

Before merging, please add a focused regression test as required by CONTRIBUTING.md. A parameterized test in tests/test_helpers.py that calls typing.get_type_hints for Frame.get_portfolio_mean_return and Frame.get_portfolio_risk and verifies the weights annotation is list | np.ndarray would cover the exact bug, including on Python 3.14 where annotations are evaluated lazily.

Once the test is pushed, please ping me here and I will re-review and merge the PR.

GH #95: ensure `weights` parameter annotations in
`Frame.get_portfolio_mean_return` and `Frame.get_portfolio_risk`
resolve to `list | np.ndarray` instead of the non-type `np.array`.
@nervgh

nervgh commented Aug 2, 2026

Copy link
Copy Markdown
Author

@chilango74 thank you for your feedback. I've made requested changes.

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