{"id":6755,"library":"ops","title":"Juju Ops Framework","description":"Ops is the official Python library for writing Juju charms, enabling developers to build robust and reactive operators for cloud-native applications. It provides high-level abstractions for interacting with Juju, handling lifecycle events, managing application status, and interacting with container workloads via Pebble. The library is actively maintained, with frequent releases addressing bug fixes, performance improvements, and compatibility with the latest Juju versions, typically every few weeks.","status":"active","version":"3.7.0","language":"en","source_language":"en","source_url":"https://github.com/canonical/operator","tags":["juju","charm","operator","cloud-native","kubernetes"],"install":[{"cmd":"pip install ops","lang":"bash","label":"Core library"},{"cmd":"pip install ops[testing]","lang":"bash","label":"With testing dependencies"}],"dependencies":[{"reason":"Required for the 'testing' optional extra, used in schema validation and data parsing.","package":"pydantic","optional":true},{"reason":"Required for the 'testing' optional extra, typically for parsing charm metadata or config.","package":"pyyaml","optional":true},{"reason":"Required for the 'testing' optional extra, used for certain test utilities.","package":"dbt-osmosis","optional":true},{"reason":"Required for the 'testing' optional extra, for advanced type hinting features.","package":"typing_extensions","optional":true},{"reason":"Required for the 'testing' optional extra, for certain test execution scenarios.","package":"multiprocess","optional":true}],"imports":[{"symbol":"CharmBase","correct":"from ops.charm import CharmBase"},{"symbol":"Framework","correct":"from ops.framework import Framework"},{"symbol":"StoredState","correct":"from ops.framework import StoredState"},{"symbol":"main","correct":"from ops.main import main"},{"symbol":"Container","correct":"from ops.pebble import Container"},{"symbol":"PebbleClient","correct":"from ops.pebble import PebbleClient"},{"symbol":"Context","correct":"from ops.testing import Context"},{"symbol":"ActiveStatus","correct":"from ops.model import ActiveStatus"},{"symbol":"BlockedStatus","correct":"from ops.model import BlockedStatus"}],"quickstart":{"code":"import ops\nfrom ops.charm import CharmBase\nfrom ops.framework import StoredState\n\n\nclass MyCharm(CharmBase):\n    _stored = StoredState()\n\n    def __init__(self, framework: ops.Framework):\n        super().__init__(framework)\n        self.framework.observe(self.on.install, self._on_install)\n        self.framework.observe(self.on.config_changed, self._on_config_changed)\n        self._stored.set_default(initialized=False)\n\n    def _on_install(self, event: ops.InstallEvent):\n        # Example: Set initial status and workload version\n        self.unit.status = ops.BlockedStatus(\"Waiting for configuration\")\n        self.unit.set_workload_version(\"v1.0.0\")\n        self._stored.initialized = True\n        self.logger.info(\"Charm installed.\")\n\n    def _on_config_changed(self, event: ops.ConfigChangedEvent):\n        # Example: Update status after configuration change\n        if self._stored.initialized:\n            self.unit.status = ops.ActiveStatus(\"Ready\")\n            self.logger.info(\"Configuration changed and charm is active.\")\n\n\nif __name__ == \"__main__\":\n    # The main entry point for a Juju charm\n    ops.main(MyCharm)","lang":"python","description":"This minimal example demonstrates a basic Juju charm that handles `install` and `config_changed` events. It sets the unit's status and workload version, and uses `StoredState` to persist simple charm data across hooks. The `ops.main()` function is essential for running the charm in the Juju environment."},"warnings":[{"fix":"To maintain compatibility with older Juju environments or to avoid unexpected changes, explicitly set the desired Juju version in your `ops.testing.Context` constructor: `Context(charm_type, juju_version='2.9.x')`.","message":"The default Juju version used in `ops.testing.Context` for mock environments was updated from Juju 2.x to Juju 3.6.14. Charms or tests that implicitly relied on specific Juju 2.x behaviors during testing without explicitly setting the Juju version might encounter unexpected failures.","severity":"breaking","affected_versions":"3.6.0 and later"},{"fix":"If your tests or error handling relied on extracting the full command from `PebbleExecError` messages, you'll need to adjust your parsing logic. Ensure sensitive command arguments are not the first item if they are crucial for debugging failure context.","message":"When `PebbleClient.exec()` fails (e.g., due to timeout), the exception message now only includes the *first item* of the executed command, not the entire command string. This change protects against sensitive data leaking into exception logs.","severity":"gotcha","affected_versions":"3.6.0 and later"},{"fix":"To simplify debugging and allow direct assertion of original exception types, set the environment variable `SCENARIO_BARE_CHARM_ERRORS=true` when running your tests. This will disable the `UncaughtCharmError` wrapping.","message":"By default, exceptions raised directly from charm code during `ops.testing` state-transition tests are wrapped in an `UncaughtCharmError`. While this helps distinguish charm errors, it can obscure the original exception type, making debugging and asserting specific error types more cumbersome.","severity":"gotcha","affected_versions":"3.5.0 and later"},{"fix":"For typical charm development, prefer using the higher-level abstractions provided by the main `ops` library (e.g., `CharmBase`, `PebbleClient`, `Relation` objects). Only use `ops.hookcmds` if you specifically require direct Juju command access for advanced framework development or specific niche scenarios.","message":"The `ops.hookcmds` module provides a low-level, direct API for Juju hook commands. This API is powerful but intended primarily for building experimental charm APIs or frameworks rather than for direct use within production charms. Direct usage can lead to less portable or harder-to-maintain charm code that bypasses `ops` abstractions.","severity":"gotcha","affected_versions":"3.4.0 and later"}],"env_vars":null,"last_verified":"2026-04-15T00:00:00.000Z","next_check":"2026-07-14T00:00:00.000Z","problems":[]}