Repository: tox-dev/tox
Pull request #433 (2017-01-05 20:55:56)

Fix glob resolution from TOX_TESTENV_PASSENV env variable.
https://github.com/tox-dev/tox/issues/263#issuecomment-247790754
Glob resolution from the TOX_TESTENV_PASSENV env variable should be
supported.

This update allows for globs to be resolved from the TOX_TESTENV_PASSENV
variable.

Thanks for contributing a PR!

Here's a quick checklist of what we'd like you to think off:

- [x] Make sure to include one or more tests for your change;
- [x] ~~if an enhacement PR please create docs and at best an example~~
- [x] Add yourself to `CONTRIBUTORS`;
- [x] make a descriptive Pull Request text (it will be used for changelog)



obestwalter: @a-feld I do not understand the purpose of this PR. The comment you reference states that the wildcard passing of env vars does work already, so there is nothing to be done. 

e.g. `passenv=A*` (er the env var equivalent) like in you testcase works already in current releases of tox. Or am I missing something?
a-feld: Hi @obestwalter , the test is created to show that the test **does not** work in current releases of tox.  The fix for the currently failing test is included in this PR.
a-feld: @obestwalter I have separated the test / fix commits to make the issue more easily discernible.

To reproduce the problem, run:

1. `git checkout aa6c42f`
2. `python setup.py test`

Verify that the test added fails.

Run
1. `git checkout 44b8d14`
2. `python setup.py test`

Verify that the test added now passes.
obestwalter: Woah - now I see. I was assuming that the `passenv` entry in `tox.ini` does exactly the same as `TOX_TESTENV_PASSENV` but it doesn't so that's the problem. While you were updating the PR (thanks - very helpful) I did my own little experiment but only with passenv:

with tox.ini:

    [testenv]
    passenv = ENV_*
    commands = python envtester.py

envtester.py:

    import os

    assert os.getenv('ENV_VAR_TEST') == 'WOHOO!1!!'

This works:

    (tox) 00:52:14 oliver@ob1 [0] < ~/work/tox/tests >  1714 %
    ENV_VAR_TEST='WOHOO!1!!' tox                        
    GLOB sdist-make: /home/oliver/work/tox/tests/setup.py
    python inst-nodeps: /home/oliver/work/tox/tests/.tox/dist/dummy-setup-0.0.0.zip
    python installed: dummy-setup==0.0.0
    python runtests: PYTHONHASHSEED='1508607320'
    python runtests: commands[0] | python envtester.py
    ______________________________ summary ______________________________________________
      python: commands succeeded
      congratulations :)

But you are correct - this doesn't

with tox.ini:

    [testenv]
    commands = python envtester.py

    (tox) 00:55:11 oliver@ob1 [0] < ~/work/tox/tests >  1715 %
    TOX_TESTENV_PASSENV='ENV_*' ENV_VAR_TEST='WOHOO!1!' tox
    GLOB sdist-make: /home/oliver/work/tox/tests/setup.py
    python inst-nodeps: /home/oliver/work/tox/tests/.tox/dist/dummy-setup-0.0.0.zip
    python installed: dummy-setup==0.0.0
    python runtests: PYTHONHASHSEED='3223092831'
    python runtests: commands[0] | python envtester.py
    Traceback (most recent call last):
      File "envtester.py", line 3, in <module>
        assert os.getenv('ENV_VAR_TEST') == 'WOHOO!1!!'
    AssertionError
    ERROR: InvocationError: '/home/oliver/work/tox/tests/.tox/python/bin/python envtester.py'
    ______________________________ summary ______________________________________________
    ERROR:   python: commands failed

obestwalter: I want to have a closer look why this is tomorrow. I am not that familiar with the code base yet, but I was living under the impression that there is some magic going on that synchronizes ini settings with env vars and this is even shared between pytest and tox ... I guess I was wrong. Will check tomorow and then probably merge your fix. Thank you!
a-feld: Here's a debug summary I've compiled that may be helpful as well

Debug Summary
--------------
* in `make_envconfig`, `res=[]` if the attribute being processed is a "line-list"
  attr type and the `tox.ini` file does not contain entries for that attribute
  ([link]( https://github.com/tox-dev/tox/blob/2.5.0/tox/config.py#L795))
* `make_envconfig` calls `passenv` through the `env_attr.postprocess` attribute
  ([link](https://github.com/tox-dev/tox/blob/2.5.0/tox/config.py#L800))
* passenv reads the `TOX_TESTENV_PASSENV` environment variable and immediately
  adds it to the set of environment variables to pass to the downstream tox
  environment
  ([link](https://github.com/tox-dev/tox/blob/2.5.0/tox/config.py#L457))
* The values extracted from the `tox.ini` (held in variable `value`) are
  iterated over to resolve globs (using `fnmatchcase`) and added to the set of
  "passed" environment variables (passenv)
  ([link](https://github.com/tox-dev/tox/blob/2.5.0/tox/config.py#L472-L475))

Note that with the logic above, the globbing is not resolved for the
`TOX_TESTENV_PASSENV` environment variable

Proposed Solution
-----------------
The "value" list should be updated with the values extracted from the
`TOX_TESTENV_PASSENV` environment variable.

This solution allows globbing maintains a common code path for both the
`tox.ini` passenv attribute and the `TOX_TESTENV_PASSENV` environment variable.

Additional Considerations
-----------------------
Precedence between `TOX_TESTENV_PASSENV` and the `tox.ini` attributes should be
considered.
With the current implementation, the passenv set is the union of
set(tox_ini_attrs) and set(TOX_TESTENV_PASSENV).
The proposed solution maintains this behavior.

Duplicate variables / duplicate matches should be considered.
i.e. if the `tox.ini: passenv = foo` and `TOX_TESTENV_PASSENV=foo`, passenv
should resolve to set('foo').
The proposed solution above allows for this case since `add` is idempotent on
set data containers (adding an item already in a set will not change the
contents of the container)


Patch:
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index c82e4990..11ade1a9 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -38,3 +38,4 @@ Manuel Jacob
 Eli Collins
 Andrii Soldatenko
 Igor Duarte Cardoso
+Allan Feldman
diff --git a/tox/config.py b/tox/config.py
index 1fb586ae..af44b1ad 100755
--- a/tox/config.py
+++ b/tox/config.py
@@ -456,7 +456,8 @@ def tox_addoption(parser):
         # read in global passenv settings
         p = os.environ.get("TOX_TESTENV_PASSENV", None)
         if p is not None:
-            passenv.update(x for x in p.split() if x)
+            env_values = [x for x in p.split() if x]
+            value.extend(env_values)
 
         # we ensure that tmp directory settings are passed on
         # we could also set it to the per-venv "envtmpdir"


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
index d48b6507..b0b03993 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -935,6 +935,17 @@ class TestConfigTestEnv:
         assert "A1" in env.passenv
         assert "A2" in env.passenv
 
+    def test_passenv_glob_from_global_env(self, tmpdir, newconfig, monkeypatch):
+        monkeypatch.setenv("A1", "a1")
+        monkeypatch.setenv("A2", "a2")
+        monkeypatch.setenv("TOX_TESTENV_PASSENV", "A*")
+        config = newconfig("""
+            [testenv]
+        """)
+        env = config.envconfigs["python"]
+        assert "A1" in env.passenv
+        assert "A2" in env.passenv
+
     def test_changedir_override(self, tmpdir, newconfig):
         config = newconfig("""
             [testenv]