How do check whether a file exists without exceptions using python

image 5
Now available since Python 3.4, import and instantiate a Path object with the file name, and check the is_file method (note that this returns True for symlinks pointing to regular files as well): >>> from pathlib import Path >>> Path(\’/\’).is_file() False >>> Path(\’/initrd.img\’).is_file() True >>> Path(\’/doesnotexist\’).is_file() False If you\’re on Python 2, you can backport the pathlib module from pypi, pathlib2, or otherwise check isfile from the os.path module: >>> import os >>> os.path.isfile(\’/\’) False >>> os.path.isfile(\’/initrd.img\’) True >>> os.path.isfile(\’/doesnotexist\’) False Now the above is probably the best pragmatic direct answer here, but there\’s the possibility of a race condition (depending on what you\’re trying to accomplish), and the fact that the underlying implementation uses a try, but Python uses try everywhere in its implementation. Because Python uses try everywhere, there\’s really no reason to avoid an implementation that uses it. But the rest of this answer attempts to consider these caveats.

Longer, much more pedantic answer

Available since Python 3.4, use the new Path object in pathlib. Note that .exists is not quite right, because directories are not files (except in the unix sense that everything is a file). >>> from pathlib import Path >>> root = Path(\’/\’) >>> root.exists() True So we need to use is_file: >>> root.is_file() False Here\’s the help on is_file: is_file(self) Whether this path is a regular file (also True for symlinks pointing to regular files). So let\’s get a file that we know is a file: >>> import tempfile >>> file = tempfile.NamedTemporaryFile() >>> filepathobj = Path( >>> filepathobj.is_file() True >>> filepathobj.exists() True By default, NamedTemporaryFile deletes the file when closed (and will automatically close when no more references exist to it). >>> del file >>> filepathobj.exists() False >>> filepathobj.is_file() False If you dig into the implementation, though, you\’ll see that is_file uses try: def is_file(self): “”” Whether this path is a regular file (also True for symlinks pointing to regular files). “”” try: return S_ISREG(self.stat().st_mode) except OSError as e: if e.errno not in (ENOENT, ENOTDIR): raise # Path doesn\’t exist or is a broken symlink # (see return False

Race Conditions: Why we like try

We like try because it avoids race conditions. With try, you simply attempt to read your file, expecting it to be there, and if not, you catch the exception and perform whatever fallback behavior makes sense. If you want to check that a file exists before you attempt to read it, and you might be deleting it and then you might be using multiple threads or processes, or another program knows about that file and could delete it – you risk the chance of a race condition if you check it exists, because you are then racing to open it before its condition (its existence) changes. Race conditions are very hard to debug because there\’s a very small window in which they can cause your program to fail. But if this is your motivation, you can get the value of a try statement by using the suppress context manager.

Avoiding race conditions without a try statement: suppress

Python 3.4 gives us the suppress context manager (previously the ignore context manager), which does semantically exactly the same thing in fewer lines, while also (at least superficially) meeting the original ask to avoid a try statement: from contextlib import suppress from pathlib import Path Usage: >>> with suppress(OSError), Path(\’doesnotexist\’).open() as f: … for line in f: … print(line) … >>> >>> with suppress(OSError): … Path(\’doesnotexist\’).unlink() … >>> For earlier Pythons, you could roll your own suppress, but without a try will be more verbose than with. I do believe this actually is the only answer that doesn\’t use try at any level in the Python that can be applied to prior to Python 3.4 because it uses a context manager instead: class suppress(object): def init(self, *exceptions): self.exceptions = exceptions def enter(self): return self def exit(self, exc_type, exc_value, traceback): if exc_type is not None: return issubclass(exc_type, self.exceptions) Perhaps easier with a try: from contextlib import contextmanager @contextmanager def suppress(*exceptions): try: yield except exceptions: pass

Other options that don\’t meet the ask for “without try”:

isfile import os os.path.isfile(path) from the docs:
os.path.isfile(path) Return True if path is an existing regular file. This follows symbolic links, so both islink() and isfile() can be true for the same path.
But if you examine the source of this function, you\’ll see it actually does use a try statement:
# This follows symbolic links, so both islink() and isdir() can be true # for the same path on systems that support symlinks def isfile(path): “””Test whether a path is a regular file””” try: st = os.stat(path) except os.error: return False return stat.S_ISREG(st.st_mode)
>>> OSError is os.error True All it\’s doing is using the given path to see if it can get stats on it, catching OSError and then checking if it\’s a file if it didn\’t raise the exception. If you intend to do something with the file, I would suggest directly attempting it with a try-except to avoid a race condition: try: with open(path) as f: except OSError: pass os.access Available for Unix and Windows is os.access, but to use you must pass flags, and it does not differentiate between files and directories. This is more used to test if the real invoking user has access in an elevated privilege environment: import os os.access(path, os.F_OK) It also suffers from the same race condition problems as isfile. From the docs:
Note: Using access() to check if a user is authorized to e.g. open a file before actually doing so using open() creates a security hole, because the user might exploit the short time interval between checking and opening the file to manipulate it. It’s preferable to use EAFP techniques. For example: if os.access(“myfile”, os.R_OK): with open(“myfile”) as fp: return return “some default data” is better written as: try: fp = open(“myfile”) except IOError as e: if e.errno == errno.EACCES: return “some default data” # Not a permission error. raise else: with fp: return
Avoid using os.access. It is a low level function that has more opportunities for user error than the higher level objects and functions discussed above.

Criticism of another answer:

Another answer says this about os.access:
Personally, I prefer this one because under the hood, it calls native APIs (via “${PYTHON_SRC_DIR}/Modules/posixmodule.c”), but it also opens a gate for possible user errors, and it\’s not as Pythonic as other variants:
This answer says it prefers a non-Pythonic, error-prone method, with no justification. It seems to encourage users to use low-level APIs without understanding them. It also creates a context manager which, by unconditionally returning True, allows all Exceptions (including KeyboardInterrupt and SystemExit!) to pass silently, which is a good way to hide bugs. This seems to encourage users to adopt poor practices.

Leave a Reply