Bram Vanroy
Bram Vanroy

Reputation: 28505

Check if file is readable with Python: try or if/else?

I have the following code:

import glob, os
for file in glob.glob("\\*.txt"):
    if os.access(file, os.R_OK):
        # Do something
    else:
        if not os.access(file, os.R_OK):
            print(file, "is not readable")
        else:
            print("Something went wrong with file/dir", file)
        break

But I'm not entirely sure if this the right way to do it. Is it better to use try and catch the error? If so, how do I try for readability? Note the break in my else statement. As soon as a file can't be read I want to abort the loop.

Upvotes: 32

Views: 54052

Answers (4)

Tagar
Tagar

Reputation: 14939

A more explicit way to check if file is actually a file and not directory for example, and it is readable:

from os import access, R_OK
from os.path import isfile

file = "/some/path/to/file"

assert isfile(file) and access(file, R_OK), \
       f"File {file} doesn't exist or isn't readable"

Upvotes: 56

bereal
bereal

Reputation: 34290

In Python culture, it's more common to ask forgiveness, not permission, so it's preferable to catch the exception:

for filename in glob.glob('*.txt'):
    try:
        with open(filename) as fp:
            # work with the file

    except IOError as err:
        print "Error reading the file {0}: {1}".format(filename, err)
        break

That way you will also avoid any double-checks or race conditions.

Upvotes: 10

user126885
user126885

Reputation: 157

try:
        # check to see if file is readable
        with open(filename) as tempFile:





except Exception as e:
        print e
        # here you can modify the error message to your liking

This is usually what I do. It is robust and straight forward

Upvotes: 2

Robᵩ
Robᵩ

Reputation: 168766

For me, using a try-except at the same scope as one would use an if-else gains no readability. The value of exceptions is that they can be caught at a higher level in the call tree.

Moving out just one level, we avoid the break statement:

import glob, os
try:
    for file in glob.glob("\\*.txt"):
        with open(file) as fp:
            # do something with file
except IOError:
    print("could not read", file)

But the true genius of exceptions is when the code simply disappears:

# Operate on several files
# SUCCESS: Returns None
# FAIL: Raises exception
def do_some_files():
    for file in glob.glob("\\*.txt"):
        with open(file) as fp:
            # do something with file

Now it is the calling program's responsibility to display a useful error message on failure. We have removed responsibility for dealing with failure completely out of this code and into a whole other realm.

In fact, one can move the responsibility completely out of our program and into the interpreter. In that case, the interpreter will print some useful error message and terminate our program. If Python's default message is good enough for your users, I recommend not checking for errors at all. Thus, your original script becomes:

import glob, os
for file in glob.glob("\\*.txt"):
    # Do something

Upvotes: 20

Related Questions