Open justinlovinger opened 8 years ago
does it happen on linux as well?
It seems there's a thread running in background preventing the nodes created by xdist to terminate properly...
Also I notice you are trying to forcefully kill the AppEngine process you started... isn't there a cleaner way to do the same, using an API perhaps?
Also, I would suggest taking a look at pytest-beds: Fixtures for testing Google Appengine (GAE) apps.
According to the App Engine docs, "To stop the local server: with Mac OS X or Unix, press Control-C or with Windows, press Control-Break in your command prompt window." (https://cloud.google.com/appengine/docs/python/tools/using-local-server).
Modifying the above code to send the Control-Break signal:
def test_REMOVE():
port = 9999
admin_port = 9998
cmd = '"{python}" "{gaepath}/dev_appserver.py" "{apppath}"' \
' -A test-{port} --port={port} --admin_port={adminport}' \
' --datastore_path=C:/tmp/test_datastore_{port}' \
' --clear_datastore=yes'.format(python=sys.executable,
gaepath=GAE_PATH,
apppath=HELLO_PATH,
port=port, adminport=admin_port)
app_engine = subprocess.Popen(cmd)
time.sleep(8)
app_engine.send_signal(signal.CTRL_BREAK_EVENT) # New Code
def test_REMOVE_2():
port = 10001
admin_port = 10000
cmd = '"{python}" "{gaepath}/dev_appserver.py" "{apppath}"' \
' -A test-{port} --port={port} --admin_port={adminport}' \
' --datastore_path=C:/tmp/test_datastore_{port}' \
' --clear_datastore=yes'.format(python=sys.executable,
gaepath=GAE_PATH,
apppath=HELLO_PATH,
port=port, adminport=admin_port)
app_engine = subprocess.Popen(cmd)
time.sleep(8)
app_engine.send_signal(signal.CTRL_BREAK_EVENT) # New Code
Now actually terminates pytest:
============================= test session starts =============================
platform win32 -- Python 2.7.11, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
rootdir: ..., inifile:
plugins: cov-2.2.1, xdist-1.14
collected 2 items
test_xdist.py .^C
This occurs whether xdist is used or not.
P.S. It looks like pytest-beds only has fixtures for the testbed, which is not used for end to end tests that require the actual server. Also, I make my own fixtures for the actual tests. The above code is just for demonstration and xdist testing.
P.P.S. I will take a look at Linux when I get a change to setup App Engine on my Linux machine. I suspect the --boxed option may fix this, but is not available on windows.
the --boxed
option is going to go away
@PaintingInAir Sorry, it wasn't clear to me: did send the signal using send_signal
fix the issue?
@nicoddemus send_signal
results in a new issue. Specifically, it kills the parent process as well as the child process. Some investigation indicates that this is a bug with Python itself.
There appears to be a workaround for the send_signal
bug, which involves creating a stand alone process (not a child) and using inter-process communication to send the kill command. However, with this bug, it's impossible to tell if sending the proper Control-Break command to the App Engine process will fix the issue with xdist.
Regardless, it appears the underlying issue is a loose thread that causes xdist to hang. I guess the first question is whether or not xdist should be able to handle this situation. Note that pytest can finish under these circumstances, when xdist is not used. If this is a bug worth fixing, it is probably worth isolating the underlying cause (such as a loose thread) first.
This is happening on AppVeyor.
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> File "<string>", line 984, in _send
INTERNALERROR> File "<string>", line 430, in to_io
INTERNALERROR> File "<string>", line 396, in write
INTERNALERROR> OSError: [Errno 22] Invalid argument
INTERNALERROR>
INTERNALERROR> During handling of the above exception, another exception occurred:
INTERNALERROR>
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\main.py", line 98, in wrap_session
INTERNALERROR> session.exitstatus = doit(config, session) or 0
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\main.py", line 133, in _main
INTERNALERROR> config.hook.pytest_runtestloop(session=session)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 745, in __call__
INTERNALERROR> return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 339, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 334, in <lambda>
INTERNALERROR> _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 614, in execute
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File "<remote exec>", line 61, in pytest_runtestloop
INTERNALERROR> File "<remote exec>", line 77, in run_tests
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 745, in __call__
INTERNALERROR> return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 339, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 334, in <lambda>
INTERNALERROR> _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 613, in execute
INTERNALERROR> return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 254, in _wrapped_call
INTERNALERROR> return call_outcome.get_result()
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 279, in get_result
INTERNALERROR> raise ex[1].with_traceback(ex[2])
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 265, in __init__
INTERNALERROR> self.result = func()
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 613, in execute
INTERNALERROR> return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 254, in _wrapped_call
INTERNALERROR> return call_outcome.get_result()
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 279, in get_result
INTERNALERROR> raise ex[1].with_traceback(ex[2])
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 265, in __init__
INTERNALERROR> self.result = func()
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 614, in execute
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\runner.py", line 66, in pytest_runtest_protocol
INTERNALERROR> runtestprotocol(item, nextitem=nextitem)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\runner.py", line 79, in runtestprotocol
INTERNALERROR> reports.append(call_and_report(item, "call", log))
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\runner.py", line 137, in call_and_report
INTERNALERROR> hook.pytest_runtest_logreport(report=report)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 745, in __call__
INTERNALERROR> return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 339, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 334, in <lambda>
INTERNALERROR> _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR> File "c:\python36-x64\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 614, in execute
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File "<remote exec>", line 92, in pytest_runtest_logreport
INTERNALERROR> File "<remote exec>", line 25, in sendevent
INTERNALERROR> File "<string>", line 717, in send
INTERNALERROR> File "<string>", line 990, in _send
INTERNALERROR> OSError: cannot send (already closed?)
I've experienced this as well and had to switch to --capture=no
as a default in my pytest.ini
, then teammates can re-enable capture if they need to.
Also: I noticed the issue less when I was using pytest-sugar
plugin, I wonder if somehow that affected the capturing in a way that was less prone to this issue.
also having this problem, does anyone fixed it?
I am seeing the same issue on linux. Is there any fix/workaround? I am using pytest version 3.1.0.
Hi, I'm also facing same issue but it occures only when tests are in classes. is there any workaround ?
Hi,
Unfortunately it is hard to nail down the problem; usually it is not a bug in pytest-xdist per-se, but some bad interaction of the system under testing. Is anybody able to provide a minimal reproducible example?
Seeing same issue while running in a Docker container; image based off of python:3.7.2-alpine3.9
here is gist with minimal reproducible example: https://gist.github.com/rohansb/3699278d4f7388c3881ced81a0fa66c9
docker build .
from fooproject root
@rohansb thanks, but strangely I don't even see pytest-xdist
being installed in your Docker image... are you sure that reproduces the problem or is related to xdist?
@nicoddemus that's right, does that indicate its not related to xdist
? Here is sample output i generated using same code from above gist
.. this is where it hangs indefinitely, off-course!
FYI.. issue persists even after installing pytest-xdist
Just thinking --
It may be infeasible to solve for all envs all the time, it's actually a somewhat hard problem
Something that could be tried: if one cannot put capture=no overall, could tell it to capture output and logs to file and never stdout/stderr. If needed, you could use pytest hookspec to cat those tempfiles at end of session as needed
I'm having this same problem in a different context. I'm running pytest-django tests locally with xdist, and it hangs in the same way. Based on the conversation here, I tried running them with the --capture=no option, but still had the problem.
Any suggestions on how to debug & work around this in my case?
I would try to attach a remote debugger and see where the processes are getting stuck (PyCharm has a remote debugger for example).
Had the same issue running pytest with xdist in a docker. Docker never terminted after test completion. Pytest on pid 1 was unkillable and no stop signal could kill it.
--boxed fixed the issues but broke scopes for fixtures resulting in a lot of setup and tear down.
--dist loadscope fixed the issue but had (acceptable in my case) impact on how the cli received log output. I pass it to reportportal anyways. ¯_(ツ)_/¯
I've dumped stack traces (with Pyrasyte) from the hanged master process. It has many threads like this:
Thread 0x7f3c7a336700
File "/home/art/dwh/.env3/lib/python3.7/site-packages/execnet/gateway_base.py", line 285, in _perform_spawn
reply.run()
File "/home/art/dwh/.env3/lib/python3.7/site-packages/execnet/gateway_base.py", line 220, in run
self._result = func(*args, **kwargs)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/execnet/gateway_base.py", line 967, in _thread_receiver
msg = Message.from_io(io)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/execnet/gateway_base.py", line 432, in from_io
header = io.read(9) # type 1, channel 4, payload 4
File "/home/art/dwh/.env3/lib/python3.7/site-packages/execnet/gateway_base.py", line 400, in read
data = self._read(numbytes - len(buf))
Most probably, these threads communicate with spawned subprocesses.
And there is one thread, which hangs on the Queue:
Thread 0x7f3c87c67700
File "/home/art/.pycharm_helpers/pycharm/_jb_pytest_runner.py", line 42, in <module>
pytest.main(args, plugins_to_load + [Plugin])
File "/home/art/dwh/.env3/lib/python3.7/site-packages/_pytest/config/__init__.py", line 67, in main
return config.hook.pytest_cmdline_main(config=config)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/pluggy/hooks.py", line 286, in __call__
return self._hookexec(self, self.get_hookimpls(), kwargs)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/pluggy/manager.py", line 93, in _hookexec
return self._inner_hookexec(hook, methods, kwargs)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/pluggy/manager.py", line 87, in <lambda>
firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
File "/home/art/dwh/.env3/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
res = hook_impl.function(*args)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/_pytest/main.py", line 208, in pytest_cmdline_main
return wrap_session(config, _main)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/_pytest/main.py", line 178, in wrap_session
session.exitstatus = doit(config, session) or 0
File "/home/art/dwh/.env3/lib/python3.7/site-packages/_pytest/main.py", line 215, in _main
config.hook.pytest_runtestloop(session=session)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/pluggy/hooks.py", line 286, in __call__
return self._hookexec(self, self.get_hookimpls(), kwargs)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/pluggy/manager.py", line 93, in _hookexec
return self._inner_hookexec(hook, methods, kwargs)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/pluggy/manager.py", line 87, in <lambda>
firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
File "/home/art/dwh/.env3/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
res = hook_impl.function(*args)
File "/home/art/dwh/.env3/lib/python3.7/site-packages/xdist/dsession.py", line 115, in pytest_runtestloop
self.loop_once()
File "/home/art/dwh/.env3/lib/python3.7/site-packages/xdist/dsession.py", line 129, in loop_once
eventcall = self.queue.get(timeout=2.0)
File "/usr/lib/python3.7/queue.py", line 179, in get
self.not_empty.wait(remaining)
File "/usr/lib/python3.7/threading.py", line 300, in wait
gotit = waiter.acquire(True, timeout)
I am having same issue, tests works fine when tried to debug using debugger in vscode. It seems there is some problem with threads. OS: Ubuntu 20.04 Python 3.6.6
hi, i experienced the same problem with my fastapi
app.
in my case, the cause is pymysqlpool
package.
to solve this problem, i created mock class to replace the pymysqlpool.Pool
.
If it can help someone, I had the same symptom with pytest hanging
The root cause was that postgresql requires to close all sessions, otherwise there are still open locks
:fire: Solved here (SQLAlchemy Session and Sessionmaker + Pytest Fixture + Coverage interaction) :fire:
Here's the backstory:
The host system is a FastAPI application and we started to have the same issue with our test suite when using pytest + coverage + pytest-cov
. Our lockups were tied to tests which directly needed a SQLAlchemy Session
object (from a test fixture), for things like direct manipulation of database objects, instead of going thru the FastAPI calls.
We have a generalized way of grabbing a Session
object, from a global Sessionmaker
which is also tied to a global Engine
session_factory: Callable[[], Session] = sessionmaker(
autocommit=False, autoflush=True, bind=dors_engine
)
def get_session() -> Iterator[Session]:
db_session: Session = session_factory()
try:
yield db_session
finally:
db_session.close()
And this is the test fixture we had:
@pytest.fixture
def db_session(app_config) -> Session:
yield from get_session()
The db_session.close()
call above does its job fine during normal (API) workloads, but for some reason, the test fixture was hanging after the third or so time it tried to acquire a database session during those db-oriented tests (and only if we were using coverage or pytest-cov.
We changed the fixture to include a new-ish SQLAlchemy 1.4 utility function (that says in its docstring that "might be useful for test scenarios" - so I guess there's something to that) that closes all outstanding sessions it has a weakref to.
@pytest.fixture
def db_session(app_config) -> Session:
yield from get_session()
##########################
# This needs to be here because tests hang when run with "coverage" or "pytest --cov"
# The problem does not arise when using a simple `pytest` invocation
# Something in the way coverage's hooks work might be allowing database sessions
# to become dangling/lost in context (and never freed)
##########################
from sqlalchemy.orm import close_all_sessions
close_all_sessions()
And just like that, our test suite was back up and running, both locally and on GitHub Actions (No need to downgrade or play around with pytest / coverage versions or anything like that).
Hope this helps someone else!
Just spent an unpleasant couple of weeks trying to find the reason for this in our codebase. Thanks @flipbit03 and @gpouilloux for the hint.
SQLA sessions are stored as weak refs (see code here) and we use pypy which is a bit lazy about garbage-collection. I assume (but may be wrong) that pytest coverage can also interfering with the gc mechanism for these sessions too.
What that means practically is that if you accidentally check out a session in a fixture and forget to remove it and there is no gc cycle you can get a locked table when you try to drop it. Either a gc.collect
, or on newer SQLA versions a close_all_sessions
seem to be sufficient.
I run into this behavior when running web tests with Google App Engine. However, since this issue only occurs when using xdist, I do not believe it is caused by App Engine itself.
Run the following two tests in parallel. Tests will complete, but pytest will not finish:
I ran the tests with the following line:
py.test -k REMOVE -n 2
The result is:
Killing the process results in the following traceback:
Finally, I am on windows.
Note that this test is run with the hello world application given by Google. Any other app engine application I've tested causes the same hang.
Also, I have tried a Popen that simply runs python, and this does not result in the same hang.