使用 py2exe 时导入 `requests` 的问题

problems with importing `requests` when using py2exe

TL;DR

当冻结 py2exe 时,在 Python3 项目中导入 requests 库失败。 解冻项目有效。 该问题似乎与 requests 模块中的相对导入有关。

详细说明

我使用 Python3 创建了一个小项目,并想将其转换为 w32 的可执行文件。 该项目依赖于 PySiderequests(2.13.0;实际上这是使用的 pyapi-gitlab 包的次要依赖项)。 所有依赖项都已通过 pip3 安装。 Python 版本是 3.4.4(因为 PySide 不支持任何更高版本的 Py3)。 我正在使用 py2exe 0.9.2.2.

一切(例如导入和使用 PySide)都很好,但 import requests 内部深处失败了。

这里有一个稍微简单的例子来说明这个问题:

import logging
logging.basicConfig()
log = logging.getLogger()

import sys
print("Python: %s" % (sys.version,))

try:
    import requests
except ImportError:
    log.fatal("failed to import 'requests'", exc_info=True)

当我从 cmdline 中 运行 时,我得到:

E:\p2e> python p2e.py
Python: 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 19:28:18) [MSC v.1600 32 bit (Intel)]
E:\p2e>

不错。 以下最小 setup.py 应该将其转换为 exe:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from distutils.core import setup
import py2exe

setup(console=['p2e.py'])

进行实际转换会给出一些 "missing modules" 警告:

E:\p2e> python setup.py py2exe
running py2exe

  24 missing Modules                 
  ------------------                 
? Cookie                              imported from requests.compat
? OpenSSL                             imported from requests.packages.urllib3.contrib.pyopenssl
? Queue                               imported from requests.packages.urllib3.connectionpool
? _abcoll                             imported from requests.packages.urllib3.packages.ordered_dict
? backports                           imported from requests.packages.urllib3.packages.ssl_match_hostname
? certifi                             imported from requests.certs
? chardet                             imported from requests.packages
? cookielib                           imported from requests.compat
? cryptography                        imported from requests.packages.urllib3.contrib.pyopenssl
? idna                                imported from requests.models, requests.packages.urllib3.contrib.pyopenssl
? netbios                             imported from uuid
? readline                            imported from cmd, code, pdb
? simplejson                          imported from requests.compat
? six                                 imported from requests.packages.urllib3.contrib.pyopenssl
? socks                               imported from requests.packages.urllib3.contrib.socks
? urllib.getproxies                   imported from requests.compat
? urllib.proxy_bypass                 imported from requests.compat
? urllib.quote                        imported from requests.compat
? urllib.quote_plus                   imported from requests.compat
? urllib.unquote                      imported from requests.compat
? urllib.unquote_plus                 imported from requests.compat
? urllib.urlencode                    imported from requests.compat
? urllib3                             imported from requests.packages
? win32wnet                           imported from uuid
Building 'dist\p2e.exe'.
Building shared code archive 'dist\library.zip'.
Copy c:\windows\system32\python34.dll to dist
Copy C:\Python34\DLLs\_ssl.pyd to dist\_ssl.pyd
Copy C:\Python34\DLLs\_lzma.pyd to dist\_lzma.pyd
Copy C:\Python34\DLLs\select.pyd to dist\select.pyd
Copy C:\Python34\DLLs\_hashlib.pyd to dist\_hashlib.pyd
Copy C:\Python34\DLLs\_ctypes.pyd to dist\_ctypes.pyd
Copy C:\Python34\DLLs\_bz2.pyd to dist\_bz2.pyd
Copy C:\Python34\DLLs\_socket.pyd to dist\_socket.pyd
Copy C:\Python34\DLLs\pyexpat.pyd to dist\pyexpat.pyd
Copy C:\Python34\DLLs\unicodedata.pyd to dist\unicodedata.pyd
E:\p2e>

我经常忽略这些警告,运行 生成的 exe:

E:\p2e> dist\p2e.exe
CRITICAL:root:failed to import 'requests'
Traceback (most recent call last):
  File "c:\Python34\lib\site-packages\requests\packages\__init__.py", line 27, in <module>
    from . import urllib3
  File "c:\Python34\lib\site-packages\requests\packages\urllib3\__init__.py", line 8, in <module>
    from .connectionpool import (
  File "c:\Python34\lib\site-packages\requests\packages\urllib3\connectionpool.py", line 28, in <module>
    from .packages.six.moves import queue
  File "c:\Python34\lib\site-packages\requests\packages\urllib3\packages\six.py", line 92, in __get__
    result = self._resolve()
  File "c:\Python34\lib\site-packages\requests\packages\urllib3\packages\six.py", line 115, in _resolve
    return _import_module(self.mod)
  File "c:\Python34\lib\site-packages\requests\packages\urllib3\packages\six.py", line 82, in _import_module
    __import__(name)
ImportError: No module named 'queue'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "p2e.py", line 9, in <module>
  File "c:\Python34\lib\site-packages\requests\__init__.py", line 63, in <module>
    from . import utils
  File "c:\Python34\lib\site-packages\requests\utils.py", line 24, in <module>
    from ._internal_utils import to_native_string
  File "c:\Python34\lib\site-packages\requests\_internal_utils.py", line 11, in <module>
    from .compat import is_py2, builtin_str, str
  File "c:\Python34\lib\site-packages\requests\compat.py", line 11, in <module>
    from .packages import chardet
  File "c:\Python34\lib\site-packages\requests\packages\__init__.py", line 29, in <module>
    import urllib3
ImportError: No module named 'urllib3'
Python: 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 19:28:18) [MSC v.1600 32 bit (Intel)]
E:\p2e>

我百思不得其解

似乎 requests 包对 - 支持包含的第 3 方库 (urllib) 和包管理器安装的库。 - 同时支持 Py2 和 Py3(使用 six 的本地副本)

两者似乎都会产生问题。

我可以修改 urllib3 问题,通过修补 requests 以仅在相对路径中搜索该包(即使原始代码也是如此,只是用 try/catch 捕获任何 ImportError 然后退回到全局导入。)

我不知道如何解决 import queue 问题。

毕竟,当我 运行 Py3 解释器中的脚本时,它 正常工作。

注意

有一个类似(可能相同)的问题,但它没有包含那么多信息:py2exe "requests" module missing

看起来 urllib3 试图导入失败的 queue 模块。如果失败,请求将无法导入 `urllib3.

要强制 py2exe 包含 queue 模块,请在调用它时使用 --includes queue