Исключение UnknownTimezoneError, созданное с помощью приложения Python, составленного с помощью Py2Exe
У меня проблема с распространением приложения, которое использует 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'
Я предполагаю, что это потому, что информация о часовом поясе не входит в комплект с исполняемым файлом, но я не уверен, как это сделать.
EDIT:
Простым решением было бы добавить каталог zoneinfo из модуля pytz в каталоге python site-packages в 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()
Ответы
Ответ 1
Простым решением было бы добавить каталог zoneinfo из модуля pytz в каталог python site-packages в 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()
(Ответил вопрос)
Ответ 2
Замена зоныinfo вручную (как описано Jason S) действительно помогла создать пакет на одном из моих компьютеров.
Однако, когда я построил пакет на другом компьютере - ошибка вернулась!
Поиск причины вызвал у меня некоторое время - так что мне лучше поделиться.
Предлагаемое решение не работает с новыми версиями pytz (по крайней мере, с 2014.7)!
Копаем, почему выясняется, что pytz изменил формат файлов zoneinfo с pyc на некоторый двоичный формат. Для меня это похоже, с этим изменением они "сломали" вариант пакет pytz в zip, так как встроенный механизм zipimport python не работает для загрузки двоичных файлов. На самом деле эта проблема должна быть исправлена 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" -option в "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 )