Python pytest(py.test)在cygwin中启动非常慢
在cygwin中,py.test启动非常慢。这看起来不像是一个收集问题,因为有两个原因:相同的测试在linux中很快启动。有时,如果在cygwin中以足够快的速度重新运行相同的测试,它将在不到1秒的时间内启动。运行time命令时,如果提供了Python pytest(py.test)在cygwin中启动非常慢,python,pytest,Python,Pytest,在cygwin中,py.test启动非常慢。这看起来不像是一个收集问题,因为有两个原因:相同的测试在linux中很快启动。有时,如果在cygwin中以足够快的速度重新运行相同的测试,它将在不到1秒的时间内启动。运行time命令时,如果提供了--collection only选项以避免运行实际测试,则它将在0.4秒或11.7秒后启动。我还向hookspytest\u configure()和pytest\u ignore\u collect()添加了一个打印,以确保它确实在收集开始之前 还有其他的
--collection only
选项以避免运行实际测试,则它将在0.4秒或11.7秒后启动。我还向hookspytest\u configure()
和pytest\u ignore\u collect()
添加了一个打印,以确保它确实在收集开始之前
还有其他的问题,比如linke等等,但我不知道为什么在cygwin的领导下它会变慢,以及如何解决这个问题
更新:通过python-mcprofile-scumulat~/…/py.test conftest.py
运行分析。下面是排名前20位的结果。我倾向于认为这是cygwin或cygwin python包中的posix.stat
的问题,或者存在于不同的地方
104699 function calls (102659 primitive calls) in 12.223 CPU seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.016 0.016 12.223 12.223 {execfile}
1 0.000 0.000 12.223 12.223 <string>:1(<module>)
1 0.000 0.000 12.207 12.207 py.test:4(<module>)
1 0.000 0.000 12.051 12.051 config.py:23(main)
48/22 0.000 0.000 12.051 0.548 core.py:526(_docall)
48/22 0.000 0.000 12.051 0.548 core.py:520(__call__)
129/82 0.000 0.000 12.051 0.147 core.py:387(execute)
1 0.000 0.000 11.926 11.926 config.py:634(pytest_cmdline_parse)
1 0.000 0.000 11.926 11.926 config.py:70(_prepareconfig)
1 0.000 0.000 11.926 11.926 config.py:741(parse)
4/3 0.000 0.000 11.926 3.975 core.py:97(wrapped_call)
4/3 0.000 0.000 11.926 3.975 core.py:121(__init__)
1 0.000 0.000 11.911 11.911 config.py:706(_preparse)
70 0.000 0.000 11.817 0.169 local.py:363(check)
260 11.817 0.045 11.817 0.045 {posix.stat} <<<<this one???
1 0.000 0.000 9.302 9.302 config.py:698(_initini)
1 0.000 0.000 9.286 9.286 config.py:896(determine_setup)
188 0.000 0.000 9.286 0.049 genericpath.py:15(exists) <<<<this one???
18 0.000 0.000 6.861 0.381 config.py:845(exists) <<<<this one???
1 0.000 0.000 6.861 6.861 config.py:851(getcfg) <<<<this one???
1 0.000 0.000 2.531 2.531 config.py:694(pytest_load_initial_conftests)
1 0.000 0.000 2.531 2.531 config.py:477(setinitial)
1 0.000 0.000 2.531 2.531 config.py:503(_try_load_conftest)
13 0.000 0.000 2.531 0.195 config.py:511(getconftestmodules)
32 0.000 0.000 2.531 0.079 genericpath.py:26(isfile) <<<<this one???
8 0.000 0.000 2.425 0.303 common.py:261(exists)
1 0.000 0.000 0.156 0.156 pytest.py:4(<module>)
1 0.000 0.000 0.125 0.125 main.py:73(wrap_session)
1 0.000 0.000 0.125 0.125 config.py:615(do_configure)
1 0.000 0.000 0.125 0.125 main.py:115(pytest_cmdline_main)
结论摘要:格式错误的文件路径,如//setup.py
(应该是/setup.py
,开头没有双斜杠),显示了四个软件部分之一的问题:pytest config.determinate\u setup()、pytest config使用的py.path、cygwin python库、,或者cygwin posix实现 问题在于pytest搜索//pytest.ini
,//tox.ini
,//setup.cfg
和//setup.py
。它们都导致genericpath.exists()
或genericpath.isfile()
消耗约2.5秒
修复方法是将下面的行添加到genericpath.exists()
和genericpath.isfile()
以跳过这四个特定路径
if path.startswith(r'//'):
return False
另一种修复方法是修改\u pytest/config.py
,这样它就不会在搜索路径中形成那些双斜杠
用于找出确切问题的代码粘贴在下面。设置myshow=True
,这样它将显示搜索的每个文件的时间消耗情况
$ diff -u /usr/lib/python2.6/genericpath.py genericpath.py
--- /usr/lib/python2.6/genericpath.py 2012-06-09 08:33:12.000000000 -0700
+++ genericpath.py 2015-06-11 11:46:33.674285900 -0700
@@ -9,14 +9,29 @@
__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime',
'getsize', 'isdir', 'isfile']
+myshow = False
+import time as mytime
+mybasetime = mytime.time()
+def myshowtime():
+ currenttime = mytime.time()
+ tmdiff = currenttime - mybasetime
+ global mybasetime
+ mybasetime = currenttime
+ return tmdiff
# Does a path exist?
# This is false for dangling symbolic links on systems that support them.
def exists(path):
"""Test whether a path exists. Returns False for broken symbolic links"""
+ pretime = myshowtime()
+ if path.startswith(r'//'):
+ if myshow: print "\n genericpath exists %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n"
+ return False
try:
st = os.stat(path)
+ if myshow: print "\n genericpath exists %8.3f %8.3f True " % (pretime, myshowtime()), " ", path, "\n"
except os.error:
+ if myshow: print "\n genericpath exists %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n"
return False
return True
@@ -25,9 +40,15 @@
# for the same path ono systems that support symlinks
def isfile(path):
"""Test whether a path is a regular file"""
+ pretime = myshowtime()
+ if path.startswith(r'//'):
+ if myshow: print "\n genericpath isfile %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n"
+ return False
try:
st = os.stat(path)
+ if myshow: print "\n genericpath isfile %8.3f %8.3f True " % (pretime, myshowtime()), " ", path, "\n"
except os.error:
+ if myshow: print "\n genericpath isfile %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n"
return False
return stat.S_ISREG(st.st_mode)
另一种不编辑/破解安装的方法是让cygwin调用windows本机python而不是cygwin python。也就是说,如果您的windows python安装在c:/python/path/Scripts/python.exe c:/ython/path/Scripts/py.test script.py
,则将命令py.test
替换为c:/python/path
。缩短路径的相对路径或使用非cygwin Python不会遇到同样的问题。具体问题是,格式错误的文件路径,如//setup.py
(应该是/setup.py
),揭示了四个软件部分之一的问题:pytest config.determinate\u setup()、py.path、cygwin python、,绝对路径如果不是以双斜杠开头的话,那么它可以很好地工作。关于绝对路径或相对路径。要记住的主要一点是,每个路径组件上都有时间。因此,您希望使用路径组件数最少的组件。在文件系统之间切换会有时间损失,绝对路径比相对路径更容易发生这种情况。尤其是在cygwin上,使用路径的速度似乎很慢。双斜杠使这个问题变得尖锐,但很显然,你需要记住这一点。
$ diff -u /usr/lib/python2.6/genericpath.py genericpath.py
--- /usr/lib/python2.6/genericpath.py 2012-06-09 08:33:12.000000000 -0700
+++ genericpath.py 2015-06-11 11:46:33.674285900 -0700
@@ -9,14 +9,29 @@
__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime',
'getsize', 'isdir', 'isfile']
+myshow = False
+import time as mytime
+mybasetime = mytime.time()
+def myshowtime():
+ currenttime = mytime.time()
+ tmdiff = currenttime - mybasetime
+ global mybasetime
+ mybasetime = currenttime
+ return tmdiff
# Does a path exist?
# This is false for dangling symbolic links on systems that support them.
def exists(path):
"""Test whether a path exists. Returns False for broken symbolic links"""
+ pretime = myshowtime()
+ if path.startswith(r'//'):
+ if myshow: print "\n genericpath exists %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n"
+ return False
try:
st = os.stat(path)
+ if myshow: print "\n genericpath exists %8.3f %8.3f True " % (pretime, myshowtime()), " ", path, "\n"
except os.error:
+ if myshow: print "\n genericpath exists %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n"
return False
return True
@@ -25,9 +40,15 @@
# for the same path ono systems that support symlinks
def isfile(path):
"""Test whether a path is a regular file"""
+ pretime = myshowtime()
+ if path.startswith(r'//'):
+ if myshow: print "\n genericpath isfile %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n"
+ return False
try:
st = os.stat(path)
+ if myshow: print "\n genericpath isfile %8.3f %8.3f True " % (pretime, myshowtime()), " ", path, "\n"
except os.error:
+ if myshow: print "\n genericpath isfile %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n"
return False
return stat.S_ISREG(st.st_mode)