Me gustaría ver cuál es la mejor manera de determinar el actual directorio de scripts en python?

Descubrí que, debido a las muchas formas de llamar a código python, es difícil encontrar una buena solución.

Aquí hay algunos problemas:

  • __file__ no está definido si el script se ejecuta con exec, execfile
  • __module__ se define sólo en los módulos de

Casos de uso:

  • ./myfile.py
  • python myfile.py
  • ./somedir/myfile.py
  • python somedir/myfile.py
  • execfile('myfile.py') (desde otro script, que puede estar ubicada en otro directorio, y que puede tener otro directorio actual.

Sé que no existe una solución perfecta, pero estoy buscando la mejor solución que resuelve la mayoría de los casos.

El más utilizado enfoque es os.path.dirname(os.path.abspath(__file__)) pero esto realmente no funciona si se ejecuta el script desde otro con exec().

Advertencia

Cualquier solución que utiliza el directorio actual se producirá un error, esto puede ser diferente dependiendo de la forma en que el script se llama o se puede cambiar dentro de la secuencia de comandos en ejecución.

  • Puedes ser más específico donde usted tiene que saber que el archivo proviene de? – en el código que eso es importar el archivo (incluyen-consciente de host) o en el archivo de eso importa? (auto-consciente de esclavo)
  • Ver a Ron Kalian del pathlib solución si estás usando python 3.4 o superior: stackoverflow.com/a/48931294/1011724
  • Así que la solución es NO utilizar ningún directorio actual en el código, pero el uso de algún archivo de configuración?
  • Interesante descubrimiento, acabo de hacer: Al hacer python myfile.py desde un shell, funciona, pero ambos :!python % y :!python myfile.py desde dentro de vim error con El sistema no puede encontrar la ruta especificada. Esto es bastante molesto. ¿Alguien puede comentar sobre la razón detrás de esto y posibles soluciones?
InformationsquelleAutor bogdan | 2010-09-15

11 Comentarios

  1. 205
    os.path.dirname(os.path.abspath(__file__))

    de hecho es la mejor que vas a obtener.

    Es inusual para la ejecución de una secuencia de comandos con exec/execfile; normalmente, usted debe estar utilizando el módulo de infraestructura para la carga de los scripts. Si usted debe utilizar estos métodos, sugiero configuración __file__ en el globals de pasar el script para que pueda leer ese nombre de archivo.

    No hay otra forma de obtener el nombre de archivo en execed código: como nota, el CWD puede estar en un lugar completamente diferente.

    • Nunca digas nunca? De acuerdo con esto: stackoverflow.com/a/18489147 responder a una cruz-plataforma de la solución es abspath(getsourcefile(lambda:0))? O es que hay algo que me falta?
  2. 124

    Si usted realmente desea para cubrir el caso de que un script se llama a través de execfile(...), puede utilizar el inspect módulo para deducir el nombre de archivo (incluida la ruta de acceso). Hasta donde yo soy consciente, esto va a funcionar para todos los casos que se enumeran:

    filename = inspect.getframeinfo(inspect.currentframe()).filename
    path = os.path.dirname(os.path.abspath(filename))
    • Creo que este es el método más robusto, pero me pregunta el OP declaró la necesidad de este. A menudo veo a los desarrolladores hacer esto cuando están utilizando los datos de los archivos en las ubicaciones relativas a la ejecución del módulo, pero IMO de los archivos de datos se deben colocar en una ubicación conocida.
    • LOL, si sería grande si usted podría ser capaz de definir una «ubicación conocida» que es multiplataforma y que también viene con el módulo. Estoy dispuesto a apostar que el único lugar seguro es la ubicación del script. Tenga en cuenta que esta no es la carne que el script debe escribir en este lugar, pero para la lectura de datos es segura.
    • Aún así, la solución no es buena, solo intenta llamar chdir() antes de la función, que va a cambiar el resultado. También llamar a la secuencia de comandos de python desde otro directorio que se va a alterar el resultado, así que no es una buena solución.
    • os.path.expanduser("~") es una cruz-plataforma de manera de obtener el directorio del usuario. Lamentablemente, no es el de Windows, la mejor práctica por dónde meter los datos de la aplicación.
    • He intentado chdir() antes de ejecutar la secuencia de comandos; se produce el resultado correcto. He intentado llamar al script desde otro directorio y también funciona. Los resultados son los mismos, como inspect.getabsfile()solución basada.
    • Este método funciona incluso cuando se ejecuta desde un python basado en marco, mientras que archivo no.

  3. 40
    #!/usr/bin/env python
    import inspect
    import os
    import sys
    
    def get_script_dir(follow_symlinks=True):
        if getattr(sys, 'frozen', False): # py2exe, PyInstaller, cx_Freeze
            path = os.path.abspath(sys.executable)
        else:
            path = inspect.getabsfile(get_script_dir)
        if follow_symlinks:
            path = os.path.realpath(path)
        return os.path.dirname(path)
    
    print(get_script_dir())

    Funciona en CPython, Jython, Pypy. Funciona si el script se ejecuta utilizando execfile() (sys.argv[0] y __file__ soluciones basadas en fallaría aquí). Funciona si el script está dentro de un archivo ejecutable archivo zip (/un huevo). Funciona si el script es «importado» (PYTHONPATH=/path/to/library.zip python -mscript_to_run) desde un archivo zip; devuelve la ruta de acceso del archivo en este caso. Funciona si el script se compila en un archivo ejecutable independiente (sys.frozen). Funciona para enlaces simbólicos (realpath elimina los enlaces simbólicos). Funciona en un intérprete interactivo; devuelve el directorio actual de trabajo en este caso.

    • Funciona perfectamente bien con PyInstaller.
    • ¿Hay alguna razón por la que getabsfile(..) no se menciona en la documentación de inspect? Aparece en la fuente a la que se enlaza fuera de la página.
    • podría ser un error. Es un simple contenedor de getsourcefile(), getfile() que están documentados.
  4. 22

    En Python 3.4+ puede utilizar el más simple pathlib módulo:

    from inspect import currentframe, getframeinfo
    from pathlib import Path
    
    filename = getframeinfo(currentframe()).filename
    parent = Path(filename).resolve().parent
    • Excelente simplicidad!
    • Usted probablemente puede utilizar Path(__file__) (no es necesario para la inspect módulo).
    • haciendo que produce una ruta de acceso, incluyendo el nombre del archivo actual, no el directorio padre. Si estoy tratando de conseguir que el actual directorio de secuencia de comandos con el fin de apuntar a un archivo en el mismo directorio por ejemplo, esperando para cargar un archivo de configuración en el mismo directorio que el script, Path(__file__) da /path/to/script/currentscript.py cuando el OP quería conseguir /path/to/script/
    • Oh no he entendido, te refieres a evitar el inspeccionar módulo y sólo tiene que utilizar algo como parent = Path(__file__).resolve().parent Que es mucho más agradable.
    • En Windows (10), tengo este error: TypeError: unsupported operand type(s) for +: 'WindowsPath' and 'str' cuando traté de anexar otro string con la ruta del archivo con el + operador. Una solución que funcionó fue envolviendo *parent* en str() función.
    • A. Usted debe utilizar .joinpath() (o el / operador) para esto, no +.

  5. 6

    La os.path... enfoque fue el «hacer algo» en Python 2.

    En Python 3, usted puede encontrar el directorio de secuencia de comandos de la siguiente manera:

    from pathlib import Path
    cwd = Path(__file__).parents[0]
    • O simplemente Path(__file__).parent. Pero cwd es un término equivocado, que no es el directorio de trabajo actual, pero directorio del archivo. Podrían ser el mismo, pero que no es generalmente el caso.
  6. 5

    Solo uso os.path.dirname(os.path.abspath(__file__)) y examinar muy detenidamente si existe una necesidad real para el caso de que exec se utiliza. Podría ser un signo de difíciles diseño si no son capaces de utilizar el script como un módulo.

    Tenga en cuenta el Zen de Python #8, y si usted cree que hay un buen argumento para un caso de uso donde se debe trabajar para exec, por favor, háganoslo saber algunos detalles más sobre el fondo del problema.

    • Si no se ejecuta con exec (), perderá el depurador de contexto. También exec() se supone que es considerablemente más rápido que el de partida de un nuevo proceso.
    • No es una cuestión de exec vs iniciar un nuevo proceso, por lo que es un strawman argumento. Es una cuestión de exec vs usando una importación o llamada a la función.
  7. 4

    Sería

    import os
    cwd = os.getcwd()

    hacer lo que quieres? No estoy seguro de qué es exactamente lo que entendemos por «corriente de secuencia de comandos de directorio». ¿Cuál sería el resultado esperado será para los casos de uso que dio?

    • No ayuda. Creo que @bogdan está buscando en el directorio que el script que está en la parte superior de la pila de llamadas. es decir, en todos sus casos, se debe imprimir el directorio donde ‘myfile.py’ se sienta. Sin embargo, su método de impresión sólo el directorio del archivo que se llama exec('myfile.py'), mismo que __file__ y sys.argv[0].
    • Sí, eso tiene sentido. Yo sólo quería asegurarme de @bogdan no era con vistas a algo simple, y yo no podía decir exactamente lo que quería.
  8. 3

    Primero.. un par faltan casos de uso aquí si estamos hablando de formas de inyectar código anónimo..

    code.compile_command()
    code.interact()
    imp.load_compiled()
    imp.load_dynamic()
    imp.load_module()
    __builtin__.compile()
    loading C compiled shared objects? example: _socket?)

    Pero, la pregunta real es, ¿cuál es su objetivo – que están tratando de imponer algún tipo de seguridad? O simplemente estás interesado en cuál está siendo cargado.

    Si usted está interesado en seguridad, el nombre del archivo que se importa a través de exec/execfile es intrascendente – usted debe utilizar rexec, que ofrece lo siguiente:

    Este módulo contiene la RExec clase,
    que apoya r_eval(), r_execfile(),
    r_exec(), y r_import() métodos, que
    están restringidos versiones de la norma
    En Python las funciones eval(), execfile() y
    el exec de importación y declaraciones. Código
    se ejecuta en este entorno restringido
    sólo tendrán acceso a los módulos y
    las funciones que se consideran seguros; puede
    subclase RExec agregar o quitar funciones como
    deseado.

    Sin embargo, si esto es más de una actividad académica.. aquí hay un par de goofy enfoques que
    podría ser capaz de profundizar un poco más en..

    Secuencias de comandos de ejemplo:

    ./deep.py

    print ' >> level 1'
    execfile('deeper.py')
    print ' << level 1'

    ./deeper.py

    print '\t >> level 2'
    exec("import sys; sys.path.append('/tmp'); import deepest")
    print '\t << level 2'

    /tmp/deepest.py

    print '\t\t >> level 3'
    print '\t\t\t I can see the earths core.'
    print '\t\t << level 3'

    ./codespy.py

    import sys, os
    
    def overseer(frame, event, arg):
        print "loaded(%s)" % os.path.abspath(frame.f_code.co_filename)
    
    sys.settrace(overseer)
    execfile("deep.py")
    sys.exit(0)

    Salida

    loaded(/Users/synthesizerpatel/deep.py)
    >> level 1
    loaded(/Users/synthesizerpatel/deeper.py)
        >> level 2
    loaded(/Users/synthesizerpatel/<string>)
    loaded(/tmp/deepest.py)
            >> level 3
                I can see the earths core.
            << level 3
        << level 2
    << level 1

    Por supuesto, este es un recurso intensivo manera de hacerlo, tendría que ser el seguimiento
    todo el código.. No es muy eficiente. Pero, creo que es un nuevo enfoque
    puesto que sigue funcionando incluso a medida que te adentras en el nido.
    No se puede invalidar ‘eval’. Aunque puede reemplazar execfile().

    Nota, este enfoque sólo coveres exec/execfile, no ‘importar’.
    Para el nivel más alto ‘módulo’ carga de enganche usted podría ser capaz de utilizar
    sys.path_hooks (Write-up cortesía de PyMOTW).

    Eso es todo lo que tengo en la parte superior de mi cabeza.

  9. 2

    Aquí es una solución parcial, aún mejor de todos los publicados tan lejos.

    import sys, os, os.path, inspect
    
    #os.chdir("..")
    
    if '__file__' not in locals():
        __file__ = inspect.getframeinfo(inspect.currentframe())[0]
    
    print os.path.dirname(os.path.abspath(__file__))

    Ahora funciona esto va a todas las llamadas, pero si alguien usa chdir() para cambiar el directorio actual, este también se producirá un error.

    Notas:

    • sys.argv[0] no va a funcionar, va a volver -c si se ejecuta el script con python -c "execfile('path-tester.py')"
    • He publicado una prueba completa en https://gist.github.com/1385555 y eres bienvenido a mejorar.
  10. 1

    Esto debería funcionar en la mayoría de los casos:

    import os,sys
    dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
    • Esta solución utiliza el directorio actual y es explícitamente en la pregunta de que tal solución producirá un error.
  11. 1

    Espero que esta ayuda:-
    Si ejecuta una secuencia de comandos/módulo de cualquier lugar que usted será capaz de acceder a la __file__ variable que es un módulo de la variable que representa la ubicación de la secuencia de comandos.

    Por otro lado, si usted está utilizando el intérprete no tiene acceso a esa variable, donde usted obtendrá un nombre de NameError y os.getcwd() le dará el directorio incorrecto, si estás ejecutando el archivo desde otro lugar.

    Este solución debe darle lo que usted está buscando en todos los casos:

    from inspect import getsourcefile
    from os.path import abspath
    abspath(getsourcefile(lambda:0))

    No he probado a fondo, pero lo resuelto mi problema.

Dejar respuesta

Please enter your comment!
Please enter your name here