Registry / devops / ops
library3.7.1pypypi✓ verified 84d ago

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.

pip install ops
INSTALL
IMPORT
SIG · OPS
O
ops
devopspythonv3.7.1
Install
2.1s avg
Import
549ms
Disk
23MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.93.13
musl
3.93.13
Install & Compatibility
Where this runs
tested against v3.7.1 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
py 3.103.940 runs
installs and imports cleanly · install 0.0s · import 0.574s · 23.9MB
glibc
py 3.103.940 runs
installs and imports cleanly · install 2.1s · import 0.523s · 25MB
23MB installed
● package 23MB
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

CharmBase
from ops.charm import CharmBase
Framework
from ops.framework import Framework
StoredState
from ops.framework import StoredState
main
from ops.main import main
Container
from ops.pebble import Container
PebbleClient
from ops.pebble import PebbleClient
Context
from ops.testing import Context
ActiveStatus
from ops.model import ActiveStatus
BlockedStatus
from ops.model import BlockedStatus

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.

import ops from ops.charm import CharmBase from ops.framework import StoredState class MyCharm(CharmBase): _stored = StoredState() def __init__(self, framework: ops.Framework): super().__init__(framework) self.framework.observe(self.on.install, self._on_install) self.framework.observe(self.on.config_changed, self._on_config_changed) self._stored.set_default(initialized=False) def _on_install(self, event: ops.InstallEvent): # Example: Set initial status and workload version self.unit.status = ops.BlockedStatus("Waiting for configuration") self.unit.set_workload_version("v1.0.0") self._stored.initialized = True self.logger.info("Charm installed.") def _on_config_changed(self, event: ops.ConfigChangedEvent): # Example: Update status after configuration change if self._stored.initialized: self.unit.status = ops.ActiveStatus("Ready") self.logger.info("Configuration changed and charm is active.") if __name__ == "__main__": # The main entry point for a Juju charm ops.main(MyCharm)
Debug
Known issues
breakingThe 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.
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')`.
affects: 3.6.0 and later
gotchaWhen `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.
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.
affects: 3.6.0 and later
gotchaBy 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.
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.
affects: 3.5.0 and later
gotchaThe `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.
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.
affects: 3.4.0 and later
Errors
Common errors & fixes
ModuleNotFoundError: No module named 'ops'
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.
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`.
ops.model.ModelError: ERROR name is missing
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.
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.
AttributeError: 'X' object has no attribute 'Y'
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.
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.
Charm enters BlockedStatus
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.
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.
Upgrade
Version history
3.7.1latest on PyPI · released May 28, 2026
Audit
Dependencies
pydanticoptionalRequired for the 'testing' optional extra, used in schema validation and data parsing.
pyyamloptionalRequired for the 'testing' optional extra, typically for parsing charm metadata or config.
dbt-osmosisoptionalRequired for the 'testing' optional extra, used for certain test utilities.
typing_extensionsoptionalRequired for the 'testing' optional extra, for advanced type hinting features.
multiprocessoptionalRequired for the 'testing' optional extra, for certain test execution scenarios.
Agent activity
2 hits · last 30 days
node
2
Resources