pip测试套件使用子进程调用来运行集成测试。最近放置了PR,删除了一些旧的兼容性代码。特别是它替换了b()
函数,明确使用b""
文字。然而,这似乎打破了特定子进程调用永远挂起的地方。更糟糕的是,它只会永远挂在Python 3.3(可能只有Python 3.3.5)上,并且不能轻易地在Travis之外复制。
相关的拉动请求:
其他Pull请求也会出现类似的问题,但是在不同版本的Python和不同的测试用例中它们会失败。这些Pull请求是:
另一位用户今天在IRC上向我报告了一个类似的问题,他们说他们可以在Ubuntu 14.04上使用Python 3.3从死神(但不是在OSX上)本地复制它,而不仅仅是像我一样在Travis上复制它。迄今为止能够做到的。他们已经给我发送了重现的步骤:
$ git clone git@github.com:xavfernandez/pip.git
$ cd pip
$ git checkout debug_stuck
$ pip install pytest==2.5.2 scripttest==1.3 virtualenv==1.11.6 mock==1.0.1 pretend==1.0.8 setuptools==4.0
$ # The below should pass just fine
$ py.test -k test_env_vars_override_config_file -v -s
$ # Now edit pip/req/req_set.py and remove method remove_me_to_block or change its content to print('KO') or pass
$ # The below should hang forever
$ py.test -k test_env_vars_override_config_file -v -s
在上面的例子中,remove_me_to_block
方法不会在任何地方被调用,只是它的存在足以使测试不被阻塞,并且它不存在(或改变它的内容) )足以让测试块永远存在。
大部分调试都是在这个PR(https://github.com/pypa/pip/pull/1901)中进行了更改。在测试通过之前推送了一次提交,直到应用了这个特定的提交 - https://github.com/dstufft/pip/commit/d296df620916b4cd2379d9fab988cbc088e28fe0。具体而言,使用b'\r\n'
或(entry + endline).encode("utf-8")
的更改都会触发它,但这些事情都不在pip install -vvv INITools
的执行路径中,这是它无法执行的命令。< / p>
在尝试追查问题时,我注意到如果我用"something".encode("utf8")
替换至少一次(lambda: "something")().encode("utf8")
来电,那就可以了。
尝试调试时的另一个问题是,我尝试过的各种事情(添加打印语句,无操作atexit
函数,使用异常的异步子进程)只会将问题从特定Python版本的特定测试用例,以及不同版本的Python上的不同测试用例。
我知道如果你直接从subprocess
读/写,subprocess.Popen().stdout/stderr/stdin
模块就会死锁。但是,此代码使用的communicate()
方法可以解决这些问题。它位于wait()
调用内部,communicate()
执行该进程永远挂起,等待pip进程退出。
其他信息:
sys.exit()
被调用。sys.exit()
替换os._exit()
修复了所有悬而未决的问题,但是我不愿意这样做,因为我们正在跳过Python解释器的清理工作。threading.enumerate
验证)。subprocess.PIPE
用于stdout / stderr / stdin,我也有一些更改的组合,但是其他组合会让它不挂起那些没有被使用(或者它将转移到不同的测试用例/ python版本)。PYTHONDONTWRITEBYTECODE=1
禁用字节码生成并且在一个组合中产生效果,但在其他组合中它没有效果。subproccess
调用此内容来重现这一点,但是,如果它不是或者不是,我就不知道这个事实。与此有关。我完全不知道是什么造成了这种情况。
更新#1
使用faulthandler.dump_traceback_later()
我得到了这个结果:
Timeout (0:00:05)!
Current thread 0x00007f417bd92740:
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
[ Duplicate Lines Snipped ]
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/requests/packages/urllib3/response.py", line 287 in closed
Timeout (0:00:05)!
Current thread 0x00007f417bd92740:
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
[ Duplicate Lines Snipped ]
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/requests/packages/urllib3/response.py", line 287 in closed
Timeout (0:00:05)!
Current thread 0x00007f417bd92740:
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
[ Duplicate Lines Snipped ]
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
Timeout (0:00:05)!
Current thread 0x00007f417bd92740:
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
[ Duplicate Lines Snipped ]
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
Timeout (0:00:05)!
Current thread 0x00007f417bd92740:
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
[ Duplicate Lines Snipped ]
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
Timeout (0:00:05)!
Current thread 0x00007f417bd92740:
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
[ Duplicate Lines Snipped ]
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
Timeout (0:00:05)!
Current thread 0x00007f417bd92740:
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
[ Duplicate Lines Snipped ]
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
Timeout (0:00:05)!
Current thread 0x00007f417bd92740:
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
[ Duplicate Lines Snipped ]
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/requests/packages/urllib3/response.py", line 285 in closed
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
[ Duplicate Lines Snipped ]
File "/tmp/pytest-10/test_env_vars_override_config_file0/pip_src/pip/_vendor/cachecontrol/filewrapper.py", line 24 in __getattr__
这告诉我,问题可能与垃圾收集和urllib3有关吗? Filewrapper
中的pip._vendor.cachecontrol.filewrapper
用作urllib3响应对象(子类io.IOBase
)的包装器,以便我们可以通过read()
方法存储每个读取的结果调用缓冲区并返回它,然后一旦文件被完全消耗,就用该缓冲区的内容运行回调,以便我们可以将项存储在缓存中。这可能会以某种方式与GC进行交互吗?
更新#2
如果我在Filewrapper类中添加def __del__(self): pass
方法,那么在我尝试的情况下一切正常。我进行了测试以确保这不是因为我恰好定义了一个方法(通常将其修改为#{1}},然后它又开始失败了。我不确定为什么这完全有效,而且无操作def __del2__(self): pass
方法似乎不太理想。
更新#3
在执行挂起的pip命令期间两次向stderr添加__del__
打印的东西,它们是:
import gc; gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
然后
gc: uncollectable <CallbackFileWrapper 0x7f66385c1cd0>
gc: uncollectable <dict 0x7f663821d5a8>
gc: uncollectable <functools.partial 0x7f663831de10>
gc: uncollectable <_io.BytesIO 0x7f663804dd50>
gc: uncollectable <method 0x7f6638219170>
gc: uncollectable <tuple 0x7f663852bd40>
gc: uncollectable <HTTPResponse 0x7f663831c7d0>
gc: uncollectable <PreparedRequest 0x7f66385c1a90>
gc: uncollectable <dict 0x7f663852cb48>
gc: uncollectable <dict 0x7f6637fdcab8>
gc: uncollectable <HTTPHeaderDict 0x7f663831cb90>
gc: uncollectable <CaseInsensitiveDict 0x7f66385c1ad0>
gc: uncollectable <dict 0x7f6638218ab8>
gc: uncollectable <RequestsCookieJar 0x7f663805d7d0>
gc: uncollectable <dict 0x7f66382140e0>
gc: uncollectable <dict 0x7f6638218680>
gc: uncollectable <list 0x7f6638218e18>
gc: uncollectable <dict 0x7f6637f14878>
gc: uncollectable <dict 0x7f663852c5a8>
gc: uncollectable <dict 0x7f663852cb00>
gc: uncollectable <method 0x7f6638219d88>
gc: uncollectable <DefaultCookiePolicy 0x7f663805d590>
gc: uncollectable <list 0x7f6637f14518>
gc: uncollectable <list 0x7f6637f285a8>
gc: uncollectable <list 0x7f6637f144d0>
gc: uncollectable <list 0x7f6637f14ab8>
gc: uncollectable <list 0x7f6637f28098>
gc: uncollectable <list 0x7f6637f14c20>
gc: uncollectable <list 0x7f6637f145a8>
gc: uncollectable <list 0x7f6637f14440>
gc: uncollectable <list 0x7f663852c560>
gc: uncollectable <list 0x7f6637f26170>
gc: uncollectable <list 0x7f663821e4d0>
gc: uncollectable <list 0x7f6637f2d050>
gc: uncollectable <list 0x7f6637f14fc8>
gc: uncollectable <list 0x7f6637f142d8>
gc: uncollectable <list 0x7f663821d050>
gc: uncollectable <list 0x7f6637f14128>
gc: uncollectable <tuple 0x7f6637fa8d40>
gc: uncollectable <tuple 0x7f66382189e0>
gc: uncollectable <tuple 0x7f66382183f8>
gc: uncollectable <tuple 0x7f663866cc68>
gc: uncollectable <tuple 0x7f6637f1e710>
gc: uncollectable <tuple 0x7f6637fc77a0>
gc: uncollectable <tuple 0x7f6637f289e0>
gc: uncollectable <tuple 0x7f6637f19f80>
gc: uncollectable <tuple 0x7f6638534d40>
gc: uncollectable <tuple 0x7f6637f259e0>
gc: uncollectable <tuple 0x7f6637f1c7a0>
gc: uncollectable <tuple 0x7f6637fc8c20>
gc: uncollectable <tuple 0x7f6638603878>
gc: uncollectable <tuple 0x7f6637f23440>
gc: uncollectable <tuple 0x7f663852c248>
gc: uncollectable <tuple 0x7f6637f2a0e0>
gc: uncollectable <tuple 0x7f66386a6ea8>
gc: uncollectable <tuple 0x7f663852f9e0>
gc: uncollectable <tuple 0x7f6637f28560>
这是有用的信息吗?我之前从未使用过那面旗帜所以我不知道这是不寻常的。
答案 0 :(得分:19)
在Python 2中,如果一组对象在链(引用循环)中链接在一起,并且至少一个对象具有__del__
方法,则垃圾收集器将不会删除这些对象。如果您有一个参考周期,添加__del__()
方法可能只是隐藏错误(解决方法错误)。
根据您的更新#3,您似乎遇到了这样的问题。