Light Mode

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 63e8268

Browse files
[ENH] Add test to validate execution of README Python examples (#713)
Adds a pytest that parses `README.md` and executes all python code blocks to ensure examples remain valid. Closes #669 - Uses regex to extract Python blocks - Executes them sequentially in a shared namespace (since examples are stateful) - Runs from repo root so relative paths like `tests/resources/...` resolve correctly - Adds repo root to `sys.path` so `pypfopt` can be imported without installation This verifies that README examples run as documented and helps prevent regressions in imports or usage.
1 parent b63f293 commit 63e8268

File tree

3 files changed

+42
-3
lines changed
  • tests
    • test_discrete_allocation.py
    • test_efficient_frontier.py
    • test_readme_examples.py

3 files changed

+42
-3
lines changed

tests/test_discrete_allocation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
from cvxpy.error import SolverError
12
import numpy as np
23
import pandas as pd
34
import pytest
4-
from cvxpy.error import SolverError
55

66
from pypfopt.discrete_allocation import DiscreteAllocation, get_latest_prices
77
from tests.utilities_for_tests import get_data, setup_efficient_frontier

tests/test_efficient_frontier.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ def test_min_vol_pair_constraint():
608608
ef.min_volatility()
609609
old_sum = ef.weights[:2].sum()
610610
ef = setup_efficient_frontier()
611-
ef.add_constraint(lambda w: (w[1] + w[0] <= old_sum / 2))
611+
ef.add_constraint(lambda w: w[1] + w[0] <= old_sum / 2)
612612
ef.min_volatility()
613613
new_sum = ef.weights[:2].sum()
614614
assert new_sum <= old_sum / 2 + 1e-4
@@ -620,7 +620,7 @@ def test_max_sharpe_pair_constraint():
620620
old_sum = ef.weights[:2].sum()
621621

622622
ef = setup_efficient_frontier()
623-
ef.add_constraint(lambda w: (w[1] + w[0] <= old_sum / 2))
623+
ef.add_constraint(lambda w: w[1] + w[0] <= old_sum / 2)
624624
ef.max_sharpe(risk_free_rate=0.02)
625625
new_sum = ef.weights[:2].sum()
626626
assert new_sum <= old_sum / 2 + 1e-4

tests/test_readme_examples.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import os
2+
from pathlib import Path
3+
import re
4+
import sys
5+
6+
README_PATH = Path(__file__).resolve().parent.parent / "README.md"
7+
8+
9+
def _extract_python_blocks(markdown_text: str) -> list[str]:
10+
pattern = re.compile(r"```python[^\n]*\n(.*?)```", re.DOTALL)
11+
return [block.strip() for block in pattern.findall(markdown_text)]
12+
13+
14+
def test_readme_python_examples_run():
15+
readme_text = README_PATH.read_text(encoding="utf-8")
16+
python_blocks = _extract_python_blocks(readme_text)
17+
18+
assert python_blocks, "No python code blocks found in README.md"
19+
20+
repo_root = README_PATH.parent
21+
22+
# Make package importable
23+
sys.path.insert(0, str(repo_root))
24+
25+
# Run from repo root so relative paths work
26+
old_cwd = os.getcwd()
27+
os.chdir(repo_root)
28+
29+
globals_dict = {"__name__": "__main__"}
30+
31+
try:
32+
for idx, block in enumerate(python_blocks, start=1):
33+
exec(block, globals_dict)
34+
35+
except Exception as e:
36+
raise AssertionError(f"README python block #{idx} failed:\n{block}") from e
37+
38+
finally:
39+
os.chdir(old_cwd)

0 commit comments

Comments
(0)