You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: Proposals/NNNN-swift-subprocess.md
+39-10Lines changed: 39 additions & 10 deletions
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,12 @@
10
10
## Revision History
11
11
12
12
***v1**: Initial draft
13
+
***v2**: Minor Updates:
14
+
- Switched `AsyncBytes` to be backed by `DispatchIO`.
15
+
- Introduced `resolveExecutablePath(withEnvironment:)` to enable explicit lookup of the executable path.
16
+
- Added a new option, `closeWhenDone`, to automatically close the file descriptors passed in via `.readFrom` and friends.
17
+
- Introduced a new parameter, `shouldSendToProcessGroup`, in the `sendSignal` function to control whether the signal should be sent to the process or the process group.
18
+
- Introduced a section on "Future Directions."
13
19
14
20
## Introduction
15
21
@@ -280,8 +286,9 @@ public struct Subprocess: Sendable {
280
286
// The standard error of the child process, expressed as AsyncSequence<UInt8>
281
287
// This property is `nil` if the standard error is discarded or written to disk
282
288
publicvar standardError: AsyncBytes? { get }
283
-
284
-
publicfuncsendSignal(_signal: Signal) throws
289
+
// If `shouldSendToProcessGroup` is `true`, the signal will be send to the entire process
@@ -491,7 +498,7 @@ _(We welcome community input on which Linux and Windows "escape hatches" we shou
491
498
492
499
In addition to supporting the direct passing of `Sequence<UInt8>` and `AsyncSequence<UInt8>` as the standard input to the child process, `Subprocess` also provides a `Subprocess.InputMethod` type that includes two additional input options:
493
500
-`.noInput`: Specifies that the subprocess does not require any standard input. This is the default value.
494
-
-`.readingFrom`: Specifies that the subprocess should read its standard input from a file descriptor provided by the developer.
501
+
-`.readFrom`: Specifies that the subprocess should read its standard input from a file descriptor provided by the developer. Subprocess will automatically close the file descriptor after the process exits if `closeWhenDone` is set to `true`.
@@ -514,7 +521,7 @@ let ls = try await Subprocess.run(executing: .named("ls"))
514
521
515
522
// Alteratively, developers could pass in a file descriptor
516
523
let fd: FileDescriptor =...
517
-
let cat =tryawait Subprocess.run(executing: .named("cat"), input: .readingFrom(fd))
524
+
let cat =tryawait Subprocess.run(executing: .named("cat"), input: .readFrom(fd, closeWhenDone: true))
518
525
519
526
// Pass in a async sequence directly
520
527
let sequence: AsyncSequence =...
@@ -526,7 +533,7 @@ let exe = try await Subprocess.run(executing: .at("/some/executable"), input: se
526
533
527
534
`Subprocess` uses two types to describe where the standard output and standard error of the child process should be redirected. These two types, `Subprocess.collectOutputMethod` and `Subprocess.redirectOutputMethod`, correspond to the two general categories of `run` methods mentioned above. Similar to `InputMethod`, both `OutputMethod`s add two general output destinations:
528
535
-`.discard`: Specifies that the child process's output should be discarded, effectively written to `/dev/null`.
529
-
-`.writeTo`: Specifies that the child process should write its output to a file descriptor provided by the developer. This file descriptor will be closed once the write operation is complete.
536
+
-`.writeTo`: Specifies that the child process should write its output to a file descriptor provided by the developer. Subprocess will automatically close the file descriptor after the process exits if `closeWhenDone`is set to `true`.
530
537
531
538
`CollectedOutMethod` adds one more option to non-closure-based `run` methods that return a `CollectedResult`: `.collect` and its variation `.collect(limit:)`. This option specifies that `Subprocess` should collect the output as `Data`. Since the output of a child process could be arbitrarily large, `Subprocess` imposes a limit on how many bytes it will collect. By default, this limit is 16kb (when specifying `.collect`). Developers can override this limit by specifying `.collect(limit: newLimit)`:
532
539
@@ -542,7 +549,7 @@ extension Subprocess {
542
549
// Collect the output as Data with the default 16kb limit
`struct Environment` is used to configure how should the process being lunched receive its environment values:
682
+
`struct Environment` is used to configure how should the process being launched receive its environment values:
673
683
674
684
```swift
675
685
extensionSubprocess {
@@ -826,6 +836,25 @@ extension Subprocess {
826
836
No impact on existing code is anticipated. All introduced changes are additive.
827
837
828
838
839
+
## Future Directions
840
+
841
+
### Automatic Splitting of `Arguments`
842
+
843
+
Ideally, the `Arguments` feature should automatically split a string, such as "-a -n 1024 -v 'abc'", into an array of arguments. This enhancement would enable `Arguments` to conform to `ExpressibleByStringLiteral`, allowing developers to conveniently pass either a `String` or `[String]` as `Arguments`.
844
+
845
+
I decided to defer this feature because it turned out to be a "hard problem" -- different platforms handle arguments differently, requiring careful consideration to ensure correctness.
846
+
847
+
For reference, Python uses [`shlex.split`](https://docs.python.org/3/library/shlex.html), which could serve as a valuable starting point for implementation.
848
+
849
+
## Combined `stdout` and `stderr`
850
+
851
+
In Python's `Subprocess`, developers can merge standard output and standard error into a single stream. This is particularly useful when an executable improperly utilizes standard error as standard output (or vice versa). We should explore the most effective way to achieve this enhancement without introducing confusion to existing parameters—perhaps by introducing a new property.
852
+
853
+
## Automatic `Subprocess` Cancellation
854
+
855
+
Currently, `Subprocess` does not terminate the spawned process, even if the `Subprocess` instance goes out of scope. It would be beneficial to investigate whether it is more sensible to attempt terminating the spawned process when the `Subprocess` itself goes out of scope or when the parent task is canceled.
0 commit comments