{"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":"python","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,"search_vec":"'abstract':32 'activ':53 'address':58 'applic':26,41 'bug':59 'build':17 'charm':13,75 'cloud':24,78 'cloud-nat':23,77 'compat':64 'contain':46 'develop':15 'enabl':14 'event':39 'everi':71 'fix':60 'framework':3 'frequent':56 'handl':37 'high':30 'high-level':29 'improv':62 'interact':34,44 'juju':1,12,36,68,74 'kubernet':80 'latest':67 'level':31 'librari':9,51 'lifecycl':38 'maintain':54 'manag':40 'nativ':25,79 'offici':7 'op':2,4 'oper':21,76 'pebbl':49 'perform':61 'provid':28 'python':8 'reactiv':20 'releas':57 'robust':18 'status':42 'typic':70 'version':69 'via':48 'week':73 'workload':47 'write':11","created_at":"2026-04-15T18:41:34.993009+00:00","updated_at":"2026-04-16T17:47:10.576570+00:00","problems":[{"fix":"Ensure 'ops' is listed in your charm's `requirements.txt` file and `charmcraft pack` is used to build the charm, which handles packaging dependencies. For development or testing, make sure 'ops' is installed in your active Python environment or add the charm's 'lib' and 'venv' directories to `PYTHONPATH`.","cause":"The Python interpreter cannot find the 'ops' package, typically because it's not installed in the charm's virtual environment or the Python path is not correctly configured during charm execution.","error":"ModuleNotFoundError: No module named 'ops'"},{"fix":"Review the `ops` charm code, specifically around `ops.model` interactions, to ensure all mandatory 'name' parameters are supplied when defining or operating on Juju entities. For example, when creating a secret, ensure the secret name is passed.","cause":"This error occurs when an `ops.model` operation attempts to create or modify a Juju entity (such as a secret, relation data, or application component) without a required 'name' attribute being provided.","error":"ops.model.ModelError: ERROR name is missing"},{"fix":"Debug the code to inspect the type and value of the object ('X') before the attribute access (e.g., `print(type(self.model.unit))`, `print(event.relation)`). Ensure the object is properly initialized and available in the current scope, and add checks for `None` or appropriate conditionals if the object's presence is not guaranteed. Also, double-check for typos in attribute names.","cause":"A common Python error, often encountered in `ops` charms, where an attribute or method ('Y') is accessed on an object ('X') that does not possess it. This frequently happens when an object (e.g., `self.model.unit`, an event object, or a configuration item) is `None` because it hasn't been initialized, is not available in the current event context, or due to a typo.","error":"AttributeError: 'X' object has no attribute 'Y'"},{"fix":"Examine the `juju debug-log` output for the specific unit to find the message provided by the charm that explains why `BlockedStatus` was set. Then, address the underlying cause, which could involve providing necessary configuration, establishing required relations, or resolving an internal application issue as indicated by the charm's status message.","cause":"While not a Python exception, a charm entering a `BlockedStatus` is a critical operational state. It indicates that the charm's logic, typically within an event handler like `update_status`, has explicitly set the unit or application status to `BlockedStatus` because of an unmet dependency, configuration error, missing relation, or other issue preventing the application from becoming operational.","error":"Charm enters BlockedStatus"}],"ecosystem":"pypi","meta_description":null,"install_score":null,"quickstart_score":null,"quickstart_tag":null,"pypi_latest":"3.7.1","cli_name":"","cli_version":null,"type":"library","homepage":"https://documentation.ubuntu.com/ops/latest/","github":"https://github.com/canonical/operator","docs":"https://documentation.ubuntu.com/ops/latest/","changelog":"https://github.com/canonical/operator/blob/main/CHANGES.md","pypi":"https://pypi.org/project/ops/","npm":null,"openapi_spec":null,"status_page":null,"smithery":null,"categories":["devops"],"base_url":null,"auth_type":null,"provenance":{"verified_status":"passing","verified_at":"2026-06-28","last_verified":"2026-06-28","next_check":"2026-07-28","install_tag":null}}