Skip to content

Provide an interface for tools to implement SIGINFO handling. - #963

Open
grynspan wants to merge 12 commits into
mainfrom
jgrynspan/siginfo-handling
Open

Provide an interface for tools to implement SIGINFO handling.#963
grynspan wants to merge 12 commits into
mainfrom
jgrynspan/siginfo-handling

Conversation

@grynspan

@grynspan grynspan commented Sep 1, 2026

Copy link
Copy Markdown

Description

This PR adds a new protocol, InfoProvidingCommand, to which root command types can conform to indicate they respond to SIGINFO or platform equivalents:

  • On Darwin and the BSDs, the shell raises SIGINFO when you press Ctrl+T;
  • On Linux, SIGUSR1 is used by (weak) convention for this purpose and can be raised with kill or pkill.
  • On Windows, Ctrl+Break serves the same purpose and triggers a Win32-specific callback.

Detailed Design

The new API is an additional protocol that refines ParsableCommand:

/// A parsable command that can provide information back to the user while it
/// runs.
///
/// The user may request information from a running process via a
/// platform-specific mechanism:
///
/// - On Apple platforms, FreeBSD, and OpenBSD, by sending `SIGINFO` to the
///   process or by pressing Ctrl+T in the Terminal application.
/// - On Linux, by sending the `SIGUSR1` signal to the process.
/// - On Windows, by pressing Ctrl+Break in the Terminal application.
///
/// If your root command conforms to this protocol, Swift Argument Parser
/// automatically sets up a signal handler to listen for `SIGINFO` (or the
/// platform-specific equivalent).
///
/// On platforms that do not support providing information, conformance to this
/// protocol has no effect.
@available(anyAppleOS 26, *)
public protocol InfoProvidingParsableCommand: Sendable, ParsableCommand {
  /// Provide information about the state of the process and about the
  /// currently-running command.
  ///
  /// The information you provide is program-specific. Programs will often
  /// provide a status update such as the percentage or count of work completed,
  /// information about the currently-running work item, etc.
  ///
  /// - Important: Swift Argument Parser calls this function asynchronously
  ///   while your program is running. If your command type is actor-isolated,
  ///   ensure your ``AsyncParsableCommand/run()`` implementation periodically
  ///   yields control by suspending, sleeping, or calling [`Task.yield()`](https://developer.apple.com/documentation/swift/task/yield()).
  ///
  ///   If ``AsyncParsableCommand/run()`` never yields, the Swift runtime may
  ///   not be able to schedule calls to this function and it will appear to the
  ///   user as if it is not implemented.
  nonisolated(nonsending) func provideInfo() async
}

Types that conform to this protocol can then implement provideInfo() to print whatever information is meaningful at that moment. An example program is included to show how you might use it.

Documentation Plan

I plan to scrabble something together after the design has been critiqued.

Test Plan

Added a new unit test based on a Swift Testing exit test. Signals are generally hard to test, and I am open to adding more tests if folks have ideas.

The example program can also be run at desk to validate behavior in an interactive way.

Source Impact

There should be no impact on existing code.

Checklist

  • I've added at least one test that validates that my change is working, if appropriate
  • I've followed the code style of the rest of the project
  • I've read the Contribution Guidelines
  • I've updated the documentation if necessary

@grynspan grynspan self-assigned this Sep 1, 2026
@grynspan grynspan added the enhancement New feature or request label Sep 1, 2026
@grynspan
grynspan force-pushed the jgrynspan/siginfo-handling branch 4 times, most recently from e9b6474 to a205653 Compare September 1, 2026 16:39
/// user as if it is not implemented.
nonisolated(nonsending) func provideInfo() async
#else
func provideInfo() async

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This feels a bit repetitive ... InfoProvidingCommand.provideInfo. Would InfoProvidingCommand.information or InfoProvidingCommand.provide might be nicer.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

provide() would be too vague outside the context of the protocol. @rauhul suggested just info().

siginfoHandler?.register()
defer {
siginfoHandler?.unregister()
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Given that this pattern occurs at least twice, would it be justified to do:

withInfoHandler(...) {
  try command.run()
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Maybe. I'm not too too bothered by it but I can add a helper if other folks want it.

///
/// - Note: This class is responsible for handling `SIGINFO`, `SIGUSR1` (Linux),
/// and Ctrl+Break (Windows). Naming is hard.
final class SIGINFOHandler: Sendable {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I do wish that we had a better name than SIGINFOHandler (perhaps InfoProviderHandler?).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It's not API so we can change it. We're thinking about using it for Ctrl+C too to support cancellation. But for now, it's an implementation detail so I'm not sweating it that much.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

ControlKeyObserver maybe?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ControlKeyObserver maybe?

I really like this if you are okay with the length.

Comment thread Sources/ArgumentParser/Utilities/SIGINFOHandler.swift
This PR adds a new protocol, `InfoProvidingCommand`, to which root command types
can conform to indicate they respond to `SIGINFO` or platform equivalents:

- On Darwin and the BSDs, the shell raises `SIGINFO` when you press Ctrl+T;
- On Linux, `SIGUSR1` is used by (weak) convention for this purpose and can be
  raised with `kill` or `pkill`.
- On Windows, Ctrl+Break serves the same purpose and triggers a Win32-specific
  callback.
@grynspan
grynspan force-pushed the jgrynspan/siginfo-handling branch from 6333d8c to 29826f6 Compare September 1, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants