-
Notifications
You must be signed in to change notification settings - Fork 178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
system: subprocessing interface #911
base: master
Are you sure you want to change the base?
Conversation
Cool addition! I always wanted to have something like this in the past. I wrote my own little experiment once. I started reviewing the Python subprocess module, but got discouraged by the messy implementation details. |
Summary of community feedback:
|
I believe all community-requested features have been introduced. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice PR @perazz . Very valuable additions.
It could be helpful if the changes in stdlib_strings would have its own PR. It would be easier to review, and I believe it could be merged sooner. But it is also ok like this for me.
@@ -459,8 +459,52 @@ The result is of the same type as `string`. | |||
{!example/strings/example_zfill.f90!} | |||
``` | |||
|
|||
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --> | |||
### `join` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could have its own PR, as it is not directly associated with this PR (but needed for this PR).
#### Description | ||
|
||
Joins an array of strings into a single string. This function concatenates the strings from the input array, | ||
inserting a separator between each string (default: space). A user-defined separator may be provided, The resulting string is returned. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inserting a separator between each string (default: space). A user-defined separator may be provided, The resulting string is returned. | |
inserting a separator between each string (default: space). A user-defined separator may be provided. The resulting string is returned. |
program test_join | ||
type(string_type) :: result | ||
type(string_type), dimension(3) :: words = [string_type('hello'), string_type('world'), string_type('fortran')] | ||
result = join_string(words, ', ') ! Joins with comma and space | ||
print *, result ! Output: "hello, world, fortran" | ||
end program test_join |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be in a separate file.
|
||
`want_stdout` (optional): Shall be a `logical` flag. If `.true.`, the standard output of the process will be captured; if `.false.` (default), it will be lost. This is an `intent(in)` argument. | ||
|
||
`want_stderr` (optional): Shall be a logical flag. If `.true.`, the standard error output of the process will be captured. Default: `.false.`. This is an `intent(in)` argument. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`want_stderr` (optional): Shall be a logical flag. If `.true.`, the standard error output of the process will be captured. Default: `.false.`. This is an `intent(in)` argument. | |
`want_stderr` (optional): Shall be a `logical` flag. If `.true.`, the standard error output of the process will be captured. If `.false.` (default), it will be lost. This is an `intent(in)` argument. |
|
||
```fortran | ||
! Example usage with command line or list of arguments | ||
type(process_type) :: p |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this could be easily converted to a full program
|
||
`want_stdout` (optional): Shall be a `logical` flag. If `.true.`, the standard output of the process will be captured; if `.false.` (default), it will be lost. This is an `intent(in)` argument. | ||
|
||
`want_stderr` (optional): Shall be a logical flag. If `.true.`, the standard error output of the process will be captured. Default: `.false.`. This is an `intent(in)` argument. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`want_stderr` (optional): Shall be a logical flag. If `.true.`, the standard error output of the process will be captured. Default: `.false.`. This is an `intent(in)` argument. | |
`want_stderr` (optional): Shall be a `logical` flag. If `.true.`, the standard error output of the process will be captured. Default: `.false.`. This is an `intent(in)` argument. |
end if | ||
``` | ||
|
||
## `is_completed` - Check if a process has completed execution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the addition of is_completed
in comparison to is_running
? It seems like the result will always be is_completed(process) == .not.is_running(process)
?
With this PR I want to introduce for discussion a comprehensive API for managing external processes in Fortran.
This is a fully cross-platform implementation for synchronous (similar to
execute_command_line
) or asynchronous (fully detached process) processes, and mimics functionality from the Pythonsubprocess
module.Previous discussion
Proposed API
Here’s the simplified list of interfaces with their types and example usage:
type(process_type)
: A derived type representing the external process state containing the state handler as well asstdin
,stdout
,stderr
as strings.run
a synchronous (blocking) process:process = run(cmd [, stdin=string] [, want_stdout=.false.] [, want_stderr=.false.])
runasync
an asynchronous (non-blocking) process:process = runasync(cmd [, stdin=string] [, want_stdout=.false.] [, want_stderr=.false.])
update
query process state and update the state variable:call update(process)
is_running
check if a process is still running and update the handler:status = is_running(process)
is_completed
check if a process has finished and update the handler:status = is_completed(process)
wait
for completion (option for max waiting time in seconds):call wait(process [, max_wait_time=10.0])
elapsed
time since process start, or total process lifetime:duration = elapsed(process)
kill
a running external process:call kill(process, success)
sleep
implementation is moved to C so that a slightly better wrapper based onnanosecond
can be usedThis provides a concise overview of the interfaces for quick reference.
Key facts
interface
s are used rather than type-bound procedures because that seems more similar to the other stdlib interfaces, but I'm happy to introduce them as well and further discuss the API.stderr
,stdout
,stdin
are stored via temporary files, but that is completely internal to the implementation. So, we can later implement faster memory-only communication via pipes without changing the API.type(state_type)
general error handler is approved, we can improve this and use it throughout the subprocessing API too.Prior art