Wernight
Wernight

Reputation: 37620

Is a Python File object a real file?

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:

  1. file not in [sys.stdin, sys.stdout, sys.stderr] -- Seems safest but only for those standard types, not very generic solution.
  2. os.path.isfile(file.name) -- Seems pretty safe but might not work if new file is opened in some creation modes I guess.
  3. file.fileno() == 0 -- This supposes the method isn't implemented, default implementation returns 0, and normal implementations never return 0.
  4. 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

Answers (2)

xuhdev
xuhdev

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

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

Related Questions