14

我在分发使用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()
4

2 回答 2

3

一个简单的解决方案是将python site-packages 目录中的pytz模块中的zoneinfo目录添加到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()

(由提问者回答)

于 2014-08-02T18:05:33.503 回答
3

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

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

  • 只需将整个 pytz 目录直接复制到您的 dist 目录中
  • 在您的程序中,将 main-executable 的路径添加到 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 )
于 2014-10-27T13:18:33.733 回答