Skip to content

refactor(coordinator): extract _apply_options_to_state#170

Merged
Artic0din merged 3 commits into
devfrom
refactor/constitution-02-rebuild-engine-systemic
May 27, 2026
Merged

refactor(coordinator): extract _apply_options_to_state#170
Artic0din merged 3 commits into
devfrom
refactor/constitution-02-rebuild-engine-systemic

Conversation

@Artic0din

Copy link
Copy Markdown
Owner

Summary

Constitution P14 systemic fix — __init__ and rebuild_engine no longer duplicate provider/mode setup.

The two paths previously hand-rolled the same logic across ~175 lines each:
grid-sensor resolution (options→data fallback), the DWT vs CDR current-plan slot,
every comparator's three-state pricing-mode resolution (Amber / Flow Power / LocalVolts),
the named-comparator wiring, and the providers-dict reset.
That duplication had already drifted twice in production
(retro-review #150 surfaced the grid-sensor read; #153 caught a duplicate assignment
that would have silently negated the data-fallback).

Per Constitution P14:

If the same issue appears in multiple places, fix the underlying abstraction, shared utility, model, service, or architecture.
Do not copy the same fix into several files unless that is the correct architectural choice.

Both call sites now flow through a single _apply_options_to_state(options, data, *, strict) projector.
strict=True (init-time) raises ConfigEntryNotReady on missing required config
(cdr_plan, static-plan envelopes for non-off static_prd modes, inconsistent DWT markers per AC-10c).
strict=False (rebuild-time) degrades gracefully — logs and keeps the existing provider
on missing cdr_plan; drops a comparator to off on missing static plan.
Both semantic behaviours match the pre-refactor implementation exactly.

_build_dwt_provider now accepts (options, data) mappings instead of a ConfigEntry,
so the same builder serves both paths.

Constitution alignment

  • P14 (Systemic fixes over local patches): the refactor's reason for existing.
  • P13 (No regression by design): strict vs non-strict modes preserve both paths' previous semantics.
  • P4 (DRY, modularity): ~315 lines of duplicated logic collapsed into one method (net -253 lines).
  • P17 (Tests are part of the fix): new parametrised equivalence test class covers four scenario fixtures + the strict/non-strict gate semantics + the fix(stack): grid sensor options-fallback + state from DWT region #150 grid-sensor regression case.

Test plan

  • uv run ruff check custom_components/pricehawk/coordinator.py tests/test_coordinator.py tests/test_review_improvements.py tests/conftest.py — clean on all touched files (5 pre-existing F541s in scripts/ are unrelated, baseline confirmed).
  • uv run pyright custom_components/pricehawk/coordinator.py — 2 errors (down from 4; remaining errors are the pre-existing CdrPlanProvider/Provider protocol mismatch, not introduced by this refactor).
  • uv run pytest --tb=short tests/test_coordinator.py tests/test_runtime_data.py tests/test_reconfigure.py — 51/51 passing (43 baseline + 8 new equivalence tests).
  • uv run pytest --tb=short --ignore=tests/test_cdr_evaluator.py — 1108/1108 full suite passing.
  • Live UAT on a DWT-only entry: options-flow edit of grid-sensor must take effect without HA restart.
  • Live UAT on a CDR entry: options-flow swap from static_prd to live_api on a comparator must rebuild that provider without tearing down the integration.

Deviations

  • tests/conftest.py gained a real _StubDataUpdateCoordinator base class so PriceHawkCoordinator resolves as an actual type for unit tests. Before, DataUpdateCoordinator was a _MockModule MagicMock and class PriceHawkCoordinator(DataUpdateCoordinator[...]) ended up as a MagicMock too — meaning the existing test_review_improvements.py::TestCoordinatorReset::test_monthly_reset_handles_all_providers was never actually exercising real coordinator behaviour. That test was updated to add a minimal cdr_plan envelope so the real-class construction path succeeds.
  • 2 pyright errors remain in coordinator.py (lines 481, 599) — pre-existing CdrPlanProvider (read-only @property id) vs Provider protocol (writable id: str attribute) variance issue. Out of scope for this refactor; would require either making the Protocol attributes read-only (broad refactor across all providers) or making CdrPlanProvider.id a writable attribute. Baseline pyright confirms these 2 errors existed before the refactor — the refactor reduced the count from 4 (because the duplicate logic in rebuild_engine raised the same errors twice).

🤖 Generated with Claude Code

Artic0din added a commit that referenced this pull request May 26, 2026
…+ add DWT equivalence cases

Linus PR #170 audit fixed two real defects in _apply_options_to_state:

1. Orphan-state bug. The projector cleared self._dwt_provider = None
   BEFORE the strict-mode guard decided whether to bail. A non-strict
   rebuild with neither cdr_plan nor a DWT enable flag would null
   _dwt_provider, then early-return, leaving _current_plan_provider
   pointing at the stale DWT instance — a half-rebuilt coordinator.
   Fix moves the reset INSIDE the CDR branch, past the early-return,
   so a bad rebuild keeps every provider slot intact.
   P13 no-regression: every prior call site that depended on the CDR
   branch nulling _dwt_provider still sees that behaviour, because the
   reset still happens — just not in the orphan window.

2. DWT branch untested. _EQUIVALENCE_CASES were all CDR — the DWT vs
   CDR branch of _build_dwt_provider had no init-vs-rebuild parity
   assertion. Added dwt_oe_enabled_options_only (OpenElectricity path,
   every setting in options) and dwt_aemo_enabled_data_fallback (AEMO
   Direct, every setting in data so the opt() fallback inside
   _build_dwt_provider is what makes the assertion pass).

3. New regression test test_nonstrict_rebuild_preserves_dwt_provider_on_bad_options
   pre-seeds a DWT provider, fires a bad rebuild, asserts all three
   slots survive. Confirmed to fail pre-fix.

Minor (Linus also flagged): FlowPowerProvider(dict(options)) and
LocalVoltsProvider(dict(options)) verified safe — both constructors
accept dict[str, Any] and dict(...) coerces any Mapping to a plain dict
without behaviour change.

Validation:
- uv run ruff check on changed files: passes (pre-existing scripts/
  failures unrelated)
- uv run pyright custom_components/pricehawk/coordinator.py: same 2
  pre-existing errors / 12 warnings — no new typing issues
- uv run pytest tests/test_coordinator.py tests/test_reconfigure.py
  tests/test_runtime_data.py: 54 passed

P13 no-regression + P17 tests are part of the fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Artic0din

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd01c2a017

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread custom_components/pricehawk/coordinator.py Outdated
Artic0din added a commit that referenced this pull request May 26, 2026
…t DWT options (codex follow-up)

Codex P2 follow-up on PR #170.
``_apply_options_to_state(strict=False)`` unconditionally called ``_build_dwt_provider``, which raises ``ConfigEntryNotReady`` (AC-10c) when an entry's ``current_provider`` marker says DWT but the matching enable/API-key fields are missing.
Pre-refactor ``rebuild_engine`` never built a DWT provider from scratch, so the same inconsistent options update would log and keep the existing providers — the systemic-fix refactor regressed that behaviour and now tears the integration down mid-edit.

Fix wraps the builder call in ``try/except ConfigEntryNotReady`` that only swallows the raise on the non-strict (rebuild) path — strict mode (``__init__``) still re-raises so HA surfaces the failure at initial setup.
On the rebuild path we log and bail BEFORE touching ``_dwt_provider`` / ``_current_plan_provider`` / ``_providers``, matching the existing missing-``cdr_plan`` early-return contract (P13 no-regression).

Tests: ``tests/test_reconfigure.py::TestRebuildGracefulDegradeOnInconsistentDwt`` pins the regression — non-strict rebuild does not raise, keeps existing providers, AND strict init still raises.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# so it is safe to clear the DWT slot before reassigning the
# current-plan provider.
self._dwt_provider = None
self._current_plan_provider = CdrPlanProvider(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚫 [mypy] reported by reviewdog 🐶
Incompatible types in assignment (expression has type "CdrPlanProvider", variable has type "Provider") [assignment]

# so it is safe to clear the DWT slot before reassigning the
# current-plan provider.
self._dwt_provider = None
self._current_plan_provider = CdrPlanProvider(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 [mypy] reported by reviewdog 🐶
Protocol member Provider.id expected settable variable, got read-only attribute

# so it is safe to clear the DWT slot before reassigning the
# current-plan provider.
self._dwt_provider = None
self._current_plan_provider = CdrPlanProvider(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 [mypy] reported by reviewdog 🐶
Protocol member Provider.name expected settable variable, got read-only attribute

dict(options)
)
if self._named_comparator is not None:
self._providers["named"] = self._named_comparator

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚫 [mypy] reported by reviewdog 🐶
Incompatible types in assignment (expression has type "CdrPlanProvider", target has type "Provider") [assignment]

dict(options)
)
if self._named_comparator is not None:
self._providers["named"] = self._named_comparator

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 [mypy] reported by reviewdog 🐶
Protocol member Provider.id expected settable variable, got read-only attribute

dict(options)
)
if self._named_comparator is not None:
self._providers["named"] = self._named_comparator

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 [mypy] reported by reviewdog 🐶
Protocol member Provider.name expected settable variable, got read-only attribute

new_options.get(CONF_GRID_POWER_SENSOR)
or self.config_entry.data.get(CONF_GRID_POWER_SENSOR, "")
self._apply_options_to_state(
new_options, self.config_entry.data, strict=False,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚫 [mypy] reported by reviewdog 🐶
Item "None" of "ConfigEntry[Any] | None" has no attribute "data" [union-attr]

@Artic0din

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Artic0din and others added 3 commits May 28, 2026 07:09
Both `__init__` (initial setup) and `rebuild_engine` (options-flow
reload) duplicated grid-sensor resolution, the DWT vs CDR current-plan
slot, every comparator's pricing-mode logic, and the named comparator.
That duplication had already drifted twice in production (#150 grid
sensor; #153 grid-sensor double-assign).

Constitution P14 — "If the same issue appears in multiple places, fix
the underlying abstraction." Both call sites now flow through one
projector. Strict mode (init-time) raises ConfigEntryNotReady on
missing required config; non-strict mode (rebuild-time) degrades
gracefully — preserves existing semantics on both paths.

`_build_dwt_provider` now accepts `(options, data)` mappings instead
of a `ConfigEntry` so the same builder serves both paths.

Tests: parametrised `TestApplyOptionsToStateEquivalence` asserts the
init-time and rebuild-time paths produce identical observable state
across four scenario fixtures plus strict/non-strict gate semantics
and the #150 grid-sensor regression case. `tests/conftest.py` now
stubs `DataUpdateCoordinator` with a real subclassable class so
`PriceHawkCoordinator` resolves as an actual type for unit testing
(was a `_MockModule` MagicMock).

Validation: ruff clean on touched files; pyright net -2 errors (2
pre-existing CdrPlanProvider protocol mismatches remain, was 4
duplicates before); pytest 1108/1108 passing including 8 new
equivalence tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+ add DWT equivalence cases

Linus PR #170 audit fixed two real defects in _apply_options_to_state:

1. Orphan-state bug. The projector cleared self._dwt_provider = None
   BEFORE the strict-mode guard decided whether to bail. A non-strict
   rebuild with neither cdr_plan nor a DWT enable flag would null
   _dwt_provider, then early-return, leaving _current_plan_provider
   pointing at the stale DWT instance — a half-rebuilt coordinator.
   Fix moves the reset INSIDE the CDR branch, past the early-return,
   so a bad rebuild keeps every provider slot intact.
   P13 no-regression: every prior call site that depended on the CDR
   branch nulling _dwt_provider still sees that behaviour, because the
   reset still happens — just not in the orphan window.

2. DWT branch untested. _EQUIVALENCE_CASES were all CDR — the DWT vs
   CDR branch of _build_dwt_provider had no init-vs-rebuild parity
   assertion. Added dwt_oe_enabled_options_only (OpenElectricity path,
   every setting in options) and dwt_aemo_enabled_data_fallback (AEMO
   Direct, every setting in data so the opt() fallback inside
   _build_dwt_provider is what makes the assertion pass).

3. New regression test test_nonstrict_rebuild_preserves_dwt_provider_on_bad_options
   pre-seeds a DWT provider, fires a bad rebuild, asserts all three
   slots survive. Confirmed to fail pre-fix.

Minor (Linus also flagged): FlowPowerProvider(dict(options)) and
LocalVoltsProvider(dict(options)) verified safe — both constructors
accept dict[str, Any] and dict(...) coerces any Mapping to a plain dict
without behaviour change.

Validation:
- uv run ruff check on changed files: passes (pre-existing scripts/
  failures unrelated)
- uv run pyright custom_components/pricehawk/coordinator.py: same 2
  pre-existing errors / 12 warnings — no new typing issues
- uv run pytest tests/test_coordinator.py tests/test_reconfigure.py
  tests/test_runtime_data.py: 54 passed

P13 no-regression + P17 tests are part of the fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t DWT options (codex follow-up)

Codex P2 follow-up on PR #170.
``_apply_options_to_state(strict=False)`` unconditionally called ``_build_dwt_provider``, which raises ``ConfigEntryNotReady`` (AC-10c) when an entry's ``current_provider`` marker says DWT but the matching enable/API-key fields are missing.
Pre-refactor ``rebuild_engine`` never built a DWT provider from scratch, so the same inconsistent options update would log and keep the existing providers — the systemic-fix refactor regressed that behaviour and now tears the integration down mid-edit.

Fix wraps the builder call in ``try/except ConfigEntryNotReady`` that only swallows the raise on the non-strict (rebuild) path — strict mode (``__init__``) still re-raises so HA surfaces the failure at initial setup.
On the rebuild path we log and bail BEFORE touching ``_dwt_provider`` / ``_current_plan_provider`` / ``_providers``, matching the existing missing-``cdr_plan`` early-return contract (P13 no-regression).

Tests: ``tests/test_reconfigure.py::TestRebuildGracefulDegradeOnInconsistentDwt`` pins the regression — non-strict rebuild does not raise, keeps existing providers, AND strict init still raises.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Artic0din Artic0din force-pushed the refactor/constitution-02-rebuild-engine-systemic branch from e2164b1 to da5de4c Compare May 27, 2026 21:10
@Artic0din Artic0din merged commit 08993ad into dev May 27, 2026
5 of 6 checks passed
@Artic0din Artic0din deleted the refactor/constitution-02-rebuild-engine-systemic branch May 27, 2026 21:12

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: da5de4c1c8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# same projector so grid-sensor resolution, current-plan slot
# (DWT/CDR), and comparator pricing-mode logic cannot drift.
self._dwt_provider: DynamicWholesaleTariffProvider | None = None
dwt_provider = self._build_dwt_provider(entry)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Badge Update the constructor call to the new DWT signature

This helper was changed to require options and data, but the init path still calls it with the old single ConfigEntry argument. Every PriceHawkCoordinator(hass, entry) construction reaches this line before the new projector call, so integration setup now raises TypeError: _build_dwt_provider() missing 1 required positional argument: 'data' before any provider state can be initialized.

Useful? React with 👍 / 👎.

Comment on lines 1081 to 1083
oe_enabled = bool(_resolve(entry, CONF_DWT_OE_ENABLED))
aemo_enabled = bool(_resolve(entry, CONF_DWT_AEMO_ENABLED))
current_provider = _resolve(entry, CONF_CURRENT_PROVIDER)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove the stale ConfigEntry reads from the DWT builder

After the signature change, _build_dwt_provider no longer has an entry variable in scope, but these remaining _resolve(entry, ...) reads run before the new opt() fallback values are used. Any rebuild path that calls _apply_options_to_state() reaches this helper and raises NameError before it can decide whether the entry is DWT or CDR, so options-flow rebuilds are broken even for normal CDR entries.

Useful? React with 👍 / 👎.

Comment on lines +2729 to +2731
self._apply_options_to_state(
new_options, self.config_entry.data, strict=False,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Move the projector before the stale rebuild branches

Appending the new projector at the end means it is skipped by the old early-return path above: for a DWT entry whose DWT markers live in entry.data rather than new_options (the new test fixture explicitly covers this data-fallback case), lines 2587-2635 see no DWT flag and no cdr_plan and return before this call. In that options-flow scenario, edits such as changing the grid sensor or DWT region are silently ignored instead of being handled by _apply_options_to_state.

Useful? React with 👍 / 👎.

Comment on lines +867 to +870
self._grid_power_entity = (
options.get(CONF_GRID_POWER_SENSOR)
or data.get(CONF_GRID_POWER_SENSOR, "")
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve explicit grid-sensor clears in the projector

The new projector uses truthiness instead of the repository's documented options-over-data lookup, so if the options flow supplies CONF_GRID_POWER_SENSOR as an empty value (for example after the user clears the selector or the entity disappears), this falls back to the setup-time entry.data sensor and keeps reading the old entity. The helper comments above _resolve explicitly treat empty option values as intentional clears; this branch should match that absent-key-only fallback semantics.

Useful? React with 👍 / 👎.

Artic0din added a commit that referenced this pull request May 27, 2026
* fix(coordinator): repair PR #170 auto-resolve breakage

PR #170's rebase auto-merge concatenated both sides of the
`_apply_options_to_state` refactor, leaving:
- 3 duplicate `oe_enabled = bool(opt(...))` lines before `def opt(...)`
  declared (`F821 Undefined name 'opt'`)
- 1 stale `opt(CONF_API_KEY)` call in `__init__` scope where `opt` is
  not defined (should remain `_resolve(entry, CONF_API_KEY)`)

Delete the 3 duplicate lines; restore L650 to `_resolve(entry, ...)`.
ruff clean + AST parses.

* fix(tests): drop duplicate MagicMock import in test_coordinator.py

* fix(coordinator): align _build_dwt_provider call site with new signature
Artic0din added a commit that referenced this pull request May 28, 2026
The #170 auto-merge cascade left full inline copies of the
_apply_options_to_state projector in BOTH __init__ and rebuild_engine,
each running alongside the actual projector call — double application
plus 5 no-redef errors (attributes assigned twice with annotations)
and a latent bug where _api_key was read options→data via _resolve_str
then immediately overwritten by a data-only read.

- __init__: remove the dead inline DWT/CDR + providers + amber_mode +
  named_comparator block (~75 lines); keep typed placeholders + the
  single projector call. Fixes 5 no-redef.
- rebuild_engine: remove the dead inline provider-rebuild block
  (~145 lines); keep only the strict=False projector call it already
  made. Fixes 5 Mapping-vs-dict arg-type errors.
- _api_key: single options→data read via _resolve_str (drops the
  data-only override that silently ignored options-flow key edits).
- rebuild_per_tick_explanation: param retyped dict[str,dict[str,Any]]
  → ProviderBlock to match _build_providers_block + build_explanation.
  Fixes 2 arg-type errors.

explanation.py:
- _won_bullets helpers: replace `.get(key, {})` (which poisons the
  ProviderSnapshot TypedDict into a union) with None-guarded access.
  Fixes call-overload + var-annotated.

Net: 220 fewer lines, projector is now the single source of truth as
its docstring always claimed, 12 of 14 mypy errors fixed by real
code repair (not suppression).
Artic0din added a commit that referenced this pull request May 28, 2026
* ci(mypy): restore mypy with HA follow_imports=skip boundary

Root cause of the earlier mypy hell: pytest-homeassistant-custom-component
pins HA to 2023.7.3, whose stubs lag the APIs the code targets
(OptionsFlowWithReload, current SelectorOptionsType). mypy analysing those
stale internals produced 30+ false positives.

Fix per constitution §14 (systemic over local) + §19 (platform conventions):
treat `homeassistant.*` and `openelectricity.*` as Any via
`follow_imports = skip` in mypy.ini — the correct boundary for a HACS
integration whose HA API correctness is validated at runtime, not by mypy.
Our own logic stays fully type-checked.

Restores the Mypy CI step. Expect only real, non-HA type errors to remain
(arg-type, no-redef, var-annotated, call-overload) — fixed in follow-up
commits on this branch.

* fix(coordinator): de-duplicate projector logic + fix 12 type errors

The #170 auto-merge cascade left full inline copies of the
_apply_options_to_state projector in BOTH __init__ and rebuild_engine,
each running alongside the actual projector call — double application
plus 5 no-redef errors (attributes assigned twice with annotations)
and a latent bug where _api_key was read options→data via _resolve_str
then immediately overwritten by a data-only read.

- __init__: remove the dead inline DWT/CDR + providers + amber_mode +
  named_comparator block (~75 lines); keep typed placeholders + the
  single projector call. Fixes 5 no-redef.
- rebuild_engine: remove the dead inline provider-rebuild block
  (~145 lines); keep only the strict=False projector call it already
  made. Fixes 5 Mapping-vs-dict arg-type errors.
- _api_key: single options→data read via _resolve_str (drops the
  data-only override that silently ignored options-flow key edits).
- rebuild_per_tick_explanation: param retyped dict[str,dict[str,Any]]
  → ProviderBlock to match _build_providers_block + build_explanation.
  Fixes 2 arg-type errors.

explanation.py:
- _won_bullets helpers: replace `.get(key, {})` (which poisons the
  ProviderSnapshot TypedDict into a union) with None-guarded access.
  Fixes call-overload + var-annotated.

Net: 220 fewer lines, projector is now the single source of truth as
its docstring always claimed, 12 of 14 mypy errors fixed by real
code repair (not suppression).
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.

1 participant