使用 Py2Exe 编译的 Python 应用程序引发 UnknownTimezoneError 异常

Posted

技术标签:

【中文标题】使用 Py2Exe 编译的 Python 应用程序引发 UnknownTimezoneError 异常【英文标题】:UnknownTimezoneError Exception Raised with Python Application Compiled with Py2Exe 【发布时间】:2012-02-27 20:28:42 【问题描述】:

我在分发使用 pytz 的应用程序时遇到问题。我正在使用 Py2Exe 从我的 Python 源代码创建可执行文件。

对于我遇到的问题的一个简单示例,我有: pytz_test.py:

import pytz

tz_au = pytz.timezone("Australia/Sydney")
print tz_au

在 setup.py 中:

from distutils.core import setup
import py2exe

setup(console=['pytz_test.py'], options="py2exe" :  'packages': ['pytz'],  )

然后我运行 setup.py:

python setup.py py2exe

编译可执行文件。运行创建的 pytz_test.exe 我得到:

Traceback (most recent call last):
  File "pytz_test.py", line 3, in <module>
    tz_au = pytz.timezone("Australia/Sydney")
  File "pytz\__init__.pyc", line 185, in timezone
pytz.exceptions.UnknownTimeZoneError: 'Australia/Sydney'

我认为这是因为时区信息没有与可执行文件捆绑在一起,但我不确定如何实现。

编辑: 一个简单的解决方案是将 zoneinfo 目录从 python site-packages 目录中的 pytz 模块添加到 library.zip

为了自动执行此操作,我遵循了 Google Transit Data Feed 项目中使用的解决方案,来自: http://code.google.com/p/googletransitdatafeed/source/browse/trunk/python/setup.py

我修改后的 setup.py 现在看起来像:

from distutils.core import setup
import glob
import py2exe

options = 
    "py2exe" :  
        "compressed": 1, 
        "optimize": 2,
        'packages': ['pytz'], 
      


setup(console=['pytz_test.py'], options=options)

import pytz
import os 
import zipfile
zipfile_path = os.path.join("dist/" 'library.zip')
z = zipfile.ZipFile(zipfile_path, 'a')
zoneinfo_dir = os.path.join(os.path.dirname(pytz.__file__), 'zoneinfo')
disk_basedir = os.path.dirname(os.path.dirname(pytz.__file__))
for absdir, directories, filenames in os.walk(zoneinfo_dir):
    assert absdir.startswith(disk_basedir), (absdir, disk_basedir)
    zip_dir = absdir[len(disk_basedir):]
    for f in filenames:
      z.write(os.path.join(absdir, f), os.path.join(zip_dir, f))

z.close()

【问题讨论】:

您还需要当前版本的 setuptools。特别是 pkg_resources。 'import pkg_resources' 不能失败。 pkg_resources 到底在哪里使用?还是直接导入? 【参考方案1】:

手动压缩 zoneinfo(如 Jason S 所述)确实有助于在我的一台计算机上构建包。 但是,当我在另一台计算机上构建软件包时 - 错误又回来了! 找到原因花了我一段时间 - 所以我最好分享一下。

建议的解决方案不适用于新的 pytz 版本(至少对于 2014.7)! 挖掘为什么会发现 pytz 将 zoneinfo 文件的格式从 pyc 更改为某种二进制格式。在我看来,通过这种更改,他们“打破”了将 pytz 打包成 zip 的选项,因为 python 的内置 zipimport 机制不适用于加载二进制文件。 其实这个问题应该由 pytz 解决 但是现在我找到了另一个解决方案:

只需将整个 pytz 目录直接复制到您的 dist 目录中 在您的程序中,将您的主可执行文件的路径添加到 python 的搜索路径中

实际上,这意味着在您的 setup.py 中将 pytz-zipping 替换为

import pytz, os, shutil
srcDir = os.path.dirname( pytz.__file__ )
dstDir = os.path.join( 'dist', 'pytz' )
shutil.copytree( srcDir, dstDir, ignore = shutil.ignore_patterns('*.py') )

并将 pytz 从“packages”选项移动到“excludes”:

options = 
    "py2exe" :  
        "compressed": 1, 
        "optimize": 2,
        "packages": [],
        "excludes": ['pytz']
      

在程序的主入口处(以确保在导入 pytz 之前执行它),您必须添加如下内容:

import os, sys
basePath = os.path.dirname( os.path.abspath( sys.argv[0] ) )
sys.path.insert( 0, basePath )

【讨论】:

这对我有用 (pytz 2014.10),但我必须从 shutil.copytree() 中删除所有忽略。 是的!有效!最奇怪的是,我的 pytz 和 py2exe 正在工作。然后我在我的程序中修改了一些东西,完全与pytz无关,突然停止工作并给出了UnkownTimeZoneError 言下之意,完全正确!那也是我的问题。而且由于(至少对我而言)py2exe 的作用以及它如何收集它的东西并不总是很明显,我什至不得不将调试打印破解到 pytz 以找出在 py2exe 的收集阶段出了什么问题。 :-) 为我工作。谢谢你:) 我还没有添加以下行: os.environ.setdefault('PYTZ_TZDATADIR', f'basePath\\pytz\\zoneinfo') 因为它仍然没有找到数据。 【参考方案2】:

一个简单的解决方案是将 zoneinfo 目录从 python site-packages 目录中的 pytz 模块添加到 library.zip em>。

为了自动执行此操作,我遵循了 Google Transit Data Feed 项目使用的解决方案,来自: http://code.google.com/p/googletransitdatafeed/source/browse/trunk/python/setup.py

我修改后的 setup.py 现在看起来像:

from distutils.core import setup
import glob
import py2exe

options = 
    "py2exe" :  
        "compressed": 1, 
        "optimize": 2,
        'packages': ['pytz'], 
      


setup(console=['pytz_test.py'], options=options)

import pytz
import os 
import zipfile
zipfile_path = os.path.join("dist/" 'library.zip')
z = zipfile.ZipFile(zipfile_path, 'a')
zoneinfo_dir = os.path.join(os.path.dirname(pytz.__file__), 'zoneinfo')
disk_basedir = os.path.dirname(os.path.dirname(pytz.__file__))
for absdir, directories, filenames in os.walk(zoneinfo_dir):
    assert absdir.startswith(disk_basedir), (absdir, disk_basedir)
    zip_dir = absdir[len(disk_basedir):]
    for f in filenames:
      z.write(os.path.join(absdir, f), os.path.join(zip_dir, f))

z.close()

(由提问者回答)

【讨论】:

以上是关于使用 Py2Exe 编译的 Python 应用程序引发 UnknownTimezoneError 异常的主要内容,如果未能解决你的问题,请参考以下文章

是否有与 python 3.5 兼容的 py2exe 版本?

python编写游戏怎么打包——详解python+pygame游戏开发之使用Py2exe打包游戏为exe文件

python编写游戏怎么打包——详解python+pygame游戏开发之使用Py2exe打包游戏为exe文件

python编写游戏怎么打包——详解python+pygame游戏开发之使用Py2exe打包游戏为exe文件

py2exe打包python脚本

在 Windows 98 中通过网络访问 py2exe 程序会引发 ImportErrors