Controlla se un percorso è valido in Python senza creare un file nella destinazione del percorso


98

Ho un percorso (inclusi directory e nome file).
Devo verificare se il nome del file è valido, ad esempio se il file system mi consentirà di creare un file con tale nome.
Il nome del file contiene alcuni caratteri Unicode .

È lecito ritenere che il segmento di directory del percorso sia valido e accessibile ( stavo cercando di rendere la domanda più applicabile dal punto di vista generale e apparentemente ero troppo lontano ).

Non voglio assolutamente scappare da nulla a meno che non sia necessario.

Pubblicherei alcuni dei personaggi di esempio con cui ho a che fare, ma a quanto pare vengono rimossi automaticamente dal sistema di scambio di stack. Ad ogni modo, voglio mantenere entità Unicode standard come ö, e solo sfuggire a cose che non sono valide in un nome di file.


Ecco il trucco. Potrebbe esserci (o meno) già un file nella destinazione del percorso. Devo mantenere quel file se esiste e non creare un file se non esiste.

Fondamentalmente voglio controllare se ho potuto scrivere su un percorso senza realmente aprire il percorso per la scrittura (e la creazione di file / automatica dei file clobbering che in genere comporta).

Come tale:

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

da qui

Non è accettabile, perché sovrascriverà il file esistente, che non voglio toccare (se è presente), o creerà detto file se non lo è.

So che posso fare:

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

Ma questo creerà il file in filePath, che poi dovrei os.unlink.

Alla fine, sembra che stia spendendo 6 o 7 righe per fare qualcosa che dovrebbe essere semplice come os.isvalidpath(filePath)o simile.


Per inciso, ho bisogno che questo funzioni su (almeno) Windows e MacOS, quindi vorrei evitare cose specifiche della piattaforma.

``


Se vuoi verificare che il percorso esista e puoi scrivere su di esso, crea ed elimina semplicemente un altro file. Dagli un nome univoco (o il più unico possibile), per evitare problemi multiutente / multi thread. Altrimenti stai cercando di controllare le autorizzazioni che ti faranno cadere direttamente nel pasticcio specifico del sistema operativo.
Tony Hopkinson

3
@Tony Hopkinson - Fondamentalmente voglio controllare se ho potuto scrivere su un percorso senza in realtà scrivere nulla .
Nome falso

Se non hai nulla da scrivere sul file, perché devi sapere se sei in grado di farlo?
Karl Knechtel

@Karl Knechtel - Se ci scrivo e c'è già un file lì, danneggerà il file esistente.
Nome falso

2
@FakeName - Avrai sempre una condizione di gara sottile qui. Tra il controllo che il file non esiste ma potrebbe essere creato e poi la creazione del file, qualche altro processo potrebbe crearlo e il file verrà comunque cancellato. Naturalmente, dipende dal tuo utilizzo se questo è un problema realistico o meno ...
detly

Risposte:


154

tl; dr

Chiama la is_path_exists_or_creatable()funzione definita di seguito.

Rigorosamente Python 3. È così che rotoliamo.

Un racconto di due domande

La domanda: "Come si verifica la validità del percorso e, per i nomi di percorso validi, l'esistenza o la scrivibilità di quei percorsi?" sono chiaramente due domande separate. Entrambi sono interessanti, e nessuno dei due ha ricevuto una risposta veramente soddisfacente qui ... o, beh, ovunque io possa grep.

La risposta di vikki probabilmente è la più vicina, ma ha i notevoli svantaggi di:

  • Inutile aprire ( ... e quindi non chiudere in modo affidabile ) handle di file.
  • Scrivere inutilmente ( ... e quindi non riuscire a chiudere o eliminare in modo affidabile ) file a 0 byte.
  • Ignorando gli errori specifici del sistema operativo che distinguono tra nomi di percorso non ignorabili e problemi di file system ignorabili. Non sorprende che questo sia fondamentale in Windows. ( Vedi sotto. )
  • Ignorando le race condition risultanti da processi esterni simultaneamente (ri) spostando le directory padre del percorso da testare. ( Vedi sotto. )
  • Ignorando i timeout di connessione risultanti da questo percorso che risiede su filesystem obsoleti, lenti o altrimenti temporaneamente inaccessibili. Ciò potrebbe esporre i servizi rivolti al pubblico a potenziali attacchi guidati dal DoS . ( Vedi sotto. )

Sistemeremo tutto questo.

Domanda # 0: Qual è ancora la validità del nome del percorso?

Prima di lanciare le nostre fragili tute di carne nei moshpits del dolore crivellati di pitoni, dovremmo probabilmente definire cosa intendiamo per "validità del nome del percorso". Cosa definisce esattamente la validità?

Per "validità del percorso", intendiamo la correttezza sintattica di un percorso rispetto al filesystem radice del sistema corrente, indipendentemente dal fatto che quel percorso o le sue directory padre esistano fisicamente. Un nome di percorso è sintatticamente corretto in questa definizione se è conforme a tutti i requisiti sintattici del filesystem di root.

Con "root filesystem" intendiamo:

  • Su sistemi compatibili con POSIX, il filesystem è stato montato nella directory root ( /).
  • Su Windows, il filesystem è montato su %HOMEDRIVE%, la lettera di unità con il suffisso dei due punti contenente l'attuale installazione di Windows (normalmente ma non necessariamente C:).

Il significato di "correttezza sintattica", a sua volta, dipende dal tipo di filesystem di root. Per ext4(e per la maggior parte ma non per tutti i file system compatibili con POSIX), un percorso è sintatticamente corretto se e solo se quel percorso:

  • Non contiene byte nulli (cioè, \x00in Python). Questo è un requisito fondamentale per tutti i filesystem compatibili con POSIX.
  • Non contiene componenti di percorso più lunghi di 255 byte (ad esempio, 'a'*256in Python). Un componente percorso è una stringa più lunga di un percorso contenente alcun /carattere (per esempio, bergtatt, ind, i, e fjeldkamrenenel percorso /bergtatt/ind/i/fjeldkamrene).

Correttezza sintattica. File system di root. Questo è tutto.

Domanda n. 1: come faremo ora la validità del nome del percorso?

La convalida dei nomi di percorso in Python è sorprendentemente non intuitiva. Sono assolutamente d'accordo con Fake Name qui: il os.pathpacchetto ufficiale dovrebbe fornire una soluzione immediata per questo. Per ragioni sconosciute (e probabilmente poco convincenti), non è così. Fortunatamente, srotolando la propria soluzione ad-hoc non è che le budella ...

OK, in realtà lo è. È peloso; è brutto; probabilmente ridacchia mentre borbotta e ridacchia quando si illumina. Ma cosa farai? Nuthin '.

Presto scenderemo nell'abisso radioattivo del codice di basso livello. Ma prima parliamo di negozio di alto livello. Lo standard os.stat()e le os.lstat()funzioni sollevano le seguenti eccezioni quando vengono passati nomi di percorso non validi:

  • Per i nomi di percorso che risiedono in directory non esistenti, istanze di FileNotFoundError.
  • Per i nomi di percorso che risiedono nelle directory esistenti:
    • In Windows, istanze del WindowsErrorcui winerrorattributo è 123(ie, ERROR_INVALID_NAME).
    • In tutti gli altri sistemi operativi:
    • Per i nomi di percorso contenenti byte nulli (cioè '\x00'), istanze di TypeError.
    • Per i nomi di percorso contenenti componenti di percorso più lunghi di 255 byte, istanze il OSErrorcui errcodeattributo è:
      • Sotto SunOS e * BSD famiglia di sistemi operativi, errno.ERANGE. (Questo sembra essere un bug a livello di sistema operativo, altrimenti indicato come "interpretazione selettiva" dello standard POSIX.)
      • Sotto tutti gli altri sistemi operativi, errno.ENAMETOOLONG.

Fondamentalmente, questo implica che solo i nomi di percorso che risiedono nelle directory esistenti sono validabili. Le funzioni os.stat()e os.lstat()sollevano FileNotFoundErroreccezioni generiche quando vengono passati nomi di percorso che risiedono in directory non esistenti, indipendentemente dal fatto che tali nomi di percorso non siano validi o meno. L'esistenza della directory ha la precedenza sull'invalidità del nome del percorso.

Ciò significa che i nomi di percorso che risiedono in directory non esistenti non sono convalidabili? Sì, a meno che non modifichiamo quei nomi di percorso in modo che risiedano nelle directory esistenti. Tuttavia, è anche possibile in modo sicuro? La modifica di un percorso non dovrebbe impedirci di convalidare il percorso originale?

Per rispondere a questa domanda, ricorda dall'alto che i nomi di percorso sintatticamente corretti sul ext4filesystem non contengono componenti di percorso (A) contenenti byte nulli o (B) di lunghezza superiore a 255 byte. Quindi, un ext4percorso è valido se e solo se tutti i componenti del percorso in quel percorso sono validi. Questo è vero per la maggior parte dei filesystem di interesse del mondo reale .

Questa intuizione pedante ci aiuta davvero? Sì. Riduce il problema più grande di convalidare il percorso completo in un colpo solo al problema più piccolo di convalidare solo tutti i componenti del percorso in quel percorso. Qualsiasi percorso arbitrario è convalidabile (indipendentemente dal fatto che quel percorso risieda in una directory esistente o meno) in modo multipiattaforma seguendo il seguente algoritmo:

  1. Dividi quel percorso in componenti del percorso (ad esempio, il percorso /troldskog/faren/vildnell'elenco ['', 'troldskog', 'faren', 'vild']).
  2. Per ciascuno di questi componenti:
    1. Unisci il percorso di una directory garantita per esistere con quel componente in un nuovo percorso temporaneo (ad esempio, /troldskog).
    2. Passa quel percorso a os.stat()o os.lstat(). Se quel percorso e quindi quel componente non sono validi, è garantito che questa chiamata sollevi un'eccezione che espone il tipo di invalidità piuttosto che FileNotFoundErrorun'eccezione generica . Perché? Perché quel percorso risiede in una directory esistente. (La logica circolare è circolare.)

Esiste una directory garantita per esistere? Sì, ma in genere solo uno: la directory più in alto del filesystem root (come definito sopra).

Il passaggio di nomi di percorso che risiedono in qualsiasi altra directory (e quindi non è garantita l'esistenza) os.stat()o os.lstat()invita a condizioni di competizione, anche se quella directory è stata precedentemente testata per esistere. Perché? Perché non è possibile impedire ai processi esterni di rimuovere contemporaneamente quella directory dopo che il test è stato eseguito ma prima che quel percorso venga passato a os.stat()o os.lstat(). Scatena i cani della follia folle!

Esiste anche un sostanziale vantaggio collaterale nell'approccio di cui sopra: la sicurezza. (Non è che bello?) In particolare:

Applicazioni frontali che convalidano nomi di percorso arbitrari da fonti non attendibili semplicemente passando tali nomi di percorso a os.stat()o os.lstat()sono suscettibili di attacchi Denial of Service (DoS) e altri imbrogli. Gli utenti malintenzionati possono tentare di convalidare ripetutamente nomi di percorso che risiedono su file system noti per essere obsoleti o altrimenti lenti (ad esempio, condivisioni NFS Samba); in tal caso, pronunciare ciecamente i nomi di percorso in entrata rischia di fallire con timeout di connessione o consumare più tempo e risorse della tua debole capacità di resistere alla disoccupazione.

L'approccio precedente evita questo problema convalidando solo i componenti del percorso di un nome di percorso rispetto alla directory root del filesystem root. (Se anche questo è obsoleto, lento o inaccessibile, hai problemi maggiori rispetto alla convalida del nome del percorso.)

Perduto? Grande. Cominciamo. (Si presume Python 3. Vedi "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?

Fatto. Non strizzare gli occhi davanti a quel codice. ( Morde. )

Domanda n. 2: Possibile esistenza o creabilità del percorso non valido, eh?

Testare l'esistenza o la creabilità di nomi di percorso potenzialmente non validi è, data la soluzione di cui sopra, per lo più banale. La piccola chiave qui è chiamare la funzione definita in precedenza prima di testare il percorso passato:

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

Fatto e fatto. Tranne non del tutto.

Domanda n. 3: Possibile esistenza o scrivibilità del percorso non valido su Windows

Esiste un avvertimento. Certo che sì.

Come ammette la os.access()documentazione ufficiale :

Nota: le operazioni di I / O potrebbero non riuscire anche quando os.access()indica che avrebbero avuto successo, in particolare per le operazioni su file system di rete che potrebbero avere semantiche di permessi oltre il consueto modello POSIX di bit di autorizzazione.

Con sorpresa di nessuno, Windows è il solito sospetto qui. Grazie all'ampio uso di elenchi di controllo di accesso (ACL) sui file system NTFS, il semplicistico modello POSIX a bit di autorizzazione si mappa male alla realtà Windows sottostante. Anche se questo (probabilmente) non è colpa di Python, potrebbe comunque essere motivo di preoccupazione per le applicazioni compatibili con Windows.

Se sei tu, è necessaria un'alternativa più robusta. Se il percorso passato non esiste, proviamo invece a creare un file temporaneo garantito per essere immediatamente cancellato nella directory padre di quel percorso - un test più portabile (se costoso) di creabilità:

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

Nota, tuttavia, che anche questo potrebbe non essere sufficiente.

Grazie a User Access Control (UAC), l'inimitabile Windows Vista e tutte le successive iterazioni mentono palesemente sulle autorizzazioni relative alle directory di sistema. Quando gli utenti non amministratori tentano di creare file nella directory canonica C:\Windowso nelle C:\Windows\system32directory, UAC consente superficialmente all'utente di farlo isolando effettivamente tutti i file creati in un "archivio virtuale" nel profilo di quell'utente. (Chi avrebbe potuto immaginare che ingannare gli utenti avrebbe conseguenze dannose a lungo termine?)

Questo è pazzesco. Questo è Windows.

Provalo

Osiamo? È ora di provare i test di cui sopra.

Poiché NULL è l'unico carattere proibito nei nomi di percorso sui filesystem orientati a UNIX, sfruttiamolo per dimostrare la fredda e dura verità, ignorando gli imbrogli non ignorabili di Windows, che francamente mi annoiano e mi fanno arrabbiare in egual misura:

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

Oltre la sanità mentale. Oltre il dolore. Troverai problemi di portabilità di Python.


3
Sì, sono stato io! Il tentativo di mettere insieme una regex di convalida del percorso cross-portabile è un esercizio futile e garantito per fallire per i casi limite comuni. Considera la lunghezza del nome del percorso su Windows, ad esempio: "Il percorso massimo di 32.767 caratteri è approssimativo, perché il prefisso" \\? \ "Può essere espanso dal sistema a una stringa più lunga in fase di esecuzione e questa espansione si applica alla lunghezza totale . " Detto questo, è tecnicamente impossibile costruire una regex che corrisponda solo a nomi di percorso validi. È molto più ragionevole rimandare invece a Python.
Cecil Curry

2
Ah. Io (a malincuore) vedo. Stai facendo qualcosa di ancora più strano che hackerare una regex. Sì, che è garantito a fallire ancora più difficile. Anche questo non risolve completamente la domanda in questione, che non è "Come faccio a rimuovere sottostringhe non valide da un nome di base specifico di Windows?" (... che, per tua omissione, non riesci a risolvere - ancora una volta a causa di casi limite) ma "Come faccio a testare in modo incrociato la validità dei nomi dei percorsi e, per i nomi dei percorsi validi, l'esistenza o la scrivibilità di quei percorsi?"
Cecil Curry

1
I vincoli specifici del filesystem è sicuramente una preoccupazione valida, ma taglia in entrambe le direzioni. Per le applicazioni frontali che consumano nomi di percorso arbitrari da fonti non attendibili, eseguire letture alla cieca è una proposta rischiosa nella migliore delle ipotesi; in questo caso, forzare l'uso del filesystem di root non è solo sensato ma prudente. Per altre applicazioni, tuttavia, la base utenti potrebbe essere abbastanza affidabile da garantire un accesso non inibito al filesystem. È abbastanza dipendente dal contesto, direi. Grazie per aver notato astutamente questo, Nessuno ! Aggiungerò un avvertimento sopra.
Cecil Curry

2
Per quanto riguarda la nomenclatura, sono un fan pedante del prefisso dei nomi dei tester is_. Questo è il mio difetto di carattere. Tuttavia, debitamente notato: non puoi accontentare tutti, ea volte non puoi accontentare nessuno. ;)
Cecil Curry

1
Su Fedora 24, python 3.5.3, un nome di percorso con caratteri nulli incorporati genera: ValueError: byte null incorporato ... è necessario aggiungere: `` `` eccetto ValueError as exc: return False `` prima o dopo la trappola TypeError.
mMerlin

47
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

Si noti che path.existspuò fallire per più ragioni oltre a the file is not therequindi potrebbe essere necessario eseguire test più fini come test se la directory contenente esiste e così via.


Dopo la mia discussione con l'OP si è scoperto che il problema principale sembra essere che il nome del file potrebbe contenere caratteri non consentiti dal filesystem. Ovviamente devono essere rimossi, ma l'OP vuole mantenere tutta la leggibilità umana consentita dal filesystem.

Purtroppo non conosco nessuna buona soluzione per questo. Tuttavia, la risposta di Cecil Curry esamina più da vicino l'individuazione del problema.


No. Devo restituire true se il file nel percorso esiste o può essere creato . Devo restituire false se il percorso non è valido (a causa della presenza di caratteri non validi su Windows).
Fake Name

or can be createdbeh, non l'ho letto dalla tua domanda. La lettura dei permessi dipenderà dalla piattaforma in una certa misura.
Nessuno si allontana da SE il

1
@Fake Name: Sì, rimuoverà alcune delle dipendenze dalla piattaforma, ma alcune piattaforme offrono ancora cose che altre non offrono e non esiste un modo semplice per avvolgerle tutte. Ho aggiornato la mia risposta, dai un'occhiata lì.
Nessuno si allontana da SE

1
Non ho idea del motivo per cui questa risposta sia stata votata. Non è neanche lontanamente adiacente all'affrontare la domanda centrale - che, in breve, è: "Convalida i nomi di percorso, per favore?" La convalida delle autorizzazioni del percorso è una domanda ausiliaria (e in gran parte ignorabile) qui. Sebbene la chiamata a os.path.exists(filePath)sollevi tecnicamente eccezioni su nomi di percorso non validi, tali eccezioni dovrebbero essere intercettate esplicitamente e differenziate da altre eccezioni non correlate. Inoltre, la stessa chiamata restituisce Falsei percorsi esistenti per i quali l'utente corrente non dispone delle autorizzazioni di lettura. In breve, cattiveria.
Cecil Curry

1
@CecilCurry: per rispondere alle tue domande: dai un'occhiata alla cronologia delle modifiche della domanda. Come per la maggior parte delle domande, all'inizio non era così chiaro e anche ora la formulazione del titolo potrebbe essere intesa diversamente da come hai detto.
Nessuno si allontana da SE

9

Con Python 3, che ne dici di:

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

Con l'opzione "x" non dobbiamo nemmeno preoccuparci delle condizioni di gara. Consulta la documentazione qui .

Ora, questo creerà un file temporaneo di breve durata se non esiste già, a meno che il nome non sia valido. Se riesci a conviverci, semplifica molto le cose.


2
A questo punto, il progetto che ne aveva bisogno si è spostato talmente oltre il punto in cui una risposta è persino rilevante che non posso davvero accettare una risposta.
Fake Name

Ironia della sorte, la risposta pratica non è abbastanza buona. Indipendentemente da ciò, suppongo che tu possa vedere se il file esisteva. In tal caso, provare a copiare il file altrove, quindi provare a sovrascriverlo.
Matt il

5
open(filename,'r')   #2nd argument is r and not w

aprirà il file o darà un errore se non esiste. Se c'è un errore, puoi provare a scrivere nel percorso, se non puoi, ottieni un secondo errore

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

Dai anche un'occhiata qui sui permessi su Windows


1
Per evitare la necessità di scollegare esplicitamente () il file di prova, puoi usare tempfile.TemporaryFile()che distruggerà automaticamente il file temp quando esce dall'ambito.
D_Bye

@FakeName Il codice è diverso, avrei potuto usare os.access nella seconda parte ma se avessi seguito il link che ti ho dato avresti visto che non è una buona idea, questo ti lascia con la possibilità di provare ad aprire effettivamente il percorso per la scrittura.
vikki

Sto costruendo i miei percorsi con os.path.join, quindi non ho problemi di fuga. Inoltre, non ho davvero problemi di autorizzazione della directory . Ho problemi con il nome della directory (e del nome del file) .
Fake Name

@FakeName in quel caso devi solo provare ad aprirlo (non serve scrivere), python dà un errore se filenamecontiene caratteri non validi. Ho modificato la risposta
vikki

1
@HelgaIliashenko L'apertura per la scrittura sovrascriverà un file esistente (rendendolo vuoto) anche se lo chiudi immediatamente senza scrivere su di esso. Ecco perché stavo aprendo per la lettura prima perché in questo modo, se non ottieni un errore, allora sai che c'è un file esistente.
vikki

-7

prova os.path.existsquesto verificherà il percorso e restituirà Truese esiste e in Falsecaso contrario.


1
No. Devo restituire true se il file nel percorso esiste o può essere creato . Devo restituire false se il percorso non è valido (a causa della presenza di caratteri non validi su Windows).
Fake Name

quale tipo di carattere non valido?
Nilesh

Non lo so, è specifico della piattaforma.
Fake Name

2
Specifico del file system, in realtà.
Piotr Kalinowski
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.