Skip to content

Add "NoDL fragments" to the schema#80

Closed
emersonknapp wants to merge 2 commits into
mainfrom
emerson/fragments
Closed

Add "NoDL fragments" to the schema#80
emersonknapp wants to merge 2 commits into
mainfrom
emerson/fragments

Conversation

@emersonknapp

@emersonknapp emersonknapp commented May 28, 2026

Copy link
Copy Markdown
Member

Closes #72
Replaces #76

Adds a new concept of a "NoDL Fragment" that is a partial specification for a node. There are effectively two use cases for framents:

  1. The base node type (Node or LifecycleNode) which brings along with it a certain interface set
  2. "add-ons"/"extensions"/"mixins" (really haven't landed on a single term for this yet) which a node instantiates, that adds to the node's interface. The prime example is the TfBroadcaster (which creates publishers) and TfBuffer (which creates subscriptions). This pattern is also used in Navigation2 to add parameters to nodes, etc.

The fragment concept allows resolving a full specification for a node via reference to other sub-documents, while keeping it clear for code generation and documentation what specifically this node code actually provides.

enum: [node, lifecycle_node]
description: Built-in ROS 2 base node type this node inherits from.

fragments:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fragments:
includes:

type: string
description: Human-readable description of what this node does.

base:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

required. make node's definition into a fragment? Make the node schema be like:

  • base
  • my fragment
  • included fragments


description:
type: string
description: Human-readable description of what this node does.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make description required?

@emersonknapp emersonknapp marked this pull request as draft May 28, 2026 17:17
Schema (nodl_schema/schemas/nodl.schema.yaml):
  Top-level 'base' (enum of node / lifecycle_node) declares the
  built-in node interface to inherit.
  Top-level 'fragments' (array of fragment_ref) declares additional
  fragment interfaces to compose in, by relative path or 'nodl://pkg/name'.
  fragment_ref definition has required 'ref' and optional 'name' (label
  used for merge ordering and documentation).
  Generated models pick these up automatically; FragmentRef and the
  Base enum are added to NodlDocument.

nodl_schema.validator:
  validate_fragment(data) runs the regular schema validation, then
  rejects the presence of top-level 'base' or 'fragments'.
  Fragments are flat ingredients; nested composition is intentionally
  disallowed in v2 and can be lifted later without a schema change.
  load_fragment(source) wraps load_nodl with this stricter check.
  python -m nodl_schema gains a --fragment flag for build-time hooks.

nodl_schema.resolve:
  resolve(doc, source_path=None) returns a LayeredDocument carrying
  the resolved base, the resolved fragments (by label), and the main
  document.
  LayeredDocument.merged() flattens into a single NodlDocument with
  later layers winning on duplicate names; order is base -> fragments
  (insertion order) -> main.
  Every load path goes through load_fragment, so a fragment that
  declares its own base or fragments is rejected at resolve time with
  a clear error.

Bundled fragments:
  node.nodl.yaml: declares use_sim_time.
  lifecycle_node.nodl.yaml: adds the standard transition services and
  the transition_event publisher on top of the node fragment.

ament_nodl_register_fragment:
  Sibling of register_node, following the same install RENAME pattern,
  rosidl-style docstring, and "NoDL file" wording.
  Build-time validation via add_custom_command/target that runs
  'python -m nodl_schema --fragment <file>' against the source.
  The stamp target is ALL, so install won't proceed without it.

Tests:
  test_ament_nodl gains register_fragment tests (5 cases) covering
  default and explicit PACKAGE, byte-for-byte resource vs source,
  and source-file install layout.
  nodl_schema/test/test_resolve.py covers no-base, both bases,
  relative fragment resolution (success / missing / no source path),
  layer precedence (main > fragments > base), and explicit rejection
  of a fragment that declares its own base or fragments.
  nodl_schema/test/test_schema.py: the previously inverted
  base/fragments rejection tests become positive accept tests, plus
  'unknown base rejected' and 'fragment_ref missing ref rejected'
  negative cases.
  nodl_schema/test/test_validator_cli.py: adds --fragment cases for
  valid fragments, rejection of base/fragments at the top level, and
  combined invalid-schema + fragment cases.

Smoke-tested end-to-end by appending 'base: node' to a fragment fixture:
the build fails at the validate_fragment target with the expected
error and restoring brings it back green.

Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
@read-the-docs-community

Copy link
Copy Markdown

Documentation build overview

📚 nodl | 🛠️ Build #33084553 | 📁 Comparing a5f8b7d against latest (9bd4a26)

  🔍 Preview build  

1 file changed
± schema.html

Signed-off-by: Emerson Knapp <emerson@polymathrobotics.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Compose": Allow for code components that extend a node's interface.

1 participant