将使用cx_freeze打包的python程序copy到别的机器上运行时碰到这样的错误:
jerrykwan@jerrykwan:~/Downloads/exe.linux-x86_64-2.7$ ./server_family_info
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/cx_Freeze/initscripts/Console.py", line 27, in
File "server_family_info.py", line 14, in
File "/usr/local/lib/python2.7/site-packages/tornado-2.4.1-py2.7.egg/tornado/web.py", line 59, in
File "/usr/local/lib/python2.7/email/utils.py", line 27, in
File "/usr/local/lib/python2.7/random.py", line 49, in
File "/usr/local/lib/python2.7/hashlib.py", line 136, in
File "/usr/local/lib/python2.7/hashlib.py", line 71, in __get_builtin_constructor
ImportError: No module named _md5
通过日志可以看出问题很明显,应该就是在hashlib模块中出的问题.
hashlib源码如下:
# This tuple and __get_builtin_constructor() must be modified if a new # always available algorithm is added. __always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') algorithms = __always_supported __all__ = __always_supported + ('new', 'algorithms') def __get_builtin_constructor(name): try: if name in ('SHA1', 'sha1'): import _sha return _sha.new elif name in ('MD5', 'md5'): import _md5 return _md5.new elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'): import _sha256 bs = name[3:] if bs == '256': return _sha256.sha256 elif bs == '224': return _sha256.sha224 elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'): import _sha512 bs = name[3:] if bs == '512': return _sha512.sha512 elif bs == '384': return _sha512.sha384 except ImportError: pass # no extension module, this hash is unsupported. raise ValueError('unsupported hash type %s' % name) def __get_openssl_constructor(name): try: f = getattr(_hashlib, 'openssl_' + name) # Allow the C module to raise ValueError. The function will be # defined but the hash not actually available thanks to OpenSSL. f() # Use the C function directly (very fast) return f except (AttributeError, ValueError): return __get_builtin_constructor(name) def __py_new(name, string=''): """new(name, string='') - Return a new hashing object using the named algorithm; optionally initialized with a string. """ return __get_builtin_constructor(name)(string) def __hash_new(name, string=''): """new(name, string='') - Return a new hashing object using the named algorithm; optionally initialized with a string. """ try: return _hashlib.new(name, string) except ValueError: # If the _hashlib module (OpenSSL) doesn't support the named # hash, try using our builtin implementations. # This allows for SHA224/256 and SHA384/512 support even though # the OpenSSL library prior to 0.9.8 doesn't provide them. return __get_builtin_constructor(name)(string) try: import _hashlib new = __hash_new __get_hash = __get_openssl_constructor except ImportError: new = __py_new __get_hash = __get_builtin_constructor for __func_name in __always_supported: # try them all, some may not work due to the OpenSSL # version not supporting that algorithm. try: globals()[__func_name] = __get_hash(__func_name) except ValueError: import logging logging.exception('code for hash %s was not found.', __func_name) # Cleanup locals() del __always_supported, __func_name, __get_hash del __py_new, __hash_new, __get_openssl_constructor
从hashlib.py的源码很容易看出,对于hashlib提供的('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')等算法,hashlib的做法是先去检验python内置的_hashlib是否支持openssh,这点儿可以认为是python编译的时候是否加入了openssl的支持,如果已经拥有openssl的支持,则直接使用python内置的openssl提供的算法,否则使用其他第三方的算法
可以确定的是,我们使用cx_freeze进行编译的时候机器上的python是支持openssl的,但为什么编译完的程序却不具备该功能呢?
通过使用ldd查看动态库依赖关系可以看出如下信息:
jerrykwan@jerrykwan:~/Downloads/exe.linux-x86_64-2.7$ ldd server_family_info
linux-vdso.so.1 => (0x00007fff5c5ff000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe755d1f000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe755b1b000)
libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fe755917000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe75561b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe75525c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe755f53000)
jerrykwan@jerrykwan:~/Downloads/exe.linux-x86_64-2.7$ ldd _hashlib.so
linux-vdso.so.1 => (0x00007fff042f3000)
libssl.so.6 => not found
libcrypto.so.6 => not found
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f2ed15fd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2ed123e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2ed1a36000)
原来是 libssl.so.6和libcrypto.so.6等动态库找不到的原因导致hashlib模块认为使用cx_freeze编译后的python环境不具备openssl支持,进而使用第三方md5等算法,但此时运行环境中第三方md5等模块也不具备,所以程序运行时出错
为解决这一问题可以在打包时将libssl.so.6和libcrypto.so.6 copy到运行环境中,也可以调整系统的/etc/ld.so.conf以便很方便的找到libssl.so.6和libcrypto.so.6