Repository: canonical/operator

Issue:
Annoyance: Figuring out which module has my object
When writing charms, I am often able to remember the name of object that I want (`EventBase`, `StoredState`, etc.), but often forget which module it comes from. There's logic to the modules, of course -- `CharmBase` is in `charm`, the status objects live in `model`, and the things that I want when I'm maybe being a little bit hacky live in `framework`. I can make guesses, and those guesses are easy to check with the introspection tools in an IDE or a good old fashioned `grep` on the command line.

It's a little mental speed bump, however, of the sort that I think we want to eliminate.

Note: there's a warning about circular imports in `ops/__init__.py`, which means that we'd need to be thoughtful if we imported more objects into the top level module. It might be worth a spike, however. Lines like `self.model.unit.status = MaintenanceStatus("Frobbling the bitzors")` would be easier to write if I didn't first have to remember that statuses live in `ops.model` rather than `ops.charm` or `ops.framework`.


Discussion:
benhoyt: @jnsgruk Yes, very good call to update the templates. And a bunch of docs.
benhoyt: Opened https://github.com/canonical/operator/issues/921 to track updating docs, templates, and (perhaps) some charms.

Patch:
diff --git a/ops/__init__.py b/ops/__init__.py
index 5e01059..e867516 100644
--- a/ops/__init__.py
+++ b/ops/__init__.py
@@ -39,6 +39,142 @@ and day-2 operations specific to that application.
 Full developer documentation is available at https://juju.is/docs/sdk.
 """
 
-# Import here the bare minimum to break the circular import between modules
-from . import charm  # type: ignore # noqa
-from .version import version as __version__  # type: ignore # noqa
+# The isort command wants to rearrange the nicely-formatted imports below;
+# just skip it for this file.
+# isort:skip_file
+
+# Similarly, Pyright complains that all of these things are unused imports,
+# so disable it:
+# pyright: reportUnusedImport=false
+
+# Import pebble explicitly. It's the one module we don't import names from below.
+from . import pebble  # type: ignore # noqa: F401
+
+# Also import charm explicitly. This is not strictly necessary as the
+# "from .charm" import automatically does that, but be explicit since this
+# import was here previously
+from . import charm  # type: ignore # noqa: F401
+
+# Import the main module, which we've overriden in main.py to be callable.
+# This allows "import ops; ops.main(Charm)" to work as expected.
+from . import main  # type: ignore # noqa: F401
+
+# Explicitly import names from sub-modules so users can just "import ops" and
+# then use them as "ops.X".
+from .charm import (  # noqa: F401
+    ActionEvent,
+    ActionMeta,
+    CharmBase,
+    CharmEvents,
+    CharmMeta,
+    CollectMetricsEvent,
+    ConfigChangedEvent,
+    ContainerMeta,
+    ContainerStorageMeta,
+    HookEvent,
+    InstallEvent,
+    LeaderElectedEvent,
+    LeaderSettingsChangedEvent,
+    PayloadMeta,
+    PebbleReadyEvent,
+    PostSeriesUpgradeEvent,
+    PreSeriesUpgradeEvent,
+    RelationBrokenEvent,
+    RelationChangedEvent,
+    RelationCreatedEvent,
+    RelationDepartedEvent,
+    RelationEvent,
+    RelationJoinedEvent,
+    RelationMeta,
+    RelationRole,
+    RemoveEvent,
+    ResourceMeta,
+    SecretChangedEvent,
+    SecretEvent,
+    SecretExpiredEvent,
+    SecretRemoveEvent,
+    SecretRotateEvent,
+    StartEvent,
+    StopEvent,
+    StorageAttachedEvent,
+    StorageDetachingEvent,
+    StorageEvent,
+    StorageMeta,
+    UpdateStatusEvent,
+    UpgradeCharmEvent,
+    WorkloadEvent,
+)
+
+from .framework import (  # noqa: F401
+    BoundEvent,
+    BoundStoredState,
+    CommitEvent,
+    EventBase,
+    EventSource,
+    Framework,
+    FrameworkEvents,
+    Handle,
+    HandleKind,
+    LifecycleEvent,
+    NoTypeError,
+    Object,
+    ObjectEvents,
+    PreCommitEvent,
+    PrefixedEvents,
+    StoredDict,
+    StoredList,
+    StoredSet,
+    StoredState,
+    StoredStateData,
+)
+
+from .jujuversion import JujuVersion  # noqa: F401
+
+from .model import (  # noqa: F401 E402
+    ActiveStatus,
+    Application,
+    Binding,
+    BindingMapping,
+    BlockedStatus,
+    CheckInfoMapping,
+    ConfigData,
+    Container,
+    ContainerMapping,
+    ErrorStatus,
+    InvalidStatusError,
+    LazyMapping,
+    MaintenanceStatus,
+    Model,
+    ModelError,
+    MultiPushPullError,
+    Network,
+    NetworkInterface,
+    OpenedPort,
+    Pod,
+    Relation,
+    RelationData,
+    RelationDataAccessError,
+    RelationDataContent,
+    RelationDataError,
+    RelationDataTypeError,
+    RelationMapping,
+    RelationNotFoundError,
+    Resources,
+    Secret,
+    SecretInfo,
+    SecretNotFoundError,
+    SecretRotate,
+    ServiceInfoMapping,
+    StatusBase,
+    Storage,
+    StorageMapping,
+    TooManyRelatedAppsError,
+    Unit,
+    UnknownStatus,
+    WaitingStatus,
+)
+
+# NOTE: don't import testing or Harness here, as that's a test-time concern
+# rather than a runtime concern.
+
+from .version import version as __version__  # noqa: F401 E402
diff --git a/ops/main.py b/ops/main.py
index 0dbb315..88681fb 100755
--- a/ops/main.py
+++ b/ops/main.py
@@ -12,7 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Main entry point to the Operator Framework."""
+"""Main entry point to the Operator Framework.
+
+Note that this module is callable, and calls the :func:`ops.main.main` function.
+This is so that :code:`import ops` followed by :code:`ops.main(MyCharm)` works
+as expected.
+"""
 
 import logging
 import os
@@ -438,3 +443,15 @@ def main(charm_class: Type[ops.charm.CharmBase],
         framework.commit()
     finally:
         framework.close()
+
+
+# Make this module callable and call main(), so that "import ops" and then
+# "ops.main(Charm)" works as expected now that everything is imported in
+# ops/__init__.py. Idea from https://stackoverflow.com/a/48100440/68707
+class _CallableModule(sys.modules[__name__].__class__):
+    def __call__(self, charm_class: Type[ops.charm.CharmBase],
+                 use_juju_for_storage: Optional[bool] = None):
+        return main(charm_class, use_juju_for_storage=use_juju_for_storage)
+
+
+sys.modules[__name__].__class__ = _CallableModule


Test patch:
diff --git a/test/test_charm.py b/test/test_charm.py
index 1b7613f..9b7ba83 100755
--- a/test/test_charm.py
+++ b/test/test_charm.py
@@ -18,16 +18,8 @@ import tempfile
 import unittest
 from pathlib import Path
 
-from ops.charm import (
-    CharmBase,
-    CharmEvents,
-    CharmMeta,
-    ContainerMeta,
-    ContainerStorageMeta,
-    StartEvent,
-)
-from ops.framework import EventBase, EventSource, Framework
-from ops.model import Model, Storage, _ModelBackend
+import ops
+from ops.model import _ModelBackend
 from ops.storage import SQLiteStorage
 
 from .test_helpers import fake_script, fake_script_calls
@@ -48,33 +40,33 @@ class TestCharm(unittest.TestCase):
 
         self.tmpdir = Path(tempfile.mkdtemp())
         self.addCleanup(shutil.rmtree, str(self.tmpdir))
-        self.meta = CharmMeta()
+        self.meta = ops.CharmMeta()
 
-        class CustomEvent(EventBase):
+        class CustomEvent(ops.EventBase):
             pass
 
-        class TestCharmEvents(CharmEvents):
-            custom = EventSource(CustomEvent)
+        class TestCharmEvents(ops.CharmEvents):
+            custom = ops.EventSource(CustomEvent)
 
         # Relations events are defined dynamically and modify the class attributes.
         # We use a subclass temporarily to prevent these side effects from leaking.
-        CharmBase.on = TestCharmEvents()
+        ops.CharmBase.on = TestCharmEvents()
 
         def cleanup():
-            CharmBase.on = CharmEvents()
+            ops.CharmBase.on = ops.CharmEvents()
         self.addCleanup(cleanup)
 
     def create_framework(self):
-        model = Model(self.meta, _ModelBackend('local/0'))
+        model = ops.Model(self.meta, _ModelBackend('local/0'))
         # we can pass foo_event as event_name because we're not actually testing dispatch
-        framework = Framework(SQLiteStorage(':memory:'), self.tmpdir, self.meta,
-                              model)
+        framework = ops.Framework(SQLiteStorage(':memory:'), self.tmpdir, self.meta,
+                                  model)
         self.addCleanup(framework.close)
         return framework
 
     def test_basic(self):
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
 
             def __init__(self, *args):
                 super().__init__(*args)
@@ -116,7 +108,7 @@ class TestCharm(unittest.TestCase):
                 fn(charm, evt)
             return wrapper
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             def __init__(self, *args):
                 super().__init__(*args)
                 framework.observe(self.on.start, self._on_start)
@@ -132,16 +124,16 @@ class TestCharm(unittest.TestCase):
         # check that the event has been seen by the decorator
         self.assertEqual(1, len(events))
         # check that the event has been seen by the observer
-        self.assertIsInstance(charm.seen, StartEvent)
+        self.assertIsInstance(charm.seen, ops.StartEvent)
 
     def test_empty_action(self):
-        meta = CharmMeta.from_yaml('name: my-charm', '')
+        meta = ops.CharmMeta.from_yaml('name: my-charm', '')
         self.assertEqual(meta.actions, {})
 
     def test_helper_properties(self):
         framework = self.create_framework()
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             pass
 
         charm = MyCharm(framework)
@@ -153,7 +145,7 @@ class TestCharm(unittest.TestCase):
 
     def test_relation_events(self):
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             def __init__(self, *args):
                 super().__init__(*args)
                 self.seen = []
@@ -170,7 +162,7 @@ class TestCharm(unittest.TestCase):
                 self.seen.append(type(event).__name__)
 
         # language=YAML
-        self.meta = CharmMeta.from_yaml(metadata='''
+        self.meta = ops.CharmMeta.from_yaml(metadata='''
 name: my-charm
 requires:
  req1:
@@ -216,7 +208,7 @@ peers:
     def test_storage_events(self):
         this = self
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             def __init__(self, *args):
                 super().__init__(*args)
                 self.seen = []
@@ -239,7 +231,7 @@ peers:
                 self.seen.append(type(event).__name__)
 
         # language=YAML
-        self.meta = CharmMeta.from_yaml('''
+        self.meta = ops.CharmMeta.from_yaml('''
 name: my-charm
 storage:
   stor-4:
@@ -310,12 +302,12 @@ storage:
 
         charm = MyCharm(self.create_framework())
 
-        charm.on['stor1'].storage_attached.emit(Storage("stor1", 0, charm.model._backend))
-        charm.on['stor2'].storage_detaching.emit(Storage("stor2", 0, charm.model._backend))
-        charm.on['stor3'].storage_attached.emit(Storage("stor3", 0, charm.model._backend))
-        charm.on['stor-4'].storage_attached.emit(Storage("stor-4", 0, charm.model._backend))
+        charm.on['stor1'].storage_attached.emit(ops.Storage("stor1", 0, charm.model._backend))
+        charm.on['stor2'].storage_detaching.emit(ops.Storage("stor2", 0, charm.model._backend))
+        charm.on['stor3'].storage_attached.emit(ops.Storage("stor3", 0, charm.model._backend))
+        charm.on['stor-4'].storage_attached.emit(ops.Storage("stor-4", 0, charm.model._backend))
         charm.on['stor-multiple-dashes'].storage_attached.emit(
-            Storage("stor-multiple-dashes", 0, charm.model._backend))
+            ops.Storage("stor-multiple-dashes", 0, charm.model._backend))
 
         self.assertEqual(charm.seen, [
             'StorageAttachedEvent',
@@ -326,7 +318,7 @@ storage:
 
     def test_workload_events(self):
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             def __init__(self, *args):
                 super().__init__(*args)
                 self.seen = []
@@ -342,7 +334,7 @@ storage:
                 self.count += 1
 
         # language=YAML
-        self.meta = CharmMeta.from_yaml(metadata='''
+        self.meta = ops.CharmMeta.from_yaml(metadata='''
 name: my-charm
 containers:
   container-a:
@@ -368,7 +360,7 @@ containers:
     def test_relations_meta(self):
 
         # language=YAML
-        self.meta = CharmMeta.from_yaml('''
+        self.meta = ops.CharmMeta.from_yaml('''
 name: my-charm
 requires:
   database:
@@ -390,7 +382,7 @@ requires:
     def test_relations_meta_limit_type_validation(self):
         with self.assertRaisesRegex(TypeError, "limit should be an int, not <class 'str'>"):
             # language=YAML
-            self.meta = CharmMeta.from_yaml('''
+            self.meta = ops.CharmMeta.from_yaml('''
 name: my-charm
 requires:
   database:
@@ -402,7 +394,7 @@ requires:
         with self.assertRaisesRegex(TypeError,
                                     "scope should be one of 'global', 'container'; not 'foobar'"):
             # language=YAML
-            self.meta = CharmMeta.from_yaml('''
+            self.meta = ops.CharmMeta.from_yaml('''
 name: my-charm
 requires:
   database:
@@ -413,7 +405,7 @@ requires:
     @classmethod
     def _get_action_test_meta(cls):
         # language=YAML
-        return CharmMeta.from_yaml(metadata='''
+        return ops.CharmMeta.from_yaml(metadata='''
 name: my-charm
 ''', actions='''
 foo-bar:
@@ -442,7 +434,7 @@ start:
 
     def test_action_events(self):
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
 
             def __init__(self, *args):
                 super().__init__(*args)
@@ -482,7 +474,7 @@ start:
 
     def test_invalid_action_results(self):
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
 
             def __init__(self, *args):
                 super().__init__(*args)
@@ -509,7 +501,7 @@ start:
 
     def _test_action_event_defer_fails(self, cmd_type):
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
 
             def __init__(self, *args):
                 super().__init__(*args)
@@ -532,7 +524,7 @@ start:
         self._test_action_event_defer_fails('action')
 
     def test_containers(self):
-        meta = CharmMeta.from_yaml("""
+        meta = ops.CharmMeta.from_yaml("""
 name: k8s-charm
 containers:
   test1:
@@ -540,13 +532,13 @@ containers:
   test2:
     k: v
 """)
-        self.assertIsInstance(meta.containers['test1'], ContainerMeta)
-        self.assertIsInstance(meta.containers['test2'], ContainerMeta)
+        self.assertIsInstance(meta.containers['test1'], ops.ContainerMeta)
+        self.assertIsInstance(meta.containers['test2'], ops.ContainerMeta)
         self.assertEqual(meta.containers['test1'].name, 'test1')
         self.assertEqual(meta.containers['test2'].name, 'test2')
 
     def test_containers_storage(self):
-        meta = CharmMeta.from_yaml("""
+        meta = ops.CharmMeta.from_yaml("""
 name: k8s-charm
 storage:
   data:
@@ -563,13 +555,13 @@ containers:
       - storage: other
         location: /test/otherdata
 """)
-        self.assertIsInstance(meta.containers['test1'], ContainerMeta)
-        self.assertIsInstance(meta.containers['test1'].mounts["data"], ContainerStorageMeta)
+        self.assertIsInstance(meta.containers['test1'], ops.ContainerMeta)
+        self.assertIsInstance(meta.containers['test1'].mounts["data"], ops.ContainerStorageMeta)
         self.assertEqual(meta.containers['test1'].mounts["data"].location, '/test/storagemount')
         self.assertEqual(meta.containers['test1'].mounts["other"].location, '/test/otherdata')
 
     def test_containers_storage_multiple_mounts(self):
-        meta = CharmMeta.from_yaml("""
+        meta = ops.CharmMeta.from_yaml("""
 name: k8s-charm
 storage:
   data:
@@ -583,8 +575,8 @@ containers:
       - storage: data
         location: /test/otherdata
 """)
-        self.assertIsInstance(meta.containers['test1'], ContainerMeta)
-        self.assertIsInstance(meta.containers['test1'].mounts["data"], ContainerStorageMeta)
+        self.assertIsInstance(meta.containers['test1'], ops.ContainerMeta)
+        self.assertIsInstance(meta.containers['test1'].mounts["data"], ops.ContainerStorageMeta)
         self.assertEqual(
             meta.containers['test1'].mounts["data"].locations[0],
             '/test/storagemount')
@@ -594,7 +586,7 @@ containers:
             meta.containers["test1"].mounts["data"].location
 
     def test_secret_events(self):
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             def __init__(self, *args):
                 super().__init__(*args)
                 self.seen = []
@@ -625,7 +617,7 @@ containers:
                 assert event.revision == 42
                 self.seen.append(type(event).__name__)
 
-        self.meta = CharmMeta.from_yaml(metadata='name: my-charm')
+        self.meta = ops.CharmMeta.from_yaml(metadata='name: my-charm')
         charm = MyCharm(self.create_framework())
 
         charm.on.secret_changed.emit('secret:changed', None)
diff --git a/test/test_framework.py b/test/test_framework.py
index 56a46a4..950f5e4 100755
--- a/test/test_framework.py
+++ b/test/test_framework.py
@@ -27,25 +27,8 @@ from unittest.mock import patch
 
 import logassert
 
-from ops import charm
-from ops.framework import (
-    _BREAKPOINT_WELCOME_MESSAGE,
-    BoundStoredState,
-    CommitEvent,
-    EventBase,
-    EventSource,
-    Framework,
-    Handle,
-    Object,
-    ObjectEvents,
-    PreCommitEvent,
-    StoredDict,
-    StoredList,
-    StoredSet,
-    StoredState,
-    StoredStateData,
-    _event_regex,
-)
+import ops
+from ops.framework import _BREAKPOINT_WELCOME_MESSAGE, _event_regex
 from ops.storage import NoSnapshotError, SQLiteStorage
 
 
@@ -62,24 +45,24 @@ class TestFramework(BaseTestCase):
 
     def test_deprecated_init(self):
         # For 0.7, this still works, but it is deprecated.
-        framework = Framework(':memory:', None, None, None)
+        framework = ops.Framework(':memory:', None, None, None)
         self.assertLoggedWarning(
             "deprecated: Framework now takes a Storage not a path")
         self.assertIsInstance(framework._storage, SQLiteStorage)
 
     def test_handle_path(self):
         cases = [
-            (Handle(None, "root", None), "root"),
-            (Handle(None, "root", "1"), "root[1]"),
-            (Handle(Handle(None, "root", None), "child", None), "root/child"),
-            (Handle(Handle(None, "root", "1"), "child", "2"), "root[1]/child[2]"),
+            (ops.Handle(None, "root", None), "root"),
+            (ops.Handle(None, "root", "1"), "root[1]"),
+            (ops.Handle(ops.Handle(None, "root", None), "child", None), "root/child"),
+            (ops.Handle(ops.Handle(None, "root", "1"), "child", "2"), "root[1]/child[2]"),
         ]
         for handle, path in cases:
             self.assertEqual(str(handle), path)
-            self.assertEqual(Handle.from_path(path), handle)
+            self.assertEqual(ops.Handle.from_path(path), handle)
 
     def test_handle_attrs_readonly(self):
-        handle = Handle(None, 'kind', 'key')
+        handle = ops.Handle(None, 'kind', 'key')
         with self.assertRaises(AttributeError):
             handle.parent = 'foo'
         with self.assertRaises(AttributeError):
@@ -92,10 +75,10 @@ class TestFramework(BaseTestCase):
     def test_restore_unknown(self):
         framework = self.create_framework()
 
-        class Foo(Object):
+        class Foo(ops.Object):
             pass
 
-        handle = Handle(None, "a_foo", "some_key")
+        handle = ops.Handle(None, "a_foo", "some_key")
 
         framework.register_type(Foo, None, handle.kind)
 
@@ -119,7 +102,7 @@ class TestFramework(BaseTestCase):
             def restore(self, snapshot):
                 self.my_n = snapshot["My N!"] + 1
 
-        handle = Handle(None, "a_foo", "some_key")
+        handle = ops.Handle(None, "a_foo", "some_key")
         event = Foo(handle, 1)
 
         framework1 = self.create_framework(tmpdir=self.tmpdir)
@@ -151,15 +134,15 @@ class TestFramework(BaseTestCase):
     def test_simple_event_observer(self):
         framework = self.create_framework()
 
-        class MyEvent(EventBase):
+        class MyEvent(ops.EventBase):
             pass
 
-        class MyNotifier(Object):
-            foo = EventSource(MyEvent)
-            bar = EventSource(MyEvent)
-            baz = EventSource(MyEvent)
+        class MyNotifier(ops.Object):
+            foo = ops.EventSource(MyEvent)
+            bar = ops.EventSource(MyEvent)
+            baz = ops.EventSource(MyEvent)
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             def __init__(self, parent, key):
                 super().__init__(parent, key)
                 self.seen = []
@@ -193,16 +176,16 @@ class TestFramework(BaseTestCase):
 
     def test_bad_sig_observer(self):
 
-        class MyEvent(EventBase):
+        class MyEvent(ops.EventBase):
             pass
 
-        class MyNotifier(Object):
-            foo = EventSource(MyEvent)
-            bar = EventSource(MyEvent)
-            baz = EventSource(MyEvent)
-            qux = EventSource(MyEvent)
+        class MyNotifier(ops.Object):
+            foo = ops.EventSource(MyEvent)
+            bar = ops.EventSource(MyEvent)
+            baz = ops.EventSource(MyEvent)
+            qux = ops.EventSource(MyEvent)
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             def _on_foo(self):
                 assert False, 'should not be reached'
 
@@ -230,9 +213,9 @@ class TestFramework(BaseTestCase):
     def test_on_pre_commit_emitted(self):
         framework = self.create_framework(tmpdir=self.tmpdir)
 
-        class PreCommitObserver(Object):
+        class PreCommitObserver(ops.Object):
 
-            _stored = StoredState()
+            _stored = ops.StoredState()
 
             def __init__(self, parent, key):
                 super().__init__(parent, key)
@@ -259,7 +242,7 @@ class TestFramework(BaseTestCase):
 
         self.assertEqual(obs._stored.myinitdata, 41)
         self.assertEqual(obs._stored.mydata, 42)
-        self.assertTrue(obs.seen, [PreCommitEvent, CommitEvent])
+        self.assertTrue(obs.seen, [ops.PreCommitEvent, ops.CommitEvent])
         framework.close()
 
         other_framework = self.create_framework(tmpdir=self.tmpdir)
@@ -275,17 +258,17 @@ class TestFramework(BaseTestCase):
     def test_defer_and_reemit(self):
         framework = self.create_framework()
 
-        class MyEvent(EventBase):
+        class MyEvent(ops.EventBase):
             pass
 
-        class MyNotifier1(Object):
-            a = EventSource(MyEvent)
-            b = EventSource(MyEvent)
+        class MyNotifier1(ops.Object):
+            a = ops.EventSource(MyEvent)
+            b = ops.EventSource(MyEvent)
 
-        class MyNotifier2(Object):
-            c = EventSource(MyEvent)
+        class MyNotifier2(ops.Object):
+            c = ops.EventSource(MyEvent)
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             def __init__(self, parent, key):
                 super().__init__(parent, key)
                 self.seen = []
@@ -312,11 +295,11 @@ class TestFramework(BaseTestCase):
         pub2.c.emit()
 
         # Events remain stored because they were deferred.
-        ev_a_handle = Handle(pub1, "a", "1")
+        ev_a_handle = ops.Handle(pub1, "a", "1")
         framework.load_snapshot(ev_a_handle)
-        ev_b_handle = Handle(pub1, "b", "2")
+        ev_b_handle = ops.Handle(pub1, "b", "2")
         framework.load_snapshot(ev_b_handle)
-        ev_c_handle = Handle(pub2, "c", "3")
+        ev_c_handle = ops.Handle(pub2, "c", "3")
         framework.load_snapshot(ev_c_handle)
         # make sure the objects are gone before we reemit them
         gc.collect()
@@ -345,7 +328,7 @@ class TestFramework(BaseTestCase):
     def test_custom_event_data(self):
         framework = self.create_framework()
 
-        class MyEvent(EventBase):
+        class MyEvent(ops.EventBase):
             def __init__(self, handle, n):
                 super().__init__(handle)
                 self.my_n = n
@@ -357,10 +340,10 @@ class TestFramework(BaseTestCase):
                 super().restore(snapshot)
                 self.my_n = snapshot["My N!"] + 1
 
-        class MyNotifier(Object):
-            foo = EventSource(MyEvent)
+        class MyNotifier(ops.Object):
+            foo = ops.EventSource(MyEvent)
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             def __init__(self, parent, key):
                 super().__init__(parent, key)
                 self.seen = []
@@ -399,16 +382,16 @@ class TestFramework(BaseTestCase):
 
         observed_events = []
 
-        class MyEvent(EventBase):
+        class MyEvent(ops.EventBase):
             pass
 
-        class MyEvents(ObjectEvents):
-            foo = EventSource(MyEvent)
+        class MyEvents(ops.ObjectEvents):
+            foo = ops.EventSource(MyEvent)
 
-        class MyNotifier(Object):
+        class MyNotifier(ops.Object):
             on = MyEvents()
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             def _on_foo(self, event):
                 observed_events.append("foo")
 
@@ -428,7 +411,7 @@ class TestFramework(BaseTestCase):
     def test_forget_and_multiple_objects(self):
         framework = self.create_framework()
 
-        class MyObject(Object):
+        class MyObject(ops.Object):
             pass
 
         o1 = MyObject(framework, "path")
@@ -453,7 +436,7 @@ class TestFramework(BaseTestCase):
     def test_forget_and_multiple_objects_with_load_snapshot(self):
         framework = self.create_framework(tmpdir=self.tmpdir)
 
-        class MyObject(Object):
+        class MyObject(ops.Object):
             def __init__(self, parent, name):
                 super().__init__(parent, name)
                 self.value = name
@@ -496,17 +479,17 @@ class TestFramework(BaseTestCase):
     def test_events_base(self):
         framework = self.create_framework()
 
-        class MyEvent(EventBase):
+        class MyEvent(ops.EventBase):
             pass
 
-        class MyEvents(ObjectEvents):
-            foo = EventSource(MyEvent)
-            bar = EventSource(MyEvent)
+        class MyEvents(ops.ObjectEvents):
+            foo = ops.EventSource(MyEvent)
+            bar = ops.EventSource(MyEvent)
 
-        class MyNotifier(Object):
+        class MyNotifier(ops.Object):
             on = MyEvents()
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             def __init__(self, parent, key):
                 super().__init__(parent, key)
                 self.seen = []
@@ -534,23 +517,23 @@ class TestFramework(BaseTestCase):
         self.assertEqual(repr(pub.on), f"<{fqn}: bar, foo>")
 
     def test_conflicting_event_attributes(self):
-        class MyEvent(EventBase):
+        class MyEvent(ops.EventBase):
             pass
 
-        event = EventSource(MyEvent)
+        event = ops.EventSource(MyEvent)
 
-        class MyEvents(ObjectEvents):
+        class MyEvents(ops.ObjectEvents):
             foo = event
 
         with self.assertRaises(RuntimeError) as cm:
-            class OtherEvents(ObjectEvents):
+            class OtherEvents(ops.ObjectEvents):
                 foo = event
         self.assertEqual(
             str(cm.exception.__cause__),
             "EventSource(MyEvent) reused as MyEvents.foo and OtherEvents.foo")
 
         with self.assertRaises(RuntimeError) as cm:
-            class MyNotifier(Object):
+            class MyNotifier(ops.Object):
                 on = MyEvents()
                 bar = event
         self.assertEqual(
@@ -563,13 +546,13 @@ class TestFramework(BaseTestCase):
 
         framework = self.create_framework()
 
-        class MyEvent(EventBase):
+        class MyEvent(ops.EventBase):
             pass
 
-        class MyNotifier(Object):
-            foo = EventSource(MyEvent)
+        class MyNotifier(ops.Object):
+            foo = ops.EventSource(MyEvent)
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             def __init__(self, parent, key):
                 super().__init__(parent, key)
                 self.seen = []
@@ -602,20 +585,20 @@ class TestFramework(BaseTestCase):
     def test_auto_register_event_types(self):
         framework = self.create_framework()
 
-        class MyFoo(EventBase):
+        class MyFoo(ops.EventBase):
             pass
 
-        class MyBar(EventBase):
+        class MyBar(ops.EventBase):
             pass
 
-        class MyEvents(ObjectEvents):
-            foo = EventSource(MyFoo)
+        class MyEvents(ops.ObjectEvents):
+            foo = ops.EventSource(MyFoo)
 
-        class MyNotifier(Object):
+        class MyNotifier(ops.Object):
             on = MyEvents()
-            bar = EventSource(MyBar)
+            bar = ops.EventSource(MyBar)
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             def __init__(self, parent, key):
                 super().__init__(parent, key)
                 self.seen = []
@@ -645,17 +628,17 @@ class TestFramework(BaseTestCase):
     def test_dynamic_event_types(self):
         framework = self.create_framework()
 
-        class MyEventsA(ObjectEvents):
+        class MyEventsA(ops.ObjectEvents):
             handle_kind = 'on_a'
 
-        class MyEventsB(ObjectEvents):
+        class MyEventsB(ops.ObjectEvents):
             handle_kind = 'on_b'
 
-        class MyNotifier(Object):
+        class MyNotifier(ops.Object):
             on_a = MyEventsA()
             on_b = MyEventsB()
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             def __init__(self, parent, key):
                 super().__init__(parent, key)
                 self.seen = []
@@ -671,16 +654,16 @@ class TestFramework(BaseTestCase):
         pub = MyNotifier(framework, "1")
         obs = MyObserver(framework, "1")
 
-        class MyFoo(EventBase):
+        class MyFoo(ops.EventBase):
             pass
 
-        class MyBar(EventBase):
+        class MyBar(ops.EventBase):
             pass
 
-        class DeadBeefEvent(EventBase):
+        class DeadBeefEvent(ops.EventBase):
             pass
 
-        class NoneEvent(EventBase):
+        class NoneEvent(ops.EventBase):
             pass
 
         pub.on_a.define_event("foo", MyFoo)
@@ -711,7 +694,7 @@ class TestFramework(BaseTestCase):
             pub.on_a.define_event("foo", MyFoo)
 
     def test_event_key_roundtrip(self):
-        class MyEvent(EventBase):
+        class MyEvent(ops.EventBase):
             def __init__(self, handle, value):
                 super().__init__(handle)
                 self.value = value
@@ -722,10 +705,10 @@ class TestFramework(BaseTestCase):
             def restore(self, value):
                 self.value = value
 
-        class MyNotifier(Object):
-            foo = EventSource(MyEvent)
+        class MyNotifier(ops.Object):
+            foo = ops.EventSource(MyEvent)
 
-        class MyObserver(Object):
+        class MyObserver(ops.Object):
             has_deferred = False
 
             def __init__(self, parent, key):
@@ -768,7 +751,7 @@ class TestFramework(BaseTestCase):
         framework.model = 'test-model'
         framework.meta = 'test-meta'
 
-        my_obj = Object(framework, 'my_obj')
+        my_obj = ops.Object(framework, 'my_obj')
         self.assertEqual(my_obj.model, framework.model)
 
     def test_ban_concurrent_frameworks(self):
@@ -782,11 +765,11 @@ class TestFramework(BaseTestCase):
         # this can not be saved, as it has not simple types!
         to_be_saved = {"bar": TestFramework}
 
-        class FooEvent(EventBase):
+        class FooEvent(ops.EventBase):
             def snapshot(self):
                 return to_be_saved
 
-        handle = Handle(None, "a_foo", "some_key")
+        handle = ops.Handle(None, "a_foo", "some_key")
         event = FooEvent(handle)
 
         framework = self.create_framework()
@@ -799,20 +782,20 @@ class TestFramework(BaseTestCase):
         self.assertEqual(str(cm.exception), expected)
 
     def test_unobserved_events_dont_leave_cruft(self):
-        class FooEvent(EventBase):
+        class FooEvent(ops.EventBase):
             def snapshot(self):
                 return {'content': 1}
 
-        class Events(ObjectEvents):
-            foo = EventSource(FooEvent)
+        class Events(ops.ObjectEvents):
+            foo = ops.EventSource(FooEvent)
 
-        class Emitter(Object):
+        class Emitter(ops.Object):
             on = Events()
 
         framework = self.create_framework()
         e = Emitter(framework, 'key')
         e.on.foo.emit()
-        ev_1_handle = Handle(e.on, "foo", "1")
+        ev_1_handle = ops.Handle(e.on, "foo", "1")
         with self.assertRaises(NoSnapshotError):
             framework.load_snapshot(ev_1_handle)
         # Committing will save the framework's state, but no other snapshots should be saved
@@ -839,14 +822,14 @@ class TestFramework(BaseTestCase):
     def test_remove_unreferenced_events(self):
         framework = self.create_framework()
 
-        class Evt(EventBase):
+        class Evt(ops.EventBase):
             pass
 
-        class Events(ObjectEvents):
-            event = EventSource(Evt)
+        class Events(ops.ObjectEvents):
+            event = ops.EventSource(Evt)
 
-        class ObjectWithStorage(Object):
-            _stored = StoredState()
+        class ObjectWithStorage(ops.Object):
+            _stored = ops.StoredState()
             on = Events()
 
             def __init__(self, framework, key):
@@ -859,7 +842,7 @@ class TestFramework(BaseTestCase):
 
         # This is an event that 'happened in the past' that doesn't have an associated notice.
         o = ObjectWithStorage(framework, 'obj')
-        handle = Handle(o.on, 'event', '100')
+        handle = ops.Handle(o.on, 'event', '100')
         event = Evt(handle)
         framework.save_snapshot(event)
         self.assertEqual(list(framework._storage.list_snapshots()), [handle.path])
@@ -890,26 +873,34 @@ class TestStoredState(BaseTestCase):
         self.addCleanup(shutil.rmtree, str(self.tmpdir))
 
     def test_stored_dict_repr(self):
-        self.assertEqual(repr(StoredDict(None, {})), "ops.framework.StoredDict()")
-        self.assertEqual(repr(StoredDict(None, {"a": 1})), "ops.framework.StoredDict({'a': 1})")
+        self.assertEqual(repr(ops.StoredDict(None, {})), "ops.framework.StoredDict()")
+        self.assertEqual(
+            repr(
+                ops.StoredDict(
+                    None, {
+                        "a": 1})), "ops.framework.StoredDict({'a': 1})")
 
     def test_stored_list_repr(self):
-        self.assertEqual(repr(StoredList(None, [])), "ops.framework.StoredList()")
-        self.assertEqual(repr(StoredList(None, [1, 2, 3])), 'ops.framework.StoredList([1, 2, 3])')
+        self.assertEqual(repr(ops.StoredList(None, [])), "ops.framework.StoredList()")
+        self.assertEqual(
+            repr(
+                ops.StoredList(
+                    None, [
+                        1, 2, 3])), 'ops.framework.StoredList([1, 2, 3])')
 
     def test_stored_set_repr(self):
-        self.assertEqual(repr(StoredSet(None, set())), 'ops.framework.StoredSet()')
-        self.assertEqual(repr(StoredSet(None, {1})), 'ops.framework.StoredSet({1})')
+        self.assertEqual(repr(ops.StoredSet(None, set())), 'ops.framework.StoredSet()')
+        self.assertEqual(repr(ops.StoredSet(None, {1})), 'ops.framework.StoredSet({1})')
 
     def test_basic_state_storage(self):
-        class SomeObject(Object):
-            _stored = StoredState()
+        class SomeObject(ops.Object):
+            _stored = ops.StoredState()
 
         self._stored_state_tests(SomeObject)
 
     def test_straight_subclass(self):
-        class SomeObject(Object):
-            _stored = StoredState()
+        class SomeObject(ops.Object):
+            _stored = ops.StoredState()
 
         class Sub(SomeObject):
             pass
@@ -917,8 +908,8 @@ class TestStoredState(BaseTestCase):
         self._stored_state_tests(Sub)
 
     def test_straight_sub_subclass(self):
-        class SomeObject(Object):
-            _stored = StoredState()
+        class SomeObject(ops.Object):
+            _stored = ops.StoredState()
 
         class Sub(SomeObject):
             pass
@@ -929,8 +920,8 @@ class TestStoredState(BaseTestCase):
         self._stored_state_tests(SubSub)
 
     def test_two_subclasses(self):
-        class SomeObject(Object):
-            _stored = StoredState()
+        class SomeObject(ops.Object):
+            _stored = ops.StoredState()
 
         class SubA(SomeObject):
             pass
@@ -942,11 +933,11 @@ class TestStoredState(BaseTestCase):
         self._stored_state_tests(SubB)
 
     def test_the_crazy_thing(self):
-        class NoState(Object):
+        class NoState(ops.Object):
             pass
 
         class StatedObject(NoState):
-            _stored = StoredState()
+            _stored = ops.StoredState()
 
         class Sibling(NoState):
             pass
@@ -1000,8 +991,8 @@ class TestStoredState(BaseTestCase):
         framework_copy.close()
 
     def test_two_subclasses_no_conflicts(self):
-        class Base(Object):
-            _stored = StoredState()
+        class Base(ops.Object):
+            _stored = ops.StoredState()
 
         class SubA(Base):
             pass
@@ -1031,8 +1022,8 @@ class TestStoredState(BaseTestCase):
         self.assertEqual(z2._stored.foo, {1})
 
     def test_two_names_one_state(self):
-        class Mine(Object):
-            _stored = StoredState()
+        class Mine(ops.Object):
+            _stored = ops.StoredState()
             _stored2 = _stored
 
         framework = self.create_framework()
@@ -1051,11 +1042,11 @@ class TestStoredState(BaseTestCase):
         self.assertNotIn("_stored2", obj.__dict__)
 
     def test_same_name_two_classes(self):
-        class Base(Object):
+        class Base(ops.Object):
             pass
 
         class A(Base):
-            _stored = StoredState()
+            _stored = ops.StoredState()
 
         class B(Base):
             _stored = A._stored
@@ -1079,8 +1070,8 @@ class TestStoredState(BaseTestCase):
     def test_mutable_types_invalid(self):
         framework = self.create_framework()
 
-        class SomeObject(Object):
-            _stored = StoredState()
+        class SomeObject(ops.Object):
+            _stored = ops.StoredState()
 
         obj = SomeObject(framework, '1')
         try:
@@ -1161,7 +1152,7 @@ class TestStoredState(BaseTestCase):
             lambda a, b: a.append(b),
             lambda res, expected_res: (
                 self.assertEqual(res, expected_res),
-                self.assertIsInstance(res[1], StoredList),
+                self.assertIsInstance(res[1], ops.StoredList),
             )
         ), (
             lambda: ['a', ['c']],
@@ -1176,7 +1167,7 @@ class TestStoredState(BaseTestCase):
             lambda a, b: a.insert(1, b),
             lambda res, expected_res: (
                 self.assertEqual(res, expected_res),
-                self.assertIsInstance(res[1], StoredList)
+                self.assertIsInstance(res[1], ops.StoredList)
             ),
         ), (
             lambda: ['b', 'a', ['c']],
@@ -1186,7 +1177,7 @@ class TestStoredState(BaseTestCase):
             lambda a, b: a.__setitem__(1, b),
             lambda res, expected_res: (
                 self.assertEqual(res, expected_res),
-                self.assertIsInstance(res[1], StoredList)
+                self.assertIsInstance(res[1], ops.StoredList)
             ),
         ), (
             lambda: ['b', ['d'], 'a', ['c']],
@@ -1251,10 +1242,10 @@ class TestStoredState(BaseTestCase):
             lambda res, expected_res: self.assertEqual(res, expected_res)
         )]
 
-        class SomeObject(Object):
-            _stored = StoredState()
+        class SomeObject(ops.Object):
+            _stored = ops.StoredState()
 
-        class WrappedFramework(Framework):
+        class WrappedFramework(ops.Framework):
             def __init__(self, store, charm_dir, meta, model, event_name):
                 super().__init__(store, charm_dir, meta, model, event_name)
                 self.snapshots = []
@@ -1271,7 +1262,7 @@ class TestStoredState(BaseTestCase):
             obj = SomeObject(framework, '1')
 
             obj._stored.a = get_a()
-            self.assertTrue(isinstance(obj._stored, BoundStoredState))
+            self.assertTrue(isinstance(obj._stored, ops.BoundStoredState))
 
             op(obj._stored.a, b)
             validate_op(obj._stored.a, expected_res)
@@ -1280,7 +1271,7 @@ class TestStoredState(BaseTestCase):
             framework.commit()
             # We should see an update for initializing a
             self.assertEqual(framework.snapshots, [
-                (StoredStateData, {'a': get_a()}),
+                (ops.StoredStateData, {'a': get_a()}),
             ])
             del obj
             gc.collect()
@@ -1388,8 +1379,8 @@ class TestStoredState(BaseTestCase):
             False
         )]
 
-        class SomeObject(Object):
-            _stored = StoredState()
+        class SomeObject(ops.Object):
+            _stored = ops.StoredState()
 
         framework = self.create_framework()
 
@@ -1427,8 +1418,8 @@ class TestStoredState(BaseTestCase):
             set()
         )]
 
-        class SomeObject(Object):
-            _stored = StoredState()
+        class SomeObject(ops.Object):
+            _stored = ops.StoredState()
 
         framework = self.create_framework()
 
@@ -1459,8 +1450,8 @@ class TestStoredState(BaseTestCase):
     def test_set_default(self):
         framework = self.create_framework()
 
-        class StatefulObject(Object):
-            _stored = StoredState()
+        class StatefulObject(ops.Object):
+            _stored = ops.StoredState()
         parent = StatefulObject(framework, 'key')
         parent._stored.set_default(foo=1)
         self.assertEqual(parent._stored.foo, 1)
@@ -1481,7 +1472,7 @@ class TestStoredState(BaseTestCase):
         #       parent._stored._data.dirty is False?
 
 
-class GenericObserver(Object):
+class GenericObserver(ops.Object):
     """Generic observer for the tests."""
 
     def __init__(self, parent, key):
@@ -1719,7 +1710,7 @@ class DebugHookTests(BaseTestCase):
         framework = self.create_framework()
         framework._juju_debug_at = {'hook'}
 
-        publisher = charm.CharmEvents(framework, "1")
+        publisher = ops.CharmEvents(framework, "1")
         observer = GenericObserver(framework, "1")
         framework.observe(publisher.install, observer.callback_method)
 
@@ -1733,7 +1724,7 @@ class DebugHookTests(BaseTestCase):
         self.assertEqual(mock.call_count, 1)
         expected_callback, expected_event = mock.call_args[0]
         self.assertEqual(expected_callback, observer.callback_method)
-        self.assertIsInstance(expected_event, EventBase)
+        self.assertIsInstance(expected_event, ops.EventBase)
         self.assertFalse(observer.called)
 
         # Verify proper message was given to the user.
@@ -1744,8 +1735,8 @@ class DebugHookTests(BaseTestCase):
         framework = self.create_framework(model=test_model)
         framework._juju_debug_at = {'all'}
 
-        class CustomEvents(ObjectEvents):
-            foobar_action = EventSource(charm.ActionEvent)
+        class CustomEvents(ops.ObjectEvents):
+            foobar_action = ops.EventSource(ops.ActionEvent)
 
         publisher = CustomEvents(framework, "1")
         observer = GenericObserver(framework, "1")
@@ -1765,8 +1756,8 @@ class DebugHookTests(BaseTestCase):
         framework = self.create_framework(model=test_model)
         framework._juju_debug_at = {'hook'}
 
-        class CustomEvents(ObjectEvents):
-            foobar_action = EventSource(charm.ActionEvent)
+        class CustomEvents(ops.ObjectEvents):
+            foobar_action = ops.EventSource(ops.ActionEvent)
 
         publisher = CustomEvents(framework, "1")
         observer = GenericObserver(framework, "1")
@@ -1782,9 +1773,9 @@ class DebugHookTests(BaseTestCase):
         self.assertFalse(observer.called)
 
     def test_internal_events_not_interrupted(self):
-        class MyNotifier(Object):
+        class MyNotifier(ops.Object):
             """Generic notifier for the tests."""
-            bar = EventSource(EventBase)
+            bar = ops.EventSource(ops.EventBase)
 
         framework = self.create_framework()
         framework._juju_debug_at = {'hook'}
@@ -1803,7 +1794,7 @@ class DebugHookTests(BaseTestCase):
         framework = self.create_framework()
         framework._juju_debug_at = {'foo', 'hook', 'all', 'whatever'}
 
-        publisher = charm.CharmEvents(framework, "1")
+        publisher = ops.CharmEvents(framework, "1")
         observer = GenericObserver(framework, "1")
         framework.observe(publisher.install, observer.callback_method)
 
@@ -1818,7 +1809,7 @@ class DebugHookTests(BaseTestCase):
         framework = self.create_framework()
         framework._juju_debug_at = {'hook'}
 
-        publisher = charm.CharmEvents(framework, "1")
+        publisher = ops.CharmEvents(framework, "1")
         observer = GenericObserver(framework, "1")
 
         with patch('pdb.runcall') as mock:
@@ -1831,7 +1822,7 @@ class DebugHookTests(BaseTestCase):
         framework = self.create_framework()
         framework._juju_debug_at = {'something-else'}
 
-        publisher = charm.CharmEvents(framework, "1")
+        publisher = ops.CharmEvents(framework, "1")
         observer = GenericObserver(framework, "1")
         framework.observe(publisher.install, observer.callback_method)
 
@@ -1846,7 +1837,7 @@ class DebugHookTests(BaseTestCase):
         framework = self.create_framework()
         framework._juju_debug_at = set()
 
-        publisher = charm.CharmEvents(framework, "1")
+        publisher = ops.CharmEvents(framework, "1")
         observer = GenericObserver(framework, "1")
         framework.observe(publisher.install, observer.callback_method)
 
@@ -1860,7 +1851,7 @@ class DebugHookTests(BaseTestCase):
         framework = self.create_framework()
         framework._juju_debug_at = {'hook'}
 
-        publisher = charm.CharmEvents(framework, "1")
+        publisher = ops.CharmEvents(framework, "1")
         observer = GenericObserver(framework, "1")
         framework.observe(publisher.install, observer.callback_method)
 
diff --git a/test/test_helpers.py b/test/test_helpers.py
index 83ae8cd..34f951e 100755
--- a/test/test_helpers.py
+++ b/test/test_helpers.py
@@ -19,9 +19,8 @@ import subprocess
 import tempfile
 import unittest
 
-from ops.charm import CharmMeta
-from ops.framework import Framework
-from ops.model import Model, _ModelBackend
+import ops
+from ops.model import _ModelBackend
 from ops.storage import SQLiteStorage
 
 
@@ -120,7 +119,7 @@ class BaseTestCase(unittest.TestCase):
             data_fpath = tmpdir / "framework.data"
             charm_dir = tmpdir
 
-        framework = Framework(
+        framework = ops.Framework(
             SQLiteStorage(data_fpath),
             charm_dir,
             meta=None,
@@ -131,6 +130,6 @@ class BaseTestCase(unittest.TestCase):
     def create_model(self):
         """Create a Model object."""
         backend = _ModelBackend(unit_name='myapp/0')
-        meta = CharmMeta()
-        model = Model(meta, backend)
+        meta = ops.CharmMeta()
+        model = ops.Model(meta, backend)
         return model
diff --git a/test/test_jujuversion.py b/test/test_jujuversion.py
index 542b81e..0d180a6 100755
--- a/test/test_jujuversion.py
+++ b/test/test_jujuversion.py
@@ -15,7 +15,7 @@
 import os
 import unittest.mock  # in this file, importing just 'patch' would be confusing
 
-from ops.jujuversion import JujuVersion
+import ops
 
 
 class TestJujuVersion(unittest.TestCase):
@@ -34,7 +34,7 @@ class TestJujuVersion(unittest.TestCase):
         ]
 
         for vs, major, minor, tag, patch, build in test_cases:
-            v = JujuVersion(vs)
+            v = ops.JujuVersion(vs)
             self.assertEqual(v.major, major)
             self.assertEqual(v.minor, minor)
             self.assertEqual(v.tag, tag)
@@ -44,34 +44,34 @@ class TestJujuVersion(unittest.TestCase):
     @unittest.mock.patch('os.environ', new={})
     def test_from_environ(self):
         # JUJU_VERSION is not set
-        v = JujuVersion.from_environ()
-        self.assertEqual(v, JujuVersion('0.0.0'))
+        v = ops.JujuVersion.from_environ()
+        self.assertEqual(v, ops.JujuVersion('0.0.0'))
 
         os.environ['JUJU_VERSION'] = 'no'
         with self.assertRaisesRegex(RuntimeError, 'not a valid Juju version'):
-            JujuVersion.from_environ()
+            ops.JujuVersion.from_environ()
 
         os.environ['JUJU_VERSION'] = '2.8.0'
-        v = JujuVersion.from_environ()
-        self.assertEqual(v, JujuVersion('2.8.0'))
+        v = ops.JujuVersion.from_environ()
+        self.assertEqual(v, ops.JujuVersion('2.8.0'))
 
     def test_has_app_data(self):
-        self.assertTrue(JujuVersion('2.8.0').has_app_data())
-        self.assertTrue(JujuVersion('2.7.0').has_app_data())
-        self.assertFalse(JujuVersion('2.6.9').has_app_data())
+        self.assertTrue(ops.JujuVersion('2.8.0').has_app_data())
+        self.assertTrue(ops.JujuVersion('2.7.0').has_app_data())
+        self.assertFalse(ops.JujuVersion('2.6.9').has_app_data())
 
     def test_is_dispatch_aware(self):
-        self.assertTrue(JujuVersion('2.8.0').is_dispatch_aware())
-        self.assertFalse(JujuVersion('2.7.9').is_dispatch_aware())
+        self.assertTrue(ops.JujuVersion('2.8.0').is_dispatch_aware())
+        self.assertFalse(ops.JujuVersion('2.7.9').is_dispatch_aware())
 
     def test_has_controller_storage(self):
-        self.assertTrue(JujuVersion('2.8.0').has_controller_storage())
-        self.assertFalse(JujuVersion('2.7.9').has_controller_storage())
+        self.assertTrue(ops.JujuVersion('2.8.0').has_controller_storage())
+        self.assertFalse(ops.JujuVersion('2.7.9').has_controller_storage())
 
     def test_has_secrets(self):
-        self.assertTrue(JujuVersion('3.0.2').has_secrets)
-        self.assertFalse(JujuVersion('3.0.1').has_secrets)
-        self.assertFalse(JujuVersion('2.9.30').has_secrets)
+        self.assertTrue(ops.JujuVersion('3.0.2').has_secrets)
+        self.assertFalse(ops.JujuVersion('3.0.1').has_secrets)
+        self.assertFalse(ops.JujuVersion('2.9.30').has_secrets)
 
     def test_parsing_errors(self):
         invalid_versions = [
@@ -90,7 +90,7 @@ class TestJujuVersion(unittest.TestCase):
         ]
         for v in invalid_versions:
             with self.assertRaises(RuntimeError):
-                JujuVersion(v)
+                ops.JujuVersion(v)
 
     def test_equality(self):
         test_cases = [
@@ -118,8 +118,8 @@ class TestJujuVersion(unittest.TestCase):
         ]
 
         for a, b, expected in test_cases:
-            self.assertEqual(JujuVersion(a) == JujuVersion(b), expected)
-            self.assertEqual(JujuVersion(a) == b, expected)
+            self.assertEqual(ops.JujuVersion(a) == ops.JujuVersion(b), expected)
+            self.assertEqual(ops.JujuVersion(a) == b, expected)
 
     def test_comparison(self):
         test_cases = [
@@ -149,12 +149,12 @@ class TestJujuVersion(unittest.TestCase):
 
         for a, b, expected_strict, expected_weak in test_cases:
             with self.subTest(a=a, b=b):
-                self.assertEqual(JujuVersion(a) < JujuVersion(b), expected_strict)
-                self.assertEqual(JujuVersion(a) <= JujuVersion(b), expected_weak)
-                self.assertEqual(JujuVersion(b) > JujuVersion(a), expected_strict)
-                self.assertEqual(JujuVersion(b) >= JujuVersion(a), expected_weak)
+                self.assertEqual(ops.JujuVersion(a) < ops.JujuVersion(b), expected_strict)
+                self.assertEqual(ops.JujuVersion(a) <= ops.JujuVersion(b), expected_weak)
+                self.assertEqual(ops.JujuVersion(b) > ops.JujuVersion(a), expected_strict)
+                self.assertEqual(ops.JujuVersion(b) >= ops.JujuVersion(a), expected_weak)
                 # Implicit conversion.
-                self.assertEqual(JujuVersion(a) < b, expected_strict)
-                self.assertEqual(JujuVersion(a) <= b, expected_weak)
-                self.assertEqual(b > JujuVersion(a), expected_strict)
-                self.assertEqual(b >= JujuVersion(a), expected_weak)
+                self.assertEqual(ops.JujuVersion(a) < b, expected_strict)
+                self.assertEqual(ops.JujuVersion(a) <= b, expected_weak)
+                self.assertEqual(b > ops.JujuVersion(a), expected_strict)
+                self.assertEqual(b >= ops.JujuVersion(a), expected_weak)
diff --git a/test/test_main.py b/test/test_main.py
index 6e49297..5c81d82 100755
--- a/test/test_main.py
+++ b/test/test_main.py
@@ -28,37 +28,9 @@ from unittest.mock import patch
 
 import logassert
 
-from ops.charm import (
-    ActionEvent,
-    CharmBase,
-    CharmEvents,
-    CharmMeta,
-    CollectMetricsEvent,
-    ConfigChangedEvent,
-    HookEvent,
-    InstallEvent,
-    LeaderSettingsChangedEvent,
-    PebbleReadyEvent,
-    RelationBrokenEvent,
-    RelationChangedEvent,
-    RelationDepartedEvent,
-    RelationEvent,
-    RelationJoinedEvent,
-    SecretChangedEvent,
-    SecretEvent,
-    SecretExpiredEvent,
-    SecretRemoveEvent,
-    SecretRotateEvent,
-    StartEvent,
-    StorageAttachedEvent,
-    UpdateStatusEvent,
-    UpgradeCharmEvent,
-    WorkloadEvent,
-)
-from ops.framework import Framework, LifecycleEvent, StoredStateData
-from ops.main import CHARM_STATE_FILE, _should_use_controller_storage, main
+import ops
+from ops.main import CHARM_STATE_FILE, _should_use_controller_storage
 from ops.storage import SQLiteStorage
-from ops.version import version
 
 from .charms.test_main.src.charm import MyCharmEvents
 from .test_helpers import fake_script, fake_script_calls
@@ -69,7 +41,7 @@ TEST_CHARM_DIR = Path(f"{__file__}/../charms/test_main").resolve()
 
 VERSION_LOGLINE = [
     'juju-log', '--log-level', 'DEBUG', '--',
-    f'Operator Framework {version} up and running.',
+    f'Operator Framework {ops.__version__} up and running.',
 ]
 
 logger = logging.getLogger(__name__)
@@ -105,7 +77,7 @@ class CharmInitTestCase(unittest.TestCase):
 
     @patch('sys.stderr', new_callable=io.StringIO)
     def test_breakpoint(self, fake_stderr):
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             pass
         self._check(MyCharm, extra_environ={'JUJU_DEBUG_AT': 'all'})
 
@@ -116,7 +88,7 @@ class CharmInitTestCase(unittest.TestCase):
         self.assertIn('Starting pdb to debug charm operator', fake_stderr.getvalue())
 
     def test_no_debug_breakpoint(self):
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             pass
         self._check(MyCharm, extra_environ={'JUJU_DEBUG_AT': ''})
 
@@ -144,10 +116,10 @@ class CharmInitTestCase(unittest.TestCase):
                             fh.write(b'name: test')
                         mock_charmdir.return_value = tmpdirname
 
-                        main(charm_class, **kwargs)
+                        ops.main(charm_class, **kwargs)
 
     def test_init_signature_passthrough(self):
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
 
             def __init__(self, *args):
                 super().__init__(*args)
@@ -157,7 +129,7 @@ class CharmInitTestCase(unittest.TestCase):
         self.assertEqual(warn_cm, [])
 
     def test_init_signature_old_key_argument(self):
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
 
             def __init__(self, framework, somekey):
                 super().__init__(framework, somekey)
@@ -167,7 +139,7 @@ class CharmInitTestCase(unittest.TestCase):
             self._check(MyCharm)
 
     def test_init_signature_only_framework(self):
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
 
             def __init__(self, framework):
                 super().__init__(framework)
@@ -183,21 +155,21 @@ class CharmInitTestCase(unittest.TestCase):
             with self.assertRaisesRegex(
                     RuntimeError,
                     'charm set use_juju_for_storage=True, but Juju .* does not support it'):
-                self._check(CharmBase, use_juju_for_storage=True)
+                self._check(ops.CharmBase, use_juju_for_storage=True)
 
     def test_storage_with_storage(self):
         # here we patch juju_backend_available, so it gets set up and falls over when used
         with patch('ops.storage.juju_backend_available') as juju_backend_available:
             juju_backend_available.return_value = True
             with self.assertRaisesRegex(FileNotFoundError, 'state-get'):
-                self._check(CharmBase, use_juju_for_storage=True)
+                self._check(ops.CharmBase, use_juju_for_storage=True)
 
     def test_controller_storage_deprecated(self):
         with patch('ops.storage.juju_backend_available') as juju_backend_available:
             juju_backend_available.return_value = True
             with self.assertWarnsRegex(DeprecationWarning, 'Controller storage'):
                 with self.assertRaisesRegex(FileNotFoundError, 'state-get'):
-                    self._check(CharmBase, use_juju_for_storage=True)
+                    self._check(ops.CharmBase, use_juju_for_storage=True)
 
 
 @patch('sys.argv', new=("hooks/config-changed",))
@@ -205,7 +177,7 @@ class CharmInitTestCase(unittest.TestCase):
 class TestDispatch(unittest.TestCase):
     def _check(self, *, with_dispatch=False, dispatch_path=''):
         """Helper for below tests."""
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             def __init__(self, framework):
                 super().__init__(framework)
 
@@ -233,7 +205,7 @@ class TestDispatch(unittest.TestCase):
                 with patch('ops.main._emit_charm_event') as mock_charm_event:
                     with patch('ops.main._get_charm_dir') as mock_charmdir:
                         mock_charmdir.return_value = tmpdir
-                        main(MyCharm)
+                        ops.main(MyCharm)
 
         self.assertEqual(mock_charm_event.call_count, 1)
         return mock_charm_event.call_args[0][1]
@@ -285,12 +257,12 @@ class _TestMain(abc.ABC):
 
         # Relations events are defined dynamically and modify the class attributes.
         # We use a subclass temporarily to prevent these side effects from leaking.
-        class TestCharmEvents(CharmEvents):
+        class TestCharmEvents(ops.CharmEvents):
             pass
-        CharmBase.on = TestCharmEvents()
+        ops.CharmBase.on = TestCharmEvents()
 
         def cleanup():
-            CharmBase.on = CharmEvents()
+            ops.CharmBase.on = ops.CharmEvents()
         self.addCleanup(cleanup)
 
         fake_script(self, 'juju-log', "exit 0")
@@ -337,10 +309,10 @@ class _TestMain(abc.ABC):
                 af = (self.JUJU_CHARM_DIR / 'actions.yaml')
                 if af.exists():
                     with af.open() as a:
-                        meta = CharmMeta.from_yaml(m, a)
+                        meta = ops.CharmMeta.from_yaml(m, a)
                 else:
-                    meta = CharmMeta.from_yaml(m)
-            framework = Framework(storage, self.JUJU_CHARM_DIR, meta, None, event_name)
+                    meta = ops.CharmMeta.from_yaml(m)
+            framework = ops.Framework(storage, self.JUJU_CHARM_DIR, meta, None, event_name)
 
             class ThisCharmEvents(MyCharmEvents):
                 pass
@@ -355,7 +327,7 @@ class _TestMain(abc.ABC):
             storage.commit()
             framework.close()
         else:
-            stored = StoredStateData(None, None)
+            stored = ops.StoredStateData(None, None)
         return stored
 
     def _simulate_event(self, event_spec):
@@ -372,16 +344,16 @@ class _TestMain(abc.ABC):
         })
         if event_spec.set_in_env is not None:
             env.update(event_spec.set_in_env)
-        if issubclass(event_spec.event_type, SecretEvent):
+        if issubclass(event_spec.event_type, ops.SecretEvent):
             env.update({
                 'JUJU_SECRET_ID': event_spec.secret_id,
                 'JUJU_SECRET_LABEL': event_spec.secret_label or '',
             })
-        if issubclass(event_spec.event_type, (SecretRemoveEvent, SecretExpiredEvent)):
+        if issubclass(event_spec.event_type, (ops.SecretRemoveEvent, ops.SecretExpiredEvent)):
             env.update({
                 'JUJU_SECRET_REVISION': str(event_spec.secret_revision or ''),
             })
-        if issubclass(event_spec.event_type, RelationEvent):
+        if issubclass(event_spec.event_type, ops.RelationEvent):
             rel_name = event_spec.event_name.split('_')[0]
             env.update({
                 'JUJU_RELATION': rel_name,
@@ -406,11 +378,11 @@ class _TestMain(abc.ABC):
                 'JUJU_REMOTE_UNIT': '',
                 'JUJU_REMOTE_APP': '',
             })
-        if issubclass(event_spec.event_type, WorkloadEvent):
+        if issubclass(event_spec.event_type, ops.WorkloadEvent):
             env.update({
                 'JUJU_WORKLOAD_NAME': event_spec.workload_name,
             })
-        if issubclass(event_spec.event_type, ActionEvent):
+        if issubclass(event_spec.event_type, ops.ActionEvent):
             event_filename = event_spec.event_name[:-len('_action')].replace('_', '-')
             env.update({
                 event_spec.env_var: event_filename,
@@ -430,14 +402,14 @@ class _TestMain(abc.ABC):
 
     def test_event_reemitted(self):
         # First run "install" to make sure all hooks are set up.
-        state = self._simulate_event(EventSpec(InstallEvent, 'install'))
+        state = self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
         self.assertEqual(list(state.observed_event_types), ['InstallEvent'])
 
-        state = self._simulate_event(EventSpec(ConfigChangedEvent, 'config-changed'))
+        state = self._simulate_event(EventSpec(ops.ConfigChangedEvent, 'config-changed'))
         self.assertEqual(list(state.observed_event_types), ['ConfigChangedEvent'])
 
         # Re-emit should pick the deferred config-changed.
-        state = self._simulate_event(EventSpec(UpdateStatusEvent, 'update-status'))
+        state = self._simulate_event(EventSpec(ops.UpdateStatusEvent, 'update-status'))
         self.assertEqual(
             list(state.observed_event_types),
             ['ConfigChangedEvent', 'UpdateStatusEvent'])
@@ -446,15 +418,15 @@ class _TestMain(abc.ABC):
         fake_script(self, 'add-metric', 'exit 0')
 
         # First run "install" to make sure all hooks are set up.
-        state = self._simulate_event(EventSpec(InstallEvent, 'install'))
+        state = self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
         self.assertEqual(list(state.observed_event_types), ['InstallEvent'])
 
-        state = self._simulate_event(EventSpec(ConfigChangedEvent, 'config-changed'))
+        state = self._simulate_event(EventSpec(ops.ConfigChangedEvent, 'config-changed'))
         self.assertEqual(list(state.observed_event_types), ['ConfigChangedEvent'])
 
         # Re-emit should not pick the deferred config-changed because
         # collect-metrics runs in a restricted context.
-        state = self._simulate_event(EventSpec(CollectMetricsEvent, 'collect-metrics'))
+        state = self._simulate_event(EventSpec(ops.CollectMetricsEvent, 'collect-metrics'))
         self.assertEqual(list(state.observed_event_types), ['CollectMetricsEvent'])
 
     def test_multiple_events_handled(self):
@@ -465,19 +437,19 @@ class _TestMain(abc.ABC):
         # Sample events with a different amount of dashes used
         # and with endpoints from different sections of metadata.yaml
         events_under_test = [(
-            EventSpec(InstallEvent, 'install'),
+            EventSpec(ops.InstallEvent, 'install'),
             {},
         ), (
-            EventSpec(StartEvent, 'start'),
+            EventSpec(ops.StartEvent, 'start'),
             {},
         ), (
-            EventSpec(UpdateStatusEvent, 'update_status'),
+            EventSpec(ops.UpdateStatusEvent, 'update_status'),
             {},
         ), (
-            EventSpec(LeaderSettingsChangedEvent, 'leader_settings_changed'),
+            EventSpec(ops.LeaderSettingsChangedEvent, 'leader_settings_changed'),
             {},
         ), (
-            EventSpec(RelationJoinedEvent, 'db_relation_joined',
+            EventSpec(ops.RelationJoinedEvent, 'db_relation_joined',
                       relation_id=1,
                       remote_app='remote',
                       remote_unit='remote/0'),
@@ -486,7 +458,7 @@ class _TestMain(abc.ABC):
              'app_name': 'remote',
              'unit_name': 'remote/0'},
         ), (
-            EventSpec(RelationChangedEvent, 'mon_relation_changed',
+            EventSpec(ops.RelationChangedEvent, 'mon_relation_changed',
                       relation_id=2,
                       remote_app='remote',
                       remote_unit='remote/0'),
@@ -495,7 +467,7 @@ class _TestMain(abc.ABC):
              'app_name': 'remote',
              'unit_name': 'remote/0'},
         ), (
-            EventSpec(RelationChangedEvent, 'mon_relation_changed',
+            EventSpec(ops.RelationChangedEvent, 'mon_relation_changed',
                       relation_id=2,
                       remote_app='remote',
                       remote_unit=None),
@@ -504,7 +476,7 @@ class _TestMain(abc.ABC):
              'app_name': 'remote',
              'unit_name': None},
         ), (
-            EventSpec(RelationDepartedEvent, 'mon_relation_departed',
+            EventSpec(ops.RelationDepartedEvent, 'mon_relation_departed',
                       relation_id=2,
                       remote_app='remote',
                       remote_unit='remote/0',
@@ -515,13 +487,13 @@ class _TestMain(abc.ABC):
              'unit_name': 'remote/0',
              'departing_unit_name': 'remote/42'},
         ), (
-            EventSpec(RelationBrokenEvent, 'ha_relation_broken',
+            EventSpec(ops.RelationBrokenEvent, 'ha_relation_broken',
                       relation_id=3),
             {'relation_name': 'ha',
              'relation_id': 3},
         ), (
             # Events without a remote app specified (for Juju < 2.7).
-            EventSpec(RelationJoinedEvent, 'db_relation_joined',
+            EventSpec(ops.RelationJoinedEvent, 'db_relation_joined',
                       relation_id=1,
                       remote_unit='remote/0'),
             {'relation_name': 'db',
@@ -529,7 +501,7 @@ class _TestMain(abc.ABC):
              'app_name': 'remote',
              'unit_name': 'remote/0'},
         ), (
-            EventSpec(RelationChangedEvent, 'mon_relation_changed',
+            EventSpec(ops.RelationChangedEvent, 'mon_relation_changed',
                       relation_id=2,
                       remote_unit='remote/0'),
             {'relation_name': 'mon',
@@ -537,7 +509,7 @@ class _TestMain(abc.ABC):
              'app_name': 'remote',
              'unit_name': 'remote/0'},
         ), (
-            EventSpec(RelationDepartedEvent, 'mon_relation_departed',
+            EventSpec(ops.RelationDepartedEvent, 'mon_relation_departed',
                       relation_id=2,
                       remote_unit='remote/0',
                       departing_unit_name='remote/42'),
@@ -547,26 +519,26 @@ class _TestMain(abc.ABC):
              'unit_name': 'remote/0',
              'departing_unit_name': 'remote/42'},
         ), (
-            EventSpec(ActionEvent, 'start_action',
+            EventSpec(ops.ActionEvent, 'start_action',
                       env_var='JUJU_ACTION_NAME'),
             {},
         ), (
-            EventSpec(ActionEvent, 'foo_bar_action',
+            EventSpec(ops.ActionEvent, 'foo_bar_action',
                       env_var='JUJU_ACTION_NAME'),
             {},
         ), (
-            EventSpec(PebbleReadyEvent, 'test_pebble_ready',
+            EventSpec(ops.PebbleReadyEvent, 'test_pebble_ready',
                       workload_name='test'),
             {'container_name': 'test'},
         ), (
-            EventSpec(SecretChangedEvent, 'secret_changed',
+            EventSpec(ops.SecretChangedEvent, 'secret_changed',
                       secret_id='secret:12345',
                       secret_label='foo'),
             {'id': 'secret:12345',
              'label': 'foo',
              'revision': 42}
         ), (
-            EventSpec(SecretRotateEvent, 'secret_rotate',
+            EventSpec(ops.SecretRotateEvent, 'secret_rotate',
                       secret_id='secret:12345',
                       secret_label='foo',
                       secret_revision='42'),
@@ -574,7 +546,7 @@ class _TestMain(abc.ABC):
              'label': 'foo',
              'revision': 42}
         ), (
-            EventSpec(SecretRemoveEvent, 'secret_remove',
+            EventSpec(ops.SecretRemoveEvent, 'secret_remove',
                       secret_id='secret:12345',
                       secret_label='foo',
                       secret_revision='42'),
@@ -582,7 +554,7 @@ class _TestMain(abc.ABC):
              'label': 'foo',
              'revision': 42}
         ), (
-            EventSpec(SecretExpiredEvent, 'secret_expired',
+            EventSpec(ops.SecretExpiredEvent, 'secret_expired',
                       secret_id='secret:12345',
                       secret_label='foo',
                       secret_revision='42'),
@@ -594,7 +566,7 @@ class _TestMain(abc.ABC):
         logger.debug('Expected events %s', events_under_test)
 
         # First run "install" to make sure all hooks are set up.
-        self._simulate_event(EventSpec(InstallEvent, 'install'))
+        self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
 
         # Simulate hook executions for every event.
         for event_spec, expected_event_data in events_under_test:
@@ -624,25 +596,25 @@ class _TestMain(abc.ABC):
         hook_path.symlink_to('install')
 
         try:
-            self._simulate_event(EventSpec(HookEvent, 'not-implemented-event'))
+            self._simulate_event(EventSpec(ops.HookEvent, 'not-implemented-event'))
         except subprocess.CalledProcessError:
             self.fail('Event simulation for an unsupported event'
                       ' results in a non-zero exit code returned')
 
     def test_no_actions(self):
         (self.JUJU_CHARM_DIR / 'actions.yaml').unlink()
-        self._simulate_event(EventSpec(InstallEvent, 'install'))
+        self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
 
     def test_empty_actions(self):
         (self.JUJU_CHARM_DIR / 'actions.yaml').write_text('')
-        self._simulate_event(EventSpec(InstallEvent, 'install'))
+        self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
 
     def test_collect_metrics(self):
         fake_script(self, 'add-metric', 'exit 0')
-        self._simulate_event(EventSpec(InstallEvent, 'install'))
+        self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
         # Clear the calls during 'install'
         fake_script_calls(self, clear=True)
-        self._simulate_event(EventSpec(CollectMetricsEvent, 'collect_metrics'))
+        self._simulate_event(EventSpec(ops.CollectMetricsEvent, 'collect_metrics'))
 
         expected = [
             VERSION_LOGLINE,
@@ -654,10 +626,10 @@ class _TestMain(abc.ABC):
         self.assertEqual(calls, expected)
 
     def test_custom_event(self):
-        self._simulate_event(EventSpec(InstallEvent, 'install'))
+        self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
         # Clear the calls during 'install'
         fake_script_calls(self, clear=True)
-        self._simulate_event(EventSpec(UpdateStatusEvent, 'update-status',
+        self._simulate_event(EventSpec(ops.UpdateStatusEvent, 'update-status',
                                        set_in_env={'EMIT_CUSTOM_EVENT': "1"}))
 
         expected = [
@@ -673,24 +645,24 @@ class _TestMain(abc.ABC):
         fake_script(self, 'action-get', "echo '{}'")
 
         test_cases = [(
-            EventSpec(ActionEvent, 'log_critical_action', env_var='JUJU_ACTION_NAME'),
+            EventSpec(ops.ActionEvent, 'log_critical_action', env_var='JUJU_ACTION_NAME'),
             ['juju-log', '--log-level', 'CRITICAL', '--', 'super critical'],
         ), (
-            EventSpec(ActionEvent, 'log_error_action',
+            EventSpec(ops.ActionEvent, 'log_error_action',
                       env_var='JUJU_ACTION_NAME'),
             ['juju-log', '--log-level', 'ERROR', '--', 'grave error'],
         ), (
-            EventSpec(ActionEvent, 'log_warning_action',
+            EventSpec(ops.ActionEvent, 'log_warning_action',
                       env_var='JUJU_ACTION_NAME'),
             ['juju-log', '--log-level', 'WARNING', '--', 'wise warning'],
         ), (
-            EventSpec(ActionEvent, 'log_info_action',
+            EventSpec(ops.ActionEvent, 'log_info_action',
                       env_var='JUJU_ACTION_NAME'),
             ['juju-log', '--log-level', 'INFO', '--', 'useful info'],
         )]
 
         # Set up action symlinks.
-        self._simulate_event(EventSpec(InstallEvent, 'install'))
+        self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
 
         for event_spec, calls in test_cases:
             self._simulate_event(event_spec)
@@ -698,7 +670,7 @@ class _TestMain(abc.ABC):
 
     def test_excepthook(self):
         with self.assertRaises(subprocess.CalledProcessError):
-            self._simulate_event(EventSpec(InstallEvent, 'install',
+            self._simulate_event(EventSpec(ops.InstallEvent, 'install',
                                            set_in_env={'TRY_EXCEPTHOOK': '1'}))
 
         calls = [' '.join(i) for i in fake_script_calls(self)]
@@ -723,7 +695,7 @@ class _TestMain(abc.ABC):
 
         fake_script(self, 'action-get', "echo '{}'")
         state = self._simulate_event(EventSpec(
-            ActionEvent, 'get_model_name_action',
+            ops.ActionEvent, 'get_model_name_action',
             env_var='JUJU_ACTION_NAME',
             model_name='test-model-name'))
         self.assertIsNotNone(state)
@@ -735,7 +707,7 @@ class _TestMain(abc.ABC):
         fake_script(self, 'action-get', "echo '{}'")
         fake_script(self, 'status-get', """echo '{"status": "unknown", "message": ""}'""")
         state = self._simulate_event(EventSpec(
-            ActionEvent, 'get_status_action',
+            ops.ActionEvent, 'get_status_action',
             env_var='JUJU_ACTION_NAME'))
         self.assertIsNotNone(state)
         self.assertEqual(state.status_name, 'unknown')
@@ -743,7 +715,7 @@ class _TestMain(abc.ABC):
         fake_script(
             self, 'status-get', """echo '{"status": "blocked", "message": "help meeee"}'""")
         state = self._simulate_event(EventSpec(
-            ActionEvent, 'get_status_action',
+            ops.ActionEvent, 'get_status_action',
             env_var='JUJU_ACTION_NAME'))
         self.assertIsNotNone(state)
         self.assertEqual(state.status_name, 'blocked')
@@ -811,13 +783,13 @@ class TestMainWithNoDispatch(_TestMain, unittest.TestCase):
         """Test auto-creation of symlinks caused by initial events."""
         all_event_hooks = [f"hooks/{name.replace('_', '-')}"
                            for name, event_source in self.charm_module.Charm.on.events().items()
-                           if issubclass(event_source.event_type, LifecycleEvent)]
+                           if issubclass(event_source.event_type, ops.LifecycleEvent)]
 
         initial_events = {
-            EventSpec(InstallEvent, 'install'),
-            EventSpec(StorageAttachedEvent, 'disks-storage-attached'),
-            EventSpec(StartEvent, 'start'),
-            EventSpec(UpgradeCharmEvent, 'upgrade-charm'),
+            EventSpec(ops.InstallEvent, 'install'),
+            EventSpec(ops.StorageAttachedEvent, 'disks-storage-attached'),
+            EventSpec(ops.StartEvent, 'start'),
+            EventSpec(ops.UpgradeCharmEvent, 'upgrade-charm'),
         }
         initial_hooks = {f"hooks/{ev.event_name}" for ev in initial_events}
 
@@ -847,7 +819,7 @@ class TestMainWithNoDispatch(_TestMain, unittest.TestCase):
             _assess_event_links(initial_event)
 
     def test_setup_action_links(self):
-        self._simulate_event(EventSpec(InstallEvent, 'install'))
+        self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
         # foo-bar is one of the actions defined in actions.yaml
         action_hook = self.JUJU_CHARM_DIR / 'actions' / 'foo-bar'
         self.assertTrue(action_hook.exists())
@@ -886,10 +858,10 @@ class _TestMainWithDispatch(_TestMain):
         all_event_hooks = [f"hooks/{e.replace('_', '-')}"
                            for e in self.charm_module.Charm.on.events().keys()]
         initial_events = {
-            EventSpec(InstallEvent, 'install'),
-            EventSpec(StorageAttachedEvent, 'disks-storage-attached'),
-            EventSpec(StartEvent, 'start'),
-            EventSpec(UpgradeCharmEvent, 'upgrade-charm'),
+            EventSpec(ops.InstallEvent, 'install'),
+            EventSpec(ops.StorageAttachedEvent, 'disks-storage-attached'),
+            EventSpec(ops.StartEvent, 'start'),
+            EventSpec(ops.UpgradeCharmEvent, 'upgrade-charm'),
         }
 
         def _assess_event_links(event_spec):
@@ -908,7 +880,7 @@ class _TestMainWithDispatch(_TestMain):
         old_path = self.fake_script_path
         self.fake_script_path = self.hooks_dir
         fake_script(self, 'install', 'exit 0')
-        state = self._simulate_event(EventSpec(InstallEvent, 'install'))
+        state = self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
 
         # the script was called, *and*, the .on. was called
         self.assertEqual(fake_script_calls(self), [['install', '']])
@@ -933,7 +905,7 @@ class _TestMainWithDispatch(_TestMain):
 
     def test_non_executable_hook_and_dispatch(self):
         (self.hooks_dir / "install").write_text("")
-        state = self._simulate_event(EventSpec(InstallEvent, 'install'))
+        state = self._simulate_event(EventSpec(ops.InstallEvent, 'install'))
 
         self.assertEqual(list(state.observed_event_types), ['InstallEvent'])
 
@@ -957,7 +929,7 @@ class _TestMainWithDispatch(_TestMain):
         old_path = self.fake_script_path
         self.fake_script_path = self.hooks_dir
         fake_script(self, 'install', 'exit 42')
-        event = EventSpec(InstallEvent, 'install')
+        event = EventSpec(ops.InstallEvent, 'install')
         with self.assertRaises(subprocess.CalledProcessError):
             self._simulate_event(event)
         self.fake_script_path = old_path
@@ -975,7 +947,7 @@ class _TestMainWithDispatch(_TestMain):
         self.assertEqual(calls, expected)
 
     def test_hook_and_dispatch_but_hook_is_dispatch(self):
-        event = EventSpec(InstallEvent, 'install')
+        event = EventSpec(ops.InstallEvent, 'install')
         hook_path = self.hooks_dir / 'install'
         for ((rel, ind), path) in {
                 # relative and indirect
@@ -1007,7 +979,7 @@ class _TestMainWithDispatch(_TestMain):
         path = (self.hooks_dir / self.charm_exec_path).resolve()
         shutil.copy(str(path), str(hook_path))
 
-        event = EventSpec(InstallEvent, 'install')
+        event = EventSpec(ops.InstallEvent, 'install')
         state = self._simulate_event(event)
 
         # the .on. was only called once
@@ -1156,24 +1128,24 @@ class TestStorageHeuristics(unittest.TestCase):
         logassert.setup(self, '')
 
     def test_fallback_to_current_juju_version__too_old(self):
-        meta = CharmMeta.from_yaml("series: [kubernetes]")
+        meta = ops.CharmMeta.from_yaml("series: [kubernetes]")
         with patch.dict(os.environ, {"JUJU_VERSION": "1.0"}):
             self.assertFalse(_should_use_controller_storage(Path("/xyzzy"), meta))
             self.assertLogged('Using local storage: JUJU_VERSION=1.0.0')
 
     def test_fallback_to_current_juju_version__new_enough(self):
-        meta = CharmMeta.from_yaml("series: [kubernetes]")
+        meta = ops.CharmMeta.from_yaml("series: [kubernetes]")
         with patch.dict(os.environ, {"JUJU_VERSION": "2.8"}):
             self.assertTrue(_should_use_controller_storage(Path("/xyzzy"), meta))
             self.assertLogged('Using controller storage: JUJU_VERSION=2.8.0')
 
     def test_not_if_not_in_k8s(self):
-        meta = CharmMeta.from_yaml("series: [ecs]")
+        meta = ops.CharmMeta.from_yaml("series: [ecs]")
         with patch.dict(os.environ, {"JUJU_VERSION": "2.8"}):
             self.assertFalse(_should_use_controller_storage(Path("/xyzzy"), meta))
             self.assertLogged('Using local storage: not a Kubernetes podspec charm')
 
     def test_not_if_already_local(self):
-        meta = CharmMeta.from_yaml("series: [kubernetes]")
+        meta = ops.CharmMeta.from_yaml("series: [kubernetes]")
         with patch.dict(os.environ, {"JUJU_VERSION": "2.8"}), tempfile.NamedTemporaryFile() as fd:
             self.assertFalse(_should_use_controller_storage(Path(fd.name), meta))
diff --git a/test/test_model.py b/test/test_model.py
index 703cd7a..92a78bd 100755
--- a/test/test_model.py
+++ b/test/test_model.py
@@ -27,25 +27,16 @@ from textwrap import dedent
 
 import pytest
 
-import ops.charm
-import ops.model
+import ops
 import ops.testing
-from ops import model
+from ops import pebble
 from ops._private import yaml
-from ops.charm import RelationMeta, RelationRole
-from ops.pebble import (
-    APIError,
-    ConnectionError,
-    FileInfo,
-    FileType,
-    ServiceInfo,
-    SystemInfo,
-)
+from ops.model import _ModelBackend
 
 
 class TestModel(unittest.TestCase):
     def setUp(self):
-        self.harness = ops.testing.Harness(ops.charm.CharmBase, meta='''
+        self.harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: myapp
             provides:
               db0:
@@ -87,7 +78,7 @@ class TestModel(unittest.TestCase):
 
     def test_model_name_from_backend(self):
         self.harness.set_model_name('default')
-        m = ops.model.Model(ops.charm.CharmMeta(), self.harness._backend)
+        m = ops.Model(ops.CharmMeta(), self.harness._backend)
         self.assertEqual(m.name, 'default')
         with self.assertRaises(AttributeError):
             m.name = "changes-disallowed"
@@ -126,17 +117,17 @@ class TestModel(unittest.TestCase):
         relation_id_db0_b = self.harness.add_relation('db0', 'another')
         self.resetBackendCalls()
 
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             # You have to specify it by just the integer ID
             self.model.get_relation('db1', f'db1:{relation_id_db1}')
         rel_db1 = self.model.get_relation('db1', relation_id_db1)
-        self.assertIsInstance(rel_db1, ops.model.Relation)
+        self.assertIsInstance(rel_db1, ops.Relation)
         self.assertBackendCalls([
             ('relation_ids', 'db1'),
             ('relation_list', relation_id_db1),
         ])
         dead_rel = self.model.get_relation('db1', 7)
-        self.assertIsInstance(dead_rel, ops.model.Relation)
+        self.assertIsInstance(dead_rel, ops.Relation)
         self.assertEqual(set(dead_rel.data.keys()), {self.model.unit, self.model.unit.app})
         self.assertEqual(dead_rel.data[self.model.unit], {})
         self.assertBackendCalls([
@@ -150,7 +141,7 @@ class TestModel(unittest.TestCase):
             ('relation_ids', 'db2'),
         ])
         self.assertIs(self.model.get_relation('db1'), rel_db1)
-        with self.assertRaises(ops.model.TooManyRelatedAppsError):
+        with self.assertRaises(ops.TooManyRelatedAppsError):
             self.model.get_relation('db0')
 
         self.assertBackendCalls([
@@ -189,14 +180,14 @@ class TestModel(unittest.TestCase):
         relation_id = self.harness.add_relation('db1', 'remoteapp1')
         self.harness.add_relation_unit(relation_id, 'remoteapp1/0')
 
-        with self.assertRaises(model.RelationDataError):
+        with self.assertRaises(ops.RelationDataError):
             with self.harness._event_context('foo_event'):
                 self.harness.update_relation_data(
                     relation_id,
                     'remoteapp1/0',
                     {42: 'remoteapp1-0'})
 
-        with self.assertRaises(model.RelationDataError):
+        with self.assertRaises(ops.RelationDataError):
             with self.harness._event_context('foo_event'):
                 self.harness.update_relation_data(
                     relation_id,
@@ -253,7 +244,7 @@ class TestModel(unittest.TestCase):
 
         rel_db1 = self.model.get_relation('db1')
         # Try to get relation data for an invalid remote application.
-        random_app = self.model._cache.get(ops.model.Application, 'randomapp')
+        random_app = self.model._cache.get(ops.Application, 'randomapp')
         with self.assertRaises(KeyError):
             rel_db1.data[random_app]
 
@@ -287,7 +278,7 @@ class TestModel(unittest.TestCase):
         self.assertEqual(repr(rel_db1.data[remoteapp1_0]), "{'host': 'remoteapp1/0'}")
 
         with self.harness._event_context('foo_event'):
-            with self.assertRaises(ops.model.RelationDataError):
+            with self.assertRaises(ops.RelationDataError):
                 rel_db1.data[remoteapp1_0]['foo'] = 'bar'
         self.assertNotIn('foo', rel_db1.data[remoteapp1_0])
 
@@ -363,7 +354,7 @@ class TestModel(unittest.TestCase):
 
         with self.harness._event_context('foo_event'):
             # if we were inside an event context, we'd get:
-            with self.assertRaises(ops.model.RelationDataError):
+            with self.assertRaises(ops.RelationDataError):
                 rel_db1.data[local_app]['password'] = 'foobar'
 
         self.assertBackendCalls([('relation_ids', 'db1'),
@@ -451,7 +442,7 @@ class TestModel(unittest.TestCase):
 
         def broken_update_relation_data(relation_id, entity, key, value):
             backend._calls.append(('update_relation_data', relation_id, entity, key, value))
-            raise ops.model.ModelError()
+            raise ops.ModelError()
         backend.update_relation_data = broken_update_relation_data
 
         rel_db1 = self.model.get_relation('db1')
@@ -459,10 +450,10 @@ class TestModel(unittest.TestCase):
         self.assertIn('host', rel_db1.data[self.model.unit])
 
         with self.harness._event_context('foo_event'):
-            with self.assertRaises(ops.model.ModelError):
+            with self.assertRaises(ops.ModelError):
                 rel_db1.data[self.model.unit]['host'] = 'bar'
             self.assertEqual(rel_db1.data[self.model.unit]['host'], 'myapp-0')
-            with self.assertRaises(ops.model.ModelError):
+            with self.assertRaises(ops.ModelError):
                 del rel_db1.data[self.model.unit]['host']
             self.assertIn('host', rel_db1.data[self.model.unit])
 
@@ -491,7 +482,7 @@ class TestModel(unittest.TestCase):
                 (1, 1),
                 (None, None)
         ):
-            with self.assertRaises(ops.model.RelationDataError):
+            with self.assertRaises(ops.RelationDataError):
                 with self.harness.framework._event_context('foo_event'):
                     rel_db1.data[self.model.unit][key] = value
 
@@ -574,7 +565,7 @@ class TestModel(unittest.TestCase):
         with self.harness._event_context('foo_event'):
             self.resetBackendCalls()
 
-            with self.assertRaises(ops.model.RelationDataError):
+            with self.assertRaises(ops.RelationDataError):
                 # 'local' is there, but still:
                 rel_db1.data[local_app]['local']
 
@@ -667,7 +658,7 @@ class TestModel(unittest.TestCase):
         self.assertBackendCalls([])
 
     def test_resources(self):
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             self.harness.model.resources.fetch('foo')
 
         self.harness.add_resource('foo', 'foo contents\n')
@@ -693,7 +684,7 @@ class TestModel(unittest.TestCase):
 
         # no leader -> no set pod spec
         self.harness.set_leader(False)
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             self.harness.model.pod.set_spec({'foo': 'bar'})
 
     def test_pod_immutable(self):
@@ -702,36 +693,36 @@ class TestModel(unittest.TestCase):
 
     def test_base_status_instance_raises(self):
         with self.assertRaises(TypeError):
-            ops.model.StatusBase('test')
+            ops.StatusBase('test')
 
-        class NoNameStatus(ops.model.StatusBase):
+        class NoNameStatus(ops.StatusBase):
             pass
 
         with self.assertRaises(AttributeError):
-            ops.model.StatusBase.register_status(NoNameStatus)
+            ops.StatusBase.register_status(NoNameStatus)
 
     def test_status_repr(self):
         test_cases = {
-            "ActiveStatus('Seashell')": ops.model.ActiveStatus('Seashell'),
-            "MaintenanceStatus('Red')": ops.model.MaintenanceStatus('Red'),
-            "BlockedStatus('Magenta')": ops.model.BlockedStatus('Magenta'),
-            "WaitingStatus('Thistle')": ops.model.WaitingStatus('Thistle'),
-            'UnknownStatus()': ops.model.UnknownStatus(),
+            "ActiveStatus('Seashell')": ops.ActiveStatus('Seashell'),
+            "MaintenanceStatus('Red')": ops.MaintenanceStatus('Red'),
+            "BlockedStatus('Magenta')": ops.BlockedStatus('Magenta'),
+            "WaitingStatus('Thistle')": ops.WaitingStatus('Thistle'),
+            'UnknownStatus()': ops.UnknownStatus(),
         }
         for expected, status in test_cases.items():
             self.assertEqual(repr(status), expected)
 
     def test_status_eq(self):
         status_types = [
-            ops.model.ActiveStatus,
-            ops.model.MaintenanceStatus,
-            ops.model.BlockedStatus,
-            ops.model.WaitingStatus,
+            ops.ActiveStatus,
+            ops.MaintenanceStatus,
+            ops.BlockedStatus,
+            ops.WaitingStatus,
         ]
 
-        self.assertEqual(ops.model.UnknownStatus(), ops.model.UnknownStatus())
+        self.assertEqual(ops.UnknownStatus(), ops.UnknownStatus())
         for (i, t1) in enumerate(status_types):
-            self.assertNotEqual(t1(''), ops.model.UnknownStatus())
+            self.assertNotEqual(t1(''), ops.UnknownStatus())
             for (j, t2) in enumerate(status_types):
                 self.assertNotEqual(t1('one'), t2('two'))
                 if i == j:
@@ -740,25 +731,25 @@ class TestModel(unittest.TestCase):
                     self.assertNotEqual(t1('one'), t2('one'))
 
     def test_active_message_default(self):
-        self.assertEqual(ops.model.ActiveStatus().message, '')
+        self.assertEqual(ops.ActiveStatus().message, '')
 
     def test_local_set_valid_unit_status(self):
         self.harness._get_backend_calls(reset=True)
         test_cases = [(
             'active',
-            ops.model.ActiveStatus('Green'),
+            ops.ActiveStatus('Green'),
             ('status_set', 'active', 'Green', {'is_app': False}),
         ), (
             'maintenance',
-            ops.model.MaintenanceStatus('Yellow'),
+            ops.MaintenanceStatus('Yellow'),
             ('status_set', 'maintenance', 'Yellow', {'is_app': False}),
         ), (
             'blocked',
-            ops.model.BlockedStatus('Red'),
+            ops.BlockedStatus('Red'),
             ('status_set', 'blocked', 'Red', {'is_app': False}),
         ), (
             'waiting',
-            ops.model.WaitingStatus('White'),
+            ops.WaitingStatus('White'),
             ('status_set', 'waiting', 'White', {'is_app': False}),
         )]
 
@@ -774,19 +765,19 @@ class TestModel(unittest.TestCase):
         self.harness.set_leader(True)
         test_cases = [(
             'active',
-            ops.model.ActiveStatus('Green'),
+            ops.ActiveStatus('Green'),
             ('status_set', 'active', 'Green', {'is_app': True}),
         ), (
             'maintenance',
-            ops.model.MaintenanceStatus('Yellow'),
+            ops.MaintenanceStatus('Yellow'),
             ('status_set', 'maintenance', 'Yellow', {'is_app': True}),
         ), (
             'blocked',
-            ops.model.BlockedStatus('Red'),
+            ops.BlockedStatus('Red'),
             ('status_set', 'blocked', 'Red', {'is_app': True}),
         ), (
             'waiting',
-            ops.model.WaitingStatus('White'),
+            ops.WaitingStatus('White'),
             ('status_set', 'waiting', 'White', {'is_app': True}),
         )]
 
@@ -811,14 +802,14 @@ class TestModel(unittest.TestCase):
             self.model.app.status
 
         with self.assertRaises(RuntimeError):
-            self.model.app.status = ops.model.ActiveStatus()
+            self.model.app.status = ops.ActiveStatus()
 
     def test_set_unit_status_invalid(self):
-        with self.assertRaises(ops.model.InvalidStatusError):
+        with self.assertRaises(ops.InvalidStatusError):
             self.model.unit.status = 'blocked'
 
     def test_set_app_status_invalid(self):
-        with self.assertRaises(ops.model.InvalidStatusError):
+        with self.assertRaises(ops.InvalidStatusError):
             self.model.app.status = 'blocked'
 
     def test_remote_unit_status(self):
@@ -830,14 +821,14 @@ class TestModel(unittest.TestCase):
         self.resetBackendCalls()
 
         # Remote unit status is always unknown.
-        self.assertEqual(remote_unit.status, ops.model.UnknownStatus())
+        self.assertEqual(remote_unit.status, ops.UnknownStatus())
 
         test_statuses = (
-            ops.model.UnknownStatus(),
-            ops.model.ActiveStatus('Green'),
-            ops.model.MaintenanceStatus('Yellow'),
-            ops.model.BlockedStatus('Red'),
-            ops.model.WaitingStatus('White'),
+            ops.UnknownStatus(),
+            ops.ActiveStatus('Green'),
+            ops.MaintenanceStatus('Yellow'),
+            ops.BlockedStatus('Red'),
+            ops.WaitingStatus('White'),
         )
 
         for target_status in test_statuses:
@@ -854,14 +845,14 @@ class TestModel(unittest.TestCase):
         self.resetBackendCalls()
 
         # Remote application status is always unknown.
-        self.assertIsInstance(remoteapp1.status, ops.model.UnknownStatus)
+        self.assertIsInstance(remoteapp1.status, ops.UnknownStatus)
 
         test_statuses = (
-            ops.model.UnknownStatus(),
-            ops.model.ActiveStatus(),
-            ops.model.MaintenanceStatus('Upgrading software'),
-            ops.model.BlockedStatus('Awaiting manual resolution'),
-            ops.model.WaitingStatus('Awaiting related app updates'),
+            ops.UnknownStatus(),
+            ops.ActiveStatus(),
+            ops.MaintenanceStatus('Upgrading software'),
+            ops.BlockedStatus('Awaiting manual resolution'),
+            ops.WaitingStatus('Awaiting related app updates'),
         )
         for target_status in test_statuses:
             with self.subTest(target_status.name):
@@ -870,9 +861,9 @@ class TestModel(unittest.TestCase):
         self.assertBackendCalls([])
 
     def test_storage(self):
-        meta = ops.charm.CharmMeta()
+        meta = ops.CharmMeta()
         meta.storages = {'disks': None, 'data': None}
-        model = ops.model.Model(meta, ops.model._ModelBackend('myapp/0'))
+        model = ops.Model(meta, _ModelBackend('myapp/0'))
 
         fake_script(self, 'storage-list', '''
             if [ "$1" = disks ]; then
@@ -923,7 +914,7 @@ class TestModel(unittest.TestCase):
         ])
 
         # Try to add storage not present in charm metadata.
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             model.storages.request('deadbeef')
 
         # Invalid count parameter types.
@@ -942,9 +933,9 @@ class TestModel(unittest.TestCase):
         self.assertEqual(expected, self.harness._get_backend_calls(reset=reset))
 
     def test_run_error(self):
-        model = ops.model.Model(ops.charm.CharmMeta(), ops.model._ModelBackend('myapp/0'))
+        model = ops.Model(ops.CharmMeta(), _ModelBackend('myapp/0'))
         fake_script(self, 'status-get', """echo 'ERROR cannot get status' >&2; exit 1""")
-        with self.assertRaises(ops.model.ModelError) as cm:
+        with self.assertRaises(ops.ModelError) as cm:
             _ = model.unit.status.message
         self.assertEqual(str(cm.exception), 'ERROR cannot get status\n')
         self.assertEqual(cm.exception.args[0], 'ERROR cannot get status\n')
@@ -999,10 +990,10 @@ def test_recursive_list(case):
         file_infos, dirs = [], set()
         for f in file_list:
             file_infos.append(
-                FileInfo(
+                pebble.FileInfo(
                     path=f,
                     name=os.path.basename(f),
-                    type=FileType.FILE,
+                    type=pebble.FileType.FILE,
                     **args))
 
             # collect all the directories for the test case's files
@@ -1010,10 +1001,10 @@ def test_recursive_list(case):
             if dirpath != '' and dirpath not in dirs:
                 dirs.update(dirpath)
                 file_infos.append(
-                    FileInfo(
+                    pebble.FileInfo(
                         path=dirpath,
                         name=os.path.basename(dirpath),
-                        type=FileType.DIRECTORY,
+                        type=pebble.FileType.DIRECTORY,
                         **args))
 
         def inner(path):
@@ -1023,7 +1014,7 @@ def test_recursive_list(case):
                 # exclude file infos for separate trees and also
                 # for the directory we are listing itself - we only want its contents.
                 if not info.path.startswith(path) or (
-                        info.type == FileType.DIRECTORY and path == info.path):
+                        info.type == pebble.FileType.DIRECTORY and path == info.path):
                     continue
                 # exclude file infos for files that are in subdirectories of path.
                 # we only want files that are directly in path.
@@ -1038,14 +1029,14 @@ def test_recursive_list(case):
     case.path = os.path.normpath(case.path)
     case.files = [os.path.normpath(f) for f in case.files]
     case.want = {os.path.normpath(f) for f in case.want}
-    for f in ops.model.Container._list_recursive(
+    for f in ops.Container._list_recursive(
         list_func_gen(
             case.files), pathlib.Path(
             case.path)):
         path = f.path
         if case.dst is not None:
             # test destination path construction
-            _, path = f.path, ops.model.Container._build_destpath(
+            _, path = f.path, ops.Container._build_destpath(
                 f.path, case.path, case.dst)
         files.add(path)
     assert case.want == files, f'case {case.name!r} has wrong files: want {case.want}, got {files}'
@@ -1121,7 +1112,7 @@ recursive_push_pull_cases = [
 @pytest.mark.parametrize('case', recursive_push_pull_cases)
 def test_recursive_push_and_pull(case):
     # full "integration" test of push+pull
-    harness = ops.testing.Harness(ops.charm.CharmBase, meta='''
+    harness = ops.testing.Harness(ops.CharmBase, meta='''
         name: test-app
         containers:
           foo:
@@ -1153,7 +1144,7 @@ def test_recursive_push_and_pull(case):
     errors = []
     try:
         c.push_path(push_path, case.dst)
-    except ops.model.MultiPushPullError as err:
+    except ops.MultiPushPullError as err:
         if not case.errors:
             raise
         errors = {src[len(push_src.name):] for src, _ in err.errors}
@@ -1172,7 +1163,7 @@ def test_recursive_push_and_pull(case):
     errors = []
     try:
         c.pull_path(case.path, os.path.join(pull_dst.name, case.dst[1:]))
-    except ops.model.MultiPushPullError as err:
+    except ops.MultiPushPullError as err:
         if not case.errors:
             raise
         errors = {src for src, _ in err.errors}
@@ -1186,7 +1177,7 @@ def test_recursive_push_and_pull(case):
 class TestApplication(unittest.TestCase):
 
     def setUp(self):
-        self.harness = ops.testing.Harness(ops.charm.CharmBase, meta='''
+        self.harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: myapp
             provides:
               db0:
@@ -1292,7 +1283,7 @@ class TestApplication(unittest.TestCase):
 
 class TestContainers(unittest.TestCase):
     def setUp(self):
-        meta = ops.charm.CharmMeta.from_yaml("""
+        meta = ops.CharmMeta.from_yaml("""
 name: k8s-charm
 containers:
   c1:
@@ -1300,8 +1291,8 @@ containers:
   c2:
     k: v
 """)
-        backend = ops.model._ModelBackend('myapp/0')
-        self.model = ops.model.Model(meta, backend)
+        backend = _ModelBackend('myapp/0')
+        self.model = ops.Model(meta, backend)
 
     def test_unit_containers(self):
         containers = self.model.unit.containers
@@ -1312,9 +1303,9 @@ containers:
         self.assertNotIn('c3', containers)
         for name in ['c1', 'c2']:
             container = containers[name]
-            self.assertIsInstance(container, ops.model.Container)
+            self.assertIsInstance(container, ops.Container)
             self.assertEqual(container.name, name)
-            self.assertIsInstance(container.pebble, ops.pebble.Client)
+            self.assertIsInstance(container.pebble, pebble.Client)
         with self.assertRaises(KeyError):
             containers['c3']
 
@@ -1326,10 +1317,10 @@ containers:
         unit = self.model.unit
         for name in ['c1', 'c2']:
             container = unit.get_container(name)
-            self.assertIsInstance(container, ops.model.Container)
+            self.assertIsInstance(container, ops.Container)
             self.assertEqual(container.name, name)
-            self.assertIsInstance(container.pebble, ops.pebble.Client)
-        with self.assertRaises(ops.model.ModelError):
+            self.assertIsInstance(container.pebble, pebble.Client)
+        with self.assertRaises(ops.ModelError):
             unit.get_container('c3')
 
         with self.assertRaises(RuntimeError):
@@ -1339,14 +1330,14 @@ containers:
 
 class TestContainerPebble(unittest.TestCase):
     def setUp(self):
-        meta = ops.charm.CharmMeta.from_yaml("""
+        meta = ops.CharmMeta.from_yaml("""
 name: k8s-charm
 containers:
   c1:
     k: v
 """)
         backend = MockPebbleBackend('myapp/0')
-        self.model = ops.model.Model(meta, backend)
+        self.model = ops.Model(meta, backend)
         self.container = self.model.unit.containers['c1']
         self.pebble = self.container.pebble
 
@@ -1362,7 +1353,7 @@ containers:
         self.assertEqual(self.pebble.requests, [('replan',)])
 
     def test_can_connect(self):
-        self.pebble.responses.append(SystemInfo.from_dict({'version': '1.0.0'}))
+        self.pebble.responses.append(pebble.SystemInfo.from_dict({'version': '1.0.0'}))
         self.assertTrue(self.container.can_connect())
         self.assertEqual(self.pebble.requests, [('get_system_info',)])
 
@@ -1401,13 +1392,15 @@ containers:
     def test_restart_fallback(self):
         def restart_services(services):
             self.pebble.requests.append(('restart', services))
-            raise APIError({}, 400, "", "")
+            raise pebble.APIError({}, 400, "", "")
 
         self.pebble.restart_services = restart_services
         # Setup the Pebble client to respond to a call to get_services()
         self.pebble.responses.append([
-            ServiceInfo.from_dict({'name': 'foo', 'startup': 'enabled', 'current': 'active'}),
-            ServiceInfo.from_dict({'name': 'bar', 'startup': 'enabled', 'current': 'inactive'}),
+            pebble.ServiceInfo.from_dict(
+                {'name': 'foo', 'startup': 'enabled', 'current': 'active'}),
+            pebble.ServiceInfo.from_dict(
+                {'name': 'bar', 'startup': 'enabled', 'current': 'inactive'}),
         ])
 
         self.container.restart('foo', 'bar')
@@ -1423,10 +1416,10 @@ containers:
 
     def test_restart_fallback_non_400_error(self):
         def restart_services(services):
-            raise APIError({}, 500, "", "")
+            raise pebble.APIError({}, 500, "", "")
 
         self.pebble.restart_services = restart_services
-        with self.assertRaises(ops.pebble.APIError) as cm:
+        with self.assertRaises(pebble.APIError) as cm:
             self.container.restart('foo')
         self.assertEqual(cm.exception.code, 500)
 
@@ -1435,15 +1428,15 @@ containers:
             self.container.restart()
 
     def test_type_errors(self):
-        meta = ops.charm.CharmMeta.from_yaml("""
+        meta = ops.CharmMeta.from_yaml("""
 name: k8s-charm
 containers:
   c1:
     k: v
 """)
         # Only the real pebble Client checks types, so use actual backend class
-        backend = ops.model._ModelBackend('myapp/0')
-        model = ops.model.Model(meta, backend)
+        backend = _ModelBackend('myapp/0')
+        model = ops.Model(meta, backend)
         container = model.unit.containers['c1']
 
         with self.assertRaises(TypeError):
@@ -1455,7 +1448,7 @@ containers:
     def test_add_layer(self):
         self.container.add_layer('a', 'summary: str\n')
         self.container.add_layer('b', {'summary': 'dict'})
-        self.container.add_layer('c', ops.pebble.Layer('summary: Layer'))
+        self.container.add_layer('c', pebble.Layer('summary: Layer'))
         self.container.add_layer('d', 'summary: str\n', combine=True)
         self.assertEqual(self.pebble.requests, [
             ('add_layer', 'a', 'summary: str\n', False),
@@ -1470,15 +1463,15 @@ containers:
 
     def test_get_plan(self):
         plan_yaml = 'services:\n foo:\n  override: replace\n  command: bar'
-        self.pebble.responses.append(ops.pebble.Plan(plan_yaml))
+        self.pebble.responses.append(pebble.Plan(plan_yaml))
         plan = self.container.get_plan()
         self.assertEqual(self.pebble.requests, [('get_plan',)])
-        self.assertIsInstance(plan, ops.pebble.Plan)
+        self.assertIsInstance(plan, pebble.Plan)
         self.assertEqual(plan.to_yaml(), yaml.safe_dump(yaml.safe_load(plan_yaml)))
 
     @staticmethod
     def _make_service(name, startup, current):
-        return ops.pebble.ServiceInfo.from_dict(
+        return pebble.ServiceInfo.from_dict(
             {'name': name, 'startup': startup, 'current': current})
 
     def test_get_services(self):
@@ -1491,22 +1484,22 @@ containers:
         self.assertEqual(len(services), 2)
         self.assertEqual(set(services), {'s1', 's2'})
         self.assertEqual(services['s1'].name, 's1')
-        self.assertEqual(services['s1'].startup, ops.pebble.ServiceStartup.ENABLED)
-        self.assertEqual(services['s1'].current, ops.pebble.ServiceStatus.ACTIVE)
+        self.assertEqual(services['s1'].startup, pebble.ServiceStartup.ENABLED)
+        self.assertEqual(services['s1'].current, pebble.ServiceStatus.ACTIVE)
         self.assertEqual(services['s2'].name, 's2')
-        self.assertEqual(services['s2'].startup, ops.pebble.ServiceStartup.DISABLED)
-        self.assertEqual(services['s2'].current, ops.pebble.ServiceStatus.INACTIVE)
+        self.assertEqual(services['s2'].startup, pebble.ServiceStartup.DISABLED)
+        self.assertEqual(services['s2'].current, pebble.ServiceStatus.INACTIVE)
 
         self.pebble.responses.append(two_services)
         services = self.container.get_services('s1', 's2')
         self.assertEqual(len(services), 2)
         self.assertEqual(set(services), {'s1', 's2'})
         self.assertEqual(services['s1'].name, 's1')
-        self.assertEqual(services['s1'].startup, ops.pebble.ServiceStartup.ENABLED)
-        self.assertEqual(services['s1'].current, ops.pebble.ServiceStatus.ACTIVE)
+        self.assertEqual(services['s1'].startup, pebble.ServiceStartup.ENABLED)
+        self.assertEqual(services['s1'].current, pebble.ServiceStatus.ACTIVE)
         self.assertEqual(services['s2'].name, 's2')
-        self.assertEqual(services['s2'].startup, ops.pebble.ServiceStartup.DISABLED)
-        self.assertEqual(services['s2'].current, ops.pebble.ServiceStatus.INACTIVE)
+        self.assertEqual(services['s2'].startup, pebble.ServiceStartup.DISABLED)
+        self.assertEqual(services['s2'].current, pebble.ServiceStatus.INACTIVE)
 
         self.assertEqual(self.pebble.requests, [
             ('get_services', None),
@@ -1519,12 +1512,12 @@ containers:
         s = self.container.get_service('s1')
         self.assertEqual(self.pebble.requests, [('get_services', ('s1', ))])
         self.assertEqual(s.name, 's1')
-        self.assertEqual(s.startup, ops.pebble.ServiceStartup.ENABLED)
-        self.assertEqual(s.current, ops.pebble.ServiceStatus.ACTIVE)
+        self.assertEqual(s.startup, pebble.ServiceStartup.ENABLED)
+        self.assertEqual(s.current, pebble.ServiceStatus.ACTIVE)
 
-        # If Pebble returns no services, should be a ModelError
+        # If Pebble returns no services, should be a ops.ModelError
         self.pebble.responses.append([])
-        with self.assertRaises(ops.model.ModelError) as cm:
+        with self.assertRaises(ops.ModelError) as cm:
             self.container.get_service('s2')
         self.assertEqual(str(cm.exception), "service 's2' not found")
 
@@ -1538,13 +1531,13 @@ containers:
 
     def test_get_checks(self):
         response_checks = [
-            ops.pebble.CheckInfo.from_dict({
+            pebble.CheckInfo.from_dict({
                 'name': 'c1',
                 'status': 'up',
                 'failures': 0,
                 'threshold': 3,
             }),
-            ops.pebble.CheckInfo.from_dict({
+            pebble.CheckInfo.from_dict({
                 'name': 'c2',
                 'level': 'alive',
                 'status': 'down',
@@ -1557,34 +1550,34 @@ containers:
         checks = self.container.get_checks()
         self.assertEqual(len(checks), 2)
         self.assertEqual(checks['c1'].name, 'c1')
-        self.assertEqual(checks['c1'].level, ops.pebble.CheckLevel.UNSET)
-        self.assertEqual(checks['c1'].status, ops.pebble.CheckStatus.UP)
+        self.assertEqual(checks['c1'].level, pebble.CheckLevel.UNSET)
+        self.assertEqual(checks['c1'].status, pebble.CheckStatus.UP)
         self.assertEqual(checks['c1'].failures, 0)
         self.assertEqual(checks['c1'].threshold, 3)
         self.assertEqual(checks['c2'].name, 'c2')
-        self.assertEqual(checks['c2'].level, ops.pebble.CheckLevel.ALIVE)
-        self.assertEqual(checks['c2'].status, ops.pebble.CheckStatus.DOWN)
+        self.assertEqual(checks['c2'].level, pebble.CheckLevel.ALIVE)
+        self.assertEqual(checks['c2'].status, pebble.CheckStatus.DOWN)
         self.assertEqual(checks['c2'].failures, 2)
         self.assertEqual(checks['c2'].threshold, 2)
 
         self.pebble.responses.append(response_checks[1:2])
-        checks = self.container.get_checks('c1', 'c2', level=ops.pebble.CheckLevel.ALIVE)
+        checks = self.container.get_checks('c1', 'c2', level=pebble.CheckLevel.ALIVE)
         self.assertEqual(len(checks), 1)
         self.assertEqual(checks['c2'].name, 'c2')
-        self.assertEqual(checks['c2'].level, ops.pebble.CheckLevel.ALIVE)
-        self.assertEqual(checks['c2'].status, ops.pebble.CheckStatus.DOWN)
+        self.assertEqual(checks['c2'].level, pebble.CheckLevel.ALIVE)
+        self.assertEqual(checks['c2'].status, pebble.CheckStatus.DOWN)
         self.assertEqual(checks['c2'].failures, 2)
         self.assertEqual(checks['c2'].threshold, 2)
 
         self.assertEqual(self.pebble.requests, [
             ('get_checks', None, None),
-            ('get_checks', ops.pebble.CheckLevel.ALIVE, ('c1', 'c2')),
+            ('get_checks', pebble.CheckLevel.ALIVE, ('c1', 'c2')),
         ])
 
     def test_get_check(self):
         # Single check returned successfully
         self.pebble.responses.append([
-            ops.pebble.CheckInfo.from_dict({
+            pebble.CheckInfo.from_dict({
                 'name': 'c1',
                 'status': 'up',
                 'failures': 0,
@@ -1594,26 +1587,26 @@ containers:
         c = self.container.get_check('c1')
         self.assertEqual(self.pebble.requests, [('get_checks', None, ('c1', ))])
         self.assertEqual(c.name, 'c1')
-        self.assertEqual(c.level, ops.pebble.CheckLevel.UNSET)
-        self.assertEqual(c.status, ops.pebble.CheckStatus.UP)
+        self.assertEqual(c.level, pebble.CheckLevel.UNSET)
+        self.assertEqual(c.status, pebble.CheckStatus.UP)
         self.assertEqual(c.failures, 0)
         self.assertEqual(c.threshold, 3)
 
-        # If Pebble returns no checks, should be a ModelError
+        # If Pebble returns no checks, should be a ops.ModelError
         self.pebble.responses.append([])
-        with self.assertRaises(ops.model.ModelError) as cm:
+        with self.assertRaises(ops.ModelError) as cm:
             self.container.get_check('c2')
         self.assertEqual(str(cm.exception), "check 'c2' not found")
 
         # If Pebble returns more than one check, RuntimeError is raised
         self.pebble.responses.append([
-            ops.pebble.CheckInfo.from_dict({
+            pebble.CheckInfo.from_dict({
                 'name': 'c1',
                 'status': 'up',
                 'failures': 0,
                 'threshold': 3,
             }),
-            ops.pebble.CheckInfo.from_dict({
+            pebble.CheckInfo.from_dict({
                 'name': 'c2',
                 'level': 'alive',
                 'status': 'down',
@@ -1696,14 +1689,14 @@ containers:
         ])
 
     def test_can_connect_simple(self):
-        self.pebble.responses.append(SystemInfo.from_dict({'version': '1.0.0'}))
+        self.pebble.responses.append(pebble.SystemInfo.from_dict({'version': '1.0.0'}))
         self.assertTrue(self.container.can_connect())
 
     def test_can_connect_connection_error(self):
         def raise_error():
-            raise ConnectionError('connection error!')
+            raise pebble.ConnectionError('connection error!')
         self.pebble.get_system_info = raise_error
-        with self.assertLogs('ops.model', level='DEBUG') as cm:
+        with self.assertLogs('ops', level='DEBUG') as cm:
             self.assertFalse(self.container.can_connect())
         self.assertEqual(len(cm.output), 1)
         self.assertRegex(cm.output[0], r'DEBUG:ops.model:.*: connection error!')
@@ -1712,16 +1705,16 @@ containers:
         def raise_error():
             raise FileNotFoundError('file not found!')
         self.pebble.get_system_info = raise_error
-        with self.assertLogs('ops.model', level='DEBUG') as cm:
+        with self.assertLogs('ops', level='DEBUG') as cm:
             self.assertFalse(self.container.can_connect())
         self.assertEqual(len(cm.output), 1)
         self.assertRegex(cm.output[0], r'DEBUG:ops.model:.*: file not found!')
 
     def test_can_connect_api_error(self):
         def raise_error():
-            raise APIError('body', 404, 'status', 'api error!')
+            raise pebble.APIError('body', 404, 'status', 'api error!')
         self.pebble.get_system_info = raise_error
-        with self.assertLogs('ops.model') as cm:
+        with self.assertLogs('ops') as cm:
             self.assertFalse(self.container.can_connect())
         self.assertEqual(len(cm.output), 1)
         self.assertRegex(cm.output[0], r'WARNING:ops.model:.*: api error!')
@@ -1778,7 +1771,7 @@ containers:
         self.pebble.requests = []
 
 
-class MockPebbleBackend(ops.model._ModelBackend):
+class MockPebbleBackend(_ModelBackend):
     def get_pebble(self, socket_path):
         return MockPebbleClient(socket_path)
 
@@ -1810,8 +1803,8 @@ class MockPebbleClient:
 
     def add_layer(self, label, layer, combine=False):
         if isinstance(layer, dict):
-            layer = ops.pebble.Layer(layer).to_yaml()
-        elif isinstance(layer, ops.pebble.Layer):
+            layer = pebble.Layer(layer).to_yaml()
+        elif isinstance(layer, pebble.Layer):
             layer = layer.to_yaml()
         self.requests.append(('add_layer', label, layer, combine))
 
@@ -1859,17 +1852,17 @@ class MockPebbleClient:
 class TestModelBindings(unittest.TestCase):
 
     def setUp(self):
-        meta = ops.charm.CharmMeta()
+        meta = ops.CharmMeta()
         meta.relations = {
-            'db0': RelationMeta(
-                RelationRole.provides, 'db0', {'interface': 'db0', 'scope': 'global'}),
-            'db1': RelationMeta(
-                RelationRole.requires, 'db1', {'interface': 'db1', 'scope': 'global'}),
-            'db2': RelationMeta(
-                RelationRole.peer, 'db2', {'interface': 'db2', 'scope': 'global'}),
+            'db0': ops.RelationMeta(
+                ops.RelationRole.provides, 'db0', {'interface': 'db0', 'scope': 'global'}),
+            'db1': ops.RelationMeta(
+                ops.RelationRole.requires, 'db1', {'interface': 'db1', 'scope': 'global'}),
+            'db2': ops.RelationMeta(
+                ops.RelationRole.peer, 'db2', {'interface': 'db2', 'scope': 'global'}),
         }
-        self.backend = ops.model._ModelBackend('myapp/0')
-        self.model = ops.model.Model(meta, self.backend)
+        self.backend = _ModelBackend('myapp/0')
+        self.model = ops.Model(meta, self.backend)
 
         fake_script(self, 'relation-ids',
                     """([ "$1" = db0 ] && echo '["db0:4"]') || echo '[]'""")
@@ -1961,7 +1954,7 @@ class TestModelBindings(unittest.TestCase):
     def test_invalid_keys(self):
         # Basic validation for passing invalid keys.
         for name in (object, 0):
-            with self.assertRaises(ops.model.ModelError):
+            with self.assertRaises(ops.ModelError):
                 self.model.get_binding(name)
 
     def test_dead_relations(self):
@@ -1977,7 +1970,7 @@ class TestModelBindings(unittest.TestCase):
                 fi
             ''')
         # Validate the behavior for dead relations.
-        binding = ops.model.Binding('db0', 42, self.model._backend)
+        binding = ops.Binding('db0', 42, self.model._backend)
         self.assertEqual(binding.network.bind_address, ipaddress.ip_address('192.0.2.2'))
         self.assertEqual(fake_script_calls(self, clear=True), [
             ['network-get', 'db0', '-r', '42', '--format=json'],
@@ -2127,7 +2120,7 @@ class TestModelBackend(unittest.TestCase):
     @property
     def backend(self):
         if self._backend is None:
-            self._backend = ops.model._ModelBackend('myapp/0')
+            self._backend = _ModelBackend('myapp/0')
         return self._backend
 
     def test_relation_get_set_is_app_arg(self):
@@ -2147,10 +2140,10 @@ class TestModelBackend(unittest.TestCase):
                 self.backend.relation_get(1, 'fooentity', is_app=is_app_v)
 
     def test_is_leader_refresh(self):
-        meta = ops.charm.CharmMeta.from_yaml('''
+        meta = ops.CharmMeta.from_yaml('''
             name: myapp
         ''')
-        model = ops.model.Model(meta, self.backend)
+        model = ops.Model(meta, self.backend)
         fake_script(self, 'is-leader', 'echo false')
         self.assertFalse(model.unit.is_leader())
 
@@ -2175,42 +2168,42 @@ class TestModelBackend(unittest.TestCase):
         test_cases = [(
             lambda: fake_script(self, 'relation-list', 'echo fooerror >&2 ; exit 1'),
             lambda: self.backend.relation_list(3),
-            ops.model.ModelError,
+            ops.ModelError,
             [['relation-list', '-r', '3', '--format=json']],
         ), (
             lambda: fake_script(self, 'relation-list', f'echo {err_msg} >&2 ; exit 2'),
             lambda: self.backend.relation_list(3),
-            ops.model.RelationNotFoundError,
+            ops.RelationNotFoundError,
             [['relation-list', '-r', '3', '--format=json']],
         ), (
             lambda: fake_script(self, 'relation-set', 'echo fooerror >&2 ; exit 1'),
             lambda: self.backend.relation_set(3, 'foo', 'bar', is_app=False),
-            ops.model.ModelError,
+            ops.ModelError,
             [['relation-set', '-r', '3', '--file', '-']],
         ), (
             lambda: fake_script(self, 'relation-set', f'echo {err_msg} >&2 ; exit 2'),
             lambda: self.backend.relation_set(3, 'foo', 'bar', is_app=False),
-            ops.model.RelationNotFoundError,
+            ops.RelationNotFoundError,
             [['relation-set', '-r', '3', '--file', '-']],
         ), (
             lambda: None,
             lambda: self.backend.relation_set(3, 'foo', 'bar', is_app=True),
-            ops.model.RelationNotFoundError,
+            ops.RelationNotFoundError,
             [['relation-set', '-r', '3', '--app', '--file', '-']],
         ), (
             lambda: fake_script(self, 'relation-get', 'echo fooerror >&2 ; exit 1'),
             lambda: self.backend.relation_get(3, 'remote/0', is_app=False),
-            ops.model.ModelError,
+            ops.ModelError,
             [['relation-get', '-r', '3', '-', 'remote/0', '--format=json']],
         ), (
             lambda: fake_script(self, 'relation-get', f'echo {err_msg} >&2 ; exit 2'),
             lambda: self.backend.relation_get(3, 'remote/0', is_app=False),
-            ops.model.RelationNotFoundError,
+            ops.RelationNotFoundError,
             [['relation-get', '-r', '3', '-', 'remote/0', '--format=json']],
         ), (
             lambda: None,
             lambda: self.backend.relation_get(3, 'remote/0', is_app=True),
-            ops.model.RelationNotFoundError,
+            ops.RelationNotFoundError,
             [['relation-get', '-r', '3', '-', 'remote/0', '--app', '--format=json']],
         )]
 
@@ -2319,27 +2312,27 @@ class TestModelBackend(unittest.TestCase):
 
     def test_local_set_invalid_status(self):
         # juju returns exit code 1 if you ask to set status to 'unknown' or 'error'
-        meta = ops.charm.CharmMeta.from_yaml('''
+        meta = ops.CharmMeta.from_yaml('''
             name: myapp
         ''')
-        model = ops.model.Model(meta, self.backend)
+        model = ops.Model(meta, self.backend)
         fake_script(self, 'status-set', 'exit 1')
         fake_script(self, 'is-leader', 'echo true')
 
-        with self.assertRaises(ops.model.ModelError):
-            model.unit.status = ops.model.UnknownStatus()
-        with self.assertRaises(ops.model.ModelError):
-            model.unit.status = ops.model.ErrorStatus()
+        with self.assertRaises(ops.ModelError):
+            model.unit.status = ops.UnknownStatus()
+        with self.assertRaises(ops.ModelError):
+            model.unit.status = ops.ErrorStatus()
 
         self.assertEqual(fake_script_calls(self, True), [
             ['status-set', '--application=False', 'unknown', ''],
             ['status-set', '--application=False', 'error', ''],
         ])
 
-        with self.assertRaises(ops.model.ModelError):
-            model.app.status = ops.model.UnknownStatus()
-        with self.assertRaises(ops.model.ModelError):
-            model.app.status = ops.model.ErrorStatus()
+        with self.assertRaises(ops.ModelError):
+            model.app.status = ops.UnknownStatus()
+        with self.assertRaises(ops.ModelError):
+            model.app.status = ops.ErrorStatus()
 
         # A leadership check is needed for application status.
         self.assertEqual(fake_script_calls(self, True), [
@@ -2350,16 +2343,16 @@ class TestModelBackend(unittest.TestCase):
 
     def test_local_get_status(self):
         for name, expected_cls in (
-            ("active", ops.model.ActiveStatus),
-            ("waiting", ops.model.WaitingStatus),
-            ("blocked", ops.model.BlockedStatus),
-            ("maintenance", ops.model.MaintenanceStatus),
-            ("error", ops.model.ErrorStatus),
+            ("active", ops.ActiveStatus),
+            ("waiting", ops.WaitingStatus),
+            ("blocked", ops.BlockedStatus),
+            ("maintenance", ops.MaintenanceStatus),
+            ("error", ops.ErrorStatus),
         ):
-            meta = ops.charm.CharmMeta.from_yaml('''
+            meta = ops.CharmMeta.from_yaml('''
                 name: myapp
             ''')
-            model = ops.model.Model(meta, self.backend)
+            model = ops.Model(meta, self.backend)
 
             with self.subTest(name):
                 content = json.dumps({
@@ -2390,23 +2383,23 @@ class TestModelBackend(unittest.TestCase):
     def test_status_set_is_app_not_bool_raises(self):
         for is_app_v in [None, 1, 2.0, 'a', b'beef', object]:
             with self.assertRaises(TypeError):
-                self.backend.status_set(ops.model.ActiveStatus, is_app=is_app_v)
+                self.backend.status_set(ops.ActiveStatus, is_app=is_app_v)
 
     def test_storage_tool_errors(self):
         test_cases = [(
             lambda: fake_script(self, 'storage-list', 'echo fooerror >&2 ; exit 1'),
             lambda: self.backend.storage_list('foobar'),
-            ops.model.ModelError,
+            ops.ModelError,
             [['storage-list', 'foobar', '--format=json']],
         ), (
             lambda: fake_script(self, 'storage-get', 'echo fooerror >&2 ; exit 1'),
             lambda: self.backend.storage_get('foobar', 'someattr'),
-            ops.model.ModelError,
+            ops.ModelError,
             [['storage-get', '-s', 'foobar', 'someattr', '--format=json']],
         ), (
             lambda: fake_script(self, 'storage-add', 'echo fooerror >&2 ; exit 1'),
             lambda: self.backend.storage_add('foobar', count=2),
-            ops.model.ModelError,
+            ops.ModelError,
             [['storage-add', 'foobar=2']],
         ), (
             lambda: fake_script(self, 'storage-add', 'echo fooerror >&2 ; exit 1'),
@@ -2467,12 +2460,12 @@ class TestModelBackend(unittest.TestCase):
             lambda: fake_script(self, 'network-get',
                                 f'echo {err_no_endpoint} >&2 ; exit 1'),
             lambda: self.backend.network_get("deadbeef"),
-            ops.model.ModelError,
+            ops.ModelError,
             [['network-get', 'deadbeef', '--format=json']],
         ), (
             lambda: fake_script(self, 'network-get', f'echo {err_no_rel} >&2 ; exit 2'),
             lambda: self.backend.network_get("deadbeef", 3),
-            ops.model.RelationNotFoundError,
+            ops.RelationNotFoundError,
             [['network-get', 'deadbeef', '-r', '3', '--format=json']],
         )]
         for do_fake, run, exception, calls in test_cases:
@@ -2484,7 +2477,7 @@ class TestModelBackend(unittest.TestCase):
     def test_action_get_error(self):
         fake_script(self, 'action-get', '')
         fake_script(self, 'action-get', 'echo fooerror >&2 ; exit 1')
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             self.backend.action_get()
         calls = [['action-get', '--format=json']]
         self.assertEqual(fake_script_calls(self, clear=True), calls)
@@ -2492,7 +2485,7 @@ class TestModelBackend(unittest.TestCase):
     def test_action_set_error(self):
         fake_script(self, 'action-get', '')
         fake_script(self, 'action-set', 'echo fooerror >&2 ; exit 1')
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             self.backend.action_set(OrderedDict([('foo', 'bar'), ('dead', 'beef cafe')]))
         self.assertCountEqual(
             ["action-set", "dead=beef cafe", "foo=bar"], fake_script_calls(self, clear=True)[0])
@@ -2500,7 +2493,7 @@ class TestModelBackend(unittest.TestCase):
     def test_action_log_error(self):
         fake_script(self, 'action-get', '')
         fake_script(self, 'action-log', 'echo fooerror >&2 ; exit 1')
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             self.backend.action_log('log-message')
         calls = [["action-log", "log-message"]]
         self.assertEqual(fake_script_calls(self, clear=True), calls)
@@ -2591,7 +2584,7 @@ class TestModelBackend(unittest.TestCase):
         self.assertEqual(fake_script_calls(self, clear=True), [])
 
         fake_script(self, 'juju-log', 'exit 1')
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             self.backend.juju_log('BAR', 'foo')
         self.assertEqual(fake_script_calls(self, clear=True),
                          [['juju-log', '--log-level', 'BAR', '--', 'foo']])
@@ -2628,7 +2621,7 @@ class TestModelBackend(unittest.TestCase):
             ({'BAЯ': 4.2}, {}),
         ]
         for metrics, labels in invalid_inputs:
-            with self.assertRaises(ops.model.ModelError):
+            with self.assertRaises(ops.ModelError):
                 self.backend.add_metrics(metrics, labels)
 
     def test_invalid_metric_values(self):
@@ -2640,7 +2633,7 @@ class TestModelBackend(unittest.TestCase):
             ({'foo': '1O'}, {}),
         ]
         for metrics, labels in invalid_inputs:
-            with self.assertRaises(ops.model.ModelError):
+            with self.assertRaises(ops.ModelError):
                 self.backend.add_metrics(metrics, labels)
 
     def test_invalid_metric_labels(self):
@@ -2651,7 +2644,7 @@ class TestModelBackend(unittest.TestCase):
             ({'foo': 4.2}, {'BAЯ': 'baz'}),
         ]
         for metrics, labels in invalid_inputs:
-            with self.assertRaises(ops.model.ModelError):
+            with self.assertRaises(ops.ModelError):
                 self.backend.add_metrics(metrics, labels)
 
     def test_invalid_metric_label_values(self):
@@ -2661,7 +2654,7 @@ class TestModelBackend(unittest.TestCase):
             ({'foo': 4.2}, {'bar': 'b=az'}),
         ]
         for metrics, labels in invalid_inputs:
-            with self.assertRaises(ops.model.ModelError):
+            with self.assertRaises(ops.ModelError):
                 self.backend.add_metrics(metrics, labels)
 
     def test_relation_remote_app_name_env(self):
@@ -2725,7 +2718,7 @@ class TestLazyMapping(unittest.TestCase):
     def test_invalidate(self):
         loaded = []
 
-        class MyLazyMap(ops.model.LazyMapping):
+        class MyLazyMap(ops.LazyMapping):
             def _load(self):
                 loaded.append(1)
                 return {'foo': 'bar'}
@@ -2742,7 +2735,7 @@ class TestLazyMapping(unittest.TestCase):
 
 class TestSecrets(unittest.TestCase):
     def setUp(self):
-        self.model = ops.model.Model(ops.charm.CharmMeta(), ops.model._ModelBackend('myapp/0'))
+        self.model = ops.Model(ops.CharmMeta(), _ModelBackend('myapp/0'))
         self.app = self.model.app
         self.unit = self.model.unit
 
@@ -2750,7 +2743,7 @@ class TestSecrets(unittest.TestCase):
         fake_script(self, 'secret-add', 'echo secret:123')
 
         secret = self.app.add_secret({'foo': 'x'})
-        self.assertIsInstance(secret, model.Secret)
+        self.assertIsInstance(secret, ops.Secret)
         self.assertEqual(secret.id, 'secret:123')
         self.assertIsNone(secret.label)
 
@@ -2762,7 +2755,7 @@ class TestSecrets(unittest.TestCase):
 
         expire = datetime.datetime(2022, 12, 9, 16, 17, 0)
         secret = self.app.add_secret({'foo': 'x', 'bar': 'y'}, label='lbl', description='desc',
-                                     expire=expire, rotate=model.SecretRotate.HOURLY)
+                                     expire=expire, rotate=ops.SecretRotate.HOURLY)
         self.assertEqual(secret.id, 'secret:234')
         self.assertEqual(secret.label, 'lbl')
         self.assertEqual(secret.get_content(), {'foo': 'x', 'bar': 'y'})
@@ -2776,7 +2769,7 @@ class TestSecrets(unittest.TestCase):
         fake_script(self, 'secret-add', 'echo secret:345')
 
         secret = self.unit.add_secret({'foo': 'x'})
-        self.assertIsInstance(secret, model.Secret)
+        self.assertIsInstance(secret, ops.Secret)
         self.assertEqual(secret.id, 'secret:345')
         self.assertIsNone(secret.label)
 
@@ -2788,7 +2781,7 @@ class TestSecrets(unittest.TestCase):
 
         expire = datetime.datetime(2022, 12, 9, 16, 22, 0)
         secret = self.unit.add_secret({'foo': 'w', 'bar': 'z'}, label='l2', description='xyz',
-                                      expire=expire, rotate=model.SecretRotate.YEARLY)
+                                      expire=expire, rotate=ops.SecretRotate.YEARLY)
         self.assertEqual(secret.id, 'secret:456')
         self.assertEqual(secret.label, 'l2')
         self.assertEqual(secret.get_content(), {'foo': 'w', 'bar': 'z'})
@@ -2874,7 +2867,7 @@ class TestSecrets(unittest.TestCase):
         fake_script(self, 'secret-get', script)
         fake_script(self, 'secret-info-get', script)
 
-        with self.assertRaises(model.SecretNotFoundError):
+        with self.assertRaises(ops.SecretNotFoundError):
             self.model.get_secret(id='123')
 
     def test_get_secret_other_error(self):
@@ -2882,26 +2875,26 @@ class TestSecrets(unittest.TestCase):
         fake_script(self, 'secret-get', script)
         fake_script(self, 'secret-info-get', script)
 
-        with self.assertRaises(model.ModelError) as cm:
+        with self.assertRaises(ops.ModelError) as cm:
             self.model.get_secret(id='123')
-        self.assertNotIsInstance(cm.exception, model.SecretNotFoundError)
+        self.assertNotIsInstance(cm.exception, ops.SecretNotFoundError)
 
 
 class TestSecretInfo(unittest.TestCase):
     def test_init(self):
-        info = model.SecretInfo(
+        info = ops.SecretInfo(
             id='3',
             label='lbl',
             revision=7,
             expires=datetime.datetime(2022, 12, 9, 14, 10, 0),
-            rotation=model.SecretRotate.MONTHLY,
+            rotation=ops.SecretRotate.MONTHLY,
             rotates=datetime.datetime(2023, 1, 9, 14, 10, 0),
         )
         self.assertEqual(info.id, 'secret:3')
         self.assertEqual(info.label, 'lbl')
         self.assertEqual(info.revision, 7)
         self.assertEqual(info.expires, datetime.datetime(2022, 12, 9, 14, 10, 0))
-        self.assertEqual(info.rotation, model.SecretRotate.MONTHLY)
+        self.assertEqual(info.rotation, ops.SecretRotate.MONTHLY)
         self.assertEqual(info.rotates, datetime.datetime(2023, 1, 9, 14, 10, 0))
 
         self.assertTrue(repr(info).startswith('SecretInfo('))
@@ -2909,7 +2902,7 @@ class TestSecretInfo(unittest.TestCase):
 
     def test_from_dict(self):
         utc = datetime.timezone.utc
-        info = model.SecretInfo.from_dict('secret:4', {
+        info = ops.SecretInfo.from_dict('secret:4', {
             'label': 'fromdict',
             'revision': 8,
             'expires': '2022-12-09T14:10:00Z',
@@ -2920,10 +2913,10 @@ class TestSecretInfo(unittest.TestCase):
         self.assertEqual(info.label, 'fromdict')
         self.assertEqual(info.revision, 8)
         self.assertEqual(info.expires, datetime.datetime(2022, 12, 9, 14, 10, 0, tzinfo=utc))
-        self.assertEqual(info.rotation, model.SecretRotate.YEARLY)
+        self.assertEqual(info.rotation, ops.SecretRotate.YEARLY)
         self.assertEqual(info.rotates, datetime.datetime(2023, 1, 9, 14, 10, 0, tzinfo=utc))
 
-        info = model.SecretInfo.from_dict('secret:4', {
+        info = ops.SecretInfo.from_dict('secret:4', {
             'label': 'fromdict',
             'revision': 8,
             'rotation': 'badvalue',
@@ -2935,7 +2928,7 @@ class TestSecretInfo(unittest.TestCase):
         self.assertIsNone(info.rotation)
         self.assertIsNone(info.rotates)
 
-        info = model.SecretInfo.from_dict('5', {'revision': 9})
+        info = ops.SecretInfo.from_dict('5', {'revision': 9})
         self.assertEqual(info.id, 'secret:5')
         self.assertEqual(info.revision, 9)
 
@@ -2944,10 +2937,10 @@ class TestSecretClass(unittest.TestCase):
     maxDiff = 64 * 1024
 
     def setUp(self):
-        self.model = ops.model.Model(ops.charm.CharmMeta(), ops.model._ModelBackend('myapp/0'))
+        self.model = ops.Model(ops.CharmMeta(), _ModelBackend('myapp/0'))
 
     def make_secret(self, id=None, label=None, content=None):
-        return model.Secret(self.model._backend, id=id, label=label, content=content)
+        return ops.Secret(self.model._backend, id=id, label=label, content=content)
 
     def test_id_and_label(self):
         secret = self.make_secret(id=' abc ', label='lbl')
@@ -3065,7 +3058,7 @@ class TestSecretClass(unittest.TestCase):
             label='lab',
             description='desc',
             expire=expire,
-            rotate=model.SecretRotate.MONTHLY,
+            rotate=ops.SecretRotate.MONTHLY,
         )
 
         # If secret doesn't have an ID, we'll run secret-info-get to fetch it
@@ -3186,7 +3179,7 @@ class TestPorts(unittest.TestCase):
     def test_open_port_error(self):
         fake_script(self, 'open-port', "echo 'ERROR bad protocol' >&2; exit 1")
 
-        with self.assertRaises(model.ModelError) as cm:
+        with self.assertRaises(ops.ModelError) as cm:
             self.unit.open_port('ftp', 8080)
         self.assertEqual(str(cm.exception), 'ERROR bad protocol\n')
 
@@ -3210,7 +3203,7 @@ class TestPorts(unittest.TestCase):
     def test_close_port_error(self):
         fake_script(self, 'close-port', "echo 'ERROR bad protocol' >&2; exit 1")
 
-        with self.assertRaises(model.ModelError) as cm:
+        with self.assertRaises(ops.ModelError) as cm:
             self.unit.close_port('ftp', 8080)
         self.assertEqual(str(cm.exception), 'ERROR bad protocol\n')
 
@@ -3225,10 +3218,10 @@ class TestPorts(unittest.TestCase):
         self.assertIsInstance(ports_set, set)
         ports = sorted(ports_set, key=lambda p: (p.protocol, p.port))
         self.assertEqual(len(ports), 2)
-        self.assertIsInstance(ports[0], model.OpenedPort)
+        self.assertIsInstance(ports[0], ops.OpenedPort)
         self.assertEqual(ports[0].protocol, 'icmp')
         self.assertIsNone(ports[0].port)
-        self.assertIsInstance(ports[1], model.OpenedPort)
+        self.assertIsInstance(ports[1], ops.OpenedPort)
         self.assertEqual(ports[1].protocol, 'tcp')
         self.assertEqual(ports[1].port, 8080)
 
@@ -3248,10 +3241,10 @@ class TestPorts(unittest.TestCase):
         self.assertIsInstance(ports_set, set)
         ports = sorted(ports_set, key=lambda p: (p.protocol, p.port))
         self.assertEqual(len(ports), 2)
-        self.assertIsInstance(ports[0], model.OpenedPort)
+        self.assertIsInstance(ports[0], ops.OpenedPort)
         self.assertEqual(ports[0].protocol, 'tcp')
         self.assertEqual(ports[0].port, 8080)
-        self.assertIsInstance(ports[1], model.OpenedPort)
+        self.assertIsInstance(ports[1], ops.OpenedPort)
         self.assertEqual(ports[1].protocol, 'udp')
         self.assertEqual(ports[1].port, 1000)
 
diff --git a/test/test_pebble.py b/test/test_pebble.py
index 5cfb092..2bd206a 100644
--- a/test/test_pebble.py
+++ b/test/test_pebble.py
@@ -34,7 +34,7 @@ import uuid
 import pytest
 import websocket
 
-import ops.pebble as pebble
+from ops import pebble
 from ops._private import yaml
 
 # Ensure unittest diffs don't get truncated like "[17 chars]"
diff --git a/test/test_storage.py b/test/test_storage.py
index f1027eb..fdc3232 100755
--- a/test/test_storage.py
+++ b/test/test_storage.py
@@ -24,24 +24,24 @@ from textwrap import dedent
 
 import yaml
 
-from ops import framework, storage
+import ops
 
 
 class StoragePermutations(abc.ABC):
 
-    def create_framework(self) -> framework.Framework:
+    def create_framework(self) -> ops.Framework:
         """Create a Framework that we can use to test the backend storage."""
-        return framework.Framework(self.create_storage(), None, None, None)
+        return ops.Framework(self.create_storage(), None, None, None)
 
     @abc.abstractmethod
-    def create_storage(self) -> storage.SQLiteStorage:
+    def create_storage(self) -> ops.storage.SQLiteStorage:
         """Create a Storage backend that we can interact with."""
         return NotImplemented
 
     def test_save_and_load_snapshot(self):
         f = self.create_framework()
 
-        class Sample(framework.Object):
+        class Sample(ops.Object):
 
             def __init__(self, parent, key, content):
                 super().__init__(parent, key)
@@ -74,7 +74,7 @@ class StoragePermutations(abc.ABC):
     def test_emit_event(self):
         f = self.create_framework()
 
-        class Evt(framework.EventBase):
+        class Evt(ops.EventBase):
             def __init__(self, handle, content):
                 super().__init__(handle)
                 self.content = content
@@ -85,10 +85,10 @@ class StoragePermutations(abc.ABC):
             def restore(self, content):
                 self.content = content
 
-        class Events(framework.ObjectEvents):
-            event = framework.EventSource(Evt)
+        class Events(ops.ObjectEvents):
+            event = ops.EventSource(Evt)
 
-        class Sample(framework.Object):
+        class Sample(ops.Object):
 
             on = Events()
 
@@ -121,37 +121,37 @@ class StoragePermutations(abc.ABC):
         store.save_snapshot('foo', {1: 2})
         self.assertEqual({1: 2}, store.load_snapshot('foo'))
         store.drop_snapshot('foo')
-        with self.assertRaises(storage.NoSnapshotError):
+        with self.assertRaises(ops.storage.NoSnapshotError):
             store.load_snapshot('foo')
 
     def test_save_snapshot_empty_string(self):
         store = self.create_storage()
-        with self.assertRaises(storage.NoSnapshotError):
+        with self.assertRaises(ops.storage.NoSnapshotError):
             store.load_snapshot('foo')
         store.save_snapshot('foo', '')
         self.assertEqual('', store.load_snapshot('foo'))
         store.drop_snapshot('foo')
-        with self.assertRaises(storage.NoSnapshotError):
+        with self.assertRaises(ops.storage.NoSnapshotError):
             store.load_snapshot('foo')
 
     def test_save_snapshot_none(self):
         store = self.create_storage()
-        with self.assertRaises(storage.NoSnapshotError):
+        with self.assertRaises(ops.storage.NoSnapshotError):
             store.load_snapshot('bar')
         store.save_snapshot('bar', None)
         self.assertEqual(None, store.load_snapshot('bar'))
         store.drop_snapshot('bar')
-        with self.assertRaises(storage.NoSnapshotError):
+        with self.assertRaises(ops.storage.NoSnapshotError):
             store.load_snapshot('bar')
 
     def test_save_snapshot_zero(self):
         store = self.create_storage()
-        with self.assertRaises(storage.NoSnapshotError):
+        with self.assertRaises(ops.storage.NoSnapshotError):
             store.load_snapshot('zero')
         store.save_snapshot('zero', 0)
         self.assertEqual(0, store.load_snapshot('zero'))
         store.drop_snapshot('zero')
-        with self.assertRaises(storage.NoSnapshotError):
+        with self.assertRaises(ops.storage.NoSnapshotError):
             store.load_snapshot('zero')
 
     def test_save_notice(self):
@@ -203,7 +203,7 @@ class StoragePermutations(abc.ABC):
 class TestSQLiteStorage(StoragePermutations, BaseTestCase):
 
     def create_storage(self):
-        return storage.SQLiteStorage(':memory:')
+        return ops.storage.SQLiteStorage(':memory:')
 
 
 def setup_juju_backend(test_case, state_file):
@@ -281,38 +281,38 @@ class TestJujuStorage(StoragePermutations, BaseTestCase):
         state_file = pathlib.Path(fn)
         self.addCleanup(state_file.unlink)
         setup_juju_backend(self, state_file)
-        return storage.JujuStorage()
+        return ops.storage.JujuStorage()
 
 
 class TestSimpleLoader(BaseTestCase):
 
     def test_is_c_loader(self):
-        loader = storage._SimpleLoader(io.StringIO(''))
+        loader = ops.storage._SimpleLoader(io.StringIO(''))
         if getattr(yaml, 'CSafeLoader', None) is not None:
             self.assertIsInstance(loader, yaml.CSafeLoader)
         else:
             self.assertIsInstance(loader, yaml.SafeLoader)
 
     def test_is_c_dumper(self):
-        dumper = storage._SimpleDumper(io.StringIO(''))
+        dumper = ops.storage._SimpleDumper(io.StringIO(''))
         if getattr(yaml, 'CSafeDumper', None) is not None:
             self.assertIsInstance(dumper, yaml.CSafeDumper)
         else:
             self.assertIsInstance(dumper, yaml.SafeDumper)
 
     def test_handles_tuples(self):
-        raw = yaml.dump((1, 'tuple'), Dumper=storage._SimpleDumper)
-        parsed = yaml.load(raw, Loader=storage._SimpleLoader)
+        raw = yaml.dump((1, 'tuple'), Dumper=ops.storage._SimpleDumper)
+        parsed = yaml.load(raw, Loader=ops.storage._SimpleLoader)
         self.assertEqual(parsed, (1, 'tuple'))
 
     def assertRefused(self, obj):  # noqa: N802
         # We shouldn't allow them to be written
         with self.assertRaises(yaml.representer.RepresenterError):
-            yaml.dump(obj, Dumper=storage._SimpleDumper)
+            yaml.dump(obj, Dumper=ops.storage._SimpleDumper)
         # If they did somehow end up written, we shouldn't be able to load them
         raw = yaml.dump(obj, Dumper=yaml.Dumper)
         with self.assertRaises(yaml.constructor.ConstructorError):
-            yaml.load(raw, Loader=storage._SimpleLoader)
+            yaml.load(raw, Loader=ops.storage._SimpleLoader)
 
     def test_forbids_some_types(self):
         self.assertRefused(1 + 2j)
@@ -330,11 +330,11 @@ class TestSimpleLoader(BaseTestCase):
 class TestJujuStateBackend(BaseTestCase):
 
     def test_is_not_available(self):
-        self.assertFalse(storage.juju_backend_available())
+        self.assertFalse(ops.storage.juju_backend_available())
 
     def test_is_available(self):
         fake_script(self, 'state-get', 'echo ""')
-        self.assertTrue(storage.juju_backend_available())
+        self.assertTrue(ops.storage.juju_backend_available())
         self.assertEqual(fake_script_calls(self, clear=True), [])
 
     def test_set_encodes_args(self):
@@ -343,7 +343,7 @@ class TestJujuStateBackend(BaseTestCase):
             fake_script(self, 'state-set', dedent("""
                 cat >> {}
                 """).format(pathlib.Path(t.name).as_posix()))
-            backend = storage._JujuStorageBackend()
+            backend = ops.storage._JujuStorageBackend()
             backend.set('key', {'foo': 2})
             self.assertEqual(fake_script_calls(self, clear=True), [
                 ['state-set', '--file', '-'],
@@ -361,7 +361,7 @@ class TestJujuStateBackend(BaseTestCase):
         fake_script(self, 'state-get', dedent("""
             echo 'foo: "bar"'
             """))
-        backend = storage._JujuStorageBackend()
+        backend = ops.storage._JujuStorageBackend()
         value = backend.get('key')
         self.assertEqual(value, {'foo': 'bar'})
         self.assertEqual(fake_script_calls(self, clear=True), [
@@ -374,7 +374,7 @@ class TestJujuStateBackend(BaseTestCase):
             fake_script(self, 'state-set', dedent("""
                 cat >> {}
                 """).format(pathlib.Path(t.name).as_posix()))
-            backend = storage._JujuStorageBackend()
+            backend = ops.storage._JujuStorageBackend()
             complex_val = {
                 'foo': 2,
                 3: [1, 2, '3'],
@@ -394,7 +394,7 @@ class TestJujuStateBackend(BaseTestCase):
         outer = yaml.safe_load(content)
         key = 'Class[foo]/_stored'
         self.assertEqual(list(outer.keys()), [key])
-        inner = yaml.load(outer[key], Loader=storage._SimpleLoader)
+        inner = yaml.load(outer[key], Loader=ops.storage._SimpleLoader)
         self.assertEqual(complex_val, inner)
         self.assertEqual(content.decode('utf-8'), dedent("""\
             "Class[foo]/_stored": |
diff --git a/test/test_testing.py b/test/test_testing.py
index 581054e..17d97e1 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -33,29 +33,11 @@ from unittest.mock import MagicMock
 import pytest
 import yaml
 
+import ops
 import ops.testing
-from ops import model, pebble
-from ops.charm import (
-    CharmBase,
-    PebbleReadyEvent,
-    RelationDepartedEvent,
-    RelationEvent,
-    StorageAttachedEvent,
-    StorageDetachingEvent,
-)
-from ops.framework import Framework, Object
-from ops.model import (
-    ActiveStatus,
-    Application,
-    MaintenanceStatus,
-    ModelError,
-    RelationNotFoundError,
-    Unit,
-    UnknownStatus,
-    _ModelBackend,
-)
+from ops import pebble
+from ops.model import _ModelBackend
 from ops.testing import (
-    Harness,
     NonAbsolutePathError,
     _Directory,
     _TestingFilesystem,
@@ -66,7 +48,7 @@ from ops.testing import (
 is_linux = platform.system() == 'Linux'
 
 
-class SetLeaderErrorTester(CharmBase):
+class SetLeaderErrorTester(ops.CharmBase):
     """Sets peer relation data inside leader-elected."""
 
     def __init__(self, framework):
@@ -80,7 +62,7 @@ class SetLeaderErrorTester(CharmBase):
         peers.data[self.app]["foo"] = "bar"
 
 
-class StorageTester(CharmBase):
+class StorageTester(ops.CharmBase):
     """Record the relation-changed events."""
 
     def __init__(self, framework):
@@ -98,7 +80,7 @@ class StorageTester(CharmBase):
         self.observed_events.append(event)
 
 
-class StorageWithHyphensHelper(Object):
+class StorageWithHyphensHelper(ops.Object):
     def __init__(self, parent, key):
         super().__init__(parent, key)
         self.changes = []
@@ -114,7 +96,7 @@ class StorageWithHyphensHelper(Object):
 class TestHarness(unittest.TestCase):
 
     def test_add_relation(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             requires:
                 db:
@@ -131,7 +113,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(backend.relation_get(rel_id, 'test-app/0', is_app=False), {})
 
     def test_can_connect_default(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             containers:
               foo:
@@ -159,17 +141,17 @@ class TestHarness(unittest.TestCase):
     def test_can_connect_begin_with_initial_hooks(self):
         pebble_ready_calls = collections.defaultdict(int)
 
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             def __init__(self, *args):
                 super().__init__(*args)
                 self.framework.observe(self.on.foo_pebble_ready, self._on_pebble_ready)
                 self.framework.observe(self.on.bar_pebble_ready, self._on_pebble_ready)
 
-            def _on_pebble_ready(self, event: PebbleReadyEvent):
+            def _on_pebble_ready(self, event: ops.PebbleReadyEvent):
                 assert event.workload.can_connect()
                 pebble_ready_calls[event.workload.name] += 1
 
-        harness = Harness(MyCharm, meta='''
+        harness = ops.testing.Harness(MyCharm, meta='''
             name: test-app
             containers:
               foo:
@@ -193,7 +175,7 @@ class TestHarness(unittest.TestCase):
         container.get_plan()  # shouldn't raise ConnectionError
 
     def test_add_relation_and_unit(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             requires:
                 db:
@@ -213,7 +195,7 @@ class TestHarness(unittest.TestCase):
 
     def test_add_relation_with_remote_app_data(self):
         # language=YAML
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             requires:
                 db:
@@ -230,7 +212,7 @@ class TestHarness(unittest.TestCase):
 
     def test_add_relation_with_our_initial_data(self):
 
-        class InitialDataTester(CharmBase):
+        class InitialDataTester(ops.CharmBase):
             """Record the relation-changed events."""
 
             def __init__(self, framework):
@@ -242,7 +224,7 @@ class TestHarness(unittest.TestCase):
                 self.observed_events.append(event)
 
         # language=YAML
-        harness = Harness(InitialDataTester, meta='''
+        harness = ops.testing.Harness(InitialDataTester, meta='''
             name: test-app
             requires:
                 db:
@@ -280,7 +262,7 @@ class TestHarness(unittest.TestCase):
 
     def test_add_peer_relation_with_initial_data_leader(self):
 
-        class InitialDataTester(CharmBase):
+        class InitialDataTester(ops.CharmBase):
             """Record the relation-changed events."""
 
             def __init__(self, framework):
@@ -293,7 +275,7 @@ class TestHarness(unittest.TestCase):
                 self.observed_events.append(event)
 
         # language=YAML
-        harness = Harness(InitialDataTester, meta='''
+        harness = ops.testing.Harness(InitialDataTester, meta='''
             name: test-app
             peers:
                 cluster:
@@ -329,10 +311,10 @@ class TestHarness(unittest.TestCase):
         harness.update_relation_data(rel_id, 'test-app', {'k': 'v3'})
         self.assertEqual({'k': 'v3'}, backend.relation_get(rel_id, 'test-app', is_app=True))
         self.assertTrue(len(harness.charm.observed_events), 1)
-        self.assertIsInstance(harness.charm.observed_events[0], RelationEvent)
+        self.assertIsInstance(harness.charm.observed_events[0], ops.RelationEvent)
 
     def test_relation_get_when_broken(self):
-        harness = Harness(RelationBrokenTester, meta='''
+        harness = ops.testing.Harness(RelationBrokenTester, meta='''
             name: test-app
             requires:
                 foo:
@@ -350,7 +332,7 @@ class TestHarness(unittest.TestCase):
             harness.remove_relation(rel_id)
 
     def test_remove_relation(self):
-        harness = Harness(RelationEventCharm, meta='''
+        harness = ops.testing.Harness(RelationEventCharm, meta='''
             name: test-app
             requires:
                 db:
@@ -372,7 +354,7 @@ class TestHarness(unittest.TestCase):
         harness.remove_relation(rel_id)
         # Check relation no longer exists
         self.assertEqual(backend.relation_ids('db'), [])
-        self.assertRaises(RelationNotFoundError, backend.relation_list, rel_id)
+        self.assertRaises(ops.RelationNotFoundError, backend.relation_list, rel_id)
         # Check relation broken event is raised with correct data
         changes = harness.charm.get_changes()
         self.assertEqual(changes[0],
@@ -390,7 +372,7 @@ class TestHarness(unittest.TestCase):
                                    'relation_id': rel_id}})
 
     def test_remove_specific_relation_id(self):
-        harness = Harness(RelationEventCharm, meta='''
+        harness = ops.testing.Harness(RelationEventCharm, meta='''
             name: test-app
             requires:
                 db:
@@ -425,7 +407,7 @@ class TestHarness(unittest.TestCase):
         harness.remove_relation(rel_id_2)
         # Check second relation no longer exists but first does
         self.assertEqual(backend.relation_ids('db'), [rel_id_1])
-        self.assertRaises(RelationNotFoundError, backend.relation_list, rel_id_2)
+        self.assertRaises(ops.RelationNotFoundError, backend.relation_list, rel_id_2)
 
         # Check relation broken event is raised with correct data
         changes = harness.charm.get_changes()
@@ -444,7 +426,7 @@ class TestHarness(unittest.TestCase):
                                    'relation_id': rel_id_2}})
 
     def test_removing_invalid_relation_id_raises_exception(self):
-        harness = Harness(RelationEventCharm, meta='''
+        harness = ops.testing.Harness(RelationEventCharm, meta='''
             name: test-app
             requires:
                 db:
@@ -463,11 +445,11 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(backend.relation_list(rel_id), ['postgresql/0'])
         harness.charm.get_changes(reset=True)  # created event ignored
         # Check exception is raised if relation id is invalid
-        with self.assertRaises(RelationNotFoundError):
+        with self.assertRaises(ops.RelationNotFoundError):
             harness.remove_relation(rel_id + 1)
 
     def test_remove_relation_unit(self):
-        harness = Harness(RelationEventCharm, meta='''
+        harness = ops.testing.Harness(RelationEventCharm, meta='''
             name: test-app
             requires:
                 db:
@@ -517,7 +499,7 @@ class TestHarness(unittest.TestCase):
 
     def test_removing_relation_removes_remote_app_data(self):
         # language=YAML
-        harness = Harness(RelationEventCharm, meta='''
+        harness = ops.testing.Harness(RelationEventCharm, meta='''
             name: test-app
             requires:
                 db:
@@ -539,12 +521,12 @@ class TestHarness(unittest.TestCase):
         # Check relation and app data are removed
         self.assertEqual(backend.relation_ids('db'), [])
         with harness._event_context('foo'):
-            self.assertRaises(RelationNotFoundError, backend.relation_get,
+            self.assertRaises(ops.RelationNotFoundError, backend.relation_get,
                               rel_id, remote_app, is_app=True)
 
     def test_removing_relation_refreshes_charm_model(self):
         # language=YAML
-        harness = Harness(RelationEventCharm, meta='''
+        harness = ops.testing.Harness(RelationEventCharm, meta='''
             name: test-app
             requires:
                 db:
@@ -575,7 +557,7 @@ class TestHarness(unittest.TestCase):
         return None
 
     def test_removing_relation_unit_removes_data_also(self):
-        harness = Harness(RelationEventCharm, meta='''
+        harness = ops.testing.Harness(RelationEventCharm, meta='''
             name: test-app
             requires:
                 db:
@@ -617,7 +599,7 @@ class TestHarness(unittest.TestCase):
                                    'relation_id': rel_id}})
 
     def test_removing_relation_unit_does_not_remove_other_unit_and_data(self):
-        harness = Harness(RelationEventCharm, meta='''
+        harness = ops.testing.Harness(RelationEventCharm, meta='''
             name: test-app
             requires:
                 db:
@@ -663,7 +645,7 @@ class TestHarness(unittest.TestCase):
                                    'relation_id': rel_id}})
 
     def test_relation_events(self):
-        harness = Harness(RelationEventCharm, meta='''
+        harness = ops.testing.Harness(RelationEventCharm, meta='''
             name: test-app
             requires:
                 db:
@@ -721,7 +703,7 @@ class TestHarness(unittest.TestCase):
                 db:
                     interface: pgsql
         '''
-        harness = Harness(CharmBase, meta=charm_meta)
+        harness = ops.testing.Harness(ops.CharmBase, meta=charm_meta)
         self.addCleanup(harness.cleanup)
         rel_id = harness.add_relation('db', 'postgresql')
         harness.update_relation_data(rel_id, 'postgresql', {'remote': 'data'})
@@ -734,13 +716,13 @@ class TestHarness(unittest.TestCase):
             harness.get_relation_data(99, 'postgresql')
 
         meta = yaml.safe_load(charm_meta)
-        t_app = Application('test-app', meta, harness._backend, None)
-        t_unit0 = Unit('test-app/0', meta, harness._backend, {Application: t_app})
-        t_unit1 = Unit('test-app/1', meta, harness._backend, {Application: t_app})
+        t_app = ops.Application('test-app', meta, harness._backend, None)
+        t_unit0 = ops.Unit('test-app/0', meta, harness._backend, {ops.Application: t_app})
+        t_unit1 = ops.Unit('test-app/1', meta, harness._backend, {ops.Application: t_app})
         self.assertEqual(harness.get_relation_data(rel_id, t_app), {})
         self.assertEqual(harness.get_relation_data(rel_id, t_unit0), {})
         self.assertEqual(harness.get_relation_data(rel_id, t_unit1), None)
-        pg_app = Application('postgresql', meta, harness._backend, None)
+        pg_app = ops.Application('postgresql', meta, harness._backend, None)
         self.assertEqual(harness.get_relation_data(rel_id, pg_app), {'remote': 'data'})
 
     def test_create_harness_twice(self):
@@ -750,9 +732,9 @@ class TestHarness(unittest.TestCase):
               db:
                 interface: pgsql
             '''
-        harness1 = Harness(CharmBase, meta=metadata)
+        harness1 = ops.testing.Harness(ops.CharmBase, meta=metadata)
         self.addCleanup(harness1.cleanup)
-        harness2 = Harness(CharmBase, meta=metadata)
+        harness2 = ops.testing.Harness(ops.CharmBase, meta=metadata)
         self.addCleanup(harness2.cleanup)
         harness1.begin()
         harness2.begin()
@@ -766,7 +748,7 @@ class TestHarness(unittest.TestCase):
 
     def test_begin_twice(self):
         # language=YAML
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             requires:
                 db:
@@ -778,7 +760,7 @@ class TestHarness(unittest.TestCase):
             harness.begin()
 
     def test_update_relation_exposes_new_data(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: my-charm
             requires:
               db:
@@ -797,7 +779,7 @@ class TestHarness(unittest.TestCase):
 
     def test_update_relation_no_local_unit_change_event(self):
         # language=YAML
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: my-charm
             requires:
               db:
@@ -822,7 +804,7 @@ class TestHarness(unittest.TestCase):
 
     def test_update_peer_relation_no_local_unit_change_event(self):
         # language=YAML
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: postgresql
             peers:
               db:
@@ -859,7 +841,7 @@ class TestHarness(unittest.TestCase):
 
     def test_harness_leader_misconfig(self):
         # language=YAML
-        harness = Harness(SetLeaderErrorTester, meta='''
+        harness = ops.testing.Harness(SetLeaderErrorTester, meta='''
             name: postgresql
             peers:
               peer:
@@ -874,7 +856,7 @@ class TestHarness(unittest.TestCase):
 
     def test_update_peer_relation_app_data(self):
         # language=YAML
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: postgresql
             peers:
               db:
@@ -905,7 +887,7 @@ class TestHarness(unittest.TestCase):
 
     def test_update_relation_no_local_app_change_event(self):
         # language=YAML
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: my-charm
             requires:
               db:
@@ -930,7 +912,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(helper.changes, [])
 
     def test_update_relation_remove_data(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: my-charm
             requires:
               db:
@@ -946,7 +928,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(viewer.changes, [{'initial': 'data'}, {}])
 
     def test_no_event_on_empty_update_relation_unit_app(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: my-charm
             requires:
               db:
@@ -962,7 +944,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(viewer.changes, [{'initial': 'data'}])
 
     def test_no_event_on_no_diff_update_relation_unit_app(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: my-charm
             requires:
               db:
@@ -978,7 +960,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(viewer.changes, [{'initial': 'data'}])
 
     def test_no_event_on_empty_update_relation_unit_bag(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: my-charm
             requires:
               db:
@@ -994,7 +976,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(viewer.changes, [{'initial': 'data'}])
 
     def test_no_event_on_no_diff_update_relation_unit_bag(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: my-charm
             requires:
               db:
@@ -1011,10 +993,10 @@ class TestHarness(unittest.TestCase):
 
     def test_empty_config_raises(self):
         with self.assertRaises(TypeError):
-            Harness(RecordingCharm, config='')
+            ops.testing.Harness(RecordingCharm, config='')
 
     def test_update_config(self):
-        harness = Harness(RecordingCharm, config='''
+        harness = ops.testing.Harness(RecordingCharm, config='''
             options:
                 a:
                     description: a config option
@@ -1044,14 +1026,14 @@ class TestHarness(unittest.TestCase):
              ])
 
     def test_update_config_undefined_option(self):
-        harness = Harness(RecordingCharm)
+        harness = ops.testing.Harness(RecordingCharm)
         self.addCleanup(harness.cleanup)
         harness.begin()
         with self.assertRaises(ValueError):
             harness.update_config(key_values={'nonexistent': 'foo'})
 
     def test_update_config_bad_type(self):
-        harness = Harness(RecordingCharm, config='''
+        harness = ops.testing.Harness(RecordingCharm, config='''
             options:
                 a:
                     description: a config option
@@ -1077,7 +1059,7 @@ class TestHarness(unittest.TestCase):
 
     def test_bad_config_option_type(self):
         with self.assertRaises(RuntimeError):
-            Harness(RecordingCharm, config='''
+            ops.testing.Harness(RecordingCharm, config='''
                 options:
                     a:
                         description: a config option
@@ -1087,7 +1069,7 @@ class TestHarness(unittest.TestCase):
 
     def test_no_config_option_type(self):
         with self.assertRaises(RuntimeError):
-            Harness(RecordingCharm, config='''
+            ops.testing.Harness(RecordingCharm, config='''
                 options:
                     a:
                         description: a config option
@@ -1096,7 +1078,7 @@ class TestHarness(unittest.TestCase):
 
     def test_uncastable_config_option_type(self):
         with self.assertRaises(RuntimeError):
-            Harness(RecordingCharm, config='''
+            ops.testing.Harness(RecordingCharm, config='''
                 options:
                     a:
                         description: a config option
@@ -1105,7 +1087,7 @@ class TestHarness(unittest.TestCase):
                 ''')
 
     def test_update_config_unset_boolean(self):
-        harness = Harness(RecordingCharm, config='''
+        harness = ops.testing.Harness(RecordingCharm, config='''
             options:
                 a:
                     description: a config option
@@ -1127,7 +1109,7 @@ class TestHarness(unittest.TestCase):
              {'name': 'config-changed', 'data': {'a': False}}])
 
     def test_set_leader(self):
-        harness = Harness(RecordingCharm)
+        harness = ops.testing.Harness(RecordingCharm)
         self.addCleanup(harness.cleanup)
         # No event happens here
         harness.set_leader(False)
@@ -1148,7 +1130,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness.charm.get_changes(reset=True), [])
 
     def test_relation_set_app_not_leader(self):
-        harness = Harness(RecordingCharm, meta='''
+        harness = ops.testing.Harness(RecordingCharm, meta='''
             name: test-charm
             requires:
                 db:
@@ -1161,7 +1143,7 @@ class TestHarness(unittest.TestCase):
         harness.add_relation_unit(rel_id, 'postgresql/0')
         rel = harness.charm.model.get_relation('db')
         with harness._event_context('foo'):
-            with self.assertRaises(ModelError):
+            with self.assertRaises(ops.ModelError):
                 rel.data[harness.charm.app]['foo'] = 'bar'
         # The data has not actually been changed
         self.assertEqual(harness.get_relation_data(rel_id, 'test-charm'), {})
@@ -1170,7 +1152,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness.get_relation_data(rel_id, 'test-charm'), {'foo': 'bar'})
 
     def test_hooks_enabled_and_disabled(self):
-        harness = Harness(
+        harness = ops.testing.Harness(
             RecordingCharm,
             meta='''
                     name: test-charm
@@ -1202,7 +1184,7 @@ class TestHarness(unittest.TestCase):
             [{'name': 'config-changed', 'data': {'value': 'fourth', 'third': '3'}}])
 
     def test_hooks_disabled_contextmanager(self):
-        harness = Harness(RecordingCharm, meta='''
+        harness = ops.testing.Harness(RecordingCharm, meta='''
                 name: test-charm
                 ''', config='''
                 options:
@@ -1230,7 +1212,7 @@ class TestHarness(unittest.TestCase):
             [{'name': 'config-changed', 'data': {'value': 'fourth', 'third': '3'}}])
 
     def test_hooks_disabled_nested_contextmanager(self):
-        harness = Harness(RecordingCharm, meta='''
+        harness = ops.testing.Harness(RecordingCharm, meta='''
                 name: test-charm
             ''', config='''
                 options:
@@ -1249,7 +1231,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness.charm.get_changes(reset=True), [])
 
     def test_hooks_disabled_noop(self):
-        harness = Harness(RecordingCharm, meta='''
+        harness = ops.testing.Harness(RecordingCharm, meta='''
                 name: test-charm
             ''', config='''
             options:
@@ -1325,7 +1307,7 @@ class TestHarness(unittest.TestCase):
         self.assertIsNone(harness._backend._config._defaults['opt_no_default'])
 
     def test_set_model_name(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
         ''')
         self.addCleanup(harness.cleanup)
@@ -1333,7 +1315,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual('foo', harness.model.name)
 
     def test_set_model_name_after_begin(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
         ''')
         self.addCleanup(harness.cleanup)
@@ -1344,7 +1326,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness.model.name, 'bar')
 
     def test_set_model_uuid_after_begin(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
         ''')
         self.addCleanup(harness.cleanup)
@@ -1356,7 +1338,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness.model.uuid, '96957e90-e006-11eb-ba80-0242ac130004')
 
     def test_set_model_info_after_begin(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
         ''')
         self.addCleanup(harness.cleanup)
@@ -1376,7 +1358,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness.model.uuid, '96957e90-e006-11eb-ba80-0242ac130004')
 
     def test_add_storage_before_harness_begin(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1394,11 +1376,11 @@ class TestHarness(unittest.TestCase):
             # before begin, adding storage does not attach it.
             self.assertNotIn(s, harness._backend.storage_list("test"))
 
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             harness._backend.storage_get("test/0", "location")[-6:]
 
     def test_add_storage_then_harness_begin(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1413,19 +1395,19 @@ class TestHarness(unittest.TestCase):
 
         harness.add_storage("test", count=3)
 
-        with self.assertRaises(ops.model.ModelError):
+        with self.assertRaises(ops.ModelError):
             harness._backend.storage_get("test/0", "location")[-6:]
 
         harness.begin_with_initial_hooks()
         self.assertEqual(len(harness.charm.observed_events), 3)
         for i in range(3):
-            self.assertTrue(isinstance(harness.charm.observed_events[i], StorageAttachedEvent))
+            self.assertTrue(isinstance(harness.charm.observed_events[i], ops.StorageAttachedEvent))
 
         want = str(pathlib.PurePath('test', '0'))
         self.assertEqual(want, harness._backend.storage_get("test/0", "location")[-6:])
 
     def test_add_storage_not_attached_default(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             requires:
                 db:
@@ -1442,7 +1424,7 @@ class TestHarness(unittest.TestCase):
             'storage should start in detached state and be excluded from storage listing'
 
     def test_add_storage_without_metadata_key_fails(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             requires:
                 db:
@@ -1457,7 +1439,7 @@ class TestHarness(unittest.TestCase):
             "the key 'test' is not specified as a storage key in metadata")
 
     def test_add_storage_after_harness_begin(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1474,7 +1456,7 @@ class TestHarness(unittest.TestCase):
         harness.add_storage("test")[0]
         harness.begin_with_initial_hooks()
         self.assertEqual(len(harness.charm.observed_events), 1)
-        self.assertTrue(isinstance(harness.charm.observed_events[0], StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent))
 
         # Add additional storage
         stor_ids = harness.add_storage("test", count=3, attach=True)
@@ -1491,10 +1473,10 @@ class TestHarness(unittest.TestCase):
             self.assertTrue(harness._backend.storage_get(storage_name, "location").endswith(want))
         self.assertEqual(len(harness.charm.observed_events), 4)
         for i in range(1, 4):
-            self.assertTrue(isinstance(harness.charm.observed_events[i], StorageAttachedEvent))
+            self.assertTrue(isinstance(harness.charm.observed_events[i], ops.StorageAttachedEvent))
 
     def test_detach_storage(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1509,17 +1491,17 @@ class TestHarness(unittest.TestCase):
         stor_id = harness.add_storage("test")[0]
         harness.begin_with_initial_hooks()
         self.assertEqual(len(harness.charm.observed_events), 1)
-        self.assertTrue(isinstance(harness.charm.observed_events[0], StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent))
 
         # Detach storage
         harness.detach_storage(stor_id)
         self.assertEqual(len(harness.charm.observed_events), 2)
-        self.assertTrue(isinstance(harness.charm.observed_events[1], StorageDetachingEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageDetachingEvent))
 
         # Verify backend functions return appropriate values.
         # Real backend would return info only for actively attached storage units.
         self.assertNotIn(stor_id, harness._backend.storage_list("test"))
-        with self.assertRaises(ModelError) as cm:
+        with self.assertRaises(ops.ModelError) as cm:
             harness._backend.storage_get("test/0", "location")
         # Error message modeled after output of
         # "storage-get -s <invalid/inactive id> location" on real deployment
@@ -1531,10 +1513,10 @@ class TestHarness(unittest.TestCase):
         # Since already detached, no more hooks should fire
         harness.detach_storage(stor_id)
         self.assertEqual(len(harness.charm.observed_events), 2)
-        self.assertTrue(isinstance(harness.charm.observed_events[1], StorageDetachingEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageDetachingEvent))
 
     def test_detach_storage_before_harness_begin(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1552,7 +1534,7 @@ class TestHarness(unittest.TestCase):
                          "cannot detach storage before Harness is initialised")
 
     def test_storage_with_hyphens_works(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1573,7 +1555,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(len(helper.changes), 1)
 
     def test_attach_storage(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1588,17 +1570,17 @@ class TestHarness(unittest.TestCase):
         stor_id = harness.add_storage("test")[0]
         harness.begin_with_initial_hooks()
         self.assertEqual(len(harness.charm.observed_events), 1)
-        self.assertTrue(isinstance(harness.charm.observed_events[0], StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent))
 
         # Detach storage
         harness.detach_storage(stor_id)
         self.assertEqual(len(harness.charm.observed_events), 2)
-        self.assertTrue(isinstance(harness.charm.observed_events[1], StorageDetachingEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageDetachingEvent))
 
         # Re-attach storage
         harness.attach_storage(stor_id)
         self.assertEqual(len(harness.charm.observed_events), 3)
-        self.assertTrue(isinstance(harness.charm.observed_events[2], StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[2], ops.StorageAttachedEvent))
 
         # Verify backend functions return appropriate values.
         # Real backend would return info only for actively attached storage units.
@@ -1610,10 +1592,10 @@ class TestHarness(unittest.TestCase):
         # Since already detached, no more hooks should fire
         harness.attach_storage(stor_id)
         self.assertEqual(len(harness.charm.observed_events), 3)
-        self.assertTrue(isinstance(harness.charm.observed_events[2], StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[2], ops.StorageAttachedEvent))
 
     def test_attach_storage_before_harness_begin(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1630,7 +1612,7 @@ class TestHarness(unittest.TestCase):
         self.assertTrue(stor_id)
 
     def test_remove_storage_before_harness_begin(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1655,10 +1637,10 @@ class TestHarness(unittest.TestCase):
         harness.begin_with_initial_hooks()
         # Only one hook will fire; one won't since it was removed
         self.assertEqual(len(harness.charm.observed_events), 1)
-        self.assertTrue(isinstance(harness.charm.observed_events[0], StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent))
 
     def test_remove_storage_without_metadata_key_fails(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             requires:
                 db:
@@ -1675,7 +1657,7 @@ class TestHarness(unittest.TestCase):
             "the key 'test' is not specified as a storage key in metadata")
 
     def test_remove_storage_after_harness_begin(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1691,12 +1673,12 @@ class TestHarness(unittest.TestCase):
         stor_ids = harness.add_storage("test", count=2)
         harness.begin_with_initial_hooks()
         self.assertEqual(len(harness.charm.observed_events), 2)
-        self.assertTrue(isinstance(harness.charm.observed_events[0], StorageAttachedEvent))
-        self.assertTrue(isinstance(harness.charm.observed_events[1], StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageAttachedEvent))
 
         harness.remove_storage(stor_ids[1])
         self.assertEqual(len(harness.charm.observed_events), 3)
-        self.assertTrue(isinstance(harness.charm.observed_events[2], StorageDetachingEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[2], ops.StorageDetachingEvent))
 
         attached_storage_ids = harness._backend.storage_list("test")
         self.assertIn(self._extract_storage_index(stor_ids[0]), attached_storage_ids)
@@ -1706,7 +1688,7 @@ class TestHarness(unittest.TestCase):
         return int(stor_id.split('/')[-1])
 
     def test_remove_detached_storage(self):
-        harness = Harness(StorageTester, meta='''
+        harness = ops.testing.Harness(StorageTester, meta='''
             name: test-app
             requires:
                 db:
@@ -1724,9 +1706,9 @@ class TestHarness(unittest.TestCase):
         harness.detach_storage(stor_ids[0])
         harness.remove_storage(stor_ids[0])  # Already detached, so won't fire a hook
         self.assertEqual(len(harness.charm.observed_events), 3)
-        self.assertTrue(isinstance(harness.charm.observed_events[0], StorageAttachedEvent))
-        self.assertTrue(isinstance(harness.charm.observed_events[1], StorageAttachedEvent))
-        self.assertTrue(isinstance(harness.charm.observed_events[2], StorageDetachingEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageAttachedEvent))
+        self.assertTrue(isinstance(harness.charm.observed_events[2], ops.StorageDetachingEvent))
 
     def test_actions_from_directory(self):
         tmp = pathlib.Path(tempfile.mkdtemp())
@@ -1746,7 +1728,7 @@ class TestHarness(unittest.TestCase):
     def _get_dummy_charm_harness(self, tmp):
         self._write_dummy_charm(tmp)
         charm_mod = importlib.import_module('testcharm')
-        harness = Harness(charm_mod.MyTestingCharm)
+        harness = ops.testing.Harness(charm_mod.MyTestingCharm)
         self.addCleanup(harness.cleanup)
         return harness
 
@@ -1757,7 +1739,7 @@ class TestHarness(unittest.TestCase):
         with charm_filename.open('wt') as charmpy:
             # language=Python
             charmpy.write(textwrap.dedent('''
-                from ops.charm import CharmBase
+                from ops import CharmBase
                 class MyTestingCharm(CharmBase):
                     pass
                 '''))
@@ -1771,8 +1753,8 @@ class TestHarness(unittest.TestCase):
         self.addCleanup(cleanup)
 
     def test_actions_passed_in(self):
-        harness = Harness(
-            CharmBase,
+        harness = ops.testing.Harness(
+            ops.CharmBase,
             meta='''
                 name: test-app
             ''',
@@ -1784,11 +1766,11 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(list(harness.framework.meta.actions), ['test-action'])
 
     def test_event_context(self):
-        class MyCharm(CharmBase):
+        class MyCharm(ops.CharmBase):
             def event_handler(self, evt):
                 evt.relation.data[evt.relation.app]['foo'] = 'bar'
 
-        harness = Harness(MyCharm, meta='''
+        harness = ops.testing.Harness(MyCharm, meta='''
             name: test-charm
             requires:
                 db:
@@ -1802,12 +1784,12 @@ class TestHarness(unittest.TestCase):
         event.relation = rel
 
         with harness._event_context('my_relation_joined'):
-            with self.assertRaises(ops.model.RelationDataError):
+            with self.assertRaises(ops.RelationDataError):
                 harness.charm.event_handler(event)
 
     def test_event_context_inverse(self):
-        class MyCharm(CharmBase):
-            def __init__(self, framework: Framework):
+        class MyCharm(ops.CharmBase):
+            def __init__(self, framework: ops.Framework):
                 super().__init__(framework)
                 self.framework.observe(self.on.db_relation_joined,
                                        self._join_db)
@@ -1816,7 +1798,7 @@ class TestHarness(unittest.TestCase):
                 # do things with APIs we cannot easily mock
                 raise NotImplementedError
 
-        harness = Harness(MyCharm, meta='''
+        harness = ops.testing.Harness(MyCharm, meta='''
             name: test-charm
             requires:
                 db:
@@ -1845,7 +1827,7 @@ class TestHarness(unittest.TestCase):
         assert rel.data[harness.charm.app]['foo'] == 'bar'
 
     def test_relation_set_deletes(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
             requires:
                 db:
@@ -1862,7 +1844,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual({}, harness.get_relation_data(rel_id, 'test-charm/0'))
 
     def test_relation_set_nonstring(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
             requires:
                 db:
@@ -1873,12 +1855,12 @@ class TestHarness(unittest.TestCase):
         harness.set_leader(False)
         rel_id = harness.add_relation('db', 'postgresql')
         for invalid_value in (1, 1.2, {}, [], set(), True, object(), type):
-            with self.assertRaises(model.RelationDataError):
+            with self.assertRaises(ops.RelationDataError):
                 harness.update_relation_data(rel_id, 'test-charm/0',
                                              {'foo': invalid_value})
 
     def test_set_workload_version(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: app
             ''')
         self.addCleanup(harness.cleanup)
@@ -1888,7 +1870,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness.get_workload_version(), '1.2.3')
 
     def test_get_backend_calls(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
             requires:
                 db:
@@ -1943,7 +1925,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness._get_backend_calls(), [])
 
     def test_get_backend_calls_with_kwargs(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
             requires:
                 db:
@@ -1954,42 +1936,42 @@ class TestHarness(unittest.TestCase):
         unit = harness.charm.model.unit
         # Reset the list, because we don't care what it took to get here
         harness._get_backend_calls(reset=True)
-        unit.status = ActiveStatus()
+        unit.status = ops.ActiveStatus()
         self.assertEqual(
             [('status_set', 'active', '', {'is_app': False})], harness._get_backend_calls())
         harness.set_leader(True)
         app = harness.charm.model.app
         harness._get_backend_calls(reset=True)
-        app.status = ActiveStatus('message')
+        app.status = ops.ActiveStatus('message')
         self.assertEqual(
             [('is_leader',),
              ('status_set', 'active', 'message', {'is_app': True})],
             harness._get_backend_calls())
 
     def test_unit_status(self):
-        harness = Harness(CharmBase, meta='name: test-app')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: test-app')
         self.addCleanup(harness.cleanup)
         harness.set_leader(True)
         harness.begin()
         # default status
-        self.assertEqual(harness.model.unit.status, MaintenanceStatus(''))
-        status = ActiveStatus('message')
+        self.assertEqual(harness.model.unit.status, ops.MaintenanceStatus(''))
+        status = ops.ActiveStatus('message')
         harness.model.unit.status = status
         self.assertEqual(harness.model.unit.status, status)
 
     def test_app_status(self):
-        harness = Harness(CharmBase, meta='name: test-app')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: test-app')
         self.addCleanup(harness.cleanup)
         harness.set_leader(True)
         harness.begin()
         # default status
-        self.assertEqual(harness.model.app.status, UnknownStatus())
-        status = ActiveStatus('message')
+        self.assertEqual(harness.model.app.status, ops.UnknownStatus())
+        status = ops.ActiveStatus('message')
         harness.model.app.status = status
         self.assertEqual(harness.model.app.status, status)
 
     def test_populate_oci_resources(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -2014,7 +1996,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(path.parent.name, 'image2')
 
     def test_resource_folder_cleanup(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -2031,7 +2013,7 @@ class TestHarness(unittest.TestCase):
         self.assertFalse(path.parent.parent.exists())
 
     def test_container_isdir_and_exists(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             containers:
               foo:
@@ -2059,7 +2041,7 @@ class TestHarness(unittest.TestCase):
         self.assertTrue(c.exists(file_path))
 
     def test_add_oci_resource_custom(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -2081,7 +2063,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(contents['password'], 'custom_password')
 
     def test_add_oci_resource_no_image(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -2096,7 +2078,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(len(harness._backend._resources_map), 0)
 
     def test_add_resource_unknown(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -2108,7 +2090,7 @@ class TestHarness(unittest.TestCase):
             harness.add_resource('unknown', 'content')
 
     def test_add_resource_but_oci(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -2120,7 +2102,7 @@ class TestHarness(unittest.TestCase):
             harness.add_resource('image', 'content')
 
     def test_add_resource_string(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -2137,7 +2119,7 @@ class TestHarness(unittest.TestCase):
             self.assertEqual('foo contents\n', f.read())
 
     def test_add_resource_bytes(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -2155,7 +2137,7 @@ class TestHarness(unittest.TestCase):
             self.assertEqual(raw_contents, f.read())
 
     def test_add_resource_unknown_filename(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -2169,7 +2151,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(path.parent.name, 'image')
 
     def test_get_pod_spec(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             ''')
         self.addCleanup(harness.cleanup)
@@ -2180,7 +2162,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness.get_pod_spec(), (container_spec, k8s_resources))
 
     def test_begin_with_initial_hooks_no_relations(self):
-        harness = Harness(RecordingCharm, meta='''
+        harness = ops.testing.Harness(RecordingCharm, meta='''
             name: test-app
             ''', config='''
             options:
@@ -2206,7 +2188,7 @@ class TestHarness(unittest.TestCase):
         )
 
     def test_begin_with_initial_hooks_no_relations_not_leader(self):
-        harness = Harness(RecordingCharm, meta='''
+        harness = ops.testing.Harness(RecordingCharm, meta='''
             name: test-app
             ''', config='''
             options:
@@ -2235,7 +2217,7 @@ class TestHarness(unittest.TestCase):
             def __init__(self, framework):
                 super().__init__(framework)
                 self.observe_relation_events('peer')
-        harness = Harness(PeerCharm, meta='''
+        harness = ops.testing.Harness(PeerCharm, meta='''
             name: test-app
             peers:
               peer:
@@ -2275,7 +2257,7 @@ class TestHarness(unittest.TestCase):
             def __init__(self, framework):
                 super().__init__(framework)
                 self.observe_relation_events('peer')
-        harness = Harness(PeerCharm, meta='''
+        harness = ops.testing.Harness(PeerCharm, meta='''
             name: test-app
             peers:
               peer:
@@ -2307,7 +2289,7 @@ class TestHarness(unittest.TestCase):
             def __init__(self, framework):
                 super().__init__(framework)
                 self.observe_relation_events('db')
-        harness = Harness(CharmWithDB, meta='''
+        harness = ops.testing.Harness(CharmWithDB, meta='''
             name: test-app
             requires:
               db:
@@ -2330,7 +2312,7 @@ class TestHarness(unittest.TestCase):
             def __init__(self, framework):
                 super().__init__(framework)
                 self.observe_relation_events('db')
-        harness = Harness(CharmWithDB, meta='''
+        harness = ops.testing.Harness(CharmWithDB, meta='''
             name: test-app
             requires:
               db:
@@ -2377,7 +2359,7 @@ class TestHarness(unittest.TestCase):
             def __init__(self, framework):
                 super().__init__(framework)
                 self.observe_relation_events('db')
-        harness = Harness(CharmWithDB, meta='''
+        harness = ops.testing.Harness(CharmWithDB, meta='''
             name: test-app
             requires:
               db:
@@ -2432,7 +2414,7 @@ class TestHarness(unittest.TestCase):
             def __init__(self, framework):
                 super().__init__(framework)
                 self.observe_relation_events('db')
-        harness = Harness(CharmWithDB, meta='''
+        harness = ops.testing.Harness(CharmWithDB, meta='''
             name: test-app
             requires:
               db:
@@ -2495,7 +2477,7 @@ class TestHarness(unittest.TestCase):
             def __init__(self, framework):
                 super().__init__(framework)
                 self.observe_relation_events('db')
-        harness = Harness(CharmWithDB, meta='''
+        harness = ops.testing.Harness(CharmWithDB, meta='''
             name: test-app
             requires:
               db:
@@ -2582,7 +2564,7 @@ class TestHarness(unittest.TestCase):
     def test_begin_with_initial_hooks_unknown_status(self):
         # Verify that a charm that does not set a status in the install hook will have an
         # unknown status in the harness.
-        harness = Harness(RecordingCharm, meta='''
+        harness = ops.testing.Harness(RecordingCharm, meta='''
             name: test-app
             ''', config='''
           options:
@@ -2603,7 +2585,7 @@ class TestHarness(unittest.TestCase):
             {'status': 'unknown', 'message': ''})
 
     def test_begin_with_initial_hooks_install_sets_status(self):
-        harness = Harness(RecordingCharm, meta='''
+        harness = ops.testing.Harness(RecordingCharm, meta='''
             name: test-app
             ''', config='''
             options:
@@ -2622,7 +2604,7 @@ class TestHarness(unittest.TestCase):
             {'status': 'maintenance', 'message': 'Status set on install'})
 
     def test_get_pebble_container_plan(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             containers:
               foo:
@@ -2663,7 +2645,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(harness_plan.to_yaml(), plan.to_yaml())
 
     def test_get_pebble_container_plan_unknown(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             containers:
               foo:
@@ -2678,7 +2660,7 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(plan.to_yaml(), "{}\n")
 
     def test_container_pebble_ready(self):
-        harness = Harness(ContainerEventCharm, meta='''
+        harness = ops.testing.Harness(ContainerEventCharm, meta='''
             name: test-app
             containers:
               foo:
@@ -2702,7 +2684,7 @@ class TestHarness(unittest.TestCase):
 
 class TestNetwork(unittest.TestCase):
     def setUp(self):
-        self.harness = Harness(CharmBase, meta='''
+        self.harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
             requires:
                db:
@@ -2819,11 +2801,11 @@ class TestNetwork(unittest.TestCase):
         self.assertEqual(interface.subnet, ipaddress.IPv6Network('2001:0db8::0:0:0:0/64'))
 
     def test_network_get_relation_not_found(self):
-        with self.assertRaises(model.RelationNotFoundError):
+        with self.assertRaises(ops.RelationNotFoundError):
             self.harness.model.get_binding('db').network
 
     def test_add_network_endpoint_not_in_meta(self):
-        with self.assertRaises(model.ModelError):
+        with self.assertRaises(ops.ModelError):
             self.harness.add_network('35.0.0.1', endpoint='xyz')
 
     def test_add_network_relation_id_set_endpoint_not_set(self):
@@ -2833,16 +2815,16 @@ class TestNetwork(unittest.TestCase):
 
     def test_add_network_relation_id_incorrect(self):
         relation_id = self.harness.add_relation('db', 'postgresql')
-        with self.assertRaises(model.ModelError):
+        with self.assertRaises(ops.ModelError):
             self.harness.add_network('35.0.0.1', endpoint='db', relation_id=relation_id + 1)
 
     def test_add_network_endpoint_and_relation_id_do_not_correspond(self):
         relation_id = self.harness.add_relation('db', 'postgresql')
-        with self.assertRaises(model.ModelError):
+        with self.assertRaises(ops.ModelError):
             self.harness.add_network('35.0.0.1', endpoint='foo', relation_id=relation_id)
 
 
-class DBRelationChangedHelper(Object):
+class DBRelationChangedHelper(ops.Object):
     def __init__(self, parent, key):
         super().__init__(parent, key)
         self.changes = []
@@ -2855,7 +2837,7 @@ class DBRelationChangedHelper(Object):
             self.changes.append((event.relation.id, event.app.name))
 
 
-class RelationChangedViewer(Object):
+class RelationChangedViewer(ops.Object):
     """Track relation_changed events and saves the data seen in the relation bucket."""
 
     def __init__(self, charm, relation_name):
@@ -2871,7 +2853,7 @@ class RelationChangedViewer(Object):
         self.changes.append(dict(data))
 
 
-class RecordingCharm(CharmBase):
+class RecordingCharm(ops.CharmBase):
     """Record the events that we see, and any associated data."""
 
     def __init__(self, framework):
@@ -2895,7 +2877,7 @@ class RecordingCharm(CharmBase):
 
     def _on_install(self, _):
         if self.config.get('set_status'):
-            self.unit.status = MaintenanceStatus("Status set on install")
+            self.unit.status = ops.MaintenanceStatus("Status set on install")
         self.changes.append(dict(name='install'))
 
     def _on_start(self, _):
@@ -2966,7 +2948,7 @@ class RelationEventCharm(RecordingCharm):
             app_name = event.app.name
 
         data = dict(app=app_name, unit=unit_name, relation_id=event.relation.id)
-        if isinstance(event, RelationDepartedEvent):
+        if isinstance(event, ops.RelationDepartedEvent):
             data['departing_unit'] = event.departing_unit.name
 
         recording = dict(name=event_name, relation=event.relation.name, data=data)
@@ -3002,7 +2984,7 @@ class ContainerEventCharm(RecordingCharm):
     def _on_pebble_ready(self, event):
         self._observe_container_event('pebble-ready', event)
 
-    def _observe_container_event(self, event_name, event: PebbleReadyEvent):
+    def _observe_container_event(self, event_name, event: ops.PebbleReadyEvent):
         container_name = None
         if event.workload is not None:
             container_name = event.workload.name
@@ -3025,7 +3007,7 @@ def get_public_methods(obj):
 class TestTestingModelBackend(unittest.TestCase):
 
     def test_conforms_to_model_backend(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: app
             ''')
         self.addCleanup(harness.cleanup)
@@ -3035,7 +3017,7 @@ class TestTestingModelBackend(unittest.TestCase):
         self.assertEqual(mb_methods, backend_methods)
 
     def test_model_uuid_is_uuid_v4(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
         ''')
         self.addCleanup(harness.cleanup)
@@ -3043,7 +3025,7 @@ class TestTestingModelBackend(unittest.TestCase):
         self.assertEqual(uuid.UUID(backend.model_uuid).version, 4)
 
     def test_status_set_get_unit(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: app
             ''')
         self.addCleanup(harness.cleanup)
@@ -3057,7 +3039,7 @@ class TestTestingModelBackend(unittest.TestCase):
             {'status': 'unknown', 'message': ''})
 
     def test_status_set_get_app(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: app
             ''')
         self.addCleanup(harness.cleanup)
@@ -3071,7 +3053,7 @@ class TestTestingModelBackend(unittest.TestCase):
             {'status': 'maintenance', 'message': ''})
 
     def test_relation_ids_unknown_relation(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
             provides:
               db:
@@ -3082,29 +3064,29 @@ class TestTestingModelBackend(unittest.TestCase):
         # With no relations added, we just get an empty list for the interface
         self.assertEqual(backend.relation_ids('db'), [])
         # But an unknown interface raises a ModelError
-        with self.assertRaises(ModelError):
+        with self.assertRaises(ops.ModelError):
             backend.relation_ids('unknown')
 
     def test_relation_get_unknown_relation_id(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
             ''')
         self.addCleanup(harness.cleanup)
         backend = harness._backend
-        with self.assertRaises(RelationNotFoundError):
+        with self.assertRaises(ops.RelationNotFoundError):
             backend.relation_get(1234, 'unit/0', False)
 
     def test_relation_list_unknown_relation_id(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
             ''')
         self.addCleanup(harness.cleanup)
         backend = harness._backend
-        with self.assertRaises(RelationNotFoundError):
+        with self.assertRaises(ops.RelationNotFoundError):
             backend.relation_list(1234)
 
     def test_lazy_resource_directory(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -3122,7 +3104,7 @@ class TestTestingModelBackend(unittest.TestCase):
             msg=f'expected {path} to be a subdirectory of {backend._resource_dir.name}')
 
     def test_resource_get_no_resource(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             resources:
               image:
@@ -3131,14 +3113,14 @@ class TestTestingModelBackend(unittest.TestCase):
             ''')
         self.addCleanup(harness.cleanup)
         backend = harness._backend
-        with self.assertRaises(ModelError) as cm:
+        with self.assertRaises(ops.ModelError) as cm:
             backend.resource_get('foo')
         self.assertIn(
             "units/unit-test-app-0/resources/foo: resource#test-app/foo not found",
             str(cm.exception))
 
     def test_relation_remote_app_name(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-charm
             requires:
                db:
@@ -3158,7 +3140,7 @@ class TestTestingModelBackend(unittest.TestCase):
         self.assertIs(backend.relation_remote_app_name(7), None)
 
     def test_get_pebble_methods(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             ''')
         self.addCleanup(harness.cleanup)
@@ -3170,7 +3152,7 @@ class TestTestingModelBackend(unittest.TestCase):
 
 class _TestingPebbleClientMixin:
     def get_testing_client(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             containers:
               mycontainer: {}
@@ -4370,7 +4352,7 @@ class TestPebbleStorageAPIsUsingMocks(
 
     @unittest.skipUnless(is_linux, 'Pebble runs on Linux')
     def test_container_storage_mounts(self):
-        harness = Harness(CharmBase, meta='''
+        harness = ops.testing.Harness(ops.CharmBase, meta='''
             name: test-app
             containers:
                 c1:
@@ -4483,7 +4465,7 @@ class TestPebbleStorageAPIsUsingRealPebble(unittest.TestCase, _PebbleStorageAPIs
 
 class TestSecrets(unittest.TestCase):
     def test_add_model_secret_by_app_name_str(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
         relation_id = harness.add_relation('db', 'database')
         harness.add_relation_unit(relation_id, 'database/0')
@@ -4495,7 +4477,7 @@ class TestSecrets(unittest.TestCase):
         self.assertEqual(secret.get_content(), {'password': 'hunter2'})
 
     def test_add_model_secret_by_app_instance(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
         relation_id = harness.add_relation('db', 'database')
         harness.add_relation_unit(relation_id, 'database/0')
@@ -4508,7 +4490,7 @@ class TestSecrets(unittest.TestCase):
         self.assertEqual(secret.get_content(), {'password': 'hunter3'})
 
     def test_add_model_secret_by_unit_instance(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
         relation_id = harness.add_relation('db', 'database')
         harness.add_relation_unit(relation_id, 'database/0')
@@ -4521,14 +4503,14 @@ class TestSecrets(unittest.TestCase):
         self.assertEqual(secret.get_content(), {'password': 'hunter4'})
 
     def test_add_model_secret_invalid_content(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
 
         with self.assertRaises(ValueError):
             harness.add_model_secret('database', {'x': 'y'})  # key too short
 
     def test_set_secret_content(self):
-        harness = Harness(EventRecorder, meta='name: webapp')
+        harness = ops.testing.Harness(EventRecorder, meta='name: webapp')
         self.addCleanup(harness.cleanup)
         relation_id = harness.add_relation('db', 'database')
         harness.add_relation_unit(relation_id, 'database/0')
@@ -4541,7 +4523,7 @@ class TestSecrets(unittest.TestCase):
 
         self.assertEqual(len(harness.charm.events), 1)
         event = harness.charm.events[0]
-        self.assertIsInstance(event, ops.charm.SecretChangedEvent)
+        self.assertIsInstance(event, ops.SecretChangedEvent)
         self.assertEqual(event.secret.get_content(), {'foo': '1'})
         self.assertEqual(event.secret.get_content(refresh=True), {'foo': '2'})
         self.assertEqual(event.secret.get_content(), {'foo': '2'})
@@ -4549,7 +4531,7 @@ class TestSecrets(unittest.TestCase):
         self.assertEqual(harness.get_secret_revisions(secret_id), [1, 2])
 
     def test_set_secret_content_wrong_owner(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
 
         secret = harness.model.app.add_secret({'foo': 'bar'})
@@ -4557,14 +4539,14 @@ class TestSecrets(unittest.TestCase):
             harness.set_secret_content(secret.id, {'bar': 'foo'})
 
     def test_set_secret_content_invalid_secret_id(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
 
         with self.assertRaises(RuntimeError):
             harness.set_secret_content('asdf', {'foo': 'bar'})
 
     def test_set_secret_content_invalid_content(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
 
         secret_id = harness.add_model_secret('database', {'foo': 'bar'})
@@ -4572,7 +4554,7 @@ class TestSecrets(unittest.TestCase):
             harness.set_secret_content(secret_id, {'x': 'y'})
 
     def test_grant_secret_and_revoke_secret(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
         relation_id = harness.add_relation('db', 'database')
         harness.add_relation_unit(relation_id, 'database/0')
@@ -4584,33 +4566,33 @@ class TestSecrets(unittest.TestCase):
         self.assertEqual(secret.get_content(), {'password': 'hunter2'})
 
         harness.revoke_secret(secret_id, 'webapp')
-        with self.assertRaises(model.SecretNotFoundError):
+        with self.assertRaises(ops.SecretNotFoundError):
             harness.model.get_secret(id=secret_id)
 
     def test_grant_secret_wrong_app(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
         relation_id = harness.add_relation('db', 'database')
         harness.add_relation_unit(relation_id, 'database/0')
 
         secret_id = harness.add_model_secret('database', {'password': 'hunter2'})
         harness.grant_secret(secret_id, 'otherapp')
-        with self.assertRaises(model.SecretNotFoundError):
+        with self.assertRaises(ops.SecretNotFoundError):
             harness.model.get_secret(id=secret_id)
 
     def test_grant_secret_wrong_unit(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
         relation_id = harness.add_relation('db', 'database')
         harness.add_relation_unit(relation_id, 'database/0')
 
         secret_id = harness.add_model_secret('database', {'password': 'hunter2'})
         harness.grant_secret(secret_id, 'webapp/1')  # should be webapp/0
-        with self.assertRaises(model.SecretNotFoundError):
+        with self.assertRaises(ops.SecretNotFoundError):
             harness.model.get_secret(id=secret_id)
 
     def test_grant_secret_no_relation(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
 
         secret_id = harness.add_model_secret('database', {'password': 'hunter2'})
@@ -4618,7 +4600,7 @@ class TestSecrets(unittest.TestCase):
             harness.grant_secret(secret_id, 'webapp')
 
     def test_get_secret_grants(self):
-        harness = Harness(CharmBase, meta='name: database')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: database')
         self.addCleanup(harness.cleanup)
 
         relation_id = harness.add_relation('db', 'webapp')
@@ -4635,7 +4617,7 @@ class TestSecrets(unittest.TestCase):
         self.assertEqual(harness.get_secret_grants(secret.id, relation_id), {'webapp/0'})
 
     def test_trigger_secret_rotation(self):
-        harness = Harness(EventRecorder, meta='name: database')
+        harness = ops.testing.Harness(EventRecorder, meta='name: database')
         self.addCleanup(harness.cleanup)
 
         secret = harness.model.app.add_secret({'foo': 'x'}, label='lbl')
@@ -4646,11 +4628,11 @@ class TestSecrets(unittest.TestCase):
 
         self.assertEqual(len(harness.charm.events), 2)
         event = harness.charm.events[0]
-        self.assertIsInstance(event, ops.charm.SecretRotateEvent)
+        self.assertIsInstance(event, ops.SecretRotateEvent)
         self.assertEqual(event.secret.label, 'lbl')
         self.assertEqual(event.secret.get_content(), {'foo': 'x'})
         event = harness.charm.events[1]
-        self.assertIsInstance(event, ops.charm.SecretRotateEvent)
+        self.assertIsInstance(event, ops.SecretRotateEvent)
         self.assertEqual(event.secret.label, 'override')
         self.assertEqual(event.secret.get_content(), {'foo': 'x'})
 
@@ -4658,7 +4640,7 @@ class TestSecrets(unittest.TestCase):
             harness.trigger_secret_rotation('nosecret')
 
     def test_trigger_secret_removal(self):
-        harness = Harness(EventRecorder, meta='name: database')
+        harness = ops.testing.Harness(EventRecorder, meta='name: database')
         self.addCleanup(harness.cleanup)
 
         secret = harness.model.app.add_secret({'foo': 'x'}, label='lbl')
@@ -4669,12 +4651,12 @@ class TestSecrets(unittest.TestCase):
 
         self.assertEqual(len(harness.charm.events), 2)
         event = harness.charm.events[0]
-        self.assertIsInstance(event, ops.charm.SecretRemoveEvent)
+        self.assertIsInstance(event, ops.SecretRemoveEvent)
         self.assertEqual(event.secret.label, 'lbl')
         self.assertEqual(event.revision, 1)
         self.assertEqual(event.secret.get_content(), {'foo': 'x'})
         event = harness.charm.events[1]
-        self.assertIsInstance(event, ops.charm.SecretRemoveEvent)
+        self.assertIsInstance(event, ops.SecretRemoveEvent)
         self.assertEqual(event.secret.label, 'override')
         self.assertEqual(event.revision, 42)
         self.assertEqual(event.secret.get_content(), {'foo': 'x'})
@@ -4683,7 +4665,7 @@ class TestSecrets(unittest.TestCase):
             harness.trigger_secret_removal('nosecret', 1)
 
     def test_trigger_secret_expiration(self):
-        harness = Harness(EventRecorder, meta='name: database')
+        harness = ops.testing.Harness(EventRecorder, meta='name: database')
         self.addCleanup(harness.cleanup)
 
         secret = harness.model.app.add_secret({'foo': 'x'}, label='lbl')
@@ -4694,12 +4676,12 @@ class TestSecrets(unittest.TestCase):
 
         self.assertEqual(len(harness.charm.events), 2)
         event = harness.charm.events[0]
-        self.assertIsInstance(event, ops.charm.SecretRemoveEvent)
+        self.assertIsInstance(event, ops.SecretRemoveEvent)
         self.assertEqual(event.secret.label, 'lbl')
         self.assertEqual(event.revision, 1)
         self.assertEqual(event.secret.get_content(), {'foo': 'x'})
         event = harness.charm.events[1]
-        self.assertIsInstance(event, ops.charm.SecretRemoveEvent)
+        self.assertIsInstance(event, ops.SecretRemoveEvent)
         self.assertEqual(event.secret.label, 'override')
         self.assertEqual(event.revision, 42)
         self.assertEqual(event.secret.get_content(), {'foo': 'x'})
@@ -4708,7 +4690,7 @@ class TestSecrets(unittest.TestCase):
             harness.trigger_secret_removal('nosecret', 1)
 
 
-class EventRecorder(CharmBase):
+class EventRecorder(ops.CharmBase):
     def __init__(self, framework):
         super().__init__(framework)
         self.events = []
@@ -4719,7 +4701,7 @@ class EventRecorder(CharmBase):
 
 class TestPorts(unittest.TestCase):
     def test_ports(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
         unit = harness.model.unit
 
@@ -4731,13 +4713,13 @@ class TestPorts(unittest.TestCase):
         self.assertIsInstance(ports_set, set)
         ports = sorted(ports_set, key=lambda p: (p.protocol, p.port))
         self.assertEqual(len(ports), 3)
-        self.assertIsInstance(ports[0], model.OpenedPort)
+        self.assertIsInstance(ports[0], ops.OpenedPort)
         self.assertEqual(ports[0].protocol, 'icmp')
         self.assertIsNone(ports[0].port)
-        self.assertIsInstance(ports[1], model.OpenedPort)
+        self.assertIsInstance(ports[1], ops.OpenedPort)
         self.assertEqual(ports[1].protocol, 'tcp')
         self.assertEqual(ports[1].port, 8080)
-        self.assertIsInstance(ports[2], model.OpenedPort)
+        self.assertIsInstance(ports[2], ops.OpenedPort)
         self.assertEqual(ports[2].protocol, 'udp')
         self.assertEqual(ports[2].port, 4000)
 
@@ -4749,7 +4731,7 @@ class TestPorts(unittest.TestCase):
         self.assertIsInstance(ports_set, set)
         ports = sorted(ports_set, key=lambda p: (p.protocol, p.port))
         self.assertEqual(len(ports), 1)
-        self.assertIsInstance(ports[0], model.OpenedPort)
+        self.assertIsInstance(ports[0], ops.OpenedPort)
         self.assertEqual(ports[0].protocol, 'icmp')
         self.assertIsNone(ports[0].port)
 
@@ -4759,19 +4741,19 @@ class TestPorts(unittest.TestCase):
         self.assertEqual(ports_set, set())
 
     def test_errors(self):
-        harness = Harness(CharmBase, meta='name: webapp')
+        harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp')
         self.addCleanup(harness.cleanup)
         unit = harness.model.unit
 
-        with self.assertRaises(model.ModelError):
+        with self.assertRaises(ops.ModelError):
             unit.open_port('icmp', 8080)  # icmp cannot have port
-        with self.assertRaises(model.ModelError):
+        with self.assertRaises(ops.ModelError):
             unit.open_port('ftp', 8080)  # invalid protocol
-        with self.assertRaises(model.ModelError):
+        with self.assertRaises(ops.ModelError):
             unit.open_port('tcp')  # tcp must have port
-        with self.assertRaises(model.ModelError):
+        with self.assertRaises(ops.ModelError):
             unit.open_port('udp')  # udp must have port
-        with self.assertRaises(model.ModelError):
+        with self.assertRaises(ops.ModelError):
             unit.open_port('tcp', 0)  # port out of range
-        with self.assertRaises(model.ModelError):
+        with self.assertRaises(ops.ModelError):
             unit.open_port('tcp', 65536)  # port out of range