Verifique si una ruta es válida en Python sin crear un archivo en el destino de la ruta

98

Tengo una ruta (incluido el directorio y el nombre del archivo).
Necesito probar si el nombre del archivo es válido, por ejemplo, si el sistema de archivos me permitirá crear un archivo con ese nombre.
El nombre del archivo tiene algunos caracteres Unicode .

Es seguro asumir que el segmento de directorio de la ruta es válido y accesible ( estaba tratando de hacer que la pregunta fuera más aplicable en general, y aparentemente estaba demasiado lejos ).

Yo mucho no quiero tener que escapar nada a menos que tenga a.

Publicaría algunos de los personajes de ejemplo con los que estoy tratando, pero aparentemente el sistema de intercambio de pila los elimina automáticamente. De todos modos, quiero mantener las entidades unicode estándar como ö, y solo escapar de las cosas que no son válidas en un nombre de archivo.


Aquí está el truco. Es posible que ya exista (o no) un archivo en el destino de la ruta. Necesito conservar ese archivo si existe, y no crear un archivo si no existe.

Básicamente quiero comprobar si podía escribir en un camino sin tener que abrir el camino para la escritura (y la creación de archivos / archivo automático clobbering que por lo general conlleva).

Como tal:

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

de aquí

No es aceptable, porque sobrescribirá el archivo existente, que no quiero tocar (si está ahí), o creará dicho archivo si no lo está.

Sé que puedo hacer:

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

Pero eso creará el archivo en el filePath, que luego tendría que hacerlo os.unlink.

Al final, parece que está gastando 6 o 7 líneas para hacer algo que debería ser tan simple os.isvalidpath(filePath)o similar.


Aparte, necesito que esto se ejecute en (al menos) Windows y MacOS, así que me gustaría evitar cosas específicas de la plataforma.

''

Nombre falso
fuente
Si desea probar que la ruta existe y puede escribir en ella, simplemente cree y elimine algún otro archivo. Déle un nombre único (o tan único como pueda), para evitar problemas de múltiples usuarios / subprocesos. De lo contrario, está buscando revisar las permsiones que lo llevarán directamente al lío específico del sistema operativo.
Tony Hopkinson
3
@Tony Hopkinson - Básicamente quiero comprobar si podía escribir en un camino sin escribir nada .
Nombre falso
Si no tiene nada que escribir en el archivo, ¿por qué necesita saber si puede hacerlo?
Karl Knechtel
@Karl Knechtel: si escribo en él y ya hay un archivo allí, dañará el archivo existente.
Nombre falso
2
@FakeName: aquí siempre tendrás una condición de carrera sutil. Entre comprobar que el archivo no existe, pero que podría crearse, y luego crear el archivo, algún otro proceso podría crearlo y de todos modos lo golpeará. Por supuesto, depende de su uso si se trata de un problema realista o no ...
detly

Respuestas:

154

tl; dr

Llame a la is_path_exists_or_creatable()función definida a continuación.

Estrictamente Python 3. Así es como rodamos.

Historia de dos preguntas

La pregunta de "¿Cómo pruebo la validez del nombre de ruta y, para nombres de ruta válidos, la existencia o capacidad de escritura de esas rutas?" son claramente dos preguntas separadas. Ambos son interesantes, y ninguno ha recibido una respuesta genuinamente satisfactoria aquí ... o, bueno, en cualquier lugar donde pudiera grep.

La respuesta de vikki probablemente sea la más cercana, pero tiene las notables desventajas de:

  • Abrir innecesariamente ( ... y luego no cerrar de manera confiable ) identificadores de archivos.
  • Escribir innecesariamente ( ... y luego fallar al cerrar o eliminar de manera confiable ) archivos de 0 bytes.
  • Ignorar errores específicos del sistema operativo que diferencian entre rutas de acceso inválidas no ignorables y problemas del sistema de archivos ignorables. Como era de esperar, esto es fundamental en Windows. ( Ver más abajo ) .
  • Ignorar las condiciones de carrera que resultan de procesos externos al mismo tiempo (re) mover directorios principales del nombre de ruta que se va a probar. ( Ver más abajo ) .
  • Ignorar los tiempos de espera de conexión que resultan de este nombre de ruta que reside en sistemas de archivos obsoletos, lentos o temporalmente inaccesibles. Esto podría exponer los servicios públicos a posibles ataques impulsados ​​por DoS . ( Ver más abajo ) .

Vamos a arreglar todo eso.

Pregunta # 0: ¿Qué es la validez del nombre de ruta nuevamente?

Antes de arrojar nuestros frágiles trajes de carne a los moshpits de dolor plagados de pitones, probablemente deberíamos definir lo que queremos decir con "validez de nombre de ruta". ¿Qué define la validez exactamente?

Por "validez de nombre de ruta", nos referimos a la corrección sintáctica de un nombre de ruta con respecto a la sistema de archivos raíz del sistema actual, independientemente de si esa ruta o los directorios principales del mismo existen físicamente. Un nombre de ruta es sintácticamente correcto según esta definición si cumple con todos los requisitos sintácticos del sistema de archivos raíz.

Por "sistema de archivos raíz", queremos decir:

  • En sistemas compatibles con POSIX, el sistema de archivos montado en el directorio raíz (/ ).
  • En Windows, el sistema de archivos se monta en %HOMEDRIVE%la letra de unidad con el sufijo de dos puntos que contiene la instalación actual de Windows (normalmente, pero no necesariamente C:).

El significado de "corrección sintáctica", a su vez, depende del tipo de sistema de archivos raíz. Para los sistemas de archivos ext4(y para la mayoría, pero no todos, compatibles con POSIX), un nombre de ruta es sintácticamente correcto si y solo si ese nombre de ruta:

  • No contiene bytes nulos (es decir, \x00en Python). Este es un requisito estricto para todos los sistemas de archivos compatibles con POSIX.
  • No contiene componentes de ruta de más de 255 bytes (por ejemplo, 'a'*256en Python). Un componente de la ruta es una subcadena más larga de una ruta de acceso que no contiene /caracteres (por ejemplo, bergtatt, ind, i, y fjeldkamreneen el nombre de ruta /bergtatt/ind/i/fjeldkamrene).

Corrección sintáctica. Sistema de archivos raíz. Eso es.

Pregunta n. ° 1: ¿Cómo haremos ahora la validez del nombre de ruta?

Validar nombres de rutas en Python es sorprendentemente poco intuitivo. Estoy completamente de acuerdo con Fake Name aquí: el os.pathpaquete oficial debería proporcionar una solución lista para usar para esto. Por razones desconocidas (y probablemente poco convincentes), no es así. Afortunadamente, desenrollando su propia solución ad-hoc no es que desgarrador ...

OK, en realidad lo es. Es peludo; es desagradable; probablemente se ríe mientras burbujea y se ríe mientras brilla. Pero que vas a hacer Nada.

Pronto descenderemos al abismo radiactivo del código de bajo nivel. Pero primero, hablemos de la tienda de alto nivel. El estándar os.stat()y las os.lstat()funciones generan las siguientes excepciones cuando se pasan nombres de ruta no válidos:

  • Para los nombres de ruta que residen en directorios no existentes, instancias de FileNotFoundError.
  • Para rutas que residen en directorios existentes:
    • En Windows, instancias de WindowsErrorcuyo winerroratributo es 123( es decir, ERROR_INVALID_NAME).
    • En todos los demás sistemas operativos:
    • Para rutas que contienen bytes nulos (es decir, '\x00'), instancias de TypeError.
    • Para nombres de ruta que contienen componentes de ruta de más de 255 bytes, instancias de OSErrorcuyo errcodeatributo es:
      • En SunOS y la familia * BSD de sistemas operativos, errno.ERANGE. (Esto parece ser un error a nivel del sistema operativo, también conocido como "interpretación selectiva" del estándar POSIX).
      • En todas las demás sistemas operativos, errno.ENAMETOOLONG.

Fundamentalmente, esto implica que solo los nombres de ruta que residen en directorios existentes son validables. Las funciones os.stat()y os.lstat()generan FileNotFoundErrorexcepciones genéricas cuando se pasan nombres de ruta que residen en directorios no existentes, independientemente de si esos nombres de ruta no son válidos o no. La existencia del directorio tiene prioridad sobre la invalidez del nombre de ruta.

¿Significa esto que los nombres de ruta que residen en directorios no existentes no son validables? Sí, a menos que modifiquemos esos nombres de ruta para que residan en directorios existentes. Sin embargo, ¿es eso factible incluso con seguridad? ¿La modificación de un nombre de ruta no debería impedirnos validar el nombre de ruta original?

Para responder a esta pregunta, recuerde que los nombres de ruta sintácticamente correctos en el ext4sistema de archivos no contienen componentes de ruta (A) que contengan bytes nulos o (B) de más de 255 bytes de longitud. Por lo tanto, un ext4nombre de ruta es válido si y solo si todos los componentes de la ruta en ese nombre de ruta son válidos. Esto es cierto para la mayoría de los sistemas de archivos de interés del mundo real .

¿Nos ayuda realmente esa percepción pedante? Si. Reduce el problema mayor de validar el nombre de ruta completo de una sola vez al problema más pequeño de solo validar todos los componentes de la ruta en ese nombre de ruta. Cualquier nombre de ruta arbitrario es validable (independientemente de si ese nombre de ruta reside en un directorio existente o no) de una manera multiplataforma siguiendo el siguiente algoritmo:

  1. Divida ese nombre de ruta en componentes de ruta (por ejemplo, el nombre de ruta /troldskog/faren/vilden la lista ['', 'troldskog', 'faren', 'vild']).
  2. Para cada uno de estos componentes:
    1. Une el nombre de ruta de un directorio que se garantiza que existe con ese componente en un nuevo nombre de ruta temporal (por ejemplo, /troldskog).
    2. Pase ese nombre de ruta a os.stat()o os.lstat(). Si ese nombre de ruta y, por lo tanto, ese componente no es válido, se garantiza que esta llamada generará una excepción que exponga el tipo de invalidez en lugar de una FileNotFoundErrorexcepción genérica . ¿Por qué? Porque ese nombre de ruta reside en un directorio existente. (La lógica circular es circular).

¿Existe un directorio garantizado? Sí, pero normalmente solo uno: el directorio superior del sistema de archivos raíz (como se definió anteriormente).

Pasar nombres de ruta que residen en cualquier otro directorio (y por lo tanto no se garantiza que exista) os.stat()o os.lstat()invita a condiciones de carrera, incluso si ese directorio se probó previamente para su existencia. ¿Por qué? Debido a que no se puede evitar que los procesos externos eliminen simultáneamente ese directorio después de que se haya realizado esa prueba, pero antes de que se pase ese nombre de ruta a os.stat()o os.lstat(). ¡Libera a los perros de la locura que devora la mente!

También existe un beneficio secundario sustancial del enfoque anterior: la seguridad. (No se tiene que agradable?) Específicamente:

Aplicaciones frontales que validan nombres de rutas arbitrarios de fuentes no confiables simplemente pasando dichos nombres de ruta os.stat()o os.lstat()son susceptibles a ataques de denegación de servicio (DoS) y otras travesuras de sombrero negro. Los usuarios malintencionados pueden intentar validar repetidamente los nombres de ruta que residen en sistemas de archivos que se sabe que están obsoletos o lentos (por ejemplo, recursos compartidos de NFS Samba); en ese caso, establecer ciegamente los nombres de las rutas entrantes puede eventualmente fallar con tiempos de espera de conexión o consumir más tiempo y recursos que su débil capacidad para soportar el desempleo.

El enfoque anterior evita esto validando únicamente los componentes de la ruta de un nombre de ruta con el directorio raíz del sistema de archivos raíz. (Si incluso eso es obsoleto, lento o inaccesible, tiene problemas más grandes que la validación del nombre de ruta).

¿Perdió? Excelente. Vamos a empezar. (Se asume Python 3. Consulte "¿Qué es la esperanza frágil para 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?

Hecho. No entrecerre los ojos ante ese código. ( Muerde. )

Pregunta # 2: ¿Posiblemente existencia o capacidad de creación de nombre de ruta no válido, eh?

Probar la existencia o capacidad de creación de nombres de ruta posiblemente inválidos es, dada la solución anterior, en su mayoría trivial. La pequeña clave aquí es llamar a la función previamente definida antes de probar la ruta pasada:

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

Hecho y hecho. Excepto que no del todo.

Pregunta # 3: Posiblemente existencia de nombre de ruta no válido o capacidad de escritura en Windows

Existe una advertencia. Por supuesto que sí.

Como admite la os.access()documentación oficial :

Nota: Las operaciones de E / S pueden fallar incluso cuando os.access()indique que tendrán éxito, particularmente para operaciones en sistemas de archivos de red que pueden tener una semántica de permisos más allá del modelo habitual de bits de permiso POSIX.

Para sorpresa de nadie, Windows es el sospechoso habitual aquí. Gracias al uso extensivo de las listas de control de acceso (ACL) en los sistemas de archivos NTFS, el modelo simplista de bits de permiso POSIX se asigna mal a la realidad subyacente de Windows. Si bien esto (posiblemente) no es culpa de Python, podría ser una preocupación para las aplicaciones compatibles con Windows.

Si este es usted, se busca una alternativa más robusta. Si la ruta pasada no existe, en su lugar, intentamos crear un archivo temporal garantizado para ser eliminado inmediatamente en el directorio principal de esa ruta, una prueba de capacidad de creación más portátil (aunque costosa):

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

Sin embargo, tenga en cuenta que incluso esto puede no ser suficiente.

Gracias al Control de acceso de usuario (UAC), el siempre inimitable Windows Vista y todas las iteraciones posteriores del mismo mienten descaradamente sobre los permisos que pertenecen a los directorios del sistema. Cuando los usuarios que no son administradores intentan crear archivos en los directorios canónicos C:\Windowso en los C:\Windows\system32directorios, UAC permite superficialmente al usuario hacerlo mientras en realidad aísla todos los archivos creados en una "Tienda virtual" en el perfil de ese usuario. (¿Quién podría haber imaginado que engañar a los usuarios tendría consecuencias perjudiciales a largo plazo?)

Esto es Loco. Esto es Windows.

Pruébalo

¿Nos atrevemos? Es hora de probar las pruebas anteriores.

Dado que NULL es el único carácter prohibido en los nombres de ruta en los sistemas de archivos orientados a UNIX, aprovechemos eso para demostrar la cruda y dura verdad, ignorando las travesuras de Windows que no son ignorables, que francamente me aburren y me enojan en 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

Más allá de la cordura. Más allá del dolor. Encontrará preocupaciones sobre la portabilidad de Python.

Cecil Curry
fuente
3
¡Sí, fui yo! Intentar combinar una expresión regular de validación de nombre de ruta de portabilidad cruzada es un ejercicio inútil y está garantizado que fallará en casos extremos comunes. Tenga en cuenta la longitud del nombre de ruta en Windows, por ejemplo: "La ruta máxima de 32,767 caracteres es aproximada, porque el sistema puede expandir el prefijo '\\? \' A una cadena más larga en tiempo de ejecución, y esta expansión se aplica a la longitud total . " Dado eso, en realidad es técnicamente inviable construir una expresión regular que coincida solo con nombres de ruta válidos. En su lugar, es mucho más razonable ceder a Python.
Cecil Curry
2
¡Ah! Yo (de mala gana) veo. Estás haciendo algo aún más extraño que piratear una expresión regular. Sí, eso está garantizado para fallar aún más. Eso tampoco aborda por completo la pregunta en cuestión, que no es "¿Cómo elimino las subcadenas no válidas de un nombre de base específico de Windows?" (... que, por su propia omisión, no puede resolver, nuevamente debido a casos extremos) pero "¿Cómo puedo probar de manera cruzada la validez del nombre de ruta y, para nombres de ruta válidos, la existencia o capacidad de escritura de esas rutas?"
Cecil Curry
1
Las restricciones específicas del sistema de archivos son definitivamente una preocupación válida, pero se cortan en ambos sentidos. Para aplicaciones frontales que consumen rutas arbitrarias de fuentes que no son de confianza, realizar lecturas a ciegas es, en el mejor de los casos, una propuesta arriesgada; en este caso, forzar el uso del sistema de archivos raíz no solo es sensato sino prudente. Para otras aplicaciones, sin embargo, la base de usuarios puede ser lo suficientemente confiable como para otorgar acceso sin inhibiciones al sistema de archivos. Es bastante dependiente del contexto, diría yo. Gracias por señalar esto astutamente, ¡ Nadie ! Agregaré una advertencia arriba.
Cecil Curry
2
En cuanto a la nomenclatura, soy un fanático pedante de prefijar los nombres de los probadores por is_. Este es mi defecto de carácter. No obstante, debidamente anotado: no se puede complacer a todo el mundo y, a veces, no se puede complacer a nadie. ;)
Cecil Curry
1
En Fedora 24, python 3.5.3, un nombre de ruta con caracteres nulos incrustados arroja: ValueError: byte nulo incrustado ... es necesario agregar: "" excepto ValueError como exc: return False "" antes o después de la trampa 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

Tenga en cuenta que path.existspuede fallar por más razones además de the file is not thereque tenga que hacer pruebas más precisas, como comprobar si el directorio que lo contiene existe, etc.


Después de mi discusión con el OP, resultó que el problema principal parece ser que el nombre del archivo puede contener caracteres que no están permitidos por el sistema de archivos. Por supuesto, deben eliminarse, pero el OP quiere mantener tanta legibilidad humana como lo permita el sistema de archivos.

Lamentablemente, no conozco ninguna buena solución para esto. Sin embargo, la respuesta de Cecil Curry analiza más de cerca la detección del problema.

Nadie se aleja del SE
fuente
No. Necesito devolver verdadero si el archivo en la ruta existe o se puede crear . Necesito devolver falso si la ruta no es válida (debido a que contiene caracteres no válidos en Windows).
Nombre falso
or can be createdbueno, no leí eso de tu pregunta. La lectura de los permisos dependerá de la plataforma hasta cierto punto.
Nadie se aleja del SE
1
@Fake Name: Sí, eliminará algunas de las dependencias de la plataforma, pero aún así, algunas plataformas ofrecen cosas que otras no ofrecen y no hay una manera fácil de ajustar eso para todas. Actualicé mi respuesta, eche un vistazo allí.
Nadie se aleja del SE
1
No tengo idea de por qué esta respuesta fue votada a favor. No viene ni remotamente adyacente a abordar la pregunta central, que, de manera sucinta, es: "¿Validar nombres de ruta, por favor?" La validación de los permisos de ruta es una cuestión secundaria (y en gran parte ignorable) aquí. Si bien la llamada a os.path.exists(filePath)técnicamente genera excepciones en nombres de ruta no válidos, esas excepciones deberían detectarse y diferenciarse explícitamente de otras excepciones no relacionadas. Además, la misma llamada regresa Falseen rutas existentes para las que el usuario actual no tiene permisos de lectura. En resumen, maldad.
Cecil Curry
1
@CecilCurry: Para responder a sus preguntas: eche un vistazo al historial de edición de la pregunta. Como ocurre con la mayoría de las preguntas, no fue tan claro al principio e incluso ahora la redacción del título por sí sola podría entenderse de otra manera de lo que dijo.
Nadie se va de SE
9

Con Python 3, ¿qué tal:

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

Con la opción 'x' tampoco tenemos que preocuparnos por las condiciones de la carrera. Consulte la documentación aquí .

Ahora, esto creará un archivo temporal de muy corta duración si aún no existe, a menos que el nombre no sea válido. Si puedes vivir con eso, simplifica mucho las cosas.

Stephen Miller
fuente
2
En este punto, el proyecto que necesitaba esto se ha movido tanto más allá del punto donde una respuesta es incluso relevante que realmente no puedo aceptar una respuesta.
Nombre falso
Irónicamente, la respuesta práctica no es lo suficientemente buena. Independientemente, supongo que podría ver si el archivo existía. Si es así, intente copiar el archivo en otro lugar y luego intente sobrescribirlo.
The Matt
5
open(filename,'r')   #2nd argument is r and not w

abrirá el archivo o dará un error si no existe. Si hay un error, puede intentar escribir en la ruta, si no puede, obtendrá un segundo error

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

También eche un vistazo aquí sobre los permisos en Windows

vikki
fuente
1
Para evitar la necesidad de desvincular explícitamente () el archivo de prueba, puede usar tempfile.TemporaryFile()que destruirá automáticamente el archivo temporal cuando salga del alcance.
D_Bye
@FakeName El código es diferente, podría haber usado os.access en la segunda parte, pero si siguió el enlace que le di, habría visto que no es una buena idea, esto le deja con la opción de intentar abrir realmente el camino para escribir.
vikki
Estoy construyendo mis caminos con os.path.join, así que no tengo problemas de escape. Además, realmente no tengo problemas de permisos de directorio . Tengo problemas con el nombre del directorio (y el nombre del archivo) .
Nombre falso
@FakeName en ese caso solo necesita intentar abrirlo (no necesita escribir), Python da un error si filenamecontiene caracteres no válidos. He editado la respuesta
vikki
1
@HelgaIliashenko La apertura para escritura sobrescribirá un archivo existente (lo dejará vacío) incluso si lo cierra inmediatamente sin escribir en él. Es por eso que abrí para leer primero porque de esa manera, si no obtiene un error, entonces sabrá que hay un archivo existente.
vikki
-7

intente os.path.existsesto buscará la ruta y regresará Truesi existe y Falsesi no.

Nilesh
fuente
1
No. Necesito devolver verdadero si el archivo en la ruta existe o se puede crear . Necesito devolver falso si la ruta no es válida (debido a que contiene caracteres no válidos en Windows).
Nombre falso
¿Qué tipo de carácter no válido?
Nilesh
No sé, eso es específico de la plataforma.
Nombre falso
2
En realidad, es específico del sistema de archivos.
Piotr Kalinowski