Pregunta

Tengo una aplicación que reside en un solo archivo .py. He podido hacer que pyInstaller lo incluya en un EXE para Windows. El problema es que la aplicación requiere un archivo .cfg que siempre se encuentra justo al lado de la aplicación en el mismo directorio.

Normalmente, construyo la ruta con el siguiente código:

import os
config_name = 'myapp.cfg'
config_path = os.path.join(sys.path[0], config_name)

Sin embargo, parece que sys.path está en blanco cuando se llama desde un EXE generado por pyInstaller. Este mismo comportamiento se produce cuando ejecuta la línea de comandos interactiva de python e intenta obtener sys.path [0].

¿Existe una forma más concreta de obtener la ruta de la aplicación que se está ejecutando actualmente para que pueda encontrar archivos relativos a ella?

¿Fue útil?

Solución

He encontrado una solución. Debe comprobar si la aplicación se ejecuta como una secuencia de comandos o como un archivo ejecutado congelado:

import os
import sys

config_name = 'myapp.cfg'

# determine if application is a script file or frozen exe
if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(sys.executable)
elif __file__:
    application_path = os.path.dirname(__file__)

config_path = os.path.join(application_path, config_name)

Otros consejos

De acuerdo con la documentación de pyInstaller, el método sugerido La ruta de recuperación de la aplicación es la siguiente:

#!/usr/bin/python3
import sys, os
if getattr(sys, 'frozen', False):
    # If the application is run as a bundle, the pyInstaller bootloader
    # extends the sys module by a flag frozen=True and sets the app 
    # path into variable _MEIPASS'.
    application_path = sys._MEIPASS
else:
    application_path = os.path.dirname(os.path.abspath(__file__))

Probado para pyInstaller v3.2, pero ciertamente esto también ha funcionado para versiones anteriores.

La solución de Soviut no funciona, al menos no en general para las versiones recientes de pyInstaller (tenga en cuenta que el OP tiene muchos años). Por ejemplo, en MacOS, al agrupar una aplicación en un paquete de un solo archivo, sys.executable apunta solo a la ubicación del archivo incorporado, que es no la ubicación donde la aplicación realmente se ejecuta después de que el cargador de arranque pyInstaller haya creado un entorno de aplicación temporal. Solo sys._MEIPASS señala correctamente esa ubicación. Consulte esta página de documento para obtener más información sobre cómo funciona pyInstaller.

Acorté un poco el código.

import os, sys

if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(sys.executable)
    os.chdir(application_path)

logging.debug('CWD: ' + os.getcwd())

Pero, sys._MEIPASS apunta a un directorio incorrecto. Creo que también necesita sys._MEIPASS + \app_name

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

Eso funciona para mí.

__file__ funciona desde la línea de comandos con el ejecutable de Python. También da el nombre del archivo de script sin ruta real en modo congelado. Sin embargo, da error en modo interactivo.

Lo siguiente funcionará para los tres modos:

import sys,os

config_name = 'myapp.cfg'

if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(sys.executable)
    running_mode = 'Frozen/executable'
else:
    try:
        app_full_path = os.path.realpath(__file__)
        application_path = os.path.dirname(app_full_path)
        running_mode = "Non-interactive (e.g. 'python myapp.py')"
    except NameError:
        application_path = os.getcwd()
        running_mode = 'Interactive'

config_full_path = os.path.join(application_path, config_name)

print('Running mode:', running_mode)
print('  Appliction path  :', application_path)
print('  Config full path :', config_full_path)

Salida en tres modos diferentes:

Running mode: Interactive
  Appliction path  : C:\Projects\MyAppDir
  Config full path : C:\Projects\MyAppDir\myapp.cfg

C:\Projects\MyAppDir>myapp.exe
Running mode: Frozen/executable
  Appliction path  : C:\Program Files\myapp
  Config full path : C:\Program Files\myapp\myapp.cfg

C:\Projects\MyAppDir>python myapp.py
Running mode: Non-interactive (e.g. 'python myapp.py')
  Appliction path  : C:\Projects\MyAppDir
  Config full path : C:\Projects\MyAppDir\myapp.cfg

C:\Projects\MyAppDir>

Muchas respuestas aquí, pero encontré que esta solución funciona en la mayoría de las situaciones:

import os
import sys
import os.path as op
try:
    this_file = __file__
except NameError:
    this_file = sys.argv[0]
this_file = op.abspath(this_file)
if getattr(sys, 'frozen', False):
    application_path = getattr(sys, '_MEIPASS', op.dirname(sys.executable))
else:
    application_path = op.dirname(this_file)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top