web-dev-qa-db-ja.com

pyInstallerによって生成されたPython EXEでのアプリケーションパスの決定

単一の.pyファイルに常駐するアプリケーションがあります。 pyInstallerをWindows用のEXEに正常にバンドルすることができました。問題は、アプリケーションが常に同じディレクトリのアプリケーションのすぐ横にある.cfgファイルを必要とすることです。

通常、私は次のコードを使用してパスを作成します。

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

ただし、pyInstallerによって生成されたEXEから呼び出されたとき、sys.pathは空白のようです。 pythonインタラクティブコマンドラインを実行してsys.path [0]をフェッチしようとすると、この同じ動作が発生します。

現在実行中のアプリケーションのパスを取得して、そのアプリケーションに関連するファイルを見つけるためのより具体的な方法はありますか?

49
Soviut

解決策を見つけました。アプリケーションがスクリプトとして実行されているか、フリーズされたexeとして実行されているかを確認する必要があります。

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)
88
Soviut

PyInstallerの documentation によると、アプリケーションパスを回復するための推奨方法は次のとおりです。

#!/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__))

PyInstaller v3.2でテスト済みですが、これは確かに以前のバージョンでも機能しています。

Soviutのソリューションは機能しません。少なくとも最近のバージョンのpyInstallerでは一般的には機能しません(OPは何年も前のものであることに注意してください)。たとえば、MacOSでは、アプリケーションを1つのファイルのバンドルにバンドルする場合、sys.executableは埋め込みアーカイブの場所のみを指します。つまり、notpyInstallerブートローダーが一時的なアプリケーション環境を作成した後にアプリケーションが実際に実行される場所。 sys._MEIPASSのみがその場所を正しく指します。 pyInstallerの動作の詳細については、 this doc-page を参照してください。

24
normanius

コードを少し短くしました。

import os, sys

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

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

だが、 sys._MEIPASSが間違ったディレクトリを指しています。 sys._MEIPASS + \app_name

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

それは私にとってはうまくいきます。

3
user1986021

__file__は、コマンドラインからpython実行可能ファイルとして機能します。また、凍結モードでは実際のパスなしでスクリプトファイル名を指定します。ただし、インタラクティブモードではエラーが発生します。

以下は3つのモードすべてで機能します。

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)

3つの異なるモードでの出力:

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>
1
Rafiq

ここに多くの答えがありますが、この解決策はほとんどの状況で機能することがわかりました:

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)
0
dashesy