Skip to main content

lifecore_ros2 is a minimal lifecycle composition library for ROS 2 Jazzy — no hidden state machine.

Project description

lifecore_ros2

lifecore_ros2 logo

CI Docs Release License: Apache 2.0 Latest Release PyPI Python versions Documentation

lifecore_ros2 is a minimal lifecycle composition library for ROS 2 Jazzy — no hidden state machine.

ROS 2 lifecycle works well for nodes. lifecore_ros2 makes it practical for reusable components.

30-second view

Raw rclpy lifecycle:
LifecycleNode
 ├── publishers
 ├── subscriptions
 ├── timers
 └── lifecycle logic mixed into one class

lifecore_ros2:
LifecycleComponentNode
 ├── LifecyclePublisherComponent
 ├── LifecycleSubscriberComponent
 └── LifecycleTimerComponent

The node still owns the native ROS 2 lifecycle. The library adds a small composition layer so reusable components can follow the same lifecycle contract.

What this is not

  • not a second lifecycle state machine
  • not a plugin system or ROS 2 component container replacement
  • not a behavior tree system, orchestration middleware, or launch replacement
  • not a replacement for native ROS 2 lifecycle semantics

Installation warning: ROS 2 Jazzy required

lifecore_ros2 requires a working ROS 2 Jazzy Python environment. Install and source ROS 2 before importing this package:

source /opt/ros/jazzy/setup.bash
uv add lifecore-ros2

rclpy comes from the system ROS installation. It is intentionally not declared as a normal PyPI dependency.

Composed pipeline lifecycle walk-through: configure creates topics, activate streams data, deactivate stops data flow while topics remain (deactivate ≠ cleanup), cleanup releases resources.

The examples/composed_pipeline.py walk-through highlights the key distinction the library makes explicit: deactivate ≠ cleanup/pipeline/* topics persist across deactivate and only disappear on cleanup.

Why lifecore_ros2 exists

Audience. This library is for teams building modular ROS 2 nodes that need reusable lifecycle-aware components, especially in larger robotics stacks, embedded systems, or runtime-orchestrated applications.

Problem framing. ROS 2 provides a powerful managed-node lifecycle (configure → active → deactivate → cleanup). In practice, using it for anything beyond a trivial node leads to recurring problems:

  • lifecycle logic gets scattered across monolithic node classes with no clear ownership
  • ROS resource setup and teardown (publishers, subscriptions, timers) are easy to make inconsistent — resources allocated in the wrong place or released too late
  • runtime gating ("only process messages when active") is hand-rolled differently each time, with no shared, tested pattern
  • reusable lifecycle-aware building blocks are awkward in raw rclpy because the lifecycle contract is on the node, not on reusable sub-units

lifecore_ros2 solves these four problems with a small, explicit composition layer. It does not replace or extend the ROS 2 lifecycle state machine — it makes the lifecycle contract expressible at the component level.

Architecture at a glance

flowchart LR
    Lifecycle[ROS 2 Lifecycle]
    Node[LifecycleComponentNode]
    Components[LifecycleComponent instances]
    Lifecycle <--> Node
    Node <--> Components
    Lifecycle -. drives .-> Components

What the library provides

A small set of lifecycle-aware building blocks:

Symbol Role
LifecycleComponentNode Lifecycle node that owns and drives registered LifecycleComponent instances
LifecycleComponent Base class for a lifecycle-aware managed entity (abstract by convention — override _on_* hooks)
TopicComponent Base class for topic-oriented components (pub/sub)
LifecyclePublisherComponent Lifecycle-gated ROS publisher
LifecycleSubscriberComponent Lifecycle-gated ROS subscriber
LifecycleTimerComponent Lifecycle-gated ROS timer
LifecycleWatchdogComponent Lifecycle-gated health watchdog — polls targets and logs DEGRADED / ERROR / STALE
HealthLevel Severity enum: UNKNOWN | OK | DEGRADED | ERROR
HealthStatus Frozen dataclass capturing component health (level, reason, last_error)
ServiceComponent Base class for service-oriented components (server/client)
LifecycleServiceServerComponent Lifecycle-gated ROS service server
LifecycleServiceClientComponent Lifecycle-gated ROS service client
when_active Decorator that guards any method to the active state
LifecoreError and subclasses Typed exceptions for boundary violations
lifecore_ros2.testing Reusable fakes, fixtures, assertions, and helpers for lifecycle-focused tests

Design rules

The library stays lifecycle-native, keeps ownership in LifecycleComponentNode, and treats component hooks as explicit extension points rather than hidden orchestration.

When sibling components need deterministic ordering, prefer declaring dependencies and priority at add_component(...) so composition intent stays visible in the node assembly code. examples/composed_ordered_pipeline.py shows this pattern without constructor pass-through on library components.

See docs/architecture.rst for lifecycle design rules, docs/patterns.rst for usage patterns, and ROADMAP.md for non-goals and deferred scope. See Examples Repository Plan for the companion repository planning. See CHANGELOG.md for shipped changes or the GitHub Releases page for tagged releases.

Prerequisites

  • Python 3.12 or newer
  • ROS 2 Jazzy installed on the system
  • uv available in the workspace

rclpy is expected to come from the system ROS installation. It is intentionally not declared as a normal PyPI dependency.

Quickstart

Clone the repository, source ROS 2 Jazzy, and sync the local development environment:

git clone https://github.com/apajon/lifecore_ros2.git
cd lifecore_ros2
source /opt/ros/jazzy/setup.bash
uv sync --extra dev

Run the smallest composed lifecycle example already in the repository:

uv run python examples/minimal_node.py

From another terminal in the same ROS 2 environment, drive the node through configure and activate:

source /opt/ros/jazzy/setup.bash
ros2 lifecycle set /minimal_lifecore_node configure
ros2 lifecycle set /minimal_lifecore_node activate

For the full walkthrough, see docs/quickstart.rst. For validation and documentation commands, see docs/getting_started.rst. For the activation-gated subscriber example, continue with examples/minimal_subscriber.py or docs/examples.rst.

Lifecycle reading path

The documentation now follows the same lifecycle vocabulary as the library:

  • Configure: environment, prerequisites, ROS resource creation model
  • Activate: runtime enablement and activation gating
  • Run: examples, API usage, composed execution flow
  • Transition: ownership, propagation, and error handling rules
  • Shutdown: cleanup, release, and lifecycle end-state expectations

Recommended order:

  1. docs/quickstart.rst
  2. docs/getting_started.rst
  3. docs/concepts/mental_model.rst
  4. docs/architecture.rst
  5. docs/patterns.rst
  6. docs/testing.rst
  7. docs/examples.rst

Shortest-path example — subscriber

examples/minimal_subscriber.py is the next runnable example if you want to see activation-gated message delivery after the minimal node quickstart.

See examples/minimal_subscriber.py for the complete runnable file, docs/api_friction_audit.rst for the regression baseline, and docs/examples.rst for the walkthrough.

Component + node definition: 24 lines (regression baseline — see docs/api_friction_audit.rst).

Publisher and subscriber examples

Run the publisher and observe activation gating:

uv run python examples/minimal_publisher.py
# in another terminal:
ros2 lifecycle set /publisher_demo_node configure
ros2 lifecycle set /publisher_demo_node activate
ros2 topic echo /chatter

Messages appear only after activate. Deactivation stops them.

For the subscriber path, use the quickstart above or the full example walkthrough in docs/examples.rst.

Public API overview

All exported symbols and their stability levels are documented in ROADMAP.md.

The extension model and API buckets are defined in docs/architecture.rst and docs/api.rst.

Current limitations

  • package metadata uses Development Status :: 3 - Alpha to reflect API stability, not lack of usability
  • the public API is in the 0.x series — experimental stability level; minor bumps may include breaking changes
  • companion examples live in lifecore_ros2_examples; start with the sensor watchdog lifecycle comparison for the first applied example, then see ROADMAP.md there for future scenarios

License

This project is licensed under the Apache-2.0 License — see LICENSE.

Documentation

Documentation: https://apajon.github.io/lifecore_ros2/

Full documentation lives under docs/ and is built with Sphinx:

uv sync --extra dev --group docs
uv run --group docs python -m sphinx -b html docs docs/_build/html

Key pages:

  • docs/getting_started.rst — setup and validation commands
  • docs/architecture.rst — lifecycle design rules, error policy, member conventions
  • docs/patterns.rst — recommended patterns and anti-patterns
  • docs/testing.rst — reusable lifecycle test fakes, fixtures, assertions, and helpers
  • docs/migration_from_rclpy.rst — before/after comparison with raw rclpy
  • docs/api.rst — generated API reference
  • docs/examples.rst — example walkthroughs

Versioning

Versioning uses Conventional Commits and python-semantic-release. Preview the next version:

uv run --group release semantic-release version --print

Release (version commit + tag, skip hosted release if no token):

uv run --group release semantic-release version --no-vcs-release
git push origin main --follow-tags

See ROADMAP.md for promotion-to-1.0.0 criteria.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lifecore_ros2-0.9.0.tar.gz (6.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lifecore_ros2-0.9.0-py3-none-any.whl (52.8 kB view details)

Uploaded Python 3

File details

Details for the file lifecore_ros2-0.9.0.tar.gz.

File metadata

  • Download URL: lifecore_ros2-0.9.0.tar.gz
  • Upload date:
  • Size: 6.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lifecore_ros2-0.9.0.tar.gz
Algorithm Hash digest
SHA256 74139cb2e42d4fe72b513e65242c8e351dda030780a527010d7b0ee728c8c316
MD5 fb5a95e0be6a3703362977822e438106
BLAKE2b-256 868b5b485d3fb8a2e57c1c1ef1ddb0db7fedb981a786f6db3d40d8ecf6821d61

See more details on using hashes here.

Provenance

The following attestation bundles were made for lifecore_ros2-0.9.0.tar.gz:

Publisher: publish-pypi.yml on apajon/lifecore_ros2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lifecore_ros2-0.9.0-py3-none-any.whl.

File metadata

  • Download URL: lifecore_ros2-0.9.0-py3-none-any.whl
  • Upload date:
  • Size: 52.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lifecore_ros2-0.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6b943f28b676ade3aa2396fcbe76d5171550f54a0e362192e54f04901d49907d
MD5 e2684e6aa23089ca5deae8c3a48eaacb
BLAKE2b-256 179c53f36da41d4d0efde6ff8c7e37d1aa9e31d6323c99ab1f341d9f0d40df85

See more details on using hashes here.

Provenance

The following attestation bundles were made for lifecore_ros2-0.9.0-py3-none-any.whl:

Publisher: publish-pypi.yml on apajon/lifecore_ros2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page