2

I'm trying to use os.listdir to grab a list of subdirectories but am running into an issue when I'm lacking permissions for one of these subdirectories. I can not gain permission so I'd like to continue as gracefully as possible. Ideally, I'd be able to ignore any directory I do not have permission to and return any others so as to not miss any child directories.

I've tried using os.walk but I ran into a number of other issues (performance included) and have decided not to use it.

An example. In the root directory there are 3 children, a, b, c

root dir
|
----> dir a 
|
----> dir b
|
----> dir c

I have permissions to a and c but not b (not known ahead of time). I'd like to return [a, c]

Here's the code with some generalizations-

def get_immediate_subdirectories(directory):
"""Returns list of all subdirectories in
directory

Args:
  directory: path to directory

Returns:
  List of child directories in directory excluding 
  directories in exclude and any symbolic links

"""
exclude = ["some", "example", "excluded", "dirnames"]
sub_dirs = []
try:
    all_files = os.listdir(directory)
except OSError:
    # **Ideally I'd be able to recover some list here/continue**
for name in all_files:
    if name in exclude:
        continue
    full_path = os.path.join(directory, name)
    if os.path.isdir(full_path):
        # Keep these separate to avoid issue
        if not os.path.islink(full_path):
            sub_dirs.append(name)
return sub_dirs
Ian Panzica
  • 334
  • 1
  • 6
  • 20
  • 2
    `listdir()` doesn't fail when it hits an individual file you can't read -- it only fails when you don't have `+r` on the directory you're reading itself, or `+x` on its parents; the permissions of the files or directories within are irrelevant. – Charles Duffy Feb 24 '17 at 17:53
  • ...so, if you get an OSError from `os.listdir()`, then there's nothing to recover -- if you don't have `+r` on the directory, you have no access to read *any* of its contents. – Charles Duffy Feb 24 '17 at 17:54
  • 2
    With respect to the `os.path.isdir()` and `os.path.islink()`, btw, those both use only the syscall `stat(2)`; on MacOS, the only way that can cause `EACCES` is if a component of the path to the directory *holding* the file doesn't have `+x` -- so if you had `+r` but not `+x` on the directory, then the `listdir()` would succeed, but the `os.path.isdir()` on its contents would fail. – Charles Duffy Feb 24 '17 at 17:57

4 Answers4

2

The assumption made in this question -- that a non-readable entry partway through a directory can cause os.listdir() to fail, and that a partial result consisting of other entries is possible -- is false.

Observe:

>>> import os
>>> os.mkdir('unreadable.d')
>>> os.chmod('unreadable.d', 0)
>>> result = os.listdir('.')
>>> print result
['unreadable.d']

It's only trying to run listdir() on the unreadable directory itself that fails:

>>> os.listdir('unreadable.d')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 13] Permission denied: 'unreadable.d'
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 1
    So it's safe to say that if I ever encounter this error I can handle it by doing something as simple as returning an empty list – Ian Panzica Feb 24 '17 at 18:04
  • Thanks! I was only looking here [link](https://docs.python.org/2/library/os.html) and didn't think to look at how it really handles the permissions – Ian Panzica Feb 24 '17 at 18:07
  • Out of curiosity, what's stopping you from catching the exception and handling it there (e.g. by doing `return []` or `continue`)? – Jules Feb 24 '17 at 18:14
0

Walk the directory, and check if you have access first:

for (dirpath, dirnames, filenames) in os.walk('/path/to/folder'):
    for dir in dirnames:
        path = os.path.join(dirpath, dir)
        read_write = os.access(path, os.W_OK) and os.access(path, os.R_OK)
        # W_OK write True and R_OK read True
        if not read_write:
            continue

See: Determining Whether a Directory is Writeable

Community
  • 1
  • 1
jmunsch
  • 22,771
  • 11
  • 93
  • 114
  • 2
    `os.walk()` is using the same syscall `os.listdir()` does -- though it will just not return anything it can't read silently rather than throwing an exception. – Charles Duffy Feb 24 '17 at 18:00
0

You have to change the ownership of the files:

$ sudo chown -R <userwhorunpython>:<userwhorunpython> <yourdirectory> 

If it doesn't work, run a:

$ sudo chmod -r 777 directory

Check the chmod and chown manual for more information.

Unheilig
  • 16,196
  • 193
  • 68
  • 98
Sanhaji Omar
  • 192
  • 1
  • 8
0

You can use the PermissionError exception

try:
    os.listdir('path/to/folder')
except PermissionError:
    # do something else
Al Mahdi
  • 635
  • 1
  • 9
  • 16