Reputation: 1091
I wanted an easy way to determine a type of path so I googled alot and then I wrote this:
from stat import S_ISREG, S_ISDIR, S_ISLNK
from os import stat, lstat
from os.path import isfile, islink, isdir, lexists, exists
from enum import Enum, auto
class FileTypes(Enum):
FILE = auto()
LINK_TO_FILE = auto()
DIR = auto()
LINK_TO_DIR = auto()
BROKEN_LINK = auto()
NO_SUCH = auto()
UNDEFINED = auto()
def file_type(filename):
if lexists(filename):
if isfile(filename):
if islink(filename):
return FileTypes.LINK_TO_FILE
else:
return FileTypes.FILE
else:
if isdir(filename):
if islink(filename):
return FileTypes.LINK_TO_DIR
else:
return FileTypes.DIR
else:
if islink(filename):
return FileTypes.BROKEN_LINK
else:
return FileTypes.UNDEFINED
else:
return FileTypes.NO_SUCH
Then I googled more and wrote this:
def file_type2(filename):
if lexists(filename):
if exists(filename):
mode = stat(filename).st_mode
lmode = lstat(filename).st_mode # os.lstat doesn't follow symlinks
if S_ISREG(mode) and S_ISREG(lmode):
return FileTypes.FILE
elif S_ISREG(mode) and S_ISLNK(lmode):
return FileTypes.LINK_TO_FILE
elif S_ISDIR(mode) and S_ISDIR(lmode):
return FileTypes.DIR
elif S_ISDIR(mode) and S_ISLNK(lmode):
return FileTypes.LINK_TO_DIR
else:
return FileTypes.UNDEFINED
else:
return FileTypes.BROKEN_LINK
else:
return FileTypes.NO_SUCH
Both functions do what I want, but look kinda ugly and I think that I'm missing a simpler solution hiding in some cool python lib.
Question is: Is there a better way to do this?
Upvotes: 0
Views: 786
Reputation: 1367
We can make it more consise with utilizing bitmasking inside enum:
Let's assume first value describes if file exists: 0 for existing and 1 for not existing, second one will be symlink: 1 for links and 0 for non-links, third for directory: 1 if it is directory and 0 if it is not, and the last for file in hte same menner.
So if we wanted to describe file that existsand is a symlink to file, we would use 0(exists)1(link)0(non-dir)1(file)
With using meaningful values we can now consisely chain those values together with results returned from python stat
wrapper.
class FileTypes(Enum):
FILE = 1 #0001
LINK_TO_FILE = 5 #0101
DIR = 2 #0010
LINK_TO_DIR = 6 #0110
BROKEN_LINK = 4 #0100
NO_SUCH = 0 #1000
UNDEFINED = #0000
def file_type(filepath):
return FileTypes.NO_SUCH if lexists(filepath) else
Filetypes(int(
str(int(islink(filepath)))
+ str(int(isdir(filepath)))
+ str(int(isfile(filepath)))))
Obviously there is issue of some illegal
states like if something would report that it is both directory and file, at that point this will raise exception, it can be modified for different behaviour, but raising exception seems perfectly valid.
Also I've used pretty ugly way of adding together this value, but this is for sake of readibility. You always could do ''.join(map(str,map(int,[islink(filepath),isdir(filepath),isfile(filepath)])))
or even shorter ways
Upvotes: 0
Reputation: 149736
You can try the pathlib
module which has been in stdlib since Python 3.4 (for older pythons use pip install pathlib
). It defines the Path
class which contains methods for both checking types of files as well as resolving symlinks. Besides, it provides a pretty convenient API:
>>> from pathlib import Path
>>> path = Path("/etc/") / "passwd"
>>> path
PosixPath('/etc/passwd')
>>> path.is_file()
True
Upvotes: 1