I'm having a problem distributing an application that utilizes pytz. I'm using Py2Exe to create an executable from my Python source.
For a simple example of the problem I'm having, I have: pytz_test.py:
import pytz
tz_au = pytz.timezone("Australia/Sydney")
print tz_au
and in setup.py:
from distutils.core import setup
import py2exe
setup(console=['pytz_test.py'], options={"py2exe" : { 'packages': ['pytz'], } })
I then run setup.py:
python setup.py py2exe
Which compiles the executable. Running the created pytz_test.exe I get:
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'
I assume it is because the timezone information isn't getting bundled with the executable, but I'm not sure how to make it happen.
EDIT: A simple solution would be to add the zoneinfo directory, from the pytz module in the python site-packages directory, to the library.zip.
To do this automatically, I followed the solution in that project Google Transit Data Feed used, from: http://code.google.com/p/googletransitdatafeed/source/browse/trunk/python/setup.py
My modified setup.py now looks like:
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()
A simple solution would be to add the zoneinfo directory, from the pytz module in the python site-packages directory, to the library.zip.
To do this automatically, I followed the solution the project Google Transit Data Feed used, from: http://code.google.com/p/googletransitdatafeed/source/browse/trunk/python/setup.py
My modified setup.py now looks like:
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()
(Answered by asker)
Zipping the zoneinfo manually (as described by Jason S) helped indeed for building the package on one of my computers. However, when I built the package on another computer - the error was back! Finding the reason took me a while - so I'd better share.
The proposed solution doesn't work with new pytz-versions (at least with 2014.7)! Digging why this is revealed that pytz changed the format of the zoneinfo files from pyc to some binary format. To me it looks like, with this change they "broke" the option to pack pytz into a zip, since the builtin zipimport mechanism of python doesn't work for loading binary files. Actually, this problem should be fixed by pytz but for now I found another solution:
Practically, this means within your setup.py replace the pytz-zipping with
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') )
and move pytz from the "packages"-option to "excludes":
options = {
"py2exe" : {
"compressed": 1,
"optimize": 2,
"packages": [],
"excludes": ['pytz']
}
}
At the main entry of your program (to make sure it is executed before importing pytz), you would have to add something like:
import os, sys
basePath = os.path.dirname( os.path.abspath( sys.argv[0] ) )
sys.path.insert( 0, basePath )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With