Reputation: 37620
In Python a file object may point to a real file, stdin, stdout, stderr, or even something else. So it can be file-like, or a real file as stated on file.flush.
How to know if a file is a real file? I found some ways to guess but none seems really reliable:
file not in [sys.stdin, sys.stdout, sys.stderr]
-- Seems safest but only for those standard types, not very generic solution.os.path.isfile(file.name)
-- Seems pretty safe but might not work if new file is opened in some creation modes I guess.file.fileno() == 0
-- This supposes the method isn't implemented, default implementation returns 0, and normal implementations never return 0.file.name.startswith('<')
-- This really supposes the file system doesn't allow <
in the file name.Why I need to know this, is because I'd like to close it early and using with file as f:
may close stdin/stdout which sounds like a bad idea.
Upvotes: 4
Views: 679
Reputation: 9343
This part from Python's socket library may be helpful (where file
is a file object):
try:
fileno = file.fileno()
except (AttributeError, io.UnsupportedOperation) as err:
raise _GiveupOnSendfile(err) # not a regular file
try:
fsize = os.fstat(fileno).st_size
except OSError as err:
raise _GiveupOnSendfile(err) # not a regular file
Upvotes: 0
Reputation: 133929
Generally you should use the with ... as f:
only symmetrically, that is, with file handles that you open
or acquire in the with
statement.
On UNIX you can check .fileno()
- 0 stands for standard in, 1 for standard out and 2 for standard error; generally you shouldn't close these streams, as there is really no way to reopen them anymore.
file.name
is not really 100 % proof approach, since many streams can have a name, even though they are not real files, and that special name can clash with an existing file on the disk:
>>> import os.path
>>> os.path.exists(sys.stdin.name)
True
>>> sys.stdin.name
'<stdin>'
(hint, just moments before I did touch '<stdin>'
)
Also, in UNIX it is customary to unlink temporary files right after opening them, so they do not exist any more by that name; on the other hand even an existing file may be renamed after it is opened, and thus does not exist by the original name.
If you want to find out if a standard stream is redirected to a file, you can do:
>>> import os
>>> import stat
>>> stat.S_ISREG(os.fstat(sys.stdout.fileno()).st_mode)
False
S_ISREG
returns True
if the mode describes an ordinary file (here the output goes to terminal so it is False); likewise you can find out if any file is redirected to a terminal with isatty
:
>>> os.isatty(sys.stdout.fileno())
True
Upvotes: 6