-
-
Notifications
You must be signed in to change notification settings - Fork 672
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
Fetching sys.stdin using annotations in a command #345
Comments
I also want this for my automation scripts |
Hi, I found a solution to the problem I was working on. My automation script takes and string as CLI argument and writes it after couple of seconds. python write_text.py "write"
# I converted it in the global script so I can run it from anywhere. Now, I use it like:
write_text "write" However, there were cases where string I wanted to auto write was coming from some other bash command output so I wanted my script to run in both cases:
This was challenging but thanks to this comment, I got an idea and here's example that let you pass argument and read from stdin. import sys
import typer
def main(
name: str = typer.Argument(
... if sys.stdin.isatty() else sys.stdin.read().strip()
),
):
typer.echo(f"Hello {name}")
if __name__ == "__main__":
typer.run(main) I hope this will help you in resolving the issue |
Not sure why but the above answer doesn't seem to work for me unfortunately... @app.command()
def echo(
msg: str = typer.Argument(... if sys.stdin.isatty() else sys.stdin.read()),
):
print(msg) The following test always prints a blank line. Same occurs when
So I would definitely support an option that can read |
@theelderbeever above code example is full minimal app/script. Have you tried running that app? |
A slightly different approach/workaround looks like this. You can pass an array of strings, or, say an array of filenames into the Python script if you dot-source Of course there's no real parallelism here, the Python interpreter fires up and runs with every input piped to it, there's no benefit of setup/teardown e.g. The upside is you don't need to do anything special to your Python module, since all logic is handled on the shell side. The downside is with multiple arguments you're gonna be constructing arrays of arguments on the shell side in order to pipe in. Not sure exactly how that would look, you could probably do some splatting. PS > python -m hello world
Hello world!
PS > 'world', 'Mom', 'goodbye' | Invoke-Python -Module hello
Hello world!
Hello Mom!
Hello goodbye!
PS > Get-ChildItem -Filter *.md | Invoke-Python -Module hello
Hello C:\test.md
Hello C:\test2.md The one-liner equivalent of this is Contents of """Say hello."""
import typer
def main(name: str = typer.Argument(...)):
typer.echo(f"Hello {name}!")
if __name__ == "__main__":
typer.run(main) Contents of function Invoke-Python {
<#.SYNOPSIS
Invoke a Python module.
#>
Param(
# Arbitrary input item.
[Parameter(Mandatory, ValueFromPipeline)]$Item,
# Python module to pass input to.
[Parameter(Mandatory)]$Module
)
process {
python -m $Module $Item
}
} |
This doesn't work if you have more than one command using this pattern thanks to an inconvenient clash of the ways Python reads I/O streams and evaluates default function argument values. I'm currently working around this by using sentinel to indicate that a value should be read from standard input: # This script is complete and should run as-is.
import sys
import sentinel
import typer
PIPE = sentinel.create("PIPE_FROM_STDIN")
def main(
name: str = typer.Argument(
... if sys.stdin.isatty() else PIPE
),
):
if name == str(PIPE):
name = sys.stdin.read()
typer.echo(f"Hello {name}")
if __name__ == "__main__":
typer.run(main) |
This looks like it works to me: import sys
import typer
from typing_extensions import Annotated
def main(input_file: Annotated[typer.FileText, typer.Argument()] = sys.stdin):
typer.echo(input_file.read())
if __name__ == '__main__':
typer.run(main) |
This works, but then mypy complains with following |
First Check
Commit to Help
Example Code
Description
I would like to have typer integrate seemlessly with sys.stdin. Currently, it is quite complicated to integrate sys.stdin in a typer app. You can obviously use something like :
See this issue : #156
But it is definitely not seemless.
Wanted Solution
The goal would be to have something like in the code example provided, direct access to stdin from the argument type (which is handy when designing CLIs so that they can be used in conjunction with other unix tools -- like cut, cat, sed or find for instance)
Wanted Code
Alternatives
Use the aforementionned workaround, using '-' as the marker for an stdin input.
I also tried reading from sys.stdin directly, but it doesn't integrate well in a typer app (especially around the coding style)
Operating System
Linux, Windows, macOS
Operating System Details
Typer Version
0.3.2
Python Version
3.9.9
Additional Context
No response
The text was updated successfully, but these errors were encountered: