Skip to content

refactor: split data.py god-module and decompose B(10) blocks (#871) - #894

Merged
tschm merged 1 commit into
mainfrom
fix/871-split-data-py
Jul 18, 2026
Merged

refactor: split data.py god-module and decompose B(10) blocks (#871)#894
tschm merged 1 commit into
mainfrom
fix/871-split-data-py

Conversation

@tschm

@tschm tschm commented Jul 18, 2026

Copy link
Copy Markdown
Member

Summary

src/jquantstats/data.py was the largest module in src/ (808 LOC) and held the worst-ranking complexity blocks. This splits it into focused modules and removes the complexity hotspots.

Changes

  • New src/jquantstats/_utils/_construction.py — the frame-construction/interpolation free functions move here: _to_polars, _apply_null_strategy, interpolate, _subtract_risk_free, _require_date_col, _align_returns_benchmark, plus a new _prices_to_returns. data.py re-imports them, so jquantstats.data._subtract_risk_free and from jquantstats import interpolate keep working.
  • New src/jquantstats/_data_reshape.py — the "derive a new Data" methods (resample, copy, head, tail, truncate, _truncate_*) move into _ReshapeMixin, which Data inherits. The mixin carries empty __slots__ (frozen slotted dataclass unchanged) and rebuilds via a lazily-imported Data to avoid the accessor import cycle.
  • Complexity decomposition:
    • _apply_null_strategy B(10) → B(5) (extracted _value_columns / _columns_with_nulls)
    • Data.from_prices B(10) → A(2) (extracted _prices_to_returns)
    • _truncate_integer B(8) → A (extracted _resolve_row_bound, which also restores mypy int-narrowing)

Done-when (issue #871)

  • data.py < 500 LOC — 808 → 453
  • No block in data.py exceeds CC B(7) — worst is now B(6) (__post_init__, from_returns)
  • make test green at 100% coverage (1215 passed)
  • make typecheck green (ty + mypy strict)
  • make docs-coverage green at 100%

Compatibility

No public API change. Data's methods and the interpolate export are unchanged; jquantstats.data._subtract_risk_free (used by a test) still resolves. Behaviour is preserved (all existing tests pass unmodified).

Closes #871

🤖 Generated with Claude Code

data.py was the largest module in src/ (808 LOC) with the worst-ranking
complexity blocks. This splits it and removes those hotspots:

- Move the frame-construction/interpolation free functions into a new
  src/jquantstats/_utils/_construction.py (_to_polars, _apply_null_strategy,
  interpolate, _subtract_risk_free, _require_date_col, _align_returns_benchmark,
  plus a new _prices_to_returns). data.py re-imports them, so
  `jquantstats.data._subtract_risk_free` and `from jquantstats import interpolate`
  keep working.
- Move the "derive a new Data" methods (resample, copy, head, tail, truncate and
  the _truncate_* helpers) into src/jquantstats/_data_reshape.py as _ReshapeMixin,
  which Data now inherits. The mixin carries empty __slots__ so the frozen
  slotted dataclass is unchanged; it rebuilds via a lazily-imported Data to
  avoid the accessor import cycle.
- Decompose the B(10)/B(8) blocks: _apply_null_strategy -> B(5) (extracted
  _value_columns/_columns_with_nulls), Data.from_prices -> A(2) (extracted
  _prices_to_returns), _truncate_integer -> A (extracted _resolve_row_bound).

Result: data.py drops 808 -> 453 LOC; no block in data.py exceeds B(6).
make test (100% coverage), typecheck (ty + mypy strict) and docs-coverage
(100%) all stay green.

Closes #871

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 18, 2026 17:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the Data entry point by splitting the former src/jquantstats/data.py “god-module” into smaller, focused modules while preserving the existing public API surface (Data methods and interpolate import path).

Changes:

  • Moved frame construction / coercion / interpolation helpers into src/jquantstats/_utils/_construction.py and re-imported them from data.py for compatibility.
  • Moved “derive a new Data” operations (resample/copy/head/tail/truncate) into a new _ReshapeMixin in src/jquantstats/_data_reshape.py, then made Data inherit the mixin.
  • Decomposed price→returns logic into _prices_to_returns and simplified Data.from_prices.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/jquantstats/data.py Rewires imports to the new helper/mixin modules; Data now inherits _ReshapeMixin; from_prices delegates to _prices_to_returns.
src/jquantstats/_utils/_construction.py New helper module containing Polars coercion, null strategy handling, interpolation, benchmark alignment, risk-free subtraction, and price→returns conversion.
src/jquantstats/_data_reshape.py New _ReshapeMixin hosting Data reshaping/rebuilding operations and integer/temporal truncation logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +146 to +153
When the index is integer-based, row slicing is used instead, and
``start`` and ``end`` must be non-negative integers. Passing
non-integer bounds to an integer-indexed Data raises `TypeError`.

Args:
start: Optional lower bound (inclusive). A date/datetime value
when the index is temporal; a non-negative `int` row
index when the data has no temporal index.
@tschm
tschm merged commit cba9340 into main Jul 18, 2026
63 checks passed
@tschm
tschm deleted the fix/871-split-data-py branch July 18, 2026 18:01
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.

Refactor data.py: split the 808-LOC module and decompose its B(10) blocks

2 participants