Skip to content

test: type-check testing/src/scenario and fix uncovered errors#2615

Open
tonyandrewmeyer wants to merge 5 commits into
canonical:mainfrom
tonyandrewmeyer:fix-pyright-scenario-include
Open

test: type-check testing/src/scenario and fix uncovered errors#2615
tonyandrewmeyer wants to merge 5 commits into
canonical:mainfrom
tonyandrewmeyer:fix-pyright-scenario-include

Conversation

@tonyandrewmeyer

@tonyandrewmeyer tonyandrewmeyer commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

The pyright include glob testing/src/*.py did not match the subdirectory where the scenario source actually lives (testing/src/scenario/*.py), so that tree was silently unchecked 😞 😢. Add the correct glob and fix the 122 (!) errors that surfaced.

Highlights of the fixes:

  • mocking.py — use dict(x) instead of .copy() on Mapping-typed state fields; cast where the runtime is a dict but the annotation is intentionally Mapping (relation databags, remote_grants).
  • context.py — drop dead type: ignore comments and default the derived app_name to '' so the existing bad-metadata tests still exercise the downstream failure path.
  • state.py — widen internal validator params to Mapping, split from_context()'s **kwargs TypedDict so it no longer overlaps with named parameters, annotate default_factory=dict fields with their element types, and replace the bare '' assigned to CheckInfo.change_id (ChangeID | None) with pebble.ChangeID('').
  • _runtime.py — replace deprecated Iterator return type with Generator, parameterise TemporaryDirectory, normalise Optional unit_id.
  • _consistency_checker.py — cast action-spec lookups through Any so pyright can resolve get()/items() types.
  • _ops_main_mock.py — initialise the loop variable before the try so the except branch never sees an unbound name.

Refs #2613

tonyandrewmeyer and others added 2 commits July 4, 2026 13:41
Since ops 3.8.0, Secret.remote_grants values are frozensets, but
secret_grant and secret_revoke still called .add() and .remove() on
them, raising AttributeError when a charm revoked a pre-existing
grant.

Replace the in-place mutations with reassignment using set operators.

Fixes canonical#2613
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The pyright \`include\` glob \`testing/src/*.py\` did not match the
subdirectory where the scenario source actually lives, so all of
testing/src/scenario/*.py was silently unchecked. Add the correct
glob and fix the errors that surfaced.

Notable fixes:
- mocking.py: use dict(x) instead of .copy() on Mapping-typed state
  fields; cast where the runtime is a dict but the annotation is
  intentionally Mapping (relation databags, remote_grants).
- context.py: raise on missing app_name derivation is deferred (via ''
  fallback) so existing tests can assert the downstream error path;
  drop dead type: ignore comments now that annotations are correct.
- state.py: widen internal helper params to Mapping to match callers,
  split from_context()'s **kwargs TypedDict so it no longer overlaps
  with named parameters, annotate default_factory=dict fields with
  their element types, replace the bare '' assigned to change_id (a
  ChangeID | None) with pebble.ChangeID('').
- _runtime.py: replace deprecated Iterator return type with Generator,
  parameterize TemporaryDirectory, normalise Optional unit_id.
- _consistency_checker.py: cast action spec lookups through Any so
  pyright can resolve get()/items() types.
- _ops_main_mock.py: initialise loop variable before the try block so
  the except branch never sees an unbound name.

Refs canonical#2613
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tonyandrewmeyer tonyandrewmeyer changed the title fix(testing): Type-check testing/src/scenario and fix uncovered errors test: Type-check testing/src/scenario and fix uncovered errors Jul 4, 2026
Follow-up to the TypedDict rename in from_context(): update the
nitpick_ignore entry so the docs build stays clean.

Refs canonical#2613
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tonyandrewmeyer tonyandrewmeyer changed the title test: Type-check testing/src/scenario and fix uncovered errors test: type-check testing/src/scenario and fix uncovered errors Jul 4, 2026
The type annotation was simplified to Sequence[Address], and Sphinx
correctly resolves it to ops.testing.Address, so the comment about
prefixing is no longer accurate.

Refs canonical#2613
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@dwilding dwilding 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.

I bet finding this was a bit of a shock. Thanks for fixing!

Comment thread testing/src/scenario/_ops_main_mock.py Outdated
Comment thread testing/src/scenario/context.py Outdated
Comment on lines +759 to +762
# If neither ``app_name`` nor a ``name`` in the charm metadata is
# provided, fall back to an empty string and let the charm code fail
# later. Raising here would prevent tests from asserting on the
# downstream failure path (see ``test_init_and_run_with_bad_meta``).

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.

By "let the charm code fail", do we mean that the charm code is guaranteed to fail, because of something that Ops tries to do? That's how I interpret the downstream failure path. Just want to check I got the right understanding.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The comment is badly put. I'll fix.

Comment thread testing/src/scenario/state.py Outdated
Comment on lines +51 to +52
# Fields that from_context() forwards through **kwargs — the ones it
# doesn't already accept explicitly to merge with metadata-derived defaults.

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.

The comment wording is a bit confusing. I'm not sure what "to merge with metadata-derived defaults" is meant to apply to. Is that talking about the fields that from_context accepts or the ones that from_context doesn't accept?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I've reworded, is it clearer now?

Comment on lines 946 to +952
@property
def _layers(self) -> dict[str, pebble.Layer]:
return self._container.layers
def _layers(self) -> dict[str, pebble.Layer]: # pyright: ignore[reportIncompatibleVariableOverride]
return cast('dict[str, pebble.Layer]', self._container.layers)

@property
def _service_status(self) -> dict[str, pebble.ServiceStatus]:
return self._container.service_statuses
def _service_status(self) -> dict[str, pebble.ServiceStatus]: # pyright: ignore[reportIncompatibleVariableOverride]
return cast('dict[str, pebble.ServiceStatus]', self._container.service_statuses)

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.

I think I get the point of these. Is this a good-enough workaround for something that really should be changed in the design in the future? If no design change is needed, do you think it's worth adding comments to discourage people from trying to remove the ignores in the future?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'll add a comment. I think the best fix will be when we move Harness so it's not in ops by default, and Scenario presumably at that point stops building on top of it.

Merge the two TYPE_CHECKING blocks in _ops_main_mock.py, clarify the
app_name fallback comment in context.py, reword the _StateKwargsRest
comment in state.py, and add a comment above the pyright ignores on
_MockPebbleClient._layers/_service_status explaining the design
constraint.
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