Verifique se um caminho é válido em Python sem criar um arquivo no destino do caminho

100

Eu tenho um caminho (incluindo o diretório e o nome do arquivo).
Preciso testar se o nome do arquivo é válido, por exemplo, se o sistema de arquivos me permite criar um arquivo com esse nome.
O nome do arquivo contém alguns caracteres Unicode .

É seguro presumir que o segmento de diretório do caminho é válido e acessível ( eu estava tentando tornar a pergunta mais aplicável e, aparentemente, fui longe demais ).

Não quero muito escapar de nada, a menos que seja necessário .

Eu postaria alguns dos personagens de exemplo com os quais estou lidando, mas aparentemente eles são removidos automaticamente pelo sistema de troca de pilha. De qualquer forma, eu quero manter as entidades Unicode padrão como ö, e apenas escapar coisas que são inválidas em um nome de arquivo.


Aqui está o problema. Pode (ou não) já haver um arquivo no destino do caminho. Preciso manter esse arquivo, se ele existir, e não criar um arquivo, se não existir.

Basicamente eu quero verificar se eu poderia escrever para um caminho sem realmente abrir o caminho para escrever (e a criação do arquivo / arquivo automático clobbering que normalmente implica).

Assim sendo:

try:
    open(filename, 'w')
except OSError:
    # handle error here

daqui

Não é aceitável, porque sobrescreverá o arquivo existente, que não quero mexer (se estiver lá), ou criará o referido arquivo se não estiver.

Eu sei que posso fazer:

if not os.access(filePath, os.W_OK):
    try:
        open(filePath, 'w').close()
        os.unlink(filePath)
    except OSError:
        # handle error here

Mas isso criará o arquivo no filePath, o que eu teria que fazer os.unlink.

No final das contas, parece que está gastando 6 ou 7 linhas para fazer algo que deveria ser tão simples quanto os.isvalidpath(filePath)ou similar.


Como um aparte, eu preciso que isso seja executado em (pelo menos) Windows e MacOS, então eu gostaria de evitar coisas específicas da plataforma.

``

Nome falso
fonte
Se você deseja testar se o caminho existe e pode escrever nele, basta criar e excluir algum outro arquivo. Dê a ele um nome exclusivo (ou o mais exclusivo possível), para evitar problemas de multiusuário / multiencadeamento. Caso contrário, você está olhando para verificar as permissões que o levarão direto para a confusão específica do sistema operacional.
Tony Hopkinson
3
@Tony Hopkinson - Basicamente eu quero verificar se eu poderia escrever para um caminho sem realmente escrever qualquer coisa .
Nome falso,
Se você não tem nada para gravar no arquivo, por que precisa saber se pode?
Karl Knechtel
2
@FakeName - Você sempre terá uma condição de corrida sutil aqui. Entre verificar se o arquivo não existe, mas pode ser criado e, em seguida, criar o arquivo, algum outro processo pode criá-lo e você destruirá o arquivo de qualquer maneira. Claro, depende do seu uso, se este é um problema realista ou não ...
aproximadamente
1
Em parte, você pode verificar com os.path.isabs(PATH), mas isso não abrange o caminho relativo :-(.
pevik

Respostas:

157

tl; dr

Chame a is_path_exists_or_creatable()função definida abaixo.

Estritamente Python 3. É assim que funcionamos.

Um conto de duas perguntas

A questão de "Como faço para testar a validade do nome do caminho e, para nomes de caminho válidos, a existência ou capacidade de escrita desses caminhos?" são claramente duas questões distintas. Ambos são interessantes, e nenhum recebeu uma resposta genuinamente satisfatória aqui ... ou, bem, em qualquer lugar que eu pudesse pesquisar.

a resposta de vikki provavelmente é a que mais chega, mas tem as desvantagens notáveis ​​de:

  • Abertura desnecessária ( ... e falha ao fechar de forma confiável ) identificadores de arquivo.
  • Gravar desnecessariamente ( ... e depois não fechar ou excluir de forma confiável ) arquivos de 0 bytes.
  • Ignorar erros específicos do SO, diferenciando entre nomes de caminho inválidos não ignoráveis ​​e problemas de sistema de arquivos ignoráveis. Sem surpresa, isso é crítico no Windows. ( Veja abaixo. )
  • Ignorar as condições de corrida resultantes de processos externos (re) movendo simultaneamente os diretórios pais do nome do caminho a ser testado. ( Veja abaixo. )
  • Ignorando tempos limite de conexão resultantes deste nome de caminho residindo em sistemas de arquivos obsoletos, lentos ou temporariamente inacessíveis. Isso poderia expor os serviços voltados ao público a possíveis ataques dirigidos por DoS . ( Veja abaixo. )

Vamos consertar tudo isso.

Pergunta # 0: O que é a validade do nome do caminho novamente?

Antes de jogar nossos frágeis trajes de carne nos moshpits crivados de dor, provavelmente deveríamos definir o que queremos dizer com "validade do nome do caminho". O que define a validade, exatamente?

Por "validade de nome de caminho", queremos dizer a correção sintática de um nome de caminho com respeito ao sistema de arquivos raiz do sistema atual - independentemente de esse caminho ou seus diretórios pais existirem fisicamente. Um nome de caminho está sintaticamente correto sob esta definição se estiver em conformidade com todos os requisitos sintáticos do sistema de arquivos raiz.

Por "sistema de arquivos raiz", queremos dizer:

  • Em sistemas compatíveis com POSIX, o sistema de arquivos é montado no diretório raiz ( /).
  • No Windows, o sistema de arquivos é montado na %HOMEDRIVE%letra da unidade com sufixo de dois pontos contendo a instalação atual do Windows (normalmente, mas não necessariamente C:).

O significado de "correção sintática", por sua vez, depende do tipo de sistema de arquivos raiz. Para ext4(e para a maioria, mas não todos os sistemas de arquivos compatíveis com POSIX), um nome de caminho está sintaticamente correto se e somente se esse nome de caminho:

  • Não contém bytes nulos (ou seja, \x00em Python). Este é um requisito difícil para todos os sistemas de arquivos compatíveis com POSIX.
  • Não contém componentes de caminho com mais de 255 bytes (por exemplo, 'a'*256em Python). Um componente do caminho é uma subsequência mais longo de um caminho não contendo qualquer /caracter (por exemplo, bergtatt, ind, i, e fjeldkamreneem nome do caminho /bergtatt/ind/i/fjeldkamrene).

Correção sintática. Sistema de arquivos raiz. É isso aí.

Pergunta no. 1: Como agora devemos fazer a validade do nome do caminho?

Validar nomes de caminho em Python é surpreendentemente não intuitivo. Estou totalmente de acordo com o Fake Name aqui: o os.pathpacote oficial deve fornecer uma solução pronta para o uso para isso. Por razões desconhecidas (e provavelmente incomuns), isso não acontece. Felizmente, desenrolando sua própria solução ad-hoc não é que arrasador ...

OK, na verdade é. É cabeludo; é desagradável; provavelmente gargalha enquanto borbulha e ri enquanto brilha. Mas o que você vai fazer? Nuthin '.

Em breve desceremos ao abismo radioativo do código de baixo nível. Mas primeiro, vamos falar de uma loja de alto nível. O padrão os.stat()e as os.lstat()funções levantam as seguintes exceções quando passam nomes de caminho inválidos:

  • Para nomes de caminhos que residem em diretórios não existentes, instâncias de FileNotFoundError.
  • Para nomes de caminhos que residem em diretórios existentes:
    • No Windows, as instâncias de WindowsErrorcujo winerroratributo é 123(ou seja, ERROR_INVALID_NAME).
    • Em todos os outros sistemas operacionais:
    • Para nomes de caminho contendo bytes nulos (ou seja, '\x00'), instâncias de TypeError.
    • Para nomes de caminho contendo componentes de caminho com mais de 255 bytes, instâncias de OSErrorcujo errcodeatributo é:
      • Sob SunOS e a família * BSD de sistemas operacionais errno.ERANGE,. (Isso parece ser um bug no nível do sistema operacional, também conhecido como "interpretação seletiva" do padrão POSIX.)
      • Em todos os outros sistemas operacionais errno.ENAMETOOLONG,.

Crucialmente, isso implica que apenas nomes de caminhos residentes em diretórios existentes são validáveis. As funções os.stat()e os.lstat()levantam FileNotFoundErrorexceções genéricas quando nomes de caminho passados ​​residem em diretórios não existentes, independentemente de esses nomes de caminho serem inválidos ou não. A existência do diretório tem precedência sobre a invalidade do nome do caminho.

Isso significa que os nomes de caminhos que residem em diretórios não existentes não são validáveis? Sim - a menos que modifiquemos esses nomes de caminho para residir em diretórios existentes. No entanto, isso é ao menos viável com segurança? A modificação de um nome de caminho não deveria nos impedir de validar o nome do caminho original?

Para responder a esta pergunta, lembre-se de que os nomes de caminho sintaticamente corretos no ext4sistema de arquivos não contêm componentes de caminho (A) contendo bytes nulos ou (B) com mais de 255 bytes de comprimento. Portanto, um ext4nome de caminho é válido se e somente se todos os componentes do caminho nesse nome de caminho são válidos. Isso é verdade para a maioria dos sistemas de arquivos de interesse do mundo real .

Esse insight pedante realmente nos ajuda? Sim. Isso reduz o problema maior de validar o nome do caminho completo de uma só vez para o problema menor de apenas validar todos os componentes do caminho naquele nome de caminho. Qualquer nome de caminho arbitrário é validável (independentemente de esse nome de caminho residir em um diretório existente ou não) em uma plataforma cruzada, seguindo o seguinte algoritmo:

  1. Divida o nome do caminho em componentes do caminho (por exemplo, o nome do caminho /troldskog/faren/vildna lista ['', 'troldskog', 'faren', 'vild']).
  2. Para cada um desses componentes:
    1. Junte-se ao nome do caminho de um diretório com existência garantida com aquele componente em um novo nome do caminho temporário (por exemplo, /troldskog).
    2. Passe esse caminho para os.stat()ou os.lstat(). Se esse nome de caminho e, portanto, esse componente for inválido, essa chamada certamente levantará uma exceção expondo o tipo de invalidade em vez de uma FileNotFoundErrorexceção genérica . Por quê? Porque esse nome de caminho reside em um diretório existente. (A lógica circular é circular.)

Existe um diretório garantido para existir? Sim, mas normalmente apenas um: o diretório superior do sistema de arquivos raiz (conforme definido acima).

Passar nomes de caminho que residam em qualquer outro diretório (e, portanto, não há garantia de existência) para os.stat()ou os.lstat()convida condições de corrida, mesmo se esse diretório tiver sido testado anteriormente. Por quê? Porque os processos externos não podem ser impedidos de remover simultaneamente aquele diretório depois que o teste foi executado, mas antes que o nome do caminho seja passado para os.stat()ou os.lstat(). Liberte os cães da loucura arrebatadora!

Também existe um benefício colateral substancial para a abordagem acima: segurança. (Não é que bom?) Especificamente:

Os aplicativos frontais que validam nomes de caminhos arbitrários de fontes não confiáveis ​​simplesmente passando esses nomes de caminho para os.stat()ou os.lstat()são suscetíveis a ataques de negação de serviço (DoS) e outras travessuras de chapéu preto. Usuários mal-intencionados podem tentar validar repetidamente os nomes de caminho que residem em sistemas de arquivos conhecidos por serem obsoletos ou lentos (por exemplo, compartilhamentos NFS Samba); Nesse caso, a estatística de nomes de caminho de entrada cegamente pode falhar com o tempo limite de conexão ou consumir mais tempo e recursos do que sua capacidade insuficiente para suportar o desemprego.

A abordagem acima evita isso validando apenas os componentes do caminho de um nome de caminho em relação ao diretório raiz do sistema de arquivos raiz. (Se mesmo isso estiver desatualizado, lento ou inacessível, você terá problemas maiores do que a validação de nome de caminho.)

Perdido? Ótimo. Vamos começar. (Python 3 assumido. Consulte "What Is Fragile Hope for 300, leycec ?")

import errno, os

# Sadly, Python fails to provide the following magic number for us.
ERROR_INVALID_NAME = 123
'''
Windows-specific error code indicating an invalid pathname.

See Also
----------
https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
    Official listing of all such codes.
'''

def is_pathname_valid(pathname: str) -> bool:
    '''
    `True` if the passed pathname is a valid pathname for the current OS;
    `False` otherwise.
    '''
    # If this pathname is either not a string or is but is empty, this pathname
    # is invalid.
    try:
        if not isinstance(pathname, str) or not pathname:
            return False

        # Strip this pathname's Windows-specific drive specifier (e.g., `C:\`)
        # if any. Since Windows prohibits path components from containing `:`
        # characters, failing to strip this `:`-suffixed prefix would
        # erroneously invalidate all valid absolute Windows pathnames.
        _, pathname = os.path.splitdrive(pathname)

        # Directory guaranteed to exist. If the current OS is Windows, this is
        # the drive to which Windows was installed (e.g., the "%HOMEDRIVE%"
        # environment variable); else, the typical root directory.
        root_dirname = os.environ.get('HOMEDRIVE', 'C:') \
            if sys.platform == 'win32' else os.path.sep
        assert os.path.isdir(root_dirname)   # ...Murphy and her ironclad Law

        # Append a path separator to this directory if needed.
        root_dirname = root_dirname.rstrip(os.path.sep) + os.path.sep

        # Test whether each path component split from this pathname is valid or
        # not, ignoring non-existent and non-readable path components.
        for pathname_part in pathname.split(os.path.sep):
            try:
                os.lstat(root_dirname + pathname_part)
            # If an OS-specific exception is raised, its error code
            # indicates whether this pathname is valid or not. Unless this
            # is the case, this exception implies an ignorable kernel or
            # filesystem complaint (e.g., path not found or inaccessible).
            #
            # Only the following exceptions indicate invalid pathnames:
            #
            # * Instances of the Windows-specific "WindowsError" class
            #   defining the "winerror" attribute whose value is
            #   "ERROR_INVALID_NAME". Under Windows, "winerror" is more
            #   fine-grained and hence useful than the generic "errno"
            #   attribute. When a too-long pathname is passed, for example,
            #   "errno" is "ENOENT" (i.e., no such file or directory) rather
            #   than "ENAMETOOLONG" (i.e., file name too long).
            # * Instances of the cross-platform "OSError" class defining the
            #   generic "errno" attribute whose value is either:
            #   * Under most POSIX-compatible OSes, "ENAMETOOLONG".
            #   * Under some edge-case OSes (e.g., SunOS, *BSD), "ERANGE".
            except OSError as exc:
                if hasattr(exc, 'winerror'):
                    if exc.winerror == ERROR_INVALID_NAME:
                        return False
                elif exc.errno in {errno.ENAMETOOLONG, errno.ERANGE}:
                    return False
    # If a "TypeError" exception was raised, it almost certainly has the
    # error message "embedded NUL character" indicating an invalid pathname.
    except TypeError as exc:
        return False
    # If no exception was raised, all path components and hence this
    # pathname itself are valid. (Praise be to the curmudgeonly python.)
    else:
        return True
    # If any other exception was raised, this is an unrelated fatal issue
    # (e.g., a bug). Permit this exception to unwind the call stack.
    #
    # Did we mention this should be shipped with Python already?

Feito. Não aperte os olhos para esse código. ( Morde. )

Pergunta # 2: Possivelmente existência de nome de caminho ou capacidade de criação inválida, hein?

Testar a existência ou capacidade de criação de nomes de caminhos possivelmente inválidos é, dada a solução acima, trivial. A pequena chave aqui é chamar a função definida anteriormente antes de testar o caminho aprovado:

def is_path_creatable(pathname: str) -> bool:
    '''
    `True` if the current user has sufficient permissions to create the passed
    pathname; `False` otherwise.
    '''
    # Parent directory of the passed path. If empty, we substitute the current
    # working directory (CWD) instead.
    dirname = os.path.dirname(pathname) or os.getcwd()
    return os.access(dirname, os.W_OK)

def is_path_exists_or_creatable(pathname: str) -> bool:
    '''
    `True` if the passed pathname is a valid pathname for the current OS _and_
    either currently exists or is hypothetically creatable; `False` otherwise.

    This function is guaranteed to _never_ raise exceptions.
    '''
    try:
        # To prevent "os" module calls from raising undesirable exceptions on
        # invalid pathnames, is_pathname_valid() is explicitly called first.
        return is_pathname_valid(pathname) and (
            os.path.exists(pathname) or is_path_creatable(pathname))
    # Report failure on non-fatal filesystem complaints (e.g., connection
    # timeouts, permissions issues) implying this path to be inaccessible. All
    # other exceptions are unrelated fatal issues and should not be caught here.
    except OSError:
        return False

Feito e feito. Exceto não exatamente.

Pergunta nº 3: Possibilidade de existência de nome de caminho inválido ou capacidade de gravação no Windows

Existe uma ressalva. Claro que sim.

Conforme admite a os.access()documentação oficial :

Nota: As operações de E / S podem falhar mesmo quando os.access()indica que elas teriam sucesso, particularmente para operações em sistemas de arquivos de rede que podem ter semântica de permissões além do modelo de bit de permissão POSIX usual.

Para surpresa de ninguém, o Windows é o suspeito de sempre aqui. Graças ao uso extensivo de Listas de Controle de Acesso (ACL) em sistemas de arquivos NTFS, o modelo simplista de bits de permissão POSIX mapeia mal para a realidade subjacente do Windows. Embora isso (indiscutivelmente) não seja culpa do Python, pode ser uma preocupação para aplicativos compatíveis com o Windows.

Se este for você, uma alternativa mais robusta é desejada. Se o caminho passado não existir, tentamos criar um arquivo temporário com garantia de exclusão imediata no diretório pai desse caminho - um teste de capacidade de criação mais portátil (se caro):

import os, tempfile

def is_path_sibling_creatable(pathname: str) -> bool:
    '''
    `True` if the current user has sufficient permissions to create **siblings**
    (i.e., arbitrary files in the parent directory) of the passed pathname;
    `False` otherwise.
    '''
    # Parent directory of the passed path. If empty, we substitute the current
    # working directory (CWD) instead.
    dirname = os.path.dirname(pathname) or os.getcwd()

    try:
        # For safety, explicitly close and hence delete this temporary file
        # immediately after creating it in the passed path's parent directory.
        with tempfile.TemporaryFile(dir=dirname): pass
        return True
    # While the exact type of exception raised by the above function depends on
    # the current version of the Python interpreter, all such types subclass the
    # following exception superclass.
    except EnvironmentError:
        return False

def is_path_exists_or_creatable_portable(pathname: str) -> bool:
    '''
    `True` if the passed pathname is a valid pathname on the current OS _and_
    either currently exists or is hypothetically creatable in a cross-platform
    manner optimized for POSIX-unfriendly filesystems; `False` otherwise.

    This function is guaranteed to _never_ raise exceptions.
    '''
    try:
        # To prevent "os" module calls from raising undesirable exceptions on
        # invalid pathnames, is_pathname_valid() is explicitly called first.
        return is_pathname_valid(pathname) and (
            os.path.exists(pathname) or is_path_sibling_creatable(pathname))
    # Report failure on non-fatal filesystem complaints (e.g., connection
    # timeouts, permissions issues) implying this path to be inaccessible. All
    # other exceptions are unrelated fatal issues and should not be caught here.
    except OSError:
        return False

Observe, entretanto, que mesmo isso pode não ser suficiente.

Graças ao User Access Control (UAC), o sempre inimitável Windows Vista e todas as suas iterações subsequentes mentem descaradamente sobre as permissões relativas aos diretórios do sistema. Quando usuários não administradores tentam criar arquivos em diretórios C:\Windowsou canônicos C:\Windows\system32, o UAC permite superficialmente que o usuário faça isso enquanto, na verdade, isola todos os arquivos criados em uma "Loja Virtual" no perfil do usuário. (Quem poderia imaginar que enganar os usuários teria consequências prejudiciais a longo prazo?)

Isso é loucura. Este é o Windows.

Prove

Ousamos? É hora de testar os testes acima.

Visto que NULL é o único caractere proibido em nomes de caminho em sistemas de arquivos orientados para UNIX, vamos aproveitar isso para demonstrar a verdade nua e crua - ignorando travessuras não ignoráveis ​​do Windows, que francamente me aborrecem e irritam em igual medida:

>>> print('"foo.bar" valid? ' + str(is_pathname_valid('foo.bar')))
"foo.bar" valid? True
>>> print('Null byte valid? ' + str(is_pathname_valid('\x00')))
Null byte valid? False
>>> print('Long path valid? ' + str(is_pathname_valid('a' * 256)))
Long path valid? False
>>> print('"/dev" exists or creatable? ' + str(is_path_exists_or_creatable('/dev')))
"/dev" exists or creatable? True
>>> print('"/dev/foo.bar" exists or creatable? ' + str(is_path_exists_or_creatable('/dev/foo.bar')))
"/dev/foo.bar" exists or creatable? False
>>> print('Null byte exists or creatable? ' + str(is_path_exists_or_creatable('\x00')))
Null byte exists or creatable? False

Além da sanidade. Além da dor. Você encontrará questões de portabilidade do Python.

Cecil Curry
fonte
3
Sim, fui eu! Tentar juntar um regex de validação de nome de caminho cross-portable é um exercício de futilidade e falha garantida em casos extremos comuns. Considere o comprimento do nome do caminho no Windows, por exemplo: "O caminho máximo de 32.767 caracteres é aproximado, porque o prefixo '\\? \' Pode ser expandido para uma sequência mais longa pelo sistema em tempo de execução e esta expansão se aplica ao comprimento total . " Dado isso, é tecnicamente inviável construir uma regex que corresponda apenas a nomes de caminho válidos. É muito mais razoável simplesmente ceder ao Python.
Cecil Curry
2
Ah. Eu (relutantemente) vejo. Você está fazendo algo ainda mais estranho do que hackear um regex. Sim, isso com certeza falhará ainda mais. Isso também falha completamente em resolver a questão em questão, que não é "Como faço para retirar substrings inválidas de um nome de base específico do Windows?" (... que, por sua própria omissão, você não consegue resolver - novamente devido a casos extremos), mas "Como faço para testar a validade do nome do caminho de maneira cruzada e, para nomes de caminho válidos, a existência ou capacidade de escrita desses caminhos?"
Cecil Curry
1
Restrições específicas do sistema de arquivos são definitivamente uma preocupação válida - mas pode ser usada em ambos os sentidos. Para aplicativos frontais que consomem nomes de caminhos arbitrários de fontes não confiáveis, executar leituras cegamente é uma proposição arriscada, na melhor das hipóteses; neste caso, forçar o uso do sistema de arquivos raiz não é apenas sensato, mas também prudente. Para outros aplicativos, no entanto, a base de usuários pode ser confiável o suficiente para conceder acesso desinibido ao sistema de arquivos. É bastante dependente do contexto, eu diria. Obrigado por notar isso astutamente, Ninguém ! Vou adicionar uma advertência acima.
Cecil Curry
2
Quanto à nomenclatura, sou um fã pedante de prefixar nomes de testadores por is_. Esta é a minha falha de caráter. No entanto, devidamente observado: você não pode agradar a todos, e às vezes você não pode agradar a ninguém. ;)
Cecil Curry
1
No Fedora 24, python 3.5.3, um nome de caminho com caracteres nulos embutidos lança: ValueError: byte nulo embutido… precisa adicionar: `` `exceto ValueError como exc: return False` `` antes ou depois da armadilha TypeError.
mMerlin
48
if os.path.exists(filePath):
    #the file is there
elif os.access(os.path.dirname(filePath), os.W_OK):
    #the file does not exists but write privileges are given
else:
    #can not write there

Observe que path.existspode falhar por mais motivos do que apenas the file is not thereentão você pode ter que fazer testes mais refinados, como testar se o diretório que o contém existe e assim por diante.


Após minha discussão com o OP, descobri que o principal problema parece ser que o nome do arquivo pode conter caracteres que não são permitidos pelo sistema de arquivos. É claro que eles precisam ser removidos, mas o OP deseja manter o máximo de legibilidade humana que o sistema de arquivos permitir.

Infelizmente, não conheço nenhuma boa solução para isso. No entanto, a resposta de Cecil Curry examina mais de perto a detecção do problema.

Ninguém se afastando de SE
fonte
Não. Preciso retornar verdadeiro se o arquivo no caminho existe ou pode ser criado . Preciso retornar false se o caminho for inválido (devido a conter caracteres inválidos no windows).
Nome falso,
or can be createdbem, eu não li isso da sua pergunta. A leitura das permissões dependerá da plataforma até certo ponto.
Ninguém está se afastando de SE
1
@Fake Name: Sim, removerá algumas das dependências da plataforma, mas ainda assim algumas plataformas oferecem coisas que outras não oferecem e não há uma maneira fácil de embrulhar isso para todas elas. Eu atualizei minha resposta, dê uma olhada lá.
Ninguém está se afastando de SE
1
Não tenho ideia de por que essa resposta foi votada a favor. Não é remotamente adjacente ao tratamento da questão central - que, sucintamente, é: "Validar nomes de caminho, por favor?" Validar permissões de caminho é uma questão auxiliar (e amplamente ignorável) aqui. Enquanto a chamada para os.path.exists(filePath)tecnicamente levanta exceções em nomes de caminho inválidos, essas exceções precisam ser capturadas explicitamente e diferenciadas de outras exceções não relacionadas. Além disso, a mesma chamada retorna Falseem caminhos existentes para os quais o usuário atual não tem permissão de leitura. Em suma, maldade.
Cecil Curry
1
@CecilCurry: Para responder às suas perguntas: Dê uma olhada no histórico de edição da pergunta. Tal como acontece com a maioria das perguntas, não foi tão claro no início e mesmo agora o texto do título sozinho pode ser entendido de outra forma do que você disse.
Ninguém está se afastando de SE
9

Com Python 3, que tal:

try:
    with open(filename, 'x') as tempfile: # OSError if file exists or is invalid
        pass
except OSError:
    # handle error here

Com a opção 'x', também não precisamos nos preocupar com as condições da corrida. Veja a documentação aqui .

Agora, isso IRÁ criar um arquivo temporário de duração muito curta se ele ainda não existir - a menos que o nome seja inválido. Se você pode viver com isso, simplifica muito as coisas.

Stephen Miller
fonte
2
Nesse ponto, o projeto que precisava disso foi tão além do ponto em que uma resposta é relevante que eu realmente não posso aceitar uma resposta.
Nome falso
Ironicamente, a resposta prática não é boa o suficiente. Apesar de tudo, suponho que você pudesse ver se o arquivo existia. Em caso afirmativo, tente copiar o arquivo em outro lugar e, em seguida, tente sobrescrever.
The Matt
5
open(filename,'r')   #2nd argument is r and not w

irá abrir o arquivo ou dar um erro se ele não existir. Se houver um erro, você pode tentar escrever no caminho; se não puder, obterá um segundo erro

try:
    open(filename,'r')
    return True
except IOError:
    try:
        open(filename, 'w')
        return True
    except IOError:
        return False

Também dê uma olhada aqui sobre as permissões no Windows

vikki
fonte
1
Para evitar a necessidade de desvincular explicitamente () o arquivo de teste, você pode usar o tempfile.TemporaryFile()que destruirá automaticamente o arquivo temporário quando ele sair do escopo.
D_Bye
@FakeName O código é diferente, eu poderia ter usado os.access na segunda parte, mas se você seguisse o link que eu dei, você teria visto que não é uma boa ideia, isso deixa você com a opção de tentar realmente abrir o caminho para escrever.
vikki
Estou construindo meus caminhos com os.path.join, então não tenho problemas de escape de `\`. Além disso, não estou tendo problemas de permissão de diretório . Estou tendo problemas de nome de diretório (e nome de arquivo) .
Fake Name
@FakeName nesse caso você só precisa tentar abri-lo (você não precisa escrever), o python dá um erro se filenamecontiver caracteres inválidos. Eu editei a resposta
vikki
1
@HelgaIliashenko A abertura para gravação sobrescreverá um arquivo existente (deixará vazio), mesmo se você fechá-lo imediatamente sem gravá-lo. É por isso que eu estava abrindo para leitura primeiro, porque assim, se você não obtiver um erro, você saberá que existe um arquivo.
vikki
-7

tente os.path.existsisso irá verificar o caminho e retornar Truese existe e Falsese não.

Nilesh
fonte
1
Não. Preciso retornar verdadeiro se o arquivo no caminho existe ou pode ser criado . Preciso retornar false se o caminho for inválido (devido a conter caracteres inválidos no windows).
Nome falso,
que tipo de personagem inválido?
Nilesh
Não sei - isso é específico da plataforma.
Fake Name
2
Específico do sistema de arquivos, na verdade.
Piotr Kalinowski