Repository: tox-dev/tox

Issue:
Different basepython in tox 4
The content of `{basepython}` is different in tox 4, for instance this breaks using requirement files which names depend on the python version. AFAICT this does not seem to be clearly expected.

```console
❯ cat tox.ini    
[testenv]
skip_install = True
allowlist_externals = echo
commands = echo {basepython}

❯ tox -e py310   
py310 run-test-pre: PYTHONHASHSEED='1129533865'
py310 run-test: commands[0] | echo python3.10
python3.10
_________________________________________ summary _________________________________________
  py310: commands succeeded
  congratulations :)

❯ tox4 r -e py310
py310: commands[0]> echo py310
py310
  py310: OK (0.04=setup[0.03]+cmd[0.00] seconds)
  congratulations :) (0.08 seconds)

```

Discussion:

Patch:
diff --git a/docs/changelog/2640.feature.rst b/docs/changelog/2640.feature.rst
new file mode 100644
index 00000000..bbf9b022
--- /dev/null
+++ b/docs/changelog/2640.feature.rst
@@ -0,0 +1,3 @@
+Add ``py_dot_ver`` and ``py_impl`` constants to environments to show the current Python implementation and dot version
+(e.g. ``3.11``) for the current environment. These can be also used as substitutions in ``tox.ini`` - by
+:user:`gaborbernat`.
diff --git a/docs/faq.rst b/docs/faq.rst
index f14d6266..205d8d54 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -79,6 +79,16 @@ tox 4 - removed tox.ini keys
 | ``distdir``              | Use the ``TOX_PACKAGE`` environment variable.|
 +--------------------------+----------------------------------------------+
 
+tox 4 - basepython not resolved
++++++++++++++++++++++++++++++++
+The base python configuration is no longer resolved to ``pythonx.y`` format, instead is kept as ``py39``, and is
+the virtualenv project that handles mapping that to a Python interpreter. If you were using this variable we recommend
+moving to the newly added ``py_impl`` and ``py_dot_ver`` variables, for example:
+
+.. code-block:: ini
+
+   deps = -r{py_impl}{py_dot_ver}-req.txt
+
 tox 4 - substitutions removed
 +++++++++++++++++++++++++++++
 - The ``distshare`` substitution has been removed.
diff --git a/src/tox/tox_env/api.py b/src/tox/tox_env/api.py
index 13a9a4aa..e87710d5 100644
--- a/src/tox/tox_env/api.py
+++ b/src/tox/tox_env/api.py
@@ -323,7 +323,7 @@ class ToxEnv(ABC):
 
         result = self._load_pass_env(pass_env)
         # load/paths_env might trigger a load of the environment variables, set result here, returns current state
-        self._env_vars, self._env_vars_pass_env, set_env.changed = result, pass_env, False
+        self._env_vars, self._env_vars_pass_env, set_env.changed = result, pass_env.copy(), False
         # set PATH here in case setting and environment variable requires access to the environment variable PATH
         result["PATH"] = self._make_path()
         for key in set_env:
diff --git a/src/tox/tox_env/python/api.py b/src/tox/tox_env/python/api.py
index 09975569..6f25dd6a 100644
--- a/src/tox/tox_env/python/api.py
+++ b/src/tox/tox_env/python/api.py
@@ -40,6 +40,10 @@ class PythonInfo(NamedTuple):
     def impl_lower(self) -> str:
         return self.implementation.lower()
 
+    @property
+    def version_dot(self) -> str:
+        return f"{self.version_info.major}.{self.version_info.minor}"
+
 
 class Python(ToxEnv, ABC):
     def __init__(self, create_args: ToxEnvCreateArgs) -> None:
@@ -81,6 +85,14 @@ class Python(ToxEnv, ABC):
             desc="python executable from within the tox environment",
             value=lambda: self.env_python(),
         )
+        self.conf.add_constant("py_dot_ver", "<python major>.<python minor>", value=self.py_dot_ver)
+        self.conf.add_constant("py_impl", "python implementation", value=self.py_impl)
+
+    def py_dot_ver(self) -> str:
+        return self.base_python.version_dot
+
+    def py_impl(self) -> str:
+        return self.base_python.impl_lower
 
     def _default_pass_env(self) -> list[str]:
         env = super()._default_pass_env()


Test patch:
diff --git a/tests/session/cmd/test_show_config.py b/tests/session/cmd/test_show_config.py
index 95b422ac..cf2e719a 100644
--- a/tests/session/cmd/test_show_config.py
+++ b/tests/session/cmd/test_show_config.py
@@ -72,6 +72,15 @@ def test_show_config_unused(tox_project: ToxProjectCreator) -> None:
     assert "\n# !!! unused: magic, magical\n" in outcome.out
 
 
+def test_show_config_py_ver_impl_constants(tox_project: ToxProjectCreator) -> None:
+    tox_ini = "[testenv]\npackage=skip\ndeps= {py_impl}{py_dot_ver}"
+    outcome = tox_project({"tox.ini": tox_ini}).run("c", "-e", "py", "-k", "py_dot_ver", "py_impl", "deps")
+    outcome.assert_success()
+    py_ver = ".".join(str(i) for i in sys.version_info[0:2])
+    impl = sys.implementation.name
+    assert outcome.out == f"[testenv:py]\npy_dot_ver = {py_ver}\npy_impl = {impl}\ndeps = {impl}{py_ver}\n"
+
+
 def test_show_config_exception(tox_project: ToxProjectCreator) -> None:
     project = tox_project(
         {