Skip to content

Fix plugin functions that return or take a channel - #7715

Merged
bentsherman merged 4 commits into
masterfrom
fix-plugin-function-returning-channel
Sep 30, 2026
Merged

bentsherman merged 4 commits into
masterfrom
fix-plugin-function-returning-channel

Conversation

@bentsherman

@bentsherman bentsherman commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Related to #7694

A plugin method annotated with @Function can't be called as a function if it returns or takes a channel. It fails with Missing process or function <name>(), in both typed and untyped scripts.

The legacy fallbacks in PluginExtensionProvider register any public method that returns a write channel as a factory, and any method whose first parameter is a read channel as an operator. loadPluginExtensionMethods checks factories and operators before functions, so the @Function annotation is ignored.

Changes:

  • Skip @Function methods in the factory and operator fallbacks, so they are registered as functions.
  • Fail with a clear error when a method is annotated with both @Function and @Factory or @Operator. Previously the factory or operator annotation won and nothing was reported.
  • Docs (developing-plugins.mdx): replace the tip at the end of the Operators section with a section on writing channel factories and operators as functions, noting that plugin factories and operators aren't supported with static typing.

With this change, a plugin can provide channel sources and operators as plain functions, e.g. fromQuery(...) and sqlInsert(rows, ...). These also work in typed scripts: WorkflowBinding wraps the result in a ChannelImpl and unwraps channel arguments to v1.

Note on operator functions: channel arguments arrive as a DataflowBroadcast, which is not a DataflowReadChannel. The parameter should therefore be a DataflowWriteChannel, converted with CH.getReadChannel(), as the docs describe.

Tests:

  • HelloExtension fixture: reverseFn (a factory function) and goodbyeFn (an operator function).
  • PluginExtensionMethodsTest: both functions in untyped and typed scripts, including the ChannelImpl wrapping and typed operators applied to the result.
  • PluginExtensionProviderTest: detection of channel functions, and rejection of conflicting annotations.
  • The new tests fail without the fix. All plugin tests in nf-commons and nextflow pass (203).

Compatibility: a plugin that annotates a channel method with @Function but calls it as channel.x(...) or ch.x(...), or that puts @Function together with @Factory or @Operator, would no longer work. The first pattern only worked through the legacy fallback.

A plugin method annotated with @function that returns a DataflowWriteChannel
was picked up by the legacy factory detection and registered as a channel
factory, so it could not be called as a plain function. Skip @function
methods in the factory fallback so they are registered as functions.

Signed-off-by: Ben Sherman <bentshermann@gmail.com>
@netlify

netlify Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for nextflow-docs ready!

Name Link
🔨 Latest commit 4b4378c
🔍 Latest deploy log https://app.netlify.com/projects/nextflow-docs/deploys/6abd3e4dbd6008000809fc3e
😎 Deploy Preview https://deploy-preview-7715--nextflow-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@jorgee jorgee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. The guard fixes the issue, and the tests cover it. They fail without the fix.

Some non-blocking comments. They are outside the scope of this PR but related:

1. Operator fallback has the same problem

getDeclaredOperatorExtensionMethods0 registers any public method whose first parameter is a DataflowReadChannel as an operator, even if it has @Function. So an operator rewritten as a plain function still breaks:

@Function
DataflowWriteChannel dedupe(DataflowReadChannel source) { ... }

dedupe(ch) returns an OpCall instead of the channel, and the log says it should be marked @Operator. The same guard would fix it:

if( handle.isAnnotationPresent(Function) ) continue

2. @Function together with @Factory or @Operator

If a method has both, the factory or operator wins, and the function can't be called. There is no warning. It could fail fast with a clear error.

3. Docs

It would help to document the pattern in developing-plugins.mdx: a channel source as a @Function that returns a channel, called directly, which also works in typed scripts.

Skip @function methods in the legacy operator detection, so that a function
which takes a channel is registered as a function instead of an operator.
Fail when a method is annotated with @function and @factory or @operator.

Document how to define channel factories and operators as functions.

Signed-off-by: Ben Sherman <bentshermann@gmail.com>
@bentsherman bentsherman changed the title Fix plugin functions that return a channel Fix plugin functions that return or take a channel Sep 30, 2026
@bentsherman
bentsherman requested a review from a team as a code owner September 30, 2026 16:10
Align the test plugin functions with the documented signatures, test the
documented pipeline in typed and untyped scripts, and test that plugin
factories and operators are unsupported in typed scripts.

Simplify the docs for factories and operators as functions.

Signed-off-by: Ben Sherman <bentshermann@gmail.com>
@bentsherman

Copy link
Copy Markdown
Member Author

Thanks Jorge for the review.

1. Operator fallback has the same problem

I tested this and confirmed that operators can also be written as plain functions with the same override fix. They just need to take a DataflowWriteChannel instead of a DataflowReadChannel.

2. @Function together with @Factory or @Operator

Added an error for this.

3. Docs

Updated the docs to show how to rewrite factories / operators as plain functions.

Follow-up: consider backporting to 26.04 to enable this pattern, since this is essentially a bug fix.

@bentsherman
bentsherman merged commit c569b04 into master Sep 30, 2026
26 checks passed
@bentsherman
bentsherman deleted the fix-plugin-function-returning-channel branch September 30, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plugin channel factories are unreachable via channel.* when nextflow.enable.types = true

2 participants