Skip to content

Commit

Permalink
Allow all command line option flags to be used in config file/env-var (
Browse files Browse the repository at this point in the history
…#1763)

* Allow all command line option flags to be used in config file/env-var

Extend config file and environment variables checked for configuration to also
check aliases (e.g.
setting either ``VIRTUALENV_COPIES`` or ``VIRTUALENV_ALWAYS_COPY`` will work).

Signed-off-by: Bernat Gabor <[email protected]>

* fix CI

Signed-off-by: Bernat Gabor <[email protected]>
  • Loading branch information
gaborbernat authored Apr 8, 2020
1 parent dad4f44 commit 6b7d54b
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 10 deletions.
2 changes: 2 additions & 0 deletions docs/changelog/1763.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Extend environment variables checked for configuration to also check aliases (e.g. setting either
``VIRTUALENV_COPIES`` or ``VIRTUALENV_ALWAYS_COPY`` will work) - by :user:`gaborbernat`.
12 changes: 7 additions & 5 deletions docs/cli_interface.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ virtualenv looks for a standard ini configuration file. The exact location depen
as determined by :pypi:`appdirs` application configuration definition. The configuration file location is printed as at
the end of the output when ``--help`` is passed.

The keys of the settings are derived from the long command line option. For example, :option:`--python <python>`
would be specified as:
The keys of the settings are derived from th command line option (left strip the ``-`` characters, and replace ``-``
with ``_``). Where multiple flags are available first found wins (where order is as it shows up under the ``--help``).

For example, :option:`--python <python>` would be specified as:

.. code-block:: ini
Expand All @@ -58,9 +60,9 @@ Options that take multiple values, like :option:`extra-search-dir` can be specif
Environment Variables
^^^^^^^^^^^^^^^^^^^^^

Each command line option has a corresponding environment variables with the name format
``VIRTUALENV_<UPPER_NAME>``. The ``UPPER_NAME`` is the name of the command line options capitalized and
dashes (``'-'``) replaced with underscores (``'_'``).
Default values may be also specified via environment variables. The keys of the settings are derived from the
command line option (left strip the ``-`` characters, and replace ``-`` with ``_``, finally capitalize the name). Where
multiple flags are available first found wins (where order is as it shows up under the ``--help``).

For example, to use a custom Python binary, instead of the one virtualenv is run with, you can set the environment
variable ``VIRTUALENV_PYTHON`` like:
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ testing =
pytest-mock >= 2.0.0, <3
pytest-env >= 0.6.2, <1
pytest-timeout >= 1.3.4, <2
xonsh >= 0.9.13, <1; python_version > '3.4'
xonsh >= 0.9.16, <1; python_version > '3.4'

[options.package_data]
virtualenv.activation.bash = *.sh
Expand Down
13 changes: 11 additions & 2 deletions src/virtualenv/config/cli/parser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import absolute_import, unicode_literals

from argparse import SUPPRESS, ArgumentDefaultsHelpFormatter, ArgumentParser
from collections import OrderedDict

from virtualenv.config.convert import get_type

Expand Down Expand Up @@ -38,9 +39,17 @@ def _fix_defaults(self):
def _fix_default(self, action):
if hasattr(action, "default") and hasattr(action, "dest") and action.default != SUPPRESS:
as_type = get_type(action)
outcome = get_env_var(action.dest, as_type)
names = OrderedDict((i.lstrip("-").replace("-", "_"), None) for i in action.option_strings)
outcome = None
for name in names:
outcome = get_env_var(name, as_type)
if outcome is not None:
break
if outcome is None and self.file_config:
outcome = self.file_config.get(action.dest, as_type)
for name in names:
outcome = self.file_config.get(name, as_type)
if outcome is not None:
break
if outcome is not None:
action.default, action.default_source = outcome

Expand Down
4 changes: 3 additions & 1 deletion src/virtualenv/create/via_global_ref/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ def can_symlink(self):
class ViaGlobalRefApi(Creator):
def __init__(self, options, interpreter):
super(ViaGlobalRefApi, self).__init__(options, interpreter)
self.symlinks = getattr(options, "copies", False) is False
copies = getattr(options, "copies", False)
symlinks = getattr(options, "symlinks", False)
self.symlinks = symlinks is True and copies is False
self.enable_system_site_package = options.system_site

@classmethod
Expand Down
21 changes: 21 additions & 0 deletions tests/unit/config/test_env_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,24 @@ def test_extra_search_dir_via_env_var(tmp_path, monkeypatch):
(tmp_path / "c").mkdir()
result = parse_cli(["venv"])
assert result.seeder.extra_search_dir == [Path("a").resolve(), Path("b").resolve(), Path("c").resolve()]


def test_value_alias(monkeypatch, mocker, empty_conf):
from virtualenv.config.cli.parser import VirtualEnvConfigParser

prev = VirtualEnvConfigParser._fix_default

def func(self, action):
if action.dest == "symlinks":
action.default = True # force symlink to be true
elif action.dest == "copies":
action.default = False # force default copy to be False, we expect env-var to flip it
return prev(self, action)

mocker.patch("virtualenv.run.VirtualEnvConfigParser._fix_default", side_effect=func, autospec=True)

monkeypatch.delenv(str("SYMLINKS"), raising=False)
monkeypatch.delenv(str("VIRTUALENV_COPIES"), raising=False)
monkeypatch.setenv(str("VIRTUALENV_ALWAYS_COPY"), str("1"))
result = parse_cli(["venv"])
assert result.creator.symlinks is False
2 changes: 1 addition & 1 deletion tests/unit/seed/test_boostrap_link_via_app_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


@pytest.mark.slow
@pytest.mark.timeout(timeout=60)
@pytest.mark.timeout(timeout=120)
@pytest.mark.parametrize("copies", [False, True] if fs_supports_symlink() else [True])
def test_seed_link_via_app_data(tmp_path, coverage_env, current_fastest, copies):
current = PythonInfo.current_system()
Expand Down

0 comments on commit 6b7d54b

Please sign in to comment.