diff --git a/CHANGES.md b/CHANGES.md index bcf3a1c3e..69d6d69a5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -159,7 +159,7 @@ ## Features -* `StopEvent`, `RemoveEvent`, and all `LifeCycleEvent`s are no longer deferrable, and will raise a `RuntimeError` if `defer()` is called on the event object (#1122) +* `StopEvent`, `RemoveEvent`, and all `LifecycleEvent`s are no longer deferrable, and will raise a `RuntimeError` if `defer()` is called on the event object (#1122) * Add `ActionEvent.id`, exposing the JUJU_ACTION_UUID environment variable (#1124) * Add support for creating `pebble.Plan` objects by passing in a `pebble.PlanDict`, the ability to compare two `Plan` objects with `==`, and the ability to create an empty Plan with `Plan()` (#1134) diff --git a/docs/index.rst b/docs/index.rst index 84f58b3f8..c27e2bbe5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,10 +2,31 @@ ops library API reference ========================= +The `ops` library is a Python framework for writing and testing Juju charms. + + See more: `Charm SDK documentation `_ + +The library provides: + +- :ref:`ops_main_entry_point`, used to initialise and run your charm; +- :ref:`ops_module`, the API to respond to Juju events and manage the application; +- :ref:`ops_pebble_module`, the Pebble client, a low-level API for Kubernetes containers; +- :ref:`ops_testing_module`, the framework for unit testing charms in a simulated environment; + +You can structure your charm however you like, but with the `ops` library, you +get a framework that promotes consistency and readability by following best +practices. It also helps you organise your code better by separating different +aspects of the charm, such as managing the application’s state, handling +integrations with other services, and making the charm easier to test. + + .. toctree:: :maxdepth: 2 :caption: Contents: + +.. _ops_module: + ops module ========== @@ -13,8 +34,13 @@ ops module :exclude-members: main +.. _ops_main_entry_point: + ops.main entry point ==================== + +The main entry point to initialise and run your charm. + .. autofunction:: ops.main @@ -25,12 +51,16 @@ legacy main module :noindex: +.. _ops_pebble_module: + ops.pebble module ================= .. automodule:: ops.pebble +.. _ops_testing_module: + ops.testing module ================== diff --git a/ops/__init__.py b/ops/__init__.py index 3108647db..0e34ef169 100644 --- a/ops/__init__.py +++ b/ops/__init__.py @@ -12,31 +12,42 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""The ops library: a Python framework for writing Juju charms. +"""The API to respond to Juju events and manage the application. -The ops library is a Python framework (`available on PyPI`_) for developing -and testing Juju charms in a consistent way, using standard Python constructs -to allow for clean, maintainable, and reusable code. +This module provides code freatures to your charm, including: -A charm is an operator -- business logic encapsulated in a reusable software -package that automates every aspect of an application's life. +- :class:`~ops.CharmBase`, the base class for charms and :class:`~ops.Object`, + the base class for charm libraries. +- :class:`~ops.framework.EventBase` class and individual event types, like + the :class:`~ops.ActionEvent` class. +- :class:`~ops.Framework` class, accessible as ``self.framework`` in a charm, + the main interface for the charm to `ops` library infrastructure, including: -Charms written with ops support Kubernetes using Juju's "sidecar charm" -pattern, as well as charms that deploy to Linux-based machines and containers. + - :attr:`~ops.Framework.on` shorthand property used to + :meth:`~ops.Framework.observe` and react to Juju events. + - :attr:`~ops.Framework.model` attribute to get hold of the Model instance. -Charms should do one thing and do it well. Each charm drives a single -application and can be integrated with other charms to deliver a complex -system. A charm handles creating the application in addition to scaling, -configuration, optimisation, networking, service mesh, observability, and other -day-2 operations specific to the application. +- :class:`~ops.model.Model` class that represents the Juju model, accessible as + ``self.model`` in a charm, including: -The ops library is part of the Charm SDK (the other part being Charmcraft). -Full developer documentation for the Charm SDK is available at -https://juju.is/docs/sdk. + - :attr:`~ops.Model.app` attribute, representing the application associated + with the charm. + - :attr:`~ops.Model.unit` attribute, representing the unit of the application + the charm is running on. + - :attr:`~ops.Model.relations` attribute, which provides access to relations + (integrations) defined in the charm, allowing interaction with other applications. -To learn more about Juju, visit https://juju.is/docs/olm. +- :class:`~ops.Container` class to control Kubernetes workloads, including: -.. _available on PyPI: https://pypi.org/project/ops/ + - :meth:`~ops.Container.add_layer` and :meth:`~ops.Container.replan` methods + to update Pebble configuration. + - :meth:`~ops.Container.pull` and :meth:`~ops.Container.push` methods to copy + data to and from a container, respectively. + - :meth:`~ops.Container.exec` method to run arbitrary commands inside the + container. + +- :class:`~ops.StatusBase` class and individual status types, like the + :class:`~ops.ActiveStatus` class. """ # The "from .X import Y" imports below don't explicitly tell Pyright (or MyPy) diff --git a/ops/framework.py b/ops/framework.py index 11c09e50c..0fd089510 100644 --- a/ops/framework.py +++ b/ops/framework.py @@ -518,7 +518,11 @@ def __getattr__(self, name: str) -> BoundEvent: class LifecycleEvent(EventBase): - """Events tied to the lifecycle of the Framework object.""" + """Events tied to the lifecycle of the Framework object. + + Note: Here, "lifecycle" was poorly named and has nothing to do with the + Juju [application] lifecycle. + """ def defer(self) -> NoReturn: """Lifecycle events are not deferrable like other events. diff --git a/ops/model.py b/ops/model.py index 335dd9aa1..707ef42ba 100644 --- a/ops/model.py +++ b/ops/model.py @@ -2317,7 +2317,11 @@ def start(self, *service_names: str): self._pebble.start_services(service_names) def restart(self, *service_names: str): - """Restart the given service(s) by name.""" + """Restart the given service(s) by name. + + Listed running services will be stopped and restarted, and listed stopped + services will be started. + """ if not service_names: raise TypeError('restart expected at least 1 argument, got 0') diff --git a/ops/pebble.py b/ops/pebble.py index 785941566..5c642fd01 100644 --- a/ops/pebble.py +++ b/ops/pebble.py @@ -12,9 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Client for the Pebble API (HTTP over Unix socket). +"""Client for the Pebble API. -For a command-line interface for local testing, see test/pebble_cli.py. +This module provides a way to interact with Pebble, including: + +- :class:`~ops.pebble.Client`; communicates directly with the Pebble API. +- :class:`~ops.pebble.Layer` class to define Pebble configuration layers, + including: + + - :class:`~ops.pebble.Check` class to represent Pebble checks. + - :class:`~ops.pebble.LogTarget` class to represent Pebble log targets. + - :class:`~ops.pebble.Service` class to represent Pebble service descriptions. + +For a command-line interface for local testing, see ``test/pebble_cli.py``. + + See more: `Pebble documentation `_ """ from __future__ import annotations @@ -2187,6 +2199,9 @@ def restart_services( ) -> ChangeID: """Restart services by name and wait (poll) for them to be started. + Listed running services will be stopped and restarted, and listed stopped + services will be started. + Args: services: Non-empty list of services to restart. timeout: Seconds before restart change is considered timed out (float). If diff --git a/ops/testing.py b/ops/testing.py index a633be3d3..e93ba7bcb 100644 --- a/ops/testing.py +++ b/ops/testing.py @@ -11,8 +11,26 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Framework for unit testing charms in a simulated Juju environment. -"""Infrastructure to build unit tests for charms using the ops library.""" +The module includes: + +- :class:`ops.testing.Harness`, a class to set up the simulated environment, + that provides: + + - :meth:`~ops.testing.Harness.add_relation` method, to declare a relation + (integration) with another app. + - :meth:`~ops.testing.Harness.begin` and :meth:`~ops.testing.Harness.cleanup` + methods to start and end the testing lifecycle. + - :meth:`~ops.testing.Harness.evaluate_status` method, which aggregates the + status of the charm after test interactions. + - :attr:`~ops.testing.Harness.model` attribute, which exposes e.g. the + :attr:`~ops.Model.unit` attribute for detailed assertions on the unit's state. + +.. note:: + Unit testing is only one aspect of a comprehensive testing strategy. For more + on testing charms, see `Charm SDK | Testing `_. +""" from ._private.harness import ( ActionFailed,