phausamann: Open questions:
- Should we revert `os.environ` at the end?
- Is there a reason to support env vars whose values contain equal signs?
prusse-martin: > * Should we revert `os.environ` at the end?

Maybe not. We can let those envs var "leak" into the `conda env update`  execution IMO.

> * Is there a reason to support env vars whose values contain equal signs?

Good point. Doing `split_arg = arg.split("=", 1)` will handle that.
prusse-martin: > Actually as mentioned, we just need some docs. 👍

He is probably speaking of the section [Command-line reference in `docs/usage.rst`](https://github.com/ESSS/conda-devenv/blob/master/docs/usage.rst#command-line-reference).

nicoddemus: > He is probably speaking of the section Command-line reference in docs/usage.rst.

True, but maybe also add a new section below "Checking minimum conda-devenv version" in the README.
phausamann: Added documentation for `get_env` and the command line option. I've put the new section above the minimum devenv section so that users don't confuse it with the "Environment variables" section.
nicoddemus: (We should squash/merge this btw)
nicoddemus: Thanks a lot @phausamann for the suggestion and very well written PR!
nicoddemus: Thanks everyone! I will prepare a new release tomorrow. 👍 
phausamann: Happy to help!
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index de61cbf..9a2969e 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -64,7 +64,7 @@ Ready to contribute? Here's how to set up ``conda-devenv`` for local development
 
 3. Create a new conda environment for developing::
 
-    $ conda create -n devenv --file requirements_dev.txt
+    $ conda create -c conda-forge -n devenv --file requirements_dev.txt
     $ source activate devenv
     $ pre-commit install
     $ pip install -e .
@@ -101,6 +101,6 @@ Before you submit a pull request, check that it meets these guidelines:
 2. If the pull request adds functionality, the docs should be updated. Put
    your new functionality into a function with a docstring, and add the
    feature to the list in README.rst.
-3. The pull request should work for Python 2.7, 3.5 and 3.6. Check
-   https://travis-ci.org/ESSS/conda-devenv/pull_requests
+3. The pull request should work for Python 3.6, 3.7 and 3.8. Check
+   https://github.com/ESSS/conda-devenv/actions?query=event%3Apull_request
    and make sure that the tests pass for all supported Python versions.
diff --git a/conda_devenv/devenv.py b/conda_devenv/devenv.py
index 9320c53..621c0f8 100644
--- a/conda_devenv/devenv.py
+++ b/conda_devenv/devenv.py
@@ -40,6 +40,33 @@ def _min_conda_devenv_version(min_version):
     return ""
 
 
+def _get_env(var_name, default=None, valid=None):
+    """ Get env var value or default value and check against allowed values.
+
+    :param str var_name:
+        Name of the environment variable.
+    :param Optional[str] default:
+        Default value for the variable. If not specified, the method raises
+        an error when the variable is not set.
+    :param Optional[List[str]] valid:
+        List of allowed values of the variable.
+    :return: str
+        Value of the environment variable or default
+    """
+    value = os.environ.get(var_name, default)
+
+    if value is None:
+        raise ValueError(f"Environment variable {var_name} is not set.")
+
+    if valid is not None and value not in valid:
+        raise ValueError(
+            f"Allowed values for environment variable {var_name} are {valid}, "
+            f"got {value}"
+        )
+
+    return value
+
+
 def render_jinja(contents, filename, is_included):
     import jinja2
     import sys
@@ -69,6 +96,7 @@ def render_jinja(contents, filename, is_included):
         "win32": iswin and is32bit,
         "win64": iswin and is64bit,
         "min_conda_devenv_version": _min_conda_devenv_version,
+        "get_env": _get_env,
     }
 
     contents = preprocess_selectors(contents)
@@ -483,6 +511,25 @@ def get_env_directory(env_name):
     return None
 
 
+def parse_env_var_args(env_var_args):
+    """
+    :param List[str] env_var_args:
+        List of arguments in the form "VAR_NAME" or "VAR_NAME=VALUE"
+    :return: Dict[str,str]
+        Mapping from "VAR_NAME" to "VALUE" or empty str.
+    """
+    env_vars = {}
+    if env_var_args is not None:
+        for arg in env_var_args:
+            split_arg = arg.split("=", 1)
+            if len(split_arg) == 1:
+                env_vars[split_arg[0]] = ""
+            elif len(split_arg) == 2:
+                env_vars[split_arg[0]] = split_arg[1]
+
+    return env_vars
+
+
 def main(args=None):
     if args is None:
         args = sys.argv[1:]
@@ -516,6 +563,13 @@ def main(args=None):
     parser.add_argument(
         "--quiet", action="store_true", default=False, help="Do not show progress"
     )
+    parser.add_argument(
+        "--env-var",
+        "-e",
+        action="append",
+        help="Define or override environment variables in the form VAR_NAME or "
+        "VAR_NAME=VALUE.",
+    )
     parser.add_argument(
         "--verbose",
         "-v",
@@ -543,6 +597,9 @@ def main(args=None):
 
     is_devenv_input_file = filename.endswith(".devenv.yml")
     if is_devenv_input_file:
+        # update environment variables
+        os.environ.update(parse_env_var_args(args.env_var))
+
         # render conda-devenv file
         conda_yaml_dict, environment = load_yaml_dict(filename)
         rendered_contents = render_for_conda_env(conda_yaml_dict)
diff --git a/docs/usage.rst b/docs/usage.rst
index 4796e97..145705f 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -74,6 +74,39 @@ The following variables are available in the Jinja 2 namespace:
      - True if the current file is processed because it was included by another file.
 
 
+Using environment variables in the devenv file
+----------------------------------------------
+
+Environment variables that are defined when calling ``conda devenv`` can be read normally using ``os.environ``, but for convenience and better error messages, you can use the
+``get_env`` function:
+
+.. code-block:: yaml
+
+    dependencies:
+      - python ={{ get_env("PY") }}
+
+This will raise an error if ``PY`` is not defined. Alternatively, it is also possible to set a default value:
+
+.. code-block:: yaml
+
+    dependencies:
+      - python ={{ get_env("PY", default="3.6") }}
+
+You can also provide a list of allowed or supported values:
+
+.. code-block:: yaml
+
+    dependencies:
+      - python ={{ get_env("PY", valid=["3.6", "3.7"]) }}
+
+This will raise an error if ``PY`` is set to a different value.
+
+.. note::
+
+    Environment variables can also be set with the ``-e/--env-var`` command line option,
+    see the `Command-line reference`_ section.
+
+
 Checking minimum conda-devenv version
 -------------------------------------
 
@@ -199,21 +232,33 @@ Options
     $ conda devenv --help
 
     usage: conda-devenv [-h] [--file [FILE]] [--name [NAME]] [--print]
-                        [--no-prune] [--output-file [OUTPUT_FILE]] [--force]
+                        [--print-full] [--no-prune] [--output-file [OUTPUT_FILE]]
+                        [--quiet] [--env-var ENV_VAR] [--verbose] [--version]
 
     Work with multiple conda-environment-like yaml files in dev mode.
 
     optional arguments:
       -h, --help            show this help message and exit
       --file [FILE], -f [FILE]
-                            The environment.devenv.yml file to process.
+                            The environment.devenv.yml file to process. The
+                            default value is 'environment.devenv.yml'.
       --name [NAME], -n [NAME]
                             Name of environment.
-      --print               Only prints the rendered file to stdout and exits.
+      --print               Prints the rendered file as will be sent to conda-env
+                            to stdout and exits.
+      --print-full          Similar to --print, but also includes the
+                            'environment' section.
       --no-prune            Don't pass --prune flag to conda-env.
       --output-file [OUTPUT_FILE]
                             Output filename.
-      --force               Overrides the output file, even if it already exists.
+      --quiet               Do not show progress
+      --env-var ENV_VAR, -e ENV_VAR
+                            Define or override environment variables in the form
+                            VAR_NAME or VAR_NAME=VALUE.
+      --verbose, -v         Use once for info, twice for debug, three times for
+                            trace.
+      --version             Show version and exit
+
 
 
 ``--file``
@@ -235,3 +280,9 @@ Don't pass the ``--prune`` flag when calling ``conda env update``
 ~~~~~~~~~~~~~~~~~
 
 Specifies the ``conda-env`` file which will be created.
+
+``--env-var``
+~~~~~~~~~~~~~~~~~
+
+Define or override environment variables in the form ``VAR_NAME`` or ``VAR_NAME=VALUE``.
+Can be used multiple times for different variables.

Support additional command line arguments
I often find myself specifying flags in the devenv file via environment variables, e.g. I'd have a section like:

```
# development
{% if "PKG_DEV" in os.environ %}
- pytest
- bump2version
{% endif %}
```

for development dependencies that are separate from those which a normal user of the environment needs.

That means that I have to set this environment variable for the dev setup, but I'd much prefer something like this:

    $ conda devenv -a dev

and a function like:

```
{% if "dev" in additional_args() %}
- ...
{% endif %}
```

I'd be open to work on a PR for this.
diff --git a/tests/test_jinja.py b/tests/test_jinja.py
index 4a36b19..e15ea99 100644
--- a/tests/test_jinja.py
+++ b/tests/test_jinja.py
@@ -283,6 +283,25 @@ def test_render_jinja_with_preprocessing_selectors(monkeypatch):
     assert actual_win == expected_win
 
 
+def test_jinja_get_env(monkeypatch):
+    template = "{{ get_env('PY', valid=['3.6']) }}"
+    template_with_default = "{{ get_env('PY', default='3.6') }}"
+
+    monkeypatch.setenv("PY", "3.6")
+    assert render_jinja(template, filename="", is_included=False) == "3.6"
+
+    monkeypatch.setenv("PY", "3.7")
+    with pytest.raises(ValueError):
+        render_jinja(template, filename="", is_included=False)
+
+    monkeypatch.delenv("PY")
+    with pytest.raises(ValueError):
+        render_jinja(template, filename="", is_included=False)
+
+    monkeypatch.delenv("PY", raising=False)
+    assert render_jinja(template_with_default, filename="", is_included=False) == "3.6"
+
+
 def test_jinja_invalid_template():
     with pytest.raises(jinja2.exceptions.TemplateSyntaxError):
         render_jinja(
diff --git a/tests/test_main.py b/tests/test_main.py
index ff5231f..9c87a4c 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -253,3 +253,57 @@ def test_verbose(mocker, tmp_path):
     assert devenv.main(devenv_cmdline_args) == 0
     assert devenv._call_conda.call_count == 1
     assert argv == expected_conda_cmdline_args
+
+
+def test_parse_env_var_args():
+    """
+    Test that env var args are parsed correctly.
+    """
+    assert devenv.parse_env_var_args(None) == {}
+    assert devenv.parse_env_var_args(["DEV", "PY=3.6"]) == {"DEV": "", "PY": "3.6"}
+
+
+@pytest.mark.usefixtures("patch_conda_calls")
+def test_env_var_cmdline_args(tmpdir):
+    """
+    Test env vars passed via -e/--env_var.
+    """
+    import os
+
+    filename = tmpdir.join("environment.devenv.yml")
+    filename.write(
+        textwrap.dedent(
+            """\
+        name: a
+        dependencies:
+          - python ={{ os.environ["PY"] }}
+    """
+        )
+    )
+    assert (
+        devenv.main(
+            ["--file", str(filename), "--quiet", "-e", "DEV", "--env-var", "PY=3.6"]
+        )
+        == 0
+    )
+    assert os.environ["DEV"] == ""
+    assert os.environ["PY"] == "3.6"
+
+
+@pytest.mark.usefixtures("patch_conda_calls")
+def test_get_env(tmpdir, monkeypatch):
+    """
+    Test get_env jinja function with required env var passed via command line.
+    """
+    filename = tmpdir.join("environment.devenv.yml")
+    filename.write(
+        textwrap.dedent(
+            """\
+        name: a
+        dependencies:
+          - python ={{ get_env("PY", valid=["3.6"]) }}
+    """
+        )
+    )
+    monkeypatch.delenv("PY", raising=False)
+    assert devenv.main(["--file", str(filename), "--quiet", "-e", "PY=3.6"]) == 0

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-datadir
apt-get update apt-get install -y gcc
3.7
requirements_dev.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
Babel==2.14.0
bump2version==1.0.1
bumpversion==0.6.0
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
cfgv==3.3.1
charset-normalizer==3.4.1
-e git+https://github.com/ESSS/conda-devenv.git@163062c279ba3e0fd8959a17c2b1b6b0c7f72286#egg=conda_devenv
coverage==7.2.7
cryptography==44.0.2
distlib==0.3.9
docutils==0.17.1
exceptiongroup==1.2.2
filelock==3.12.2
flake8==5.0.4
identify==2.5.24
idna==3.10
imagesize==1.4.1
importlib-metadata==4.2.0
iniconfig==2.0.0
Jinja2==3.1.6
MarkupSafe==2.1.5
mccabe==0.7.0
nodeenv==1.9.1
packaging==24.0
platformdirs==2.6.2
pluggy==1.2.0
pre-commit==2.21.0
py==1.11.0
pycodestyle==2.9.1
pycparser==2.21
pyflakes==2.5.0
Pygments==2.17.2
pytest==7.4.4
pytest-datadir==1.4.1
pytest-mock==3.11.1
pytz==2025.2
PyYAML==6.0.1
requests==2.31.0
six==1.17.0
snowballstemmer==2.2.0
Sphinx==4.3.2
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
tomli==2.0.1
tox==3.28.0
typing_extensions==4.7.1
urllib3==2.0.7
virtualenv==20.16.2
watchdog==3.0.0
zipp==3.15.0

name: conda-devenv
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - babel==2.14.0
      - bump2version==1.0.1
      - bumpversion==0.6.0
      - cffi==1.15.1
      - cfgv==3.3.1
      - charset-normalizer==3.4.1
      - coverage==7.2.7
      - cryptography==44.0.2
      - distlib==0.3.9
      - docutils==0.17.1
      - exceptiongroup==1.2.2
      - filelock==3.12.2
      - flake8==5.0.4
      - identify==2.5.24
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.2.0
      - iniconfig==2.0.0
      - jinja2==3.1.6
      - markupsafe==2.1.5
      - mccabe==0.7.0
      - nodeenv==1.9.1
      - packaging==24.0
      - platformdirs==2.6.2
      - pluggy==1.2.0
      - pre-commit==2.21.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pycparser==2.21
      - pyflakes==2.5.0
      - pygments==2.17.2
      - pytest==7.4.4
      - pytest-datadir==1.4.1
      - pytest-mock==3.11.1
      - pytz==2025.2
      - pyyaml==6.0.1
      - requests==2.31.0
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==4.3.2
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - tomli==2.0.1
      - tox==3.28.0
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - virtualenv==20.16.2
      - watchdog==3.0.0
      - zipp==3.15.0
prefix: /opt/conda/envs/conda-devenv

tests/test_jinja.py::test_jinja_get_env tests/test_main.py::test_parse_env_var_args tests/test_main.py::test_env_var_cmdline_args tests/test_main.py::test_get_env
tests/test_jinja.py::test_jinja_root tests/test_jinja.py::test_jinja_os tests/test_jinja.py::test_jinja_sys tests/test_jinja.py::test_jinja_platform tests/test_jinja.py::test_jinja_x86 tests/test_jinja.py::test_jinja_x86_64 tests/test_jinja.py::test_jinja_linux tests/test_jinja.py::test_jinja_linux32 tests/test_jinja.py::test_jinja_linux64 tests/test_jinja.py::test_jinja_osx tests/test_jinja.py::test_jinja_unix tests/test_jinja.py::test_jinja_win tests/test_jinja.py::test_jinja_win32 tests/test_jinja.py::test_jinja_win64 tests/test_jinja.py::test_preprocess_selector_in_line tests/test_jinja.py::test_preprocess_selectors tests/test_jinja.py::test_render_jinja_with_preprocessing_selectors tests/test_jinja.py::test_jinja_invalid_template tests/test_main.py::test_handle_input_file[True-0-True-environment.devenv.yml-1] tests/test_main.py::test_handle_input_file[True-0-True-environment.yml-0] tests/test_main.py::test_handle_input_file[True-0-False-environment.devenv.yml-1] tests/test_main.py::test_handle_input_file[True-0-False-environment.yml-0] tests/test_main.py::test_handle_input_file[False-1-True-environment.devenv.yml-1] tests/test_main.py::test_handle_input_file[False-1-True-environment.yml-0] tests/test_main.py::test_handle_input_file[False-1-False-environment.devenv.yml-1] tests/test_main.py::test_handle_input_file[False-1-False-environment.yml-0] tests/test_main.py::test_print[environment.devenv.yml] tests/test_main.py::test_print[environment.yml] tests/test_main.py::test_print_full tests/test_main.py::test_min_version_failure tests/test_main.py::test_min_version_ok tests/test_main.py::test_version tests/test_main.py::test_error_message_environment_file_not_found[True] tests/test_main.py::test_error_message_environment_file_not_found[False] tests/test_main.py::test_get_env_directory tests/test_main.py::test_verbose
conda_devenv/devenv.py CONTRIBUTING.rst docs/usage.rst
conda_devenv/devenv.py CONTRIBUTING.rst docs/usage.rst