From a70dace69cce08cb9e4f39768e018aa24f367eb7 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 9 Dec 2018 18:09:59 +0100 Subject: [PATCH] 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. --- changelog/388.feature.rst | 3 ++ testing/test_remote.py | 67 +++++++++++++++++++++++++++++++++++++++ xdist/remote.py | 1 + xdist/workermanage.py | 2 ++ 4 files changed, 73 insertions(+) create mode 100644 changelog/388.feature.rst diff --git a/changelog/388.feature.rst b/changelog/388.feature.rst new file mode 100644 index 00000000..57f59433 --- /dev/null +++ b/changelog/388.feature.rst @@ -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"]``. diff --git a/testing/test_remote.py b/testing/test_remote.py index dd6ee873..d0e4757c 100644 --- a/testing/test_remote.py +++ b/testing/test_remote.py @@ -1,6 +1,8 @@ +import os import py import pprint import pytest +import sys from xdist.workermanage import WorkerController, unserialize_report from xdist.remote import serialize_report @@ -397,3 +399,68 @@ def test(): ) result = testdir.runpytest("-n2", "--max-worker-restart=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] + ) diff --git a/xdist/remote.py b/xdist/remote.py index a0afa49b..4050385f 100644 --- a/xdist/remote.py +++ b/xdist/remote.py @@ -261,6 +261,7 @@ def remote_initconfig(option_dict, args): import py config = remote_initconfig(option_dict, args) + config._parser.prog = os.path.basename(workerinput["mainargv"][0]) config.workerinput = workerinput config.workeroutput = {} # TODO: deprecated name, backward compatibility only. Remove it in future diff --git a/xdist/workermanage.py b/xdist/workermanage.py index c6f0010f..20cdad40 100644 --- a/xdist/workermanage.py +++ b/xdist/workermanage.py @@ -2,6 +2,7 @@ import fnmatch import os import re +import sys import threading import py @@ -213,6 +214,7 @@ def __init__(self, nodemanager, gateway, config, putevent): "workercount": len(nodemanager.specs), "slaveid": gateway.id, "slavecount": len(nodemanager.specs), + "mainargv": sys.argv, } # TODO: deprecated name, backward compatibility only. Remove it in future self.slaveinput = self.workerinput