Pass and use original sys.argv to/with workers

This gets used e.g. by argparse for the "prog" part.

We could explicitly pass it through and/or set it on
config._parser.prog, but that is a bit tedious just for this use case,
and it looks like "simulating" the main prog here appears to not be that
bad of a hack after all.
This commit is contained in:
Daniel Hahler
2018-12-09 18:09:59 +01:00
parent 5ecbf25450
commit a70dace69c
4 changed files with 73 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
``mainargv`` is made available in ``workerinput`` from the host's ``sys.argv``.
This can be used via ``request.config.workerinput["mainargv"]``.

View File

@@ -1,6 +1,8 @@
import os
import py import py
import pprint import pprint
import pytest import pytest
import sys
from xdist.workermanage import WorkerController, unserialize_report from xdist.workermanage import WorkerController, unserialize_report
from xdist.remote import serialize_report from xdist.remote import serialize_report
@@ -397,3 +399,68 @@ def test_remote_env_vars(testdir):
) )
result = testdir.runpytest("-n2", "--max-worker-restart=0") result = testdir.runpytest("-n2", "--max-worker-restart=0")
assert result.ret == 0 assert result.ret == 0
def test_remote_inner_argv(testdir):
"""Test/document the behavior due to execnet using `python -c`."""
testdir.makepyfile(
"""
import sys
def test_argv():
assert sys.argv == ["-c"]
"""
)
result = testdir.runpytest("-n1")
assert result.ret == 0
def test_remote_mainargv(testdir):
outer_argv = sys.argv
testdir.makepyfile(
"""
def test_mainargv(request):
assert request.config.workerinput["mainargv"] == {!r}
""".format(
outer_argv
)
)
result = testdir.runpytest("-n1")
assert result.ret == 0
def test_remote_usage_prog(testdir, request):
if not hasattr(request.config._parser, "prog"):
pytest.skip("prog not available in config parser")
prog = os.path.basename(sys.argv[0])
testdir.makeconftest(
"""
import pytest
config_parser = None
@pytest.fixture
def get_config_parser():
return config_parser
def pytest_configure(config):
global config_parser
config_parser = config._parser
"""
)
testdir.makepyfile(
"""
import sys
def test(get_config_parser, request):
get_config_parser._getparser().error("my_usage_error")
"""
)
result = testdir.runpytest_subprocess("-n1")
assert result.ret == 1
result.stdout.fnmatch_lines(
["usage: %s *" % prog, "%s: error: my_usage_error" % prog]
)

View File

@@ -261,6 +261,7 @@ if __name__ == "__channelexec__":
import py import py
config = remote_initconfig(option_dict, args) config = remote_initconfig(option_dict, args)
config._parser.prog = os.path.basename(workerinput["mainargv"][0])
config.workerinput = workerinput config.workerinput = workerinput
config.workeroutput = {} config.workeroutput = {}
# TODO: deprecated name, backward compatibility only. Remove it in future # TODO: deprecated name, backward compatibility only. Remove it in future

View File

@@ -2,6 +2,7 @@ from __future__ import print_function
import fnmatch import fnmatch
import os import os
import re import re
import sys
import threading import threading
import py import py
@@ -213,6 +214,7 @@ class WorkerController(object):
"workercount": len(nodemanager.specs), "workercount": len(nodemanager.specs),
"slaveid": gateway.id, "slaveid": gateway.id,
"slavecount": len(nodemanager.specs), "slavecount": len(nodemanager.specs),
"mainargv": sys.argv,
} }
# TODO: deprecated name, backward compatibility only. Remove it in future # TODO: deprecated name, backward compatibility only. Remove it in future
self.slaveinput = self.workerinput self.slaveinput = self.workerinput