Qual seria a melhor maneira em Python de determinar se um diretório é gravável para o usuário que está executando o script? Como isso provavelmente envolverá o uso do módulo os, devo mencionar que estou executando-o em um ambiente * nix.
Qual seria a melhor maneira em Python de determinar se um diretório é gravável para o usuário que está executando o script? Como isso provavelmente envolverá o uso do módulo os, devo mencionar que estou executando-o em um ambiente * nix.
Respostas:
Embora o que Christophe sugeriu seja uma solução mais Pythônica, o módulo os tem a função os.access para verificar o acesso:
os.access('/path/to/folder', os.W_OK)
# W_OK é para escrita, R_OK para leitura, etc.
os.access()
é verificar usando o UID e GID reais , não os efetivos . Isso pode causar estranheza em ambientes SUID / SGID. ('mas o script executa setuid root, por que não consegue gravar no arquivo?')
os.access(dirpath, os.W_OK | os.X_OK)
retorna True, mesmo se eu não tiver acesso de gravação.
Pode parecer estranho sugerir isso, mas um idioma comum do Python é
É mais fácil pedir perdão do que permissão
Seguindo esse idioma, pode-se dizer:
Tente escrever no diretório em questão e detecte o erro se não tiver permissão para fazer isso.
except: pass
- desta forma você pode sempre ser otimista e ter uma opinião elevada. / sarcasmo desligado. Agora, por que eu iria querer, por exemplo, tentar escrever algo em todos os diretórios do meu sistema de arquivos, para produzir uma lista de locais graváveis?
Minha solução usando o tempfile
módulo:
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
Atualização: Depois de testar o código novamente no Windows, vejo que realmente há um problema ao usar o arquivo temporário, consulte o problema 22107: o módulo do arquivo temporário interpreta erroneamente o erro de acesso negado no Windows . No caso de um diretório não gravável, o código trava por vários segundos e, finalmente, lança um IOError: [Errno 17] No usable temporary file name found
. Talvez seja isso o que user2171842 estava observando? Infelizmente, o problema não foi resolvido por agora, então, para lidar com isso, o erro também precisa ser detectado:
except (OSError, IOError) as e:
if e.errno == errno.EACCES or e.errno == errno.EEXIST: # 13, 17
O atraso, é claro, ainda está presente nesses casos.
tempfile
. ele só funciona quando não há nenhum OSError
significado que ele tenha permissão para gravar / excluir. caso contrário, isso não acontecerá return False
porque nenhum erro será retornado e o script não continuará sendo executado ou encerrado. nada é devolvido. está apenas preso nessa linha. entretanto, criar um arquivo não temporário, como a resposta de khattam, funciona tanto quando a permissão é permitida ou negada. Socorro?
Tropecei neste tópico procurando exemplos para alguém. Primeiro resultado no Google, parabéns!
As pessoas falam sobre a maneira Pythônica de fazer isso neste segmento, mas nenhum exemplo de código simples? Aqui está, para qualquer pessoa que tropeçar em:
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")
Isso tenta abrir um identificador de arquivo para gravação e sai com um erro se o arquivo especificado não puder ser gravado: Isso é muito mais fácil de ler e é uma maneira muito melhor de fazer isso do que fazer verificações prévias no caminho do arquivo ou no diretório , pois evita condições de corrida; casos em que o arquivo se torna não gravável entre o momento em que você executa a pré-verificação e quando você realmente tenta gravar no arquivo.
Se você só se preocupa com as permissões do arquivo, os.access(path, os.W_OK)
faça o que você pedir. Se, em vez disso, você quiser saber se pode gravar no diretório, open()
um arquivo de teste para gravação (ele não deveria existir antes), pegue e examine qualquer um IOError
e limpe o arquivo de teste depois.
De forma mais geral, para evitar ataques TOCTOU (apenas um problema se o seu script for executado com privilégios elevados - suid ou cgi ou algo assim), você não deve realmente confiar nesses testes antecipados, mas descarte privs, faça o open()
e espere a IOError
.
Verifique os bits de modo:
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)
)
Aqui está algo que criei com base na resposta 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"
if os.access(path_to_folder, os.W_OK) is not True:
print("Folder not writable")
else :
print("Folder writable")
mais informações sobre o acesso podem ser encontradas aqui
Corri para essa mesma necessidade ao adicionar um argumento via argparse. O integrado type=FileType('w')
não funcionaria para mim porque eu estava procurando um diretório. Acabei escrevendo meu próprio método para resolver meu problema. Aqui está o resultado com o snippet 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()
Isso resulta no seguinte:
$ 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 ~
Voltei e adicionei print opts.dir ao final, e tudo parece estar funcionando como desejado.
Se precisar verificar a permissão de outro usuário (sim, eu percebo que isso contradiz a pergunta, mas pode vir a calhar para alguém), você pode fazer isso através do pwd
módulo e dos bits de modo do diretório.
Aviso - não funciona no Windows, pois não usa o modelo de permissões POSIX (e o pwd
módulo não está disponível lá), por exemplo - solução apenas para sistemas * nix.
Observe que um diretório deve ter todos os 3 bits definidos - Leitura, Gravação e Execução.
Ok, R não é uma necessidade absoluta, mas sem ele você não pode listar as entradas no diretório (então você deve saber seus nomes). A execução, por outro lado, é absolutamente necessária - sem ela, o usuário não pode ler os inodes do arquivo; portanto, mesmo tendo W, sem X os arquivos não podem ser criados ou modificados. Explicação mais detalhada neste link.
Por fim, os modos estão disponíveis no stat
módulo, suas descrições estão no inode (7) man .
Código de amostra como verificar:
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