Определение того, является ли каталог доступным для записи

Каким будет лучший способ в Python определить, является ли каталог доступным для пользователя, выполняющего script? Поскольку это, скорее всего, связано с использованием модуля os, я должен упомянуть, что я запускаю его в среде * nix.

Ответ 1

Хотя то, что предложил Кристоф, является более Pythonic решением, модуль os имеет функцию os.access для проверки доступа:

os.access('/path/to/folder', os.W_OK) # W_OK для записи, R_OK для чтения и т.д.

Ответ 2

Может показаться странным предположить это, но общая идиома Python -

Проще просить прощения чем для разрешения

Следуя этой идиоме, можно сказать:

Попробуйте записать в соответствующий каталог и поймайте ошибку, если у вас нет разрешения на это.

Ответ 3

Мое решение с использованием модуля tempfile:

    import tempfile
    import errno

    def isWritable(path):
        try:
            testfile = tempfile.TemporaryFile(dir = path)
            testfile.close()
        except OSError as e:
            if e.errno == errno.EACCES:  # 13
                return False
            e.filename = path
            raise
        return True

Обновление: после повторного тестирования кода в Windows я вижу, что действительно существует проблема при использовании там временного файла, см. Проблему 22107: модуль временного файла неверно интерпретирует ошибку "отказ в доступе" в Windows. В случае IOError: [Errno 17] No usable temporary file name found для записи директории код зависает на несколько секунд и, наконец, выдает IOError: [Errno 17] No usable temporary file name found. Может быть, это то, что наблюдал пользователь 2171842? К сожалению, проблема пока не решена, поэтому для решения этой проблемы также необходимо отследить ошибку:

    except (OSError, IOError) as e:
        if e.errno == errno.EACCES or e.errno == errno.EEXIST:  # 13, 17

Задержка, конечно, все еще присутствует в этих случаях.

Ответ 4

Наткнулся на этот поток, ища примеры для кого-то. Первый результат в Google, поздравляю!

Люди говорят о питоническом способе делать это в этой теме, но нет простых примеров кода? Здесь вы идете, для всех, кто спотыкается:

import sys

filepath = 'C:\\path\\to\\your\\file.txt'

try:
    filehandle = open( filepath, 'w' )
except IOError:
    sys.exit( 'Unable to write to file ' + filepath )

filehandle.write("I am writing this text to the file\n")

Это попытка открыть дескриптор файла для записи и выйти с ошибкой, если указанный файл не может быть записан: Это намного проще читать, и это гораздо лучший способ сделать это, а не делать предварительные проверки на пути к файлу или каталог, поскольку он позволяет избежать условий гонки; случаи, когда файл становится невосприимчивым между моментом запуска предварительной проверки и когда вы пытаетесь записать файл.

Ответ 5

Если вы только заботитесь о файлах perms, os.access(path, os.W_OK) должен делать то, о чем вы просите. Если вы хотите узнать, можете ли вы умеете записывать в каталог, open() тестовый файл для записи (он не должен существовать заранее), поймайте и изучите любой IOError, и очистите затем файл теста.

В более общем плане, чтобы избежать TOCTOU атак (только проблема, если ваш script работает с повышенными привилегиями - suid или cgi или так что), вы не должны действительно доверять этим проверкам времени, но откажитесь от утилов, сделайте open() и ожидайте IOError.

Ответ 6

Проверьте бит режима:

def isWritable(name):
  uid = os.geteuid()
  gid = os.getegid()
  s = os.stat(dirname)
  mode = s[stat.ST_MODE]
  return (
     ((s[stat.ST_UID] == uid) and (mode & stat.S_IWUSR)) or
     ((s[stat.ST_GID] == gid) and (mode & stat.S_IWGRP)) or
     (mode & stat.S_IWOTH)
     )

Ответ 7

Вот что я создал на основе ответа ChristopheD:

import os

def isWritable(directory):
    try:
        tmp_prefix = "write_tester";
        count = 0
        filename = os.path.join(directory, tmp_prefix)
        while(os.path.exists(filename)):
            filename = "{}.{}".format(os.path.join(directory, tmp_prefix),count)
            count = count + 1
        f = open(filename,"w")
        f.close()
        os.remove(filename)
        return True
    except Exception as e:
        #print "{}".format(e)
        return False

directory = "c:\\"
if (isWritable(directory)):
    print "directory is writable"
else:
    print "directory is not writable"

Ответ 8

 if os.access(path_to_folder, os.W_OK) is not True:
            print("Folder not writable")
 else :
            print("Folder writable")

больше информации о доступе можно найти здесь

Ответ 9

Я столкнулся с этой же необходимостью при добавлении аргумента через argparse. Встроенный type=FileType('w') не будет работать для меня, так как я искал каталог. Я закончил писать свой собственный метод, чтобы решить мою проблему. Вот результат с фрагментом argparse.

#! /usr/bin/env python
import os
import argparse

def writable_dir(dir):
    if os.access(dir, os.W_OK) and os.path.isdir(dir):
        return os.path.abspath(dir)
    else:
        raise argparse.ArgumentTypeError(dir + " is not writable or does not exist.")

parser = argparse.ArgumentParser()
parser.add_argument("-d","--dir", type=writable_dir(), default='/tmp/',
    help="Directory to use. Default: /tmp")
opts = parser.parse_args()

Это приводит к следующему:

$ python dir-test.py -h
usage: dir-test.py [-h] [-d DIR]

optional arguments:
  -h, --help         show this help message and exit
  -d DIR, --dir DIR  Directory to use. Default: /tmp

$ python dir-test.py -d /not/real
usage: dir-test.py [-h] [-d DIR]
dir-test.py: error: argument -d/--dir: /not/real is not writable or does not exist.

$ python dir-test.py -d ~

Я вернулся и добавил print opts.dir до конца, и все, кажется, функционирует так, как нужно.

Ответ 10

Если вам нужно проверить разрешение другого пользователя (да, я понимаю, что это противоречит вопросу, но может пригодиться кому-то), вы можете сделать это через модуль pwd и биты режима каталога.

Отказ от ответственности - не работает в Windows, так как он не использует модель разрешений POSIX (а модуль pwd там недоступен), например. - решение только для систем * nix.

Обратите внимание, что в каталоге должны быть установлены все 3 бита - Чтение, Запись и eXecute.
Хорошо, R не является абсолютным обязательным, но без него вы не можете перечислить записи в каталоге (чтобы вы знали их имена). Выполнение с другой стороны абсолютно необходимо - без него пользователь не может прочитать файл inodes; поэтому даже наличие W без X файлов не может быть создано или изменено. Более подробное объяснение по этой ссылке.

Наконец, режимы доступны в модуле stat, их описания находятся в файле inode (7).

Пример кода, как проверить:

import pwd
import stat
import os

def check_user_dir(user, directory):
    dir_stat = os.stat(directory)

    user_id, group_id = pwd.getpwnam(user).pw_uid, pwd.getpwnam(user).pw_gid
    directory_mode = dir_stat[stat.ST_MODE]

    # use directory_mode as mask 
    if user_id == dir_stat[stat.ST_UID] and stat.S_IRWXU & directory_mode == stat.S_IRWXU:     # owner and has RWX
        return True
    elif group_id == dir_stat[stat.ST_GID] and stat.S_IRWXG & directory_mode == stat.S_IRWXG:  # in group & it has RWX
        return True
    elif stat.S_IRWXO & directory_mode == stat.S_IRWXO:                                        # everyone has RWX
        return True

    # no permissions
    return False