0

Consider the following code:

import click

@click.command()
@click.argument("file", type=click.File())
def cli(file):
   print(file)

if __name__ == "__main__":
    cli()

Executing as:

$ python ./cmd.py -
<_io.TextIOWrapper name='<stdin>' mode='r' encoding='utf-8'>
$ touch '<stdin>'
$ python ./cmd.py '<stdin>'
<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>

There is suprising difference in encoding.

How can I detect if the input was actual - and not anything else in python click?

3
  • '<stdin>' doesn't means file but special object sys.stdin but you run touch <stdin> to create file. So maybe you should check if your file is sys.stdin. Besides every opened file has number and sys.stdin has 0, sys.stdout has 1, and sys.sterr has 2 - you can check sys.stdin.fileno() and maybe even file.fileno() Commented Dec 12, 2023 at 21:56
  • 2
    files have also function isatty() - sys.stdout.isatty() or file.isatty() which inform if this object is connected with console/terminal. Commented Dec 12, 2023 at 22:02
  • utf-8 and UTF-8 is the same encoding. Commented Dec 12, 2023 at 22:03

1 Answer 1

1

'<stdin>' doesn't means filename but special object sys.stdin

And you can compare file == sys.stdin


Every opened file has number which system uses to work with this opened file and sys.stdin also has this number.

Normally

  • sys.stdin.fileno() is 0,
  • sys.stdout.fileno() is 1,
  • sys.sterr.fileno() is 2

So you can compare file.fileno() == 0


sys.stdin is usually also assigned to console/terminal but normal file is not assigned - and you can check it with .isatty()

So you can compare file.isatty() is False

But this is not good method because sometimes sys.stdin is not assigned - ie.

  • when it runs in script without access to terminal - like in cron
  • when it works in pipe like echo "text" | script.py (but it will be it can be assigned when it is first in pipe like script.py | sort

But this method can be useful when you want to draw colored text on screen and send not colored text to file. But this may need to check also sys.stdout.


import click
import sys

@click.command()
@click.argument("file", type=click.File())
def cli(file):
   print('file  :', file)
   print('number:', file.fileno())
   print('  ==  :', file == sys.stdin)
   print('isatty:', file.isatty())
   print('stdin :', sys.stdin.isatty())
   print('stdout:', sys.stdout.isatty())   

if __name__ == "__main__":
    cli()
$ python3 ./cmd.py -

<_io.TextIOWrapper name='<stdin>' mode='r' encoding='utf-8'>
number: 0
 ==   : True
isatty: True
$ python3 ./cmd.py cmd.py

<_io.TextIOWrapper name='cmd.py' mode='r' encoding='UTF-8'>
number: 3
 ==   : False
isatty: False
Sign up to request clarification or add additional context in comments.

4 Comments

sys.stdin.isatty() is certainly false when stdin is a pipe, or a regular file, or otherwise not a TTY; and you can also expect it to be false in the cronjob case. It's possible to configure a systemd service to have a direct file handle on a TTY, but it takes deliberate work (by default, stdin is /dev/null, and stdout and stderr are non-TTY handles that are attached to journald).
(Mind, in the script.py | sort case, it's stdout that's not a TTY; stderr is typically what you want to be checking when you want to know if there's likely to be a human reading your log messages, or if you can put color codes in them or the like)
@CharlesDuffy I was checking example to confirm it: When I run script.py | sort then it shows me True but when it as second item in pipe echo "Hello" | script.py | sort then it shows False - so all this depends on position.
@CharlesDuffy I was check only stdin but I should also check stdout in piping :) So all depends on position in pipe. if script is first in pipe then stdin is True but stdout is False. When it is last in pipe then stdin is False and stdout is True. And when it between two pipes then both are False - so all as you said. BTW: When I was writing about isatty I was also thinking about colored output on console, and not colored output to file :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.