Определение, доступен ли для записи каталог - PullRequest
90 голосов
/ 22 января 2010

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

Ответы [ 10 ]

155 голосов
/ 22 января 2010

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

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

64 голосов
/ 22 января 2010

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

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

После этой идиомы можно сказать:

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

16 голосов
/ 16 сентября 2014

Мое решение с использованием модуля 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 я вижу, что действительно существует проблема при использовании там временного файла, см. issue22107: модуль tempfile неправильно интерпретирует ошибку «отказ в доступе» в Windows . В случае недоступной для записи директории код зависает на несколько секунд и, наконец, выдает 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

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

10 голосов
/ 23 июня 2012

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

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

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")

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

9 голосов
/ 22 января 2010

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

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

7 голосов
/ 22 января 2010

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

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)
     )
4 голосов
/ 24 декабря 2011

Вот что я создал, основываясь на ответе Кристофа:

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"
3 голосов
/ 16 мая 2017
 if os.access(path_to_folder, os.W_OK) is not True:
            print("Folder not writable")
 else :
            print("Folder writable")

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

1 голос
/ 06 октября 2018

Я столкнулся с той же самой потребностью при добавлении аргумента через 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 до конца, и все, кажется, функционирует как нужно.

0 голосов
/ 14 октября 2017

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

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

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

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

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

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
...