Skip to content

Comments

fix!: drop model.Container from ops.testing for mypy#2339

Draft
james-garner-canonical wants to merge 3 commits intocanonical:mainfrom
james-garner-canonical:26-02+fix+testing-mypy
Draft

fix!: drop model.Container from ops.testing for mypy#2339
james-garner-canonical wants to merge 3 commits intocanonical:mainfrom
james-garner-canonical:26-02+fix+testing-mypy

Conversation

@james-garner-canonical
Copy link
Contributor

This PR drops the model.Container re-export from the ops.testing module, because ops.testing.Container is incorrectly inferred to be model.Container by mypy, which impacts charmers who type check their state-transition tests with mypy.

The issue

The ops.testing module currently re-exports a number of names from elsewhere in ops for backwards compatibility, as these names were accidentally exposed via ops.testing when Harness was implemented there. The Container name is one of these, but is also exported from scenario if it is installed. While Pyright (the type checker used by ops) correctly infers the type of ops.testing.Container as scenario.Container in this case, Mypy does not -- instead it infers the type as ops.model.Container, even in users state-transition tests, which causes type checking errors.

This was reported on matrix. It looks like this has always been the case with Mypy, and I guess it just flew under the radar because many charms don't type check their tests.

The fix

We'd like to encourage type checking tests, so this PR fixes this problem by dropping the ops.model.Container re-export from ops.testing.

This is technically a breaking change, but (1) charms shouldn't be using ops.testing at runtime, so it would be a testing time only breakage; and (2) only legacy Harness tests' use of ops.testing.Container would be broken -- and such tests should really have been using ops.Container instead. We could preserve the runtime behaviour of re-exporting ops.model.Container if scenario isn't installed, but I suspect it's not worth doing so, and it would be nice to instead move towards dropping these re-exported names entirely in future.

Testing the fix

To verify that this fix works as intended, I've updated the httpbin-demo example charm to add mypy to its existing linting. I've verified that this linting fails with the reported error without the update to ops/testing.py from this PR. To keep this check current, I've updated tool.uv.sources for the charm to pull ops from the local files.

Potential CI changes

We could add mypy to the linting for our other example charms, the K8s and machine tutorial charms. However, this wouldn't reflect what we actually want charmers to do when following the tutorial, so we shouldn't do so on disc. Instead, it would probably be cleaner to add a custom CI job like below. If we did so, we could use the same approach for the httpbin-demo charm.

MYPYPATH='src:../..'
cd $example 
uv run \
  --with 'mypy~=1.19.0' \
  --with=../.. \
  --with=../../testing \
  --with=types-PyYAML \
  --group=lint \
  --group=unit \
  --group=integration \
  mypy --explicit-package-bases --follow-imports=silent src tests

@benhoyt
Copy link
Collaborator

benhoyt commented Feb 23, 2026

Excellent, thanks for this. Let's discuss in daily when Tony and Dima are both back (I've added it to the top of the doc).

@dimaqq
Copy link
Contributor

dimaqq commented Feb 24, 2026

my 2c: let's bite the bullet: merge, call it out in release notes, but don't bump the major version.

_compatibility_names = [
'CharmBase',
'CharmMeta',
'Container', # If Scenario has been installed, then this will be scenario.Container.
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have containers in Harness, at all?

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.

3 participants