Py2exe和文件系统

3

我有一个Python应用程序。它通过执行以下操作来加载配置文件(以及其他各种文件):

_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CONFIG_DIR = os.path.join(_path, 'conf')

这个功能很好。然而,当我使用py2exe打包应用程序时,会发生一些不好的事情:

  File "proj\config.pyc", line 8, in <module>
WindowsError: [Error 3] The system cannot find the path specified: 'C:\\proj\
\dist\\library.zip\\conf'

显然这是一个无效的路径...有没有更加健壮的方法来解决这个问题?我不想在程序中指定绝对路径,因为它可能会放置在不同的文件夹中。我应该只说“如果它说文件夹名称是'library.zip',那么就再往下一级进入'dist'文件夹”吗?
请注意,我的目录结构很嵌套......例如,我有一个名为gui.utils.images的模块,存储在“gui/utils/images.py”中,它使用其路径来访问“gui/images/ok.png”等内容。现在py2exe版本将尝试访问“proj/dist/library.zip/gui/images/ok.png”,或者其他什么的,这将不起作用。
5个回答

10

如果我理解正确,做这种事情的常规方法如下:

  1. 检查sys.frozen是否被设置,py2exe会设定它,使用类似getattr(sys,'frozen','')的方法
  2. 如果没有被设置,那么采用通常的方法,因为您是从源代码运行的
  3. 如果已经设置,检查sys.executable ,因为它将指向.exe文件所在的位置(而不是通常指向python.exe所在的位置)。使用类似os.path.dirname(sys.executable)的方法,然后与相对路径结合使用以找到子文件夹等

例如:

frozen = getattr(sys, 'frozen', '')

if not frozen:
    # not frozen: in regular python interpreter
    approot = os.path.dirname(__file__)

elif frozen in ('dll', 'console_exe', 'windows_exe'):
    # py2exe:
    approot = os.path.dirname(sys.executable)

elif frozen in ('macosx_app',):
    # py2app:
    # Notes on how to find stuff on MAC, by an expert (Bob Ippolito):
    # http://mail.python.org/pipermail/pythonmac-sig/2004-November/012121.html
    approot = os.environ['RESOURCEPATH']

或其变体...后者处理py2app的使用。如果需要,您可以将其扩展到其他构建器。


2

使用os.path.dirname(os.path.abspath(sys.argv[0]))来从py2exe应用程序中获取路径信息,它与从python脚本中获取的方式相同(因此您可以在不创建exe文件的情况下进行测试),并且可以从exe文件中获取。

这比使用相对路径要好得多,因为您不必担心应用程序(.py或.exe)运行的位置。


2
你认为使用相对路径来包含所有的文件怎么样?我猜你可以在你关于ok.png的示例中使用sys.path.append(".." + os.path.sep + "images"),然后你就可以用open("ok.png", "rb")了。使用相对路径应该可以解决由py2exe生成的library.zip文件的问题,至少对我来说是这样的。

嗯,我会尝试一下。我正在编写一个名为getAbsPath()的函数,它接受一个相对路径并返回正确的打开方式。它将检测是否在library.zip中,如果是,则会转到不同的目录,而不是实际的文件系统。 - Claudiu
你也可以尝试将你的包含文件放入一个包目录中,因为 __init__.py__path__ 属性会存储 __init__.py 脚本的路径。 - fucx

2

以下是我的解决方案(已在Windows、Linux和Mac上测试)。如果应用程序或Python脚本通过符号链接启动,也可以正常工作。

# Get if frozen
is_frozen = bool( getattr(sys, 'frozen', None) )

# Get path variable
path = sys.path if is_frozen else sys.argv

# Get nonempty first element or raise error
if path and path[0]:
    apppath = path[0]
elif is_frozen():
    raise RuntimeError('Cannot determine app path because sys.path[0] is empty.')
else:
    raise RuntimeError('Cannot determine app path in interpreter mode.')

# Make absolute (eliminate symbolic links)
apppath = os.path.abspath( os.path.realpath(apppath) )

# Split and return
appdir, appname = os.path.split(apppath)

0

我在Windows中使用以下技巧。
当程序在py2exe“可执行文件”(即my_application.exe)中冻结时,dirname(__file__)返回您的主Python脚本运行的文件夹。 奇怪的是,该文件夹不是包含my_application.exe的文件夹,而是my_application.exe本身。
请注意,my_application.exe不是二进制文件,而是一个压缩的文件夹(您可以解压缩它),其中包含Python库和编译后的脚本。
这就是为什么您的__file__os.path.dirname实际上是my_application.exe

因此,此代码为您提供配置文件或图像的根目录(如果是冻结应用程序,则为包含py2exe可执行文件的文件夹,否则为您的Python脚本运行的文件夹):

dirname = os.path.dirname(__file__)
if dirname.endswith('.exe'):
    dirname = os.path.split(dirname)[0]

显然,你可以使用其他答案展示的更通用、可移植的方法来检测文件是否被冻结,而不是使用endswith方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接