¿Cómo recuperar la ruta de un módulo?

Quiero detectar si el módulo ha cambiado. Ahora, usar inotify es simple, solo necesita saber el directorio del que desea recibir notificaciones.

¿Cómo recupero la ruta de un módulo en python?

 import a_module print(a_module.__file__) 

En realidad, le dará la ruta al archivo .pyc que se cargó, al menos en Mac OS X. Así que supongo que puede hacer:

 import os path = os.path.dirname(a_module.__file__) 

También puedes probar:

 path = os.path.abspath(a_module.__file__) 

Para obtener el directorio del módulo.

Hay un módulo de inspect en python.

Documentacion oficial

El módulo de inspección proporciona varias funciones útiles para ayudar a obtener información sobre objetos en vivo, tales como módulos, clases, métodos, funciones, seguimientos, objetos de marco y objetos de código. Por ejemplo, puede ayudarlo a examinar el contenido de una clase, recuperar el código fuente de un método, extraer y formatear la lista de argumentos para una función u obtener toda la información que necesita para mostrar un seguimiento detallado.

Ejemplo:

 >>> import os >>> import inspect >>> inspect.getfile(os) '/usr/lib64/python2.7/os.pyc' >>> inspect.getfile(inspect) '/usr/lib64/python2.7/inspect.pyc' >>> os.path.dirname(inspect.getfile(inspect)) '/usr/lib64/python2.7' 

Como han dicho las otras respuestas, la mejor manera de hacerlo es con __file__ (se muestra de nuevo a continuación). Sin embargo, hay una advertencia importante, que es que __file__ NO existe si está ejecutando el módulo por su cuenta (es decir, como __main__ ).

Por ejemplo, digamos que tiene dos archivos (ambos de los cuales están en su PYTHONPATH):

 #/path1/foo.py import bar print(bar.__file__) 

y

 #/path2/bar.py import os print(os.getcwd()) print(__file__) 

Ejecutar foo.py dará la salida:

 /path1 # "import bar" causes the line "print(os.getcwd())" to run /path2/bar.py # then "print(__file__)" runs /path2/bar.py # then the import statement finishes and "print(bar.__file__)" runs 

SIN EMBARGO, si intenta ejecutar bar.py por su cuenta, obtendrá:

 /path2 # "print(os.getcwd())" still works fine Traceback (most recent call last): # but __file__ doesn't exist if bar.py is running as main File "/path2/bar.py", line 3, in  print(__file__) NameError: name '__file__' is not defined 

Espero que esto ayude. Esta advertencia me costó mucho tiempo y confusión al probar las otras soluciones presentadas.

Intentaré abordar algunas variaciones en esta pregunta también:

  1. Encontrar el camino del script llamado.
  2. Encontrar la ruta del script actualmente en ejecución
  3. Encontrar el directorio del script llamado.

(Algunas de estas preguntas se han formulado en SO, pero se han cerrado como duplicados y se han redirigido aquí).

Advertencias de usar __file__

Para un módulo que ha importado:

 import something something.__file__ 

Devolverá la ruta absoluta del módulo. Sin embargo, dado el siguiente script foo.py:

 #foo.py print '__file__', __file__ 

Llamándolo con ‘python foo.py’ Devolverá simplemente ‘foo.py’. Si añades un shebang:

 #!/usr/bin/python #foo.py print '__file__', __file__ 

y llámelo usando ./foo.py, devolverá ‘./foo.py’. Llamándolo desde un directorio diferente (por ejemplo, ponga foo.py en la barra de directorios), y luego llame a

 python bar/foo.py 

o agregando un shebang y ejecutando el archivo directamente:

 bar/foo.py 

devolverá ‘bar / foo.py’ (la ruta relativa ).

Encontrar el directorio

Ahora, desde allí para obtener el directorio, os.path.dirname(__file__) también puede ser complicado. Al menos en mi sistema, devuelve una cadena vacía si la llama desde el mismo directorio que el archivo. ex.

 # foo.py import os print '__file__ is:', __file__ print 'os.path.dirname(__file__) is:', os.path.dirname(__file__) 

saldrá:

 __file__ is: foo.py os.path.dirname(__file__) is: 

En otras palabras, devuelve una cadena vacía, por lo que esto no parece confiable si desea usarla para el archivo actual (a diferencia del archivo de un módulo importado). Para solucionar esto, puedes envolverlo en una llamada a abspath:

 # foo.py import os print 'os.path.abspath(__file__) is:', os.path.abspath(__file__) print 'os.path.dirname(os.path.abspath(__file__)) is:', os.path.dirname(os.path.abspath(__file__)) 

que produce algo como:

 os.path.abspath(__file__) is: /home/user/bar/foo.py os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar 

Tenga en cuenta que abspath () NO resuelve enlaces simbólicos. Si quieres hacer esto, usa realpath () en su lugar. Por ejemplo, haciendo un enlace simbólico file_import_testing_link apuntando a file_import_testing.py, con el siguiente contenido:

 import os print 'abspath(__file__)',os.path.abspath(__file__) print 'realpath(__file__)',os.path.realpath(__file__) 

ejecutando se imprimirán rutas absolutas algo así como:

 abspath(__file__) /home/user/file_test_link realpath(__file__) /home/user/file_test.py 

file_import_testing_link -> file_import_testing.py

Utilizando inspeccionar

@SummerBreeze menciona el uso del módulo de inspección .

Esto parece funcionar bien, y es bastante conciso, para módulos importados:

 import os import inspect print 'inspect.getfile(os) is:', inspect.getfile(os) 

Retorna obedientemente el camino absoluto. Sin embargo, para encontrar la ruta del script que se está ejecutando actualmente, no vi una forma de usarlo.

No entiendo por qué nadie está hablando de esto, pero para mí, la solución más simple es usar imp.find_module (“nombre de módulo”) (documentación aquí ):

 import imp imp.find_module("os") 

Da una tupla con el camino en segunda posición:

 (, '/usr/lib/python2.7/os.py', ('.py', 'U', 1)) 

La ventaja de este método sobre el “inspeccionar” es que no necesita importar el módulo para que funcione, y puede usar una cadena de entrada. Útil para verificar módulos llamados en otro script, por ejemplo.

EDITAR :

En python3, el módulo importlib debería hacer:

Doc de importlib.util.find_spec :

Devuelve la especificación para el módulo especificado.

Primero, se comprueba sys.modules para ver si el módulo ya se importó. Si es así, entonces sys.modules [nombre]. se devuelve espec . Si eso se establece en Ninguno, se genera ValueError. Si el módulo no está en sys.modules, entonces se busca en sys.meta_path una especificación adecuada con el valor de ‘ruta’ que se asigna a los buscadores. No se devuelve ninguno si no se pudo encontrar ninguna especificación.

Si el nombre es para submódulo (contiene un punto), el módulo principal se importa automáticamente.

Los argumentos de nombre y paquete funcionan igual que importlib.import_module (). En otras palabras, los nombres de módulos relativos (con puntos iniciales) funcionan.

Esto fue trivial.

Cada módulo tiene una variable __file__ que muestra su ruta relativa desde donde se encuentra ahora.

Por lo tanto, obtener un directorio para que el módulo lo notifique es simple como:

 os.path.dirname(__file__) 
 import os path = os.path.abspath(__file__) dir_path = os.path.dirname(path) 

Utilidad de línea de comandos

Puedes ajustarlo a una utilidad de línea de comandos,

 python-which  

introduzca la descripción de la imagen aquí


Crea /usr/local/bin/python-which

 #!/usr/bin/env python import importlib import os import sys args = sys.argv[1:] if len(args) > 0: module = importlib.import_module(args[0]) print os.path.dirname(module.__file__) 

Hazlo ejecutable

 sudo chmod +x /usr/local/bin/python-which 

Así que pasé una buena cantidad de tiempo tratando de hacer esto con py2exe. El problema era obtener la carpeta base del script si se estaba ejecutando como un script de python o como un ejecutable de py2exe. También para que funcione, ya sea que se esté ejecutando desde la carpeta actual, otra carpeta o (esta fue la más difícil) desde la ruta del sistema.

Eventualmente utilicé este enfoque, utilizando sys.frozen como indicador de ejecución en py2exe:

 import os,sys if hasattr(sys,'frozen'): # only when running in py2exe this exists base = sys.prefix else: # otherwise this is a regular python script base = os.path.dirname(os.path.realpath(__file__)) 
 import module print module.__path__ 

Los paquetes soportan un atributo especial más, __path__ . Esto se inicializa para ser una lista que contiene el nombre del directorio que contiene el paquete __init__.py antes de que se __init__.py el código en ese archivo. Esta variable puede ser modificada; hacerlo afecta a futuras búsquedas de módulos y subpaquetes contenidos en el paquete.

Si bien esta característica no suele ser necesaria, se puede usar para ampliar el conjunto de módulos que se encuentran en un paquete.

Fuente

simplemente puede importar su módulo y luego golpear su nombre y obtendrá su ruta completa

 >>> import os >>> os  >>> 

Si la única advertencia de usar __file__ es cuando el directorio actual, relativo está en blanco (es decir, cuando se ejecuta como un script desde el mismo directorio donde está el script), una solución trivial es:

 import os.path mydir = os.path.dirname(__file__) or '.' full = os.path.abspath(mydir) print __file__, mydir, full 

Y el resultado:

 $ python teste.py teste.py . /home/user/work/teste 

El truco está en or '.' después de la llamada dirname() . Establece el directorio como . , que significa directorio actual y es un directorio válido para cualquier función relacionada con la ruta.

Por lo tanto, el uso de abspath() no es realmente necesario. Pero si lo usa de todos modos, el truco no es necesario: abspath() acepta rutas en blanco y las interpreta correctamente como el directorio actual.

Me gustaría contribuir con un escenario común (en Python 3) y explorar algunos enfoques.

La función incorporada open () acepta una ruta relativa o absoluta como su primer argumento. La ruta relativa se trata como relativa al directorio de trabajo actual, por lo que se recomienda pasar la ruta absoluta al archivo.

En pocas palabras, si ejecuta un archivo de script con el siguiente código, no se garantiza que el archivo example.txt se creará en el mismo directorio donde se encuentra el archivo de script:

 with open('example.txt', 'w'): pass 

Para arreglar este código necesitamos obtener la ruta al script y hacerlo absoluto. Para garantizar que la ruta sea absoluta, simplemente usamos la función os.path.realpath () . Para obtener la ruta al script, hay varias funciones comunes que devuelven varios resultados de ruta:

  • os.getcwd()
  • os.path.realpath('example.txt')
  • sys.argv[0]
  • __file__

Ambas funciones, os.getcwd () y os.path.realpath () devuelven los resultados de la ruta en función del directorio de trabajo actual . Generalmente no es lo que queremos. El primer elemento de la lista sys.argv es la ruta del script raíz (el script que ejecuta) independientemente de si llama a la lista en el propio script raíz o en cualquiera de sus módulos. Puede ser útil en algunas situaciones. La variable __file__ contiene la ruta del módulo desde el que se ha llamado.


El siguiente código crea correctamente un archivo example.txt en el mismo directorio donde se encuentra el script:

 filedir = os.path.dirname(os.path.realpath(__file__)) filepath = os.path.join(filedir, 'example.txt') with open(filepath, 'w'): pass 

Desde los módulos de un paquete de python tuve que referirme a un archivo que se encontraba en el mismo directorio que el paquete. Ex.

 some_dir/ maincli.py top_package/ __init__.py level_one_a/ __init__.py my_lib_a.py level_two/ __init__.py hello_world.py level_one_b/ __init__.py my_lib_b.py 

Así que en lo anterior tuve que llamar a maincli.py desde el módulo my_lib_a.py sabiendo que top_package y maincli.py están en el mismo directorio. Así es como obtengo el camino a maincli.py:

 import sys import os import imp class ConfigurationException(Exception): pass # inside of my_lib_a.py def get_maincli_path(): maincli_path = os.path.abspath(imp.find_module('maincli')[1]) # top_package = __package__.split('.')[0] # mod = sys.modules.get(top_package) # modfile = mod.__file__ # pkg_in_dir = os.path.dirname(os.path.dirname(os.path.abspath(modfile))) # maincli_path = os.path.join(pkg_in_dir, 'maincli.py') if not os.path.exists(maincli_path): err_msg = 'This script expects that "maincli.py" be installed to the '\ 'same directory: "{0}"'.format(maincli_path) raise ConfigurationException(err_msg) return maincli_path 

Basado en la publicación de PlasmaBinturong modifiqué el código.

Si desea hacer esto dinámicamente en un “progtwig” intente este código:
Mi punto es que puede que no sepa el nombre exacto del módulo para “codificarlo”. Puede seleccionarse de una lista o puede no estar ejecutándose actualmente para usar __file__.

(Lo sé, no funcionará en Python 3)

 global modpath modname = 'os' #This can be any module name on the fly #Create a file called "modname.py" f=open("modname.py","w") f.write("import "+modname+"\n") f.write("modpath = "+modname+"\n") f.close() #Call the file with execfile() execfile('modname.py') print modpath  

Intenté deshacerme del problema “global”, pero encontré casos en los que no funcionó, creo que se puede emular “execfile ()” en Python 3. Ya que está en un progtwig, se puede poner fácilmente en un método o módulo para reutilizar.

Si desea conocer la ruta absoluta desde su script, puede usar el objeto Ruta :

 from pathlib import Path print(Path().absolute()) print(Path().resolve('.')) print(Path().cwd()) 

método cwd ()

Devuelve un nuevo objeto de ruta que representa el directorio actual (como lo devuelve os.getcwd ())

método resolver ()

Hacer el camino absoluto, resolviendo cualquier enlace simbólico. Se devuelve un nuevo objeto de ruta:

Si desea recuperar la ruta raíz del paquete desde cualquiera de sus módulos, los siguientes trabajos (probados en Python 3.6):

 from . import __path__ as ROOT_PATH print(ROOT_PATH) 

También se puede hacer referencia a la ruta principal de __init__.py utilizando __file__ en __file__ lugar.

¡Espero que esto ayude!