將使用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 <module>
File "server_family_info.py", line 14, in <module>
File "/usr/local/lib/python2.7/site-packages/tornado-2.4.1-py2.7.egg/tornado/web.py", line 59, in <module>
File "/usr/local/lib/python2.7/email/utils.py", line 27, in <module>
File "/usr/local/lib/python2.7/random.py", line 49, in <module>
File "/usr/local/lib/python2.7/hashlib.py", line 136, in <module>
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