¿Cómo obtengo la ruta del archivo ejecutado actual en Python?

193

Esto puede parecer una pregunta para novatos, pero no lo es. Algunos enfoques comunes no funcionan en todos los casos:

sys.argv [0]

Esto significa usar path = os.path.abspath(os.path.dirname(sys.argv[0])), pero esto no funciona si está ejecutando desde otro script de Python en otro directorio, y esto puede suceder en la vida real.

__expediente__

Esto significa usar path = os.path.abspath(os.path.dirname(__file__)), pero descubrí que esto no funciona:

  • py2exeno tiene un __file__atributo, pero hay una solución alternativa
  • Cuando se ejecuta desde IDLE con execute()no hay __file__atributo
  • OS X 10.6 donde consigo NameError: global name '__file__' is not defined

Preguntas relacionadas con respuestas incompletas:

Estoy buscando una solución genérica , una que funcione en todos los casos de uso anteriores.

Actualizar

Aquí está el resultado de un caso de prueba:

Salida de python a.py (en Windows)

a.py: __file__= a.py
a.py: os.getcwd()= C:\zzz

b.py: sys.argv[0]= a.py
b.py: __file__= a.py
b.py: os.getcwd()= C:\zzz

a.py

#! /usr/bin/env python
import os, sys

print "a.py: sys.argv[0]=", sys.argv[0]
print "a.py: __file__=", __file__
print "a.py: os.getcwd()=", os.getcwd()
print

execfile("subdir/b.py")

subdir / b.py

#! /usr/bin/env python
import os, sys

print "b.py: sys.argv[0]=", sys.argv[0]
print "b.py: __file__=", __file__
print "b.py: os.getcwd()=", os.getcwd()
print

árbol

C:.
|   a.py
\---subdir
        b.py
Sorin
fuente

Respuestas:

80

No puede determinar directamente la ubicación del script principal que se está ejecutando. Después de todo, a veces el guión no proviene de un archivo en absoluto. Por ejemplo, podría provenir del intérprete interactivo o del código generado dinámicamente almacenado solo en la memoria.

Sin embargo, puede determinar de manera confiable la ubicación de un módulo, ya que los módulos siempre se cargan desde un archivo. Si crea un módulo con el siguiente código y lo coloca en el mismo directorio que su script principal, entonces el script principal puede importar el módulo y usarlo para ubicarse.

some_path / module_locator.py:

def we_are_frozen():
    # All of the modules are built-in to the interpreter, e.g., by py2exe
    return hasattr(sys, "frozen")

def module_path():
    encoding = sys.getfilesystemencoding()
    if we_are_frozen():
        return os.path.dirname(unicode(sys.executable, encoding))
    return os.path.dirname(unicode(__file__, encoding))

some_path / main.py:

import module_locator
my_path = module_locator.module_path()

Si tiene varios scripts principales en diferentes directorios, es posible que necesite más de una copia de module_locator.

Por supuesto, si su script principal está cargado por alguna otra herramienta que no le permite importar módulos que se ubican junto con su script, entonces no tiene suerte. En casos como ese, la información que busca simplemente no existe en ninguna parte de su programa. Su mejor opción sería presentar un error con los autores de la herramienta.

Daniel Stutzbach
fuente
2
He mencionado que en OS 10.6 consigo NameError: global name '__file__' is not definedel uso de archivos y esto no está dentro de IDLE. Piensa que __file__se define solo dentro de los módulos.
sorin
1
@Sorin Sbarnea: Actualicé mi respuesta con cómo lo soluciono.
Daniel Stutzbach el
2
Gracias, pero en realidad el problema con la falta __file__no tuvo nada que ver con Unicode. No se porque__file__ no está definido, pero estoy buscando una solución genérica que funcione en todos los casos.
sorin
1
Lo sentimos, esto no es posible en todos los casos. Por ejemplo, trato de hacer esto en waf.googlecode.com desde dentro de un archivo wscript (python). Estos archivos se ejecutan pero no son módulos y no puede convertirlos en módulos (pueden ser cualquier subdirectorio del árbol de origen).
sorin
1
¿Eso no te dará la ubicación some_path/module_locator.py?
Casebash
68

Primero, necesita importar inspectyos

from inspect import getsourcefile
from os.path import abspath

A continuación, donde quiera encontrar el archivo fuente, simplemente use

abspath(getsourcefile(lambda:0))
ArtOfWarfare
fuente
La mejor respuesta. Gracias.
Devan Williams el
44
Gran respuesta. Gracias. También parece ser la respuesta más corta y portátil (que se ejecuta en diferentes sistemas operativos) y no encuentra problemas como NameError: global name '__file__' is not defined(otra solución causó esto).
Edward
Otra posibilidad que es igualmente corta: lambda:_. Funcionó para mí, no estoy seguro de que siempre funcione. lambda:0probablemente corre más rápido en una cantidad inconmensurablemente pequeña (o tal vez no tan pequeña ... ¿podría ser una carga inmediata o algo aún más rápido para 0una carga global para _?) Discutible si es más limpio o más fácil de leer o aún más inteligente / oscuro.
ArtOfWarfare
Como con casi todas las propuestas que he probado, esto solo me devuelve el cwd, no el archivo de directorio que estoy ejecutando (depuración).
James
1
@James: este código va en el archivo que está ejecutando ... la ejecución no getsourcefile(lambda:0)tendrá sentido y solo regresará Nonesi intenta ejecutarlo en un mensaje interactivo (ya lambdaque no estará en ningún archivo fuente). Si desea saber de dónde viene otra función u objeto en un entorno interactivo, ¿tal abspath(getsourcefile(thatFunctionOrObject))vez sea más útil para usted?
ArtOfWarfare
16

esta solución es robusta incluso en ejecutables

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))
José Crespo Barrios
fuente
2
Esta debería ser la respuesta correcta. Esto funciona incluso en entry_point: console_script, pero ninguna de las otras respuestas.
Polv
15

Me encontraba con un problema similar, y creo que esto podría resolver el problema:

def module_path(local_function):
   ''' returns the module path without the use of __file__.  Requires a function defined
   locally in the module.
   from http://stackoverflow.com/questions/729583/getting-file-path-of-imported-module'''
   return os.path.abspath(inspect.getsourcefile(local_function))

Funciona para scripts regulares y en inactivo. ¡Todo lo que puedo decir es probarlo para otros!

Mi uso típico:

from toolbox import module_path
def main():
   pass # Do stuff

global __modpath__
__modpath__ = module_path(main)

Ahora uso __modpath__ en lugar de __file__.

Garrett Berg
fuente
2
De acuerdo con la guía de estilo de codificación PEP8 , uno nunca debe crear nombres con guiones bajos iniciales y finales dobles, por lo que __modpath__debe cambiarse el nombre. Probablemente tampoco necesite la globaldeclaración. De lo contrario +1!
Martineau
44
En realidad, puede definir la función local directamente en la llamada a module_path(). es decir module_path(lambda _: None), que no dependa de los otros contenidos de la secuencia de comandos que se encuentra.
martineau
@martineau: Tomé su sugerencia lambda _: Noney la he usado durante los últimos dos años, pero justo ahora descubrí que podía condensarla en solo lambda:0. ¿Hay alguna razón particular por la que sugirió la forma que hizo, con un argumento ignorado _, en lugar de sin ningún argumento? ¿Hay algo superior sobre el Noneprefijo con un espacio en lugar de solo 0? Ambos son igualmente crípticos, creo, solo uno tiene 8 caracteres de largo mientras que el otro tiene 14 caracteres de largo.
ArtOfWarfare
@ArtOfWarfare: La diferencia entre los dos es que lambda _:es una función que toma un argumento y lambda:no toma ninguna. No importa ya que la función nunca se llama. Del mismo modo, no importa qué valor de retorno se use. Supongo que elegí Noneporque en ese momento parecía indicar que era mejor una función de no hacer nada y nunca llamarla. El espacio frente a él es opcional y de nuevo solo para mejorar la legibilidad (siempre tratar de seguir PEP8 es adictivo).
Martineau
@martineau: Sin lambdaembargo, obviamente es un abuso usarlo para algo que nunca debió hacer. Si tuviera que seguir PEP8, creo que el contenido correcto para él sería pass, no None, pero no es válido poner una declaración en a lambda, por lo que debe poner algo con un valor. Hay algunas cosas válidas de 2 caracteres que podría poner, pero creo que las únicas cosas válidas de un solo carácter que puede poner son 0-9 (o un nombre de variable de un solo carácter asignado fuera de lambda.) Creo que la 0mejor indica la nada de 0- 9)
ArtOfWarfare
6

La respuesta corta es que no hay una forma garantizada de obtener la información que desea , sin embargo, hay heurísticas que funcionan casi siempre en la práctica. Podrías mirar ¿Cómo encuentro la ubicación del ejecutable en C? . Discute el problema desde un punto de vista C, pero las soluciones propuestas se transcriben fácilmente a Python.

Dale Hagglund
fuente
Hola, mi bandera fue rechazada, así que ahora comencé una discusión sobre meta: meta.stackoverflow.com/questions/277272/…
ArtOfWarfare
Sin embargo, ya hay respuestas de trabajo simples en esta página. Mi solución funciona bien: stackoverflow.com/a/33531619/3787376 .
Edward
5

Consulte mi respuesta a la pregunta Importar módulos de la carpeta principal para obtener información relacionada, incluido por qué mi respuesta no utiliza la __file__variable no confiable . Esta solución simple debe ser compatible con diferentes sistemas operativos como los módulos osyinspect venir como parte de Python.

Primero, debe importar partes de los módulos de inspección y os .

from inspect import getsourcefile
from os.path import abspath

Luego, use la siguiente línea en cualquier otro lugar que sea necesario en su código Python:

abspath(getsourcefile(lambda:0))

Cómo funciona:

Desde el módulo incorporado os(descripción a continuación), la abspathherramienta se importa.

Rutinas del sistema operativo para Mac, NT o Posix dependiendo del sistema en el que estemos.

Luego getsourcefile(la descripción a continuación) se importa desde el módulo incorporado inspect.

Obtenga información útil de los objetos de Python en vivo.

  • abspath(path) devuelve la versión absoluta / completa de una ruta de archivo
  • getsourcefile(lambda:0) de alguna manera obtiene el archivo fuente interno del objeto de función lambda, por lo que devuelve '<pyshell#nn>' en el shell de Python o devuelve la ruta del archivo del código Python que se está ejecutando actualmente.

El uso abspathdel resultado de getsourcefile(lambda:0)debe asegurarse de que la ruta del archivo generado sea la ruta completa del archivo Python.
Esta solución explicada se basó originalmente en el código de la respuesta en ¿Cómo obtengo la ruta del archivo ejecutado actual en Python? .

Eduardo
fuente
Sí, se me ocurrió la misma solución ... mucho mejor que las respuestas que solo dicen que no se puede hacer de manera confiable ... a menos que la pregunta no pregunte qué creo que es ...
Grady Player
5

Simplemente has llamado:

path = os.path.abspath(os.path.dirname(sys.argv[0]))

en vez de:

path = os.path.dirname(os.path.abspath(sys.argv[0]))

abspath()le proporciona la ruta absoluta de sys.argv[0](el nombre del archivo en el que se encuentra su código) y dirname()devuelve la ruta del directorio sin el nombre del archivo.

dgb
fuente
3

Esto debería hacer el truco de una manera multiplataforma (siempre que no esté utilizando el intérprete o algo así):

import os, sys
non_symbolic=os.path.realpath(sys.argv[0])
program_filepath=os.path.join(sys.path[0], os.path.basename(non_symbolic))

sys.path[0]es el directorio en el que se encuentra su secuencia de comandos de llamada (el primer lugar donde busca módulos para ser utilizados por esa secuencia de comandos). Podemos quitar el nombre del archivo al final de sys.argv[0](que es lo que hice con os.path.basename). os.path.joinsimplemente los une de una manera multiplataforma. os.path.realpathsolo nos aseguramos de que si obtenemos enlaces simbólicos con nombres diferentes que el script en sí mismo, sigamos obteniendo el nombre real del script.

No tengo una Mac; Por lo tanto, no he probado esto en uno. Avísame si funciona, como parece que debería. Probé esto en Linux (Xubuntu) con Python 3.4. Tenga en cuenta que muchas soluciones para este problema no funcionan en Mac (ya que he oído eso__file__ no está presente en Mac).

Tenga en cuenta que si su script es un enlace simbólico, le dará la ruta del archivo al que se vincula (y no la ruta del enlace simbólico).

Brōtsyorfuzthrāx
fuente
2

Puedes usar Pathdesde el pathlibmódulo:

from pathlib import Path

# ...

Path(__file__)

Puede usar call to parentpara ir más lejos en el camino:

Path(__file__).parent
Gavriel Cohen
fuente
Esta respuesta utiliza la __file__variable que puede ser poco confiable (no siempre es la ruta completa del archivo, no funciona en todos los sistemas operativos, etc.) como los usuarios de StackOverflow han mencionado a menudo. Cambiar la respuesta para que no se incluya causará menos problemas y será más compatible con la compatibilidad. Para obtener más información, consulte stackoverflow.com/a/33532002/3787376 .
Edward
@ mrroot5 Ok, entonces elimine su comentario por favor.
Gavriel Cohen
1

Si el código proviene de un archivo, puede obtener su nombre completo

sys._getframe().f_code.co_filename

También puede recuperar el nombre de la función como f_code.co_name

Alex Cohn
fuente
0

Simplemente agregue lo siguiente:

from sys import *
path_to_current_file = sys.argv[0]
print(path_to_current_file)

O:

from sys import *
print(sys.argv[0])
H. Kamran
fuente
0

Mi solución es:

import os
print(os.path.dirname(os.path.abspath(__file__)))
ThienSuBS
fuente
-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))
Mohamed.Abdo
fuente
Esto no tiene sentido. Primero, '__file__'no debería ser una cadena, segundo, si lo hiciera __file__, esto solo funcionaría para el archivo en el que se encuentra esta línea de código, y no para el archivo que se ejecuta.
Andreas Storvik Strauman