Skip to content

feat: enforce keys in sources Base class#231

Open
nikbpetrov wants to merge 1 commit into
forecastingresearch:mainfrom
nikbpetrov:metadata-keys
Open

feat: enforce keys in sources Base class#231
nikbpetrov wants to merge 1 commit into
forecastingresearch:mainfrom
nikbpetrov:metadata-keys

Conversation

@nikbpetrov

@nikbpetrov nikbpetrov commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

while _allow_missing_metadata might seem a tad awkward, it actually helps preserve both tests as well as the ability to validate that a source's name value (e.g. in fred.py) actually exists in _metadata.py

Solves #205

Full pipeline test via the harness - all jobs deploy, run and achieve expected parity.

Summary by CodeRabbit

  • Bug Fixes
    • Improved fail-fast validation for source metadata requirements, now enforcing nullified_questions plus any source-specific required keys at class definition time.
    • Added controls for handling missing metadata more explicitly, reducing the chance of incomplete source configuration.
    • Standardized exported metadata for several sources to consistently include nullified_questions.
  • Tests
    • Expanded unit tests to cover missing global metadata entries, missing nullified_questions, missing source-specific required keys, and successful population when requirements are met.
    • Updated test stubs to explicitly opt out of strict enforcement where appropriate.

@houtanb

houtanb commented Jul 8, 2026

Copy link
Copy Markdown
Member

@CodeRabbit full review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

BaseSource now requires nullified_questions and subclass-specific metadata keys during class creation. Source metadata entries were updated, FredSource dropped its explicit nullified_questions attribute, YfinanceSource declared a required key, and tests now cover missing-metadata and successful-population cases.

Changes

Base source metadata validation

Layer / File(s) Summary
BaseSource metadata contract and validation
src/sources/_base.py
nullified_questions no longer has a default; new required_metadata_keys and _allow_missing_metadata class attributes control __init_subclass__ validation, which now requires nullified_questions and unions subclass required keys, raising TypeError on missing entries.
Source metadata and subclass updates
src/sources/_metadata.py, src/sources/fred.py, src/sources/yfinance.py
nullified_questions: [] was added to acled, dbnomics, infer, manifold, and metaculus metadata; FredSource drops its explicit nullified_questions attribute; YfinanceSource declares required_metadata_keys = {"ticker_renames"}.
Test coverage for validation logic
src/tests/test_base_source.py
Stub classes set _allow_missing_metadata and nullified_questions; TestInitSubclass uses monkeypatch to assert TypeError for missing metadata or keys and to verify successful attribute population.

Sequence Diagram(s)

sequenceDiagram
  participant Subclass as Source subclass
  participant BaseSource as BaseSource.__init_subclass__
  participant SOURCE_METADATA as SOURCE_METADATA

  Subclass->>BaseSource: define subclass
  BaseSource->>SOURCE_METADATA: look up SOURCE_METADATA[cls.name]
  SOURCE_METADATA-->>BaseSource: metadata entry
  BaseSource->>BaseSource: validate core keys + required_metadata_keys
  BaseSource->>Subclass: setattr populated metadata fields
Loading

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: enforcing required keys in the sources base class.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

while `_allow_missing_metadata` might seem a tad awkward, it actually helps preserve both tests as well as the ability to validate that a source's name value (e.g. in `fred.py`) actually exists in `_metadata.py`

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/tests/test_base_source.py (1)

132-154: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mutable default class attributes flagged by Ruff (RUF012).

nullified_questions = [] and required_metadata_keys = {"ticker_renames"} are unannotated mutable class attribute defaults. Ruff's rule notes this can be resolved by explicitly annotating the variable with typing.ClassVar to indicate that it is intended to be shared across all instances, or using an immutable data type (e.g. a tuple instead of a list) for the default value. Since these test stubs won't be instantiated multiple times, the risk is negligible, but annotating (or using frozenset/tuple) would silence the lint warning consistently with how the base class likely declares these as ClassVar.

🧹 Example fix using ClassVar annotations
-            class _BadSpecific(BaseSource):
-                name = "fake_missing_specific"
-                source_type = SourceType.DATASET
-                required_metadata_keys = {"ticker_renames"}
+            class _BadSpecific(BaseSource):
+                name = "fake_missing_specific"
+                source_type = SourceType.DATASET
+                required_metadata_keys: ClassVar[set[str]] = {"ticker_renames"}

Also applies to: 155-180

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/test_base_source.py` around lines 132 - 154, The test stub in
test_missing_source_specific_required_key_raises uses mutable class defaults
that Ruff flags as RUF012. Update the inline _BadSpecific class inside the test
to make the shared intent explicit by annotating nullified_questions and
required_metadata_keys as ClassVar, or switch the defaults to immutable types
like tuple/frozenset. Keep the existing BaseSource and SOURCE_METADATA setup
unchanged; only adjust the class attribute declarations in the test stubs that
mirror BaseSource metadata.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/tests/test_base_source.py`:
- Around line 132-154: The test stub in
test_missing_source_specific_required_key_raises uses mutable class defaults
that Ruff flags as RUF012. Update the inline _BadSpecific class inside the test
to make the shared intent explicit by annotating nullified_questions and
required_metadata_keys as ClassVar, or switch the defaults to immutable types
like tuple/frozenset. Keep the existing BaseSource and SOURCE_METADATA setup
unchanged; only adjust the class attribute declarations in the test stubs that
mirror BaseSource metadata.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f37b6e3a-8da3-46fb-98a8-3edc896a050f

📥 Commits

Reviewing files that changed from the base of the PR and between bc49138 and c3a7f88.

📒 Files selected for processing (5)
  • src/sources/_base.py
  • src/sources/_metadata.py
  • src/sources/fred.py
  • src/sources/yfinance.py
  • src/tests/test_base_source.py
💤 Files with no reviewable changes (1)
  • src/sources/fred.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/sources/yfinance.py
  • src/sources/_metadata.py
  • src/sources/_base.py

@houtanb houtanb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

small rename for clarity.

Also, commit message could be more clear; it looks like it was truncated relative to the PR title which is better

Comment thread src/sources/_base.py
# Source-specific metadata keys this source needs to even run, beyond the globally required set.
# Declared on the concrete subclass (e.g. yfinance -> {"ticker_renames"}) so a missing/typo'd key
# in SOURCE_METADATA fails loudly at class-definition time rather than late at runtime.
required_metadata_keys: ClassVar[set[str]] = set()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maybe call this additional_required_metadata_keys?

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