Déterminer si un répertoire est accessible en écriture

101

Quel serait le meilleur moyen en Python de déterminer si un répertoire est accessible en écriture pour l'utilisateur exécutant le script? Comme cela impliquera probablement l'utilisation du module os, je dois mentionner que je l'exécute sous un environnement * nix.

tigre illuminé
la source

Réponses:

185

Bien que ce que Christophe suggère soit une solution plus pythonique, le module os dispose de la fonction os.access pour vérifier l'accès:

os.access('/path/to/folder', os.W_OK) # W_OK est pour écrire, R_OK pour lire, etc.

Max Shawabkeh
la source
4
Selon la situation, le "plus facile de demander pardon" n'est pas le meilleur moyen, même en Python. Il est parfois conseillé de "demander la permission" comme avec la méthode os.access () mentionnée, par exemple lorsque la probabilité d'avoir à intercepter une erreur est élevée.
mjv
53
Tester un répertoire uniquement pour le bit d'écriture ne suffit pas si vous souhaitez écrire des fichiers dans le répertoire. Vous devrez également tester le bit d'exécution si vous souhaitez écrire dans le répertoire. os.access ('/ chemin / vers / dossier', os.W_OK | os.X_OK) Avec os.W_OK seul, vous ne pouvez supprimer le répertoire (et seulement si ce répertoire est vide)
fthinker
4
Un autre problème os.access()est qu'il vérifie en utilisant les vrais UID et GID, pas les effectifs . Cela pourrait provoquer des bizarreries dans les environnements SUID / SGID. ('mais le script exécute setuid root, pourquoi ne peut-il pas écrire dans le fichier?')
Alexios
5
Peut-être qu'un programme veut juste savoir sans avoir besoin d'écrire. Il peut simplement vouloir changer l'apparence et / ou le comportement d'une interface graphique en fonction de la propriété. Dans ce cas, je ne considérerais pas comme pythonique d'écrire et de supprimer un fichier simplement comme un test.
Bachsau
1
Juste testé sur un partage réseau Windows. os.access(dirpath, os.W_OK | os.X_OK)renvoie True même si je n'ai pas d'accès en écriture.
iamanigeeit
69

Cela peut sembler étrange à suggérer, mais un idiome Python commun est

Il est plus facile de demander pardon que de permission

Suivant cet idiome, on pourrait dire:

Essayez d'écrire dans le répertoire en question et détectez l'erreur si vous n'avez pas l'autorisation de le faire.

ChristopheD
la source
5
+1 Python ou pas, c'est vraiment le moyen le plus fiable de tester l'accès.
John Knoeller
5
Cela prend également en charge d'autres erreurs qui peuvent survenir lors de l'écriture sur le disque - aucun espace disque restant par exemple. C'est le pouvoir d'essayer ... vous n'avez pas besoin de vous souvenir de tout ce qui peut mal tourner ;-)
Jochen Ritzel
4
Merci les gars. J'ai décidé d'aller avec os.access car la vitesse est un facteur important dans ce que je fais ici même si je peux certainement comprendre les mérites de "il est plus facile de demander pardon que pour la permission." ;)
illuminatedtiger
4
C'est un excellent IDIO ... m - surtout lorsqu'il est associé à un autre idiome except: pass- de cette façon, vous pouvez toujours être optimiste et avoir une bonne opinion de vous-même. / sarcasme éteint. Maintenant, pourquoi voudrais-je, par exemple essayer d'écrire quelque chose dans chaque répertoire de mon système de fichiers, pour produire une liste d'emplacements inscriptibles?
Tomasz Gandor
4
Peut-être qu'un programme veut juste savoir sans avoir besoin d'écrire. Il peut simplement vouloir changer l'apparence et / ou le comportement d'une interface graphique en fonction de la propriété. Dans ce cas, je ne considérerais pas comme pythonique d'écrire et de supprimer un fichier simplement comme un test.
Bachsau
19

Ma solution utilisant le tempfilemodule:

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

Mise à jour: Après avoir testé à nouveau le code sur Windows, je vois qu'il y a effectivement un problème lors de l'utilisation de tempfile là-bas, voir issue22107: le module tempfile interprète mal une erreur d'accès refusé sur Windows . Dans le cas d'un répertoire non accessible en écriture, le code se bloque pendant plusieurs secondes et lance finalement un fichier IOError: [Errno 17] No usable temporary file name found. C'est peut-être ce qu'observait user2171842? Malheureusement, le problème n'est pas résolu pour l'instant, donc pour gérer cela, l'erreur doit également être détectée:

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

Le retard est bien sûr toujours présent dans ces cas alors.

zak
la source
1
Je pense que celui qui utilise tempfile est le plus propre car il ne laisse pas de résidus.
grasshopper
3
cette méthode ne fonctionne pas en utilisant tempfile. il ne fonctionne que lorsqu'il n'y a aucune OSErrorsignification qu'il a l'autorisation d'écrire / supprimer. sinon, ce ne sera pas le cas return Falsecar aucune erreur n'est renvoyée et le script ne continuera pas à s'exécuter ou à se terminer. rien n'est retourné. c'est juste coincé à cette ligne. cependant, la création d'un fichier non temporaire tel que la réponse de khattam fonctionne à la fois lorsque l'autorisation est autorisée ou refusée. Aidez-moi?
10

Je suis tombé sur ce fil à la recherche d'exemples pour quelqu'un. Premier résultat sur Google, bravo!

Les gens parlent de la manière pythonique de le faire dans ce fil, mais pas d'exemples de code simples? Ici vous allez, pour toute autre personne qui trébuche:

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

Cela tente d'ouvrir un descripteur de fichier pour l'écriture et se termine avec une erreur si le fichier spécifié ne peut pas être écrit: C'est beaucoup plus facile à lire et c'est une bien meilleure façon de le faire plutôt que de faire des pré-vérifications sur le chemin du fichier ou le répertoire , car il évite les conditions de course; les cas où le fichier devient inscriptible entre le moment où vous exécutez la pré-vérification et le moment où vous essayez réellement d'écrire dans le fichier.

Rohaq
la source
1
Cela s'applique à un fichier, pas à un répertoire, ce que l'OP a demandé. Vous pouvez avoir un fichier dans un répertoire et que le répertoire ne soit pas accessible en écriture, mais le fichier lui-même l'est, si le fichier existe déjà. Cela peut être important dans l'administration système où vous créez par exemple des fichiers journaux que vous voulez déjà exister mais que vous ne voulez pas que les gens utilisent un répertoire de journaux pour l'espace temporaire.
Mike S
... et en fait je l'ai rejetée, ce qui, je pense maintenant, était une erreur. Il y a des problèmes, comme Rohaq l'a mentionné, avec les conditions de course. Il y a d'autres problèmes sur différentes plates-formes où vous pouvez tester le répertoire, et il semble inscriptible, mais ce n'est pas le cas. Effectuer des vérifications d'écriture dans les répertoires multiplateformes est plus difficile qu'il n'y paraît. Donc, tant que vous êtes conscient des problèmes, cela peut être une bonne technique. Je le regardais d'un point de vue trop UNIX-y, ce qui est mon erreur. Quelqu'un a modifié cette réponse pour que je puisse supprimer mon -1.
Mike S
Je l'ai édité, au cas où vous voudriez supprimer le -1 :) Et oui, les vérifications de répertoires multiplateformes peuvent devenir plus compliquées, mais généralement vous cherchez à créer / écrire dans un fichier dans ce répertoire - auquel cas l'exemple que j'ai donné devrait toujours s'appliquer. Si un problème lié à l'autorisation de répertoire survient, il doit toujours générer une erreur IOError lors de la tentative d'ouverture du descripteur de fichier.
Rohaq
J'ai supprimé mon vote défavorable. Désolé, et merci pour votre contribution.
Mike S
Pas de soucis, les personnes qui demandent des réponses sont toujours les bienvenues!
Rohaq
9

Si vous ne vous souciez que des fichiers permanents, os.access(path, os.W_OK)faites ce que vous demandez. Si vous voulez plutôt savoir si vous pouvez écrire dans le répertoire, open()un fichier de test pour l'écriture (il ne devrait pas exister auparavant), attraper et examiner tout IOError, et nettoyer le fichier de test par la suite.

Plus généralement, pour éviter les attaques TOCTOU (seulement un problème si votre script s'exécute avec des privilèges élevés - suid ou cgi ou plus), vous ne devriez pas vraiment faire confiance à ces tests à l'avance, mais abandonner privs, faire le open(), et attendre le IOError.

sverkerw
la source
7

Vérifiez les bits de mode:

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)
     )
Joe Koberg
la source
4
Cette solution est Unix uniquement.
Björn Lindqvist du
4

Voici quelque chose que j'ai créé sur la base de la réponse de 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"
khattam
la source
3
 if os.access(path_to_folder, os.W_OK) is not True:
            print("Folder not writable")
 else :
            print("Folder writable")

plus d'informations sur l'accès peuvent être trouvées ici

Softmixt
la source
2
Il s'agit essentiellement d'une copie de la réponse de Max Shawabkeh avec une petite enveloppe autour d'elle. En fait un copier-coller rapide, mais une meilleure idée serait de l'avoir ajouté au message original de Max.
Jorrick Sleijster
1

J'ai rencontré ce même besoin en ajoutant un argument via argparse. Le intégré type=FileType('w')ne fonctionnait pas pour moi car je cherchais un répertoire. J'ai fini par écrire ma propre méthode pour résoudre mon problème. Voici le résultat avec l'extrait 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()

Cela se traduit par ce qui suit:

$ 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 ~

Je suis retourné et j'ai ajouté print opts.dir à la fin, et tout semble fonctionner comme souhaité.

796m9XfYTkmp
la source
0

Si vous avez besoin de vérifier la permission d' un autre utilisateur (oui, je réalise que cela contredit la question, mais peut être utile pour quelqu'un), vous pouvez le faire via le pwdmodule et les bits de mode du répertoire.

Clause de non- responsabilité - ne fonctionne pas sous Windows, car il n'utilise pas le modèle d'autorisations POSIX (et le pwdmodule n'y est pas disponible), par exemple - solution uniquement pour les systèmes * nix.

Notez qu'un répertoire doit avoir tous les 3 bits définis: lecture, écriture et exécution.
Ok, R n'est pas un must absolu, mais sans lui, vous ne pouvez pas lister les entrées dans le répertoire (vous devez donc connaître leurs noms). Exécuter en revanche est absolument nécessaire - sans cela, l'utilisateur ne peut pas lire les inodes du fichier; donc même avoir W, sans X, les fichiers ne peuvent pas être créés ou modifiés. Explication plus détaillée sur ce lien.

Enfin, les modes sont disponibles dans le statmodule, leurs descriptions sont en inode (7) man .

Exemple de code comment vérifier:

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
Todor Minakov
la source