From 84b8973f1e8e09f79bff91185f438be4f8740562 Mon Sep 17 00:00:00 2001
From: Facundo Batista <facundo@taniquetil.com.ar>
Date: Sun, 12 May 2024 13:23:51 -0300
Subject: [PATCH 1/6] Use the venv module only, supporting py>=3.6 only.

---
 README.rst               | 126 +++++++++++++++++++--------------------
 fades/_version.py        |   2 +-
 fades/envbuilder.py      |  80 ++++++++++---------------
 fades/file_options.py    |   4 +-
 fades/main.py            |  13 ++--
 man/fades.1              |  28 ++++-----
 pkg/debian/control       |   4 +-
 pkg/debian/copyright     |   2 +-
 setup.py                 |   8 ++-
 tests/test_envbuilder.py |  90 +++++++---------------------
 10 files changed, 143 insertions(+), 214 deletions(-)

diff --git a/README.rst b/README.rst
index 7d8c4ef..8545e11 100644
--- a/README.rst
+++ b/README.rst
@@ -19,15 +19,15 @@ What is fades?
     :alt: Appveyor Status
 
 
-fades is a system that automatically handles the virtualenvs in the
+fades is a system that automatically handles the virtual environments in the
 cases normally found when writing scripts and simple programs, and
 even helps to administer big projects.
 
 .. image:: resources/logo256.png
 
-*fades* will automagically create a new virtualenv (or reuse a previous
+*fades* will automagically create a new virtual environment (or reuse a previous
 created one), installing the necessary dependencies, and execute
-your script inside that virtualenv, with the only requirement
+your script inside that virtual environment, with the only requirement
 of executing the script with *fades* and also marking the required
 dependencies.
 
@@ -41,7 +41,7 @@ child script.
 
 *fades* can also be executed without passing a child script to execute:
 in this mode it will open a Python interactive interpreter inside the
-created/reused virtualenv (taking dependencies from ``--dependency`` or
+created/reused virtual environment (taking dependencies from ``--dependency`` or
 ``--requirement`` options).
 
 .. contents::
@@ -71,7 +71,7 @@ When you write an script, you have to take two special measures:
 - need to mark those dependencies
 
 At the moment you execute the script, fades will search a
-virtualenv with the marked dependencies, if it doesn't exists
+virtual environment with the marked dependencies, if it doesn't exists
 fades will create it, and execute the script in that environment.
 
 
@@ -119,7 +119,7 @@ The simplest comment is like::
 The ``fades`` is mandatory, in this examples the repository is PyPI,
 see `About different repositories`_ below for other examples.
 
-With that comment, *fades* will install automatically in the virtualenv the
+With that comment, *fades* will install automatically in the virtual environment the
 ``somemodule`` or ``somepackage`` from PyPI.
 
 Also, you can indicate a particular version condition, examples::
@@ -139,9 +139,9 @@ version)::
 What if no script is given to execute?
 --------------------------------------
 
-If no script or program is passed to execute, *fades* will provide a virtualenv 
+If no script or program is passed to execute, *fades* will provide a virtual environment 
 with all the indicated dependencies, and then open an interactive interpreter 
-in the context of that virtualenv.
+in the context of that virtual environment.
 
 Here is where it comes very handy the ``-i/--ipython`` option, if that REPL
 is preferred over the standard one.
@@ -155,7 +155,7 @@ Other ways to specify dependencies
 ----------------------------------
 
 Apart of marking the imports in the source file, there are other ways
-to tell *fades* which dependencies to install in the virtualenv.
+to tell *fades* which dependencies to install in the virtual environment.
 
 One way is through command line, passing the ``--dependency`` parameter.
 This option can be specified multiple times (once per dependency), and
@@ -202,7 +202,7 @@ If you prefer, you can be explicit about which kind of repository *fades* should
     -d pypi::requests
     -d vcs::git+https://github.com/kennethreitz/requests.git#egg=requests
 
-There are two basic repositories: ``pypi`` which will make *fades* to install the desired dependency from PyPI, and ``vcs``, which will make *fades* to treat the dependency as a URL for a version control system site. In the first case, for PyPI, a full range of version comparators can be specified, as usual. For ``vcs`` repositories, though, the comparison is always exact: if the very same dependency is specified, a *virtualenv* is reused, otherwise a new one will be created and populated.
+There are two basic repositories: ``pypi`` which will make *fades* to install the desired dependency from PyPI, and ``vcs``, which will make *fades* to treat the dependency as a URL for a version control system site. In the first case, for PyPI, a full range of version comparators can be specified, as usual. For ``vcs`` repositories, though, the comparison is always exact: if the very same dependency is specified, a *virtual environment* is reused, otherwise a new one will be created and populated.
 
 In both cases (specifying the repository explicitly or implicitly) there is no difference if the dependency is specified in the command line, in a ``requirements.txt`` file, in the script's docstring, etc.  In the case of marking the ``import`` directly in the script, it slightly different.
 
@@ -228,13 +228,13 @@ because we're mixing the protocol indication with the path)::
     fades -d file:///home/crazyuser/myproject/allstars/
 
 
-How to control the virtualenv creation and usage?
--------------------------------------------------
+How to control the virtual environment creation and usage?
+----------------------------------------------------------
 
-You can influence several details of all the virtualenv related process.
+You can influence several details of all the virtual environment related process.
 
 The most important detail is which version of Python will be used in
-the virtualenv. Of course, the corresponding version of Python needs to
+the virtual environment. Of course, the corresponding version of Python needs to
 be installed in your system, but you can control exactly which one to use.
 
 No matter which way you're executing the script (see above), you can
@@ -243,7 +243,7 @@ be used just with the number (``2.7``), the whole name (``python2.7``) or
 the whole path (``/usr/bin/python2.7``).
 
 Other detail is the verbosity of *fades* when telling what is doing. By
-default, *fades* only will use stderr to tell if a virtualenv is being
+default, *fades* only will use stderr to tell if a virtual environment is being
 created, and to let the user know that is doing an operation that
 requires an active network connection (e.g. installing a new dependency).
 
@@ -260,37 +260,37 @@ and it will launch this shell instead of the python one.
 You can also use ``--system-site-packages`` to create a venv with access to
 the system libs.
 
-Finally, no matter how the virtualenv was created, you can always get the
-base directory of the virtualenv in your system using the ``--where`` (or its
+Finally, no matter how the virtual environment was created, you can always get the
+base directory of the virtual environment in your system using the ``--where`` (or its
 alias ``--get-venv-dir``) option.
 
 
-Running programs in the context of the virtualenv
--------------------------------------------------
+Running programs in the context of the virtual environment
+----------------------------------------------------------
 
 The ``-x/--exec`` parameter allows you to execute any program (not just
-a Python one) in the context of the virtualenv.
+a Python one) in the context of the virtual environment.
 
 By default the mandatory given argument is considered the executable 
-name, relative to the virtualenv's ``bin`` directory, so this is 
+name, relative to the environment's ``bin`` directory, so this is 
 specially useful to execute installed scripts/program by the declared 
 dependencies. E.g.::
 
     fades -d flake8 -x flake8 my_script_to_be_verified_by_flake8.py
 
 Take in consideration that you can pass an absolute path and it will be 
-respected (but not a relative path, as it will depend of the virtualenv
+respected (but not a relative path, as it will depend of the virtual environment
 location). 
 
 For example, if you want to run a shell script that in turn runs a Python
-program that needs to be executed in the context of the virtualenv, you 
+program that needs to be executed in the context of the virtual environment, you 
 can do the following::
 
     fades -r requirements.txt --exec /var/lib/foobar/special.sh
 
 Finally, if the intended code to run is prepared to be executed as a module 
 (what you would normally run as `python3 -m some_module`), you can 
-use the same parameter with *fades* to run that module inside the virtualenv::
+use the same parameter with *fades* to run that module inside the virtual environment::
 
     fades -r requirements.txt -m some_module
 
@@ -298,27 +298,27 @@ use the same parameter with *fades* to run that module inside the virtualenv::
 How to deal with packages that are upgraded in PyPI
 ---------------------------------------------------
 
-When you tell *fades* to create a virtualenv using one dependency and
+When you tell *fades* to create a virtual environment using one dependency and
 don't specify a version, it will install the latest one from PyPI.
 
 For example, you do ``fades -d foobar`` and it installs foobar in
 version 7. At some point, there is a new version of foobar in PyPI,
 version 8, but if do ``fades -d foobar`` it will just reuse previously
-created virtualenv, with version 7, not downloading the new version and
-creating a new virtualenv with it!
+created virtual environment, with version 7, not downloading the new version and
+creating a new virtual environment with it!
 
 You can tell fades to do otherwise, just do::
 
     fades -d foobar --check-updates
 
 ...and *fades* will search updates for the package on PyPI, and as it will
-found version 8, will create a new virtualenv using the latest version. You
+found version 8, will create a new virtual environment using the latest version. You
 can also use the ``-U`` option as an alias for ``--check-updates``::
     
     fades -d foobar -U
 
 From this moment on, if you request ``fades -d foobar`` it will bring the
-virtualenv with the new version. If you want to get a virtualenv with
+virtual environment with the new version. If you want to get a virtual environment with
 not-the-latest version for any dependency, just specify the proper versions.
 
 You can even use the ``--check-updates`` parameter when specifying the package
@@ -335,7 +335,7 @@ What about pinning dependencies?
 --------------------------------
 
 One nice benefit of *fades* is that every time dependencies change in your 
-project, you actually get to use a new virtualenv automatically.
+project, you actually get to use a new virtual environment automatically.
 
 If you don't pin the dependencies in your requirements file, this has 
 another nice side effect: everytime you use them in a new environment (or
@@ -380,13 +380,11 @@ pinned, so you will get exactly what you was expecting.
 Under the hood options
 ----------------------
 
-For particular use cases you can send specifics arguments to ``virtualenv``, ``pip`` and ``python``. using the
-``--virtuaenv-options``, ``--pip-options`` and ``--python-options`` respectively. You have to use that argument for each argument
-sent.
+For particular use cases you can send specifics arguments to the ``venv`` module, ``pip`` and ``python`` itself, using the ``--venv-options``, ``--pip-options`` and ``--python-options`` modifiers respectively. You have to use that argument for each argument sent.
 
 Examples:
 
-``fades -d requests --virtualenv-options="--always-copy" --virtualenv-options="--extra-search-dir=/tmp"``
+``fades -d requests --venv-options="--symlinks"``
 
 ``fades -d requests --pip-options="--index-url='http://example.com'"``
 
@@ -420,31 +418,31 @@ options with a dash, it has to be replaced with a underscore.::
     dependency=requests;django>=1.8  # separated by semicolon
 
 There is a little difference in how fades handle these settings: "dependency", "pip-options" and
-"virtualenv-options". In these cases you have to use a semicolon separated list.
+"venv-options". In these cases you have to use a semicolon separated list.
 
 The most important thing is that these options will be merged. So if you configure in
-`/etc/fades/fades.ini` "dependency=requests" you will have requests in all the virtualenvs
+`/etc/fades/fades.ini` "dependency=requests" you will have requests in all the virtual environments
 created by fades.
 
 
-How to clean up old virtualenvs?
---------------------------------
+How to clean up old virtual environments?
+-----------------------------------------
 
 When using *fades* virtual environments are something you should not have to think about.
-*fades* will do the right thing and create a new virtualenv that matches the required
+*fades* will do the right thing and create a new virtual environment that matches the required
 dependencies. There are cases however when you'll want to do some clean up to remove
 unnecessary virtual environments from disk.
 
 By running *fades* with the ``--rm`` argument, *fades* will remove the
-virtualenv matching the provided UUID if such a virtualenv exists (one easy
-way to find out the virtualenv's UUID is calling *fades* with the
+virtual environment matching the provided UUID if such a virtual environment exists (one easy
+way to find out the virtual environment's UUID is calling *fades* with the
 ``--where`` option).
 
 Another way to clean up the cache is to remove all venvs that haven't been used for some time.
 In order to do this you need to call *fades* with ``--clean-unused-venvs``.
 When fades it's called with this option, it runs in mantain mode, this means that fades will exit
 after finished this task.
-All virtualenvs that haven't been used for more days than the value indicated in param will be
+All virtual environments that haven't been used for more days than the value indicated in param will be
 removed.
 
 It is recommended to have some automatically way of run this option;
@@ -456,7 +454,7 @@ ie, add a cron task that perform this command::
 Some command line examples
 --------------------------
 
-Execute ``foo.py`` under *fades*, passing the ``--bar`` parameter to the child program, in a virtualenv with the dependencies indicated in the source code::
+Execute ``foo.py`` under *fades*, passing the ``--bar`` parameter to the child program, in a virtual environment with the dependencies indicated in the source code::
 
     fades foo.py --bar
 
@@ -464,19 +462,19 @@ Execute ``foo.py`` under *fades*, showing all the *fades* messages (verbose mode
 
     fades -v foo.py
 
-Execute ``foo.py`` under *fades* (passing the ``--bar`` parameter to it), in a virtualenv with the dependencies indicated in the source code and also ``dependency1`` and ``dependency2`` (any version > 3.2)::
+Execute ``foo.py`` under *fades* (passing the ``--bar`` parameter to it), in a virtual environment with the dependencies indicated in the source code and also ``dependency1`` and ``dependency2`` (any version > 3.2)::
 
     fades -d dependency1 -d "dependency2>3.2" foo.py --bar
 
-Execute the Python interactive interpreter in a virtualenv with ``dependency1`` installed::
+Execute the Python interactive interpreter in a virtual environment with ``dependency1`` installed::
 
     fades -d dependency1
 
-Execute the Python interactive interpreter in a virtualenv after installing there all dependencies taken from the ``requirements.txt`` file::
+Execute the Python interactive interpreter in a virtual environment after installing there all dependencies taken from the ``requirements.txt`` file::
 
     fades -r requirements.txt
 
-Execute the Python interactive interpreter in a virtualenv after installing there all dependencies taken from files ``requirements.txt`` and ``requirements_devel.txt``::
+Execute the Python interactive interpreter in a virtual environment after installing there all dependencies taken from files ``requirements.txt`` and ``requirements_devel.txt``::
 
     fades -r requirements.txt -r requirements_devel.txt
 
@@ -484,11 +482,11 @@ Use the ``django-admin.py`` script to start a new project named ``foo``, without
 
     fades -d django -x django-admin.py startproject foo
 
-Remove a virtualenv matching the given uuid from disk and cache index::
+Remove a virtual environment matching the given uuid from disk and cache index::
 
     fades --rm 89a2bf83-c280-4918-a78d-c35506efd69d
 
-Download the script from the given pastebin and executes it (previously building a virtualenv for the dependencies indicated in that pastebin, of course)::
+Download the script from the given pastebin and executes it (previously building a virtual environment for the dependencies indicated in that pastebin, of course)::
 
     fades http://linkode.org/#4QI4TrPlGf1gK2V7jPBC47
 
@@ -501,9 +499,9 @@ Some examples using fades in project scripts
 --------------------------------------------
 
 Including *fades* in project helper scripts makes it easy to stop 
-worrying about the virtualenv activation/deactivation when working 
+worrying about the virtual environment activation/deactivation when working 
 in that project, and also solves the problem of needing to 
-update/change/fix an already created virtualenv if the 
+update/change/fix an already created virtual environment if the 
 dependencies change.
 
 This is an example of how a script to run your project may look like::
@@ -515,7 +513,7 @@ This is an example of how a script to run your project may look like::
         fades -r requirements.txt bin/start
     else
         echo 2
-        # hope you are in the correct virtualenv
+        # hope you are in the correct virtual environment
         python3 bin/start
     fi
 
@@ -529,30 +527,30 @@ of the development dependencies::
 What if Python is updated in my system?
 ---------------------------------------
 
-The virtualenvs created by fades depend on the Python version used to
+The virtual environments created by fades depend on the Python version used to
 create them, considering its major and minor version.
 
 This means that if run fades with a Python version and then run it again
-with a different Python version, it may need to create a new virtualenv.
+with a different Python version, it may need to create a new virtual environment.
 
 Let's see some examples. Let's say you run fades with ``python``, which
-is a symlink in your ``/usr/bin/`` to ``python3.4`` (running it directly
+is a symlink in your ``/usr/bin/`` to ``python3.6`` (running it directly
 by hand or because fades is installed to use that Python version).
 
-If you have Python 3.4.2 installed in your system, and it's upgraded to
-Python 3.4.3, fades will keep reusing the already created virtualenvs, as
+If you have Python 3.6.2 installed in your system, and it's upgraded to
+Python 3.6.3, fades will keep reusing the already created virtual environments, as
 only the micro version changed, not minor or major.
 
-But if Python 3.5 is installed in your system, and the default ``python``
+But if Python 3.7 is installed in your system, and the default ``python``
 is pointed to this new one, fades will start creating all the
-virtualenvs again, with this new version.
+virtual environments again, with this new version.
 
 This is a good thing, because you want that the dependencies installed
-with one specific Python in the virtualenv are kept being used by the
+with one specific Python in the virtual environment are kept being used by the
 same Python version.
 
 However, if you want to avoid this behaviour, be sure to always call fades
-with the specific Python version (``/usr/bin/python3.4`` or ``python3.4``,
+with the specific Python version (``/usr/bin/python3.6`` or ``python3.6``,
 for example), so it won't matter if a new version is available in the
 system.
 
@@ -596,7 +594,7 @@ Else, keep reading to know how to install the dependencies first, and
 Dependencies
 ------------
 
-Besides needing Python 3.3 or greater, fades depends also on the
+Besides needing Python 3.6 or greater, fades depends also on the
 ``pkg_resources`` package, that comes in with ``setuptools``.
 It's installed almost everywhere, but in any case,
 you can install it in Ubuntu/Debian with::
@@ -619,10 +617,6 @@ And on Arch Linux with::
 
     pacman -S python-xdg
 
-Fades also needs the `virtualenv <https://virtualenv.pypa.io/en/latest/>`_ 
-package to support different Python versions for child execution. (see the 
-``--python`` option.)
-
 
 For others debian and ubuntu
 ----------------------------
diff --git a/fades/_version.py b/fades/_version.py
index 1607110..bc14a4e 100644
--- a/fades/_version.py
+++ b/fades/_version.py
@@ -1,4 +1,4 @@
 """Holder of the fades version number."""
 
-VERSION = (9, 0, 2)
+VERSION = (10, 0, 0)
 __version__ = '.'.join([str(x) for x in VERSION])
diff --git a/fades/envbuilder.py b/fades/envbuilder.py
index 2c12388..348beed 100644
--- a/fades/envbuilder.py
+++ b/fades/envbuilder.py
@@ -1,4 +1,4 @@
-# Copyright 2014-2016 Facundo Batista, Nicolás Demarchi
+# Copyright 2014-2024 Facundo Batista, Nicolás Demarchi
 #
 # This program is free software: you can redistribute it and/or modify it
 # under the terms of the GNU General Public License version 3, as published
@@ -14,17 +14,11 @@
 #
 # For further info, check  https://github.com/PyAr/fades
 
-"""Extended class from EnvBuilder to create a venv using a uuid4 id.
-
-NOTE: this class only work in the same python version that Fades is
-running. So, you don't need to have installed a virtualenv tool. For
-other python versions Fades needs a virtualenv tool installed.
-"""
+"""Tools to create, destroy and handle usage of virtual environments."""
 
 import logging
 import os
 import shutil
-import sys
 
 from datetime import datetime
 from venv import EnvBuilder
@@ -39,19 +33,16 @@
 
 
 class _FadesEnvBuilder(EnvBuilder):
-    """Create always a virtualenv.
+    """Create always a virtual environment.
 
     This is structured as a class mostly to take advantage of EnvBuilder, not because
     it's provides the best interface: external callers should just use module's ``create_env``
     and ``destroy_env``.
     """
 
-    def __init__(self, env_path=None):
-        """Init."""
+    def __init__(self):
         basedir = helpers.get_basedir()
-        if env_path is None:
-            env_path = os.path.join(basedir, str(uuid4()))
-        self.env_path = env_path
+        self.env_path = os.path.join(basedir, str(uuid4()))
         self.env_bin_path = ''
         logger.debug("Env will be created at: %s", self.env_path)
 
@@ -60,9 +51,8 @@ def __init__(self, env_path=None):
             # because it doesn't work properly (it does a special magic to run the script
             # and ends up mixing external and internal pips)
             self.pip_installed = False
-            super().__init__(with_pip=False, symlinks=True)
 
-        elif sys.version_info >= (3, 4):
+        else:
             # try to install pip using default machinery (which will work in a lot
             # of systems, noticeably it won't in some debians or ubuntus, like
             # Trusty; in that cases mark it to install manually later)
@@ -72,46 +62,40 @@ def __init__(self, env_path=None):
             except ImportError:
                 self.pip_installed = False
 
-            super().__init__(with_pip=self.pip_installed, symlinks=True)
-
-        else:
-            # old Python doesn't have integrated pip
-            self.pip_installed = False
-            super().__init__(symlinks=True)
+        super().__init__(with_pip=self.pip_installed, symlinks=True)
 
-    def create_with_virtualenv(self, interpreter, virtualenv_options):
-        """Create a virtualenv using the virtualenv lib."""
-        args = ['virtualenv', '--python', interpreter, self.env_path]
-        args.extend(virtualenv_options)
+    def create_with_external_venv(self, interpreter, options):
+        """Create a virtual environment using the venv module externally."""
+        args = [interpreter, "-m", "venv", self.env_path]
+        args.extend(options)
         if not self.pip_installed:
-            args.insert(3, '--no-pip')
+            args.insert(3, '--without-pip')
+
         try:
             helpers.logged_exec(args)
-            self.env_bin_path = os.path.join(self.env_path, 'bin')
-        except FileNotFoundError as error:
-            logger.error('Virtualenv is not installed. It is needed to create a virtualenv with '
-                         'a different python version than fades (got {})'.format(error))
-            raise FadesError('virtualenv not found')
         except helpers.ExecutionError as error:
             error.dump_to_log(logger)
-            raise FadesError('virtualenv could not be run')
+            raise FadesError("Failed to run venv module externally")
         except Exception as error:
-            logger.exception("Error creating virtualenv:  %s", error)
-            raise FadesError('General error while running virtualenv')
+            logger.exception("Error creating virtual environment:  %s", error)
+            raise FadesError("General error while running external venv")
+
+        self.env_bin_path = os.path.join(self.env_path, 'bin')
 
     def create_env(self, interpreter, is_current, options):
-        """Create the virtualenv and return its info."""
+        """Create the virtual environment and return its info."""
+        venv_options = options['venv_options']
         if is_current:
-            # apply pyvenv options
-            pyvenv_options = options['pyvenv_options']
-            if "--system-site-packages" in pyvenv_options:
-                self.system_site_packages = True
-            logger.debug("Creating virtualenv with pyvenv. options=%s", pyvenv_options)
+            # apply venv options
+            logger.debug("Creating virtual environment internally; options=%s", venv_options)
+            for option in venv_options:
+                attrname = option[2:].replace("-", "_")  # '--system-packgs' ->  'system_packgs'
+                setattr(self, attrname, True)
             self.create(self.env_path)
         else:
-            virtualenv_options = options['virtualenv_options']
-            logger.debug("Creating virtualenv with virtualenv")
-            self.create_with_virtualenv(interpreter, virtualenv_options)
+            logger.debug(
+                "Creating virtual environment with external venv; options=%s", venv_options)
+            self.create_with_external_venv(interpreter, venv_options)
         logger.debug("env_bin_path: %s", self.env_bin_path)
 
         # Re check if pip was installed (supporting both binary and .exe for Windows)
@@ -130,7 +114,7 @@ def post_setup(self, context):
 
 def create_venv(requested_deps, interpreter, is_current, options, pip_options, avoid_pip_upgrade):
     """Create a new virtualvenv with the requirements of this script."""
-    # create virtualenv
+    # create virtual environment
     env = _FadesEnvBuilder()
     env_path, env_bin_path, pip_installed = env.create_env(interpreter, is_current, options)
     venv_data = {}
@@ -156,7 +140,7 @@ def create_venv(requested_deps, interpreter, is_current, options, pip_options, a
             try:
                 mgr.install(dependency)
             except Exception:
-                logger.debug("Installation Step failed, removing virtualenv")
+                logger.debug("Installation Step failed, removing virtual environment")
                 destroy_venv(env_path)
                 raise FadesError('Dependency installation failed')
 
@@ -180,7 +164,7 @@ def create_venv(requested_deps, interpreter, is_current, options, pip_options, a
 def destroy_venv(env_path, venvscache=None):
     """Destroy a venv."""
     # remove the venv itself in disk
-    logger.debug("Destroying virtualenv at: %s", env_path)
+    logger.debug("Destroying virtual environment at: %s", env_path)
     shutil.rmtree(env_path, ignore_errors=True)
 
     # remove venv from cache
@@ -245,7 +229,7 @@ def clean_unused_venvs(self, max_days_to_keep):
                         # usage_file wasn't updated.
                         continue
                     env_path = venv_meta['env_path']
-                    logger.info("Destroying virtualenv at: %s", env_path)  # #256
+                    logger.info("Destroying virtual environment at: %s", env_path)
                     destroy_venv(env_path, self.venvscache)
 
             self._write_compacted_dict_usage_to_file(venvs_dict)
diff --git a/fades/file_options.py b/fades/file_options.py
index 83c2688..2f5ea65 100644
--- a/fades/file_options.py
+++ b/fades/file_options.py
@@ -1,4 +1,4 @@
-# Copyright 2016 Facundo Batista, Nicolás Demarchi
+# Copyright 2016-2024 Facundo Batista, Nicolás Demarchi
 #
 # This program is free software: you can redistribute it and/or modify it
 # under the terms of the GNU General Public License version 3, as published
@@ -27,7 +27,7 @@
 
 CONFIG_FILES = ("/etc/fades/fades.ini", os.path.join(get_confdir(), 'fades.ini'), ".fades.ini")
 
-MERGEABLE_CONFIGS = ("dependency", "pip_options", "virtualenv-options")
+MERGEABLE_CONFIGS = ("dependency", "pip_options", "venv-options")
 
 
 def options_from_file(args):
diff --git a/fades/main.py b/fades/main.py
index d80a1f6..55aaf2f 100644
--- a/fades/main.py
+++ b/fades/main.py
@@ -1,4 +1,4 @@
-# Copyright 2014-2020 Facundo Batista, Nicolás Demarchi
+# Copyright 2014-2024 Facundo Batista, Nicolás Demarchi
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General
@@ -245,8 +245,9 @@ def go():
         '--system-site-packages', action='store_true', default=False,
         help="give the virtual environment access to the system site-packages dir.")
     parser.add_argument(
-        '--virtualenv-options', action='append', default=[],
-        help="extra options to be supplied to virtualenv (this option can be used multiple times)")
+        '--venv-options', action='append', default=[],
+        help="extra options to be supplied to the venv module "
+             "(this option can be used multiple times)")
     parser.add_argument(
         '-U', '--check-updates', action='store_true', help="check for packages updates")
     parser.add_argument(
@@ -395,11 +396,9 @@ def go():
     pip_options = args.pip_options  # pip_options mustn't store.
     python_options = args.python_options
     options = {}
-    options['pyvenv_options'] = []
-    options['virtualenv_options'] = args.virtualenv_options
+    options['venv_options'] = args.venv_options
     if args.system_site_packages:
-        options['virtualenv_options'].append("--system-site-packages")
-        options['pyvenv_options'] = ["--system-site-packages"]
+        options['venv_options'].append("--system-site-packages")
 
     create_venv = False
     venv_data = venvscache.get_venv(indicated_deps, interpreter, uuid, options)
diff --git a/man/fades.1 b/man/fades.1
index 4c47ac8..410b6d3 100644
--- a/man/fades.1
+++ b/man/fades.1
@@ -16,7 +16,7 @@ fades - A system that automatically handles the virtualenvs in the cases normall
 [\fB-p\fR \fIversion\fR][\fB--python\fR=\fIversion\fR]
 [\fB--rm\fR=\fIUUID\fR]
 [\fB--system-site-packages\fR]
-[\fB--virtualenv-options\fR=\fIoptions\fR]
+[\fB--venv-options\fR=\fIoptions\fR]
 [\fB--pip-options\fR=\fIoptions\fR]
 [\fB--python-options\fR=\fIoptions\fR]
 [\fB-U\fR][\fB--check-updates\fR]
@@ -34,11 +34,11 @@ fades - A system that automatically handles the virtualenvs in the cases normall
 
 .SH DESCRIPTION
 
-\fBfades\fR will automagically create a new virtualenv (or reuse a previous created one), installing the necessary dependencies, and execute your script inside that virtualenv, with the only requirement of executing the script with \fBfades\fR and also marking the required dependencies.
+\fBfades\fR will automagically create a new virtual environment (or reuse a previous created one), installing the necessary dependencies, and execute your script inside that virtual environment, with the only requirement of executing the script with \fBfades\fR and also marking the required dependencies.
 
 The first non-option parameter (if any) would be then the child program to execute, and any other parameters after that are passed as is to that child script.
 
-\fBfades\fR can also be executed without passing a child script to execute: in this mode it will open a Python interactive interpreter inside the created/reused virtualenv (taking dependencies from \fI--dependency\fR or \fI--requirement\fR options). If \fI--autoimport\fR is given, it will automatically import all the installed dependencies.
+\fBfades\fR can also be executed without passing a child script to execute: in this mode it will open a Python interactive interpreter inside the created/reused virtual environment (taking dependencies from \fI--dependency\fR or \fI--requirement\fR options). If \fI--autoimport\fR is given, it will automatically import all the installed dependencies.
 
 If the \fIchild_program\fR parameter is really an URL, the script will be automatically downloaded from there (supporting also the most common pastebins URLs: pastebin.com, linkode.org, gist, etc.).
 
@@ -88,19 +88,19 @@ The dependencies can be indicated in multiple places (in the Python source file,
 
 .TP
 .BR -x ", " --exec
-Execute the \fIchild_program\fR in the context of the virtualenv. The child_program, which in this case becomes a mandatory parameter, can be just the executable name (relative to the venv's bin directory) or an absolute path.
+Execute the \fIchild_program\fR in the context of the virtual environment. The child_program, which in this case becomes a mandatory parameter, can be just the executable name (relative to the venv's bin directory) or an absolute path.
 
 .TP
 .BR --rm " " \fIUUID\fR
-Remove a virtualenv by UUID.  See \fB--get-venv-dir\fR option to easily find out the UUID.
+Remove a virtual environment by UUID.  See \fB--get-venv-dir\fR option to easily find out the UUID.
 
 .TP
 .BR --system-site-packages ""
 Give the virtual environment access to thesystem site-packages dir 
 
 .TP 
-.BR --virtualenv-options=\fIVIRTUALENV_OPTION\fR
-Extra options to be supplied to virtualenv. (this option can be used multiple times)
+.BR --venv-options=\fIVIRTUALENV_OPTION\fR
+Extra options to be supplied to the venv module (this option can be used multiple times)
 
 .TP 
 .BR --pip-options=\fIPIP_OPTION\fR
@@ -120,7 +120,7 @@ Will remove all virtualenvs that haven't been used for more than MAX_DAYS_TO_KEE
 
 .TP
 .BR --where ", " --get-venv-dir
-Show the virtualenv base directory (which includes the virtualenv UUID) and quit.
+Show the virtual environment base directory (which includes the virtual environment UUID) and quit.
 
 .TP
 .BR --no-precheck-availability
@@ -140,7 +140,7 @@ Run library module as a script (same behaviour than Python's \fB-m\fR parameter)
 
 .TP
 .BR --avoid-pip-upgrade
-Disable the automatic \fBpip\fR upgrade that happens after the virtualenv is created and before the dependencies begin to be installed.
+Disable the automatic \fBpip\fR upgrade that happens after the virtual environment is created and before the dependencies begin to be installed.
 
 
 .SH EXAMPLES
@@ -148,7 +148,7 @@ Disable the automatic \fBpip\fR upgrade that happens after the virtualenv is cre
 .TP
 fades foo.py --bar
 
-Executes foo.py under fades, passing the --bar parameter to the child program, in a virtualenv with the dependencies indicated in the source code.
+Executes foo.py under fades, passing the --bar parameter to the child program, in a virtual environment with the dependencies indicated in the source code.
 
 .TP
 fades -v foo.py
@@ -158,22 +158,22 @@ Executes foo.py under fades, showing all the fades messages (verbose mode).
 .TP
 fades -d dependency1 -d dependency2>3.2 foo.py --bar
 
-Executes foo.py under fades (passing the --bar parameter to it), in a virtualenv with the dependencies indicated in the source code and also dependency1 and dependency2 (any version > 3.2).
+Executes foo.py under fades (passing the --bar parameter to it), in a virtual environment with the dependencies indicated in the source code and also dependency1 and dependency2 (any version > 3.2).
 
 .TP
 fades -d dependency1
 
-Executes the Python interactive interpreter in a virtualenv with dependency1 installed.
+Executes the Python interactive interpreter in a virtual environment with dependency1 installed.
 
 .TP
 fades -r requirements.txt
 
-Executes the Python interactive interpreter in a virtualenv after installing there all dependencies taken from the requirements.txt file.
+Executes the Python interactive interpreter in a virtual environment after installing there all dependencies taken from the requirements.txt file.
 
 .TP
 fades -r requirements.txt -r requirements_devel.txt
 
-Executes the Python interactive interpreter in a virtualenv after installing there all dependencies taken from files requirements.txt and requirements_devel.txt.
+Executes the Python interactive interpreter in a virtual environment after installing there all dependencies taken from files requirements.txt and requirements_devel.txt.
 
 .SH USING CONFIGURATION FILES
 
diff --git a/pkg/debian/control b/pkg/debian/control
index 41cd073..6e56b57 100644
--- a/pkg/debian/control
+++ b/pkg/debian/control
@@ -5,14 +5,14 @@ Build-Depends: debhelper (>= 9),
                dh-python,
                dh-translations | dh-python,
                python3-setuptools,
-               python3-all (>= 3.4),
+               python3-all (>= 3.6),
                python3-xdg
 Maintainer: Facundo Batista <facundo@taniquetil.com.ar>
 Uploaders: Debian Python Modules Team 
            <python-modules-team@lists.alioth.debian.org>
 Homepage: https://github.com/PyAr/fades
 Standards-Version: 3.9.7
-X-Python3-Version: >= 3.4
+X-Python3-Version: >= 3.6
 
 Package: fades
 Architecture: all
diff --git a/pkg/debian/copyright b/pkg/debian/copyright
index 5985318..de4b916 100644
--- a/pkg/debian/copyright
+++ b/pkg/debian/copyright
@@ -4,7 +4,7 @@ Upstream-Contact: Facundo Batista <facundo@taniquetil.com.ar>
 Source: https://github.com/PyAr/fades/
 
 Files: *
-Copyright: (C) 2014-2020
+Copyright: (C) 2014-2024
  Facundo Batista <facundo@taniquetil.com.ar>
  Nicolás Demarchi <mail@gilgamezh.me>
 License: GPL-3
diff --git a/setup.py b/setup.py
index c1d8249..1061c8d 100755
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright 2014-2017 Facundo Batista, Nicolás Demarchi
+# Copyright 2014-2024 Facundo Batista, Nicolás Demarchi
 #
 # This program is free software: you can redistribute it and/or modify it
 # under the terms of the GNU General Public License version 3, as published
@@ -53,7 +53,7 @@
 def get_version():
     """Retrieves package version from the file."""
     with open('fades/_version.py') as fh:
-        m = re.search("\(([^']*)\)", fh.read())
+        m = re.search(r"\(([^']*)\)", fh.read())
     if m is None:
         raise ValueError("Unrecognized version in 'fades/_version.py'")
     return m.groups()[0].replace(', ', '.')
@@ -123,7 +123,7 @@ def finalize_options(self):
     install_requires=['setuptools'],
     tests_require=['logassert', 'pyxdg', 'pyuca', 'pytest', 'flake8',
                    'pep257', 'rst2html5'],  # what unittests require
-    python_requires='>=3.3',  # Minimum Python version supported.
+    python_requires='>=3.6',  # Minimum Python version supported.
     extras_require={
         'pyxdg': 'pyxdg',
         'virtualenv': 'virtualenv',
@@ -158,6 +158,8 @@ def finalize_options(self):
         'Programming Language :: Python',
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3 :: Only',
+        'Programming Language :: Python :: 3.6',
+        'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
diff --git a/tests/test_envbuilder.py b/tests/test_envbuilder.py
index 393c6d3..f3b6a89 100644
--- a/tests/test_envbuilder.py
+++ b/tests/test_envbuilder.py
@@ -1,4 +1,4 @@
-# Copyright 2015-2016 Facundo Batista, Nicolás Demarchi
+# Copyright 2015-2024 Facundo Batista, Nicolás Demarchi
 #
 # This program is free software: you can redistribute it and/or modify it
 # under the terms of the GNU General Public License version 3, as published
@@ -66,9 +66,7 @@ def test_create_simple(self):
         interpreter = 'python3'
         is_current = True
         avoid_pip_upgrade = False
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": [],
-                   }
+        options = {"venv_options": []}
         pip_options = []
         with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
             with patch.object(envbuilder, 'PipManager') as mock_mgr_c:
@@ -101,7 +99,7 @@ def test_create_vcs(self):
         interpreter = 'python3'
         is_current = True
         avoid_pip_upgrade = False
-        options = {"virtualenv_options": [], "pyvenv_options": []}
+        options = {"venv_options": []}
         pip_options = []
         with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
             with patch.object(envbuilder, 'PipManager') as mock_mgr_c:
@@ -124,9 +122,7 @@ def test_unknown_repo(self):
         interpreter = 'python3'
         is_current = True
         avoid_pip_upgrade = False
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": [],
-                   }
+        options = {"venv_options": []}
         pip_options = []
         with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
             with patch.object(envbuilder, 'PipManager') as mock_mgr_c:
@@ -144,9 +140,7 @@ def test_non_existing_dep(self):
         interpreter = 'python3'
         is_current = True
         avoid_pip_upgrade = False
-        options = {'virtualenv_options': [],
-                   'pyvenv_options': [],
-                   }
+        options = {'venv_options': []}
         pip_options = []
 
         with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
@@ -161,7 +155,7 @@ def test_non_existing_dep(self):
                     self.assertEqual(str(cm.exception), 'Dependency installation failed')
                     mock_destroy.assert_called_once_with('env_path')
 
-        self.assertLoggedDebug("Installation Step failed, removing virtualenv")
+        self.assertLoggedDebug("Installation Step failed, removing virtual environment")
 
     def test_different_versions(self):
         requested = {
@@ -170,9 +164,7 @@ def test_different_versions(self):
         interpreter = 'python3'
         is_current = True
         avoid_pip_upgrade = False
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": [],
-                   }
+        options = {"venv_options": []}
         pip_options = []
         with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
             with patch.object(envbuilder, 'PipManager') as mock_mgr_c:
@@ -189,13 +181,11 @@ def test_different_versions(self):
             }
         })
 
-    def test_create_system_site_pkgs_pyvenv(self):
+    def test_create_system_site_pkgs_venv(self):
         env_builder = envbuilder._FadesEnvBuilder()
         interpreter = 'python3'
         is_current = True
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": ['--system-site-packages'],
-                   }
+        options = {"venv_options": ['--system-site-packages']}
         with patch.object(EnvBuilder, 'create') as mock_create:
             env_builder.create_env(interpreter, is_current, options)
             self.assertTrue(env_builder.system_site_packages)
@@ -205,39 +195,20 @@ def test_create_pyvenv(self):
         env_builder = envbuilder._FadesEnvBuilder()
         interpreter = 'python3'
         is_current = True
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": [],
-                   }
+        options = {"venv_options": []}
         with patch.object(EnvBuilder, 'create') as mock_create:
             env_builder.create_env(interpreter, is_current, options)
             self.assertFalse(env_builder.system_site_packages)
             self.assertTrue(mock_create.called)
 
-    def test_create_system_site_pkgs_virtualenv(self):
+    def test_create_virtual_environment(self):
         env_builder = envbuilder._FadesEnvBuilder()
         interpreter = 'pythonX.Y'
         is_current = False
-        options = {"virtualenv_options": ['--system-site-packages'],
-                   "pyvenv_options": [],
-                   }
-        with patch.object(envbuilder._FadesEnvBuilder, 'create_with_virtualenv') as mock_create:
+        options = {"venv_options": []}
+        with patch.object(envbuilder._FadesEnvBuilder, 'create_with_external_venv') as mock_create:
             env_builder.create_env(interpreter, is_current, options)
-            mock_create.assert_called_with(interpreter, options['virtualenv_options'])
-
-    def test_create_virtualenv(self):
-        env_builder = envbuilder._FadesEnvBuilder()
-        interpreter = 'pythonX.Y'
-        is_current = False
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": [],
-                   }
-        with patch.object(envbuilder._FadesEnvBuilder, 'create_with_virtualenv') as mock_create:
-            env_builder.create_env(interpreter, is_current, options)
-            mock_create.assert_called_with(interpreter, options['virtualenv_options'])
-
-    def test_custom_env_path(self):
-        builder = envbuilder._FadesEnvBuilder('some-path')
-        self.assertEqual(builder.env_path, 'some-path')
+            mock_create.assert_called_with(interpreter, options['venv_options'])
 
 
 class EnvDestructionTestCase(unittest.TestCase):
@@ -245,10 +216,7 @@ class EnvDestructionTestCase(unittest.TestCase):
     def test_destroy_venv(self):
         builder = envbuilder._FadesEnvBuilder()
         # make sure the virtualenv exists on disk
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": ['--system-site-packages'],
-                   "pip-options": [],
-                   }
+        options = {"venv_options": [], "pip-options": []}
 
         def fake_create(*_):
             """Fake venv create.
@@ -260,7 +228,7 @@ def fake_create(*_):
             builder.env_path = fake_venv_path
 
         fake_venv_path = tempfile.TemporaryDirectory().name
-        builder.create_with_virtualenv = fake_create
+        builder.create_with_external_venv = fake_create
         builder.create_env('python', False, options=options)
         assert os.path.exists(builder.env_path)
 
@@ -360,45 +328,27 @@ def test_usage_file_is_compacted_when_though_no_venv_is_removed(self):
                 else:
                     self.assertEqual(old_date, d, msg="Others envs have old date")
 
-    def test_filenotfound_exception(self):
-        env_builder = envbuilder._FadesEnvBuilder()
-        interpreter = 'python3'
-        is_current = False
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": ['--system-site-packages'],
-                   }
-        with patch('fades.envbuilder.helpers.logged_exec') as mock_lexec:
-            # mock_lexec.side_effect = envbuilder.helpers.ExecutionError('matanga!')
-            mock_lexec.side_effect = FileNotFoundError('matanga!')
-            with self.assertRaises(FadesError) as cm:
-                env_builder.create_env(interpreter, is_current, options)
-            self.assertEqual(str(cm.exception), 'virtualenv not found')
-
     def test_executionerror_exception(self):
         env_builder = envbuilder._FadesEnvBuilder()
         interpreter = 'python3'
         is_current = False
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": ['--system-site-packages'],
-                   }
+        options = {"venv_options": []}
         with patch('fades.envbuilder.helpers.logged_exec') as mock_lexec:
             mock_lexec.side_effect = envbuilder.helpers.ExecutionError(1, 'cmd', ['stdout'])
             with self.assertRaises(FadesError) as cm:
                 env_builder.create_env(interpreter, is_current, options)
-            self.assertEqual(str(cm.exception), 'virtualenv could not be run')
+            self.assertEqual(str(cm.exception), "Failed to run venv module externally")
 
     def test_general_error_exception(self):
         env_builder = envbuilder._FadesEnvBuilder()
         interpreter = 'python3'
         is_current = False
-        options = {"virtualenv_options": [],
-                   "pyvenv_options": ['--system-site-packages'],
-                   }
+        options = {"venv_options": []}
         with patch('fades.envbuilder.helpers.logged_exec') as mock_lexec:
             mock_lexec.side_effect = Exception()
             with self.assertRaises(FadesError) as cm:
                 env_builder.create_env(interpreter, is_current, options)
-            self.assertEqual(str(cm.exception), 'General error while running virtualenv')
+            self.assertEqual(str(cm.exception), "General error while running external venv")
 
     def test_when_a_venv_is_removed_it_is_removed_from_everywhere(self):
         old_date = datetime.utcnow()

From 669ac4ab995111e4141a5dd639a61120a4f19076 Mon Sep 17 00:00:00 2001
From: Facundo Batista <facundo@taniquetil.com.ar>
Date: Tue, 11 Jun 2024 17:35:04 -0400
Subject: [PATCH 2/6] Merge leftovers.

---
 fades/_version.py | 2 +-
 setup.py          | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/fades/_version.py b/fades/_version.py
index bc14a4e..1607110 100644
--- a/fades/_version.py
+++ b/fades/_version.py
@@ -1,4 +1,4 @@
 """Holder of the fades version number."""
 
-VERSION = (10, 0, 0)
+VERSION = (9, 0, 2)
 __version__ = '.'.join([str(x) for x in VERSION])
diff --git a/setup.py b/setup.py
index e1928b1..ba1125e 100755
--- a/setup.py
+++ b/setup.py
@@ -124,7 +124,6 @@ def finalize_options(self):
     python_requires='>=3.6',  # Minimum Python version supported.
     extras_require={
         'pyxdg': 'pyxdg',
-        'virtualenv': 'virtualenv',
         'packaging': 'packaging',
     },
 

From febcf3fe1108993364222251e6bb9aaf2a123243 Mon Sep 17 00:00:00 2001
From: Facundo Batista <facundo@taniquetil.com.ar>
Date: Tue, 11 Jun 2024 17:47:22 -0400
Subject: [PATCH 3/6] Removed virtualenv installation in archlinux and fedora
 integration tests.

---
 .github/workflows/integtests.yaml | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/.github/workflows/integtests.yaml b/.github/workflows/integtests.yaml
index af5361f..e61d02f 100644
--- a/.github/workflows/integtests.yaml
+++ b/.github/workflows/integtests.yaml
@@ -29,7 +29,6 @@ jobs:
           bin/fades -v -d pytest -x pytest --version
       - name: Using a different Python
         run: |
-          pacman -S --noconfirm python-virtualenv
           export TEST_PYTHON_VERSION=3.9
           python bin/fades -v --python=python3.9 -d pytest -x pytest -v tests/integtest.py
 
@@ -53,8 +52,7 @@ jobs:
           bin/fades -v -d pytest -x pytest --version
       - name: Using a different Python
         run: |
-          # XXX Facundo 2024-02-29 - remove virtualenv in this install as part of issue #411 work
-          yum install --assumeyes python3.9 python3-virtualenv
+          yum install --assumeyes python3.9
           cd /fades
           export TEST_PYTHON_VERSION=3.9
           python3.12 bin/fades -v --python=python3.9 -d pytest -x pytest -v tests/integtest.py

From 438221f19cf917fee4ece908b6035884bbdca491 Mon Sep 17 00:00:00 2001
From: Facundo Batista <facundo@taniquetil.com.ar>
Date: Tue, 11 Jun 2024 17:52:32 -0400
Subject: [PATCH 4/6] Enabled cross-python checks for ubuntu, macos and
 windows.

---
 .github/workflows/integtests.yaml | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/integtests.yaml b/.github/workflows/integtests.yaml
index e61d02f..df90331 100644
--- a/.github/workflows/integtests.yaml
+++ b/.github/workflows/integtests.yaml
@@ -76,11 +76,10 @@ jobs:
         with:
           python-version: ${{ matrix.python-version }}
 
-      # XXX Facundo 2024-03-02 - disabled until we enable the cross test below
-      # - name: Also set up Python 3.10 for cross-Python test
-      #   uses: actions/setup-python@v4
-      #   with:
-      #     python-version: "3.10"
+      - name: Also set up Python 3.10 for cross-Python test
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.10"
 
       - name: Install dependencies
         run: |
@@ -89,9 +88,7 @@ jobs:
         run: |
           ${{ steps.matrixpy.outputs.python-path }} bin/fades -v -d pytest -x pytest --version
 
-      # XXX Facundo 2024-03-02 - commented out as until we finish issue #411 work we need 'virtualenv' and it's a
-      # hassle to install it in a multiplatform way -- this should be enabled while working on that issue
-      # - name: Using a different Python
-      #   run: |
-      #     export TEST_PYTHON_VERSION=3.10
-      #     ${{ steps.matrixpy.outputs.python-path }} bin/fades -v --python=python3.10 -d pytest -x pytest -v tests/integtest.py
+      - name: Using a different Python
+        run: |
+          export TEST_PYTHON_VERSION=3.10
+          ${{ steps.matrixpy.outputs.python-path }} bin/fades -v --python=python3.10 -d pytest -x pytest -v tests/integtest.py

From 1486a3502ab9cc5aadeac00c98a0a2be9555ba6d Mon Sep 17 00:00:00 2001
From: Facundo Batista <facundo@taniquetil.com.ar>
Date: Sat, 15 Jun 2024 09:49:49 -0400
Subject: [PATCH 5/6] Separated Windows, as it has different way to set a
 environment variable.

---
 .github/workflows/integtests.yaml | 40 +++++++++++++++++++++++++++++--
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/integtests.yaml b/.github/workflows/integtests.yaml
index df90331..5fa2fdf 100644
--- a/.github/workflows/integtests.yaml
+++ b/.github/workflows/integtests.yaml
@@ -57,13 +57,49 @@ jobs:
           export TEST_PYTHON_VERSION=3.9
           python3.12 bin/fades -v --python=python3.9 -d pytest -x pytest -v tests/integtest.py
 
-  native:
+  native-windows:
+    strategy:
+      matrix:
+        # just a selection otherwise it's too much
+        # - latest OS (left here even if it's only one to simplify upgrading later)
+        # - oldest and newest Python
+        os: [windows-2022]
+        python-version: [3.8, "3.12"]
+
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
+        uses: actions/setup-python@v5
+        id: matrixpy
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Also set up Python 3.10 for cross-Python test
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.10"
+
+      - name: Install dependencies
+        run: |
+          ${{ steps.matrixpy.outputs.python-path }} -m pip install -U packaging
+      - name: Simple fades run
+        run: |
+          ${{ steps.matrixpy.outputs.python-path }} bin/fades -v -d pytest -x pytest --version
+
+      - name: Using a different Python
+        run: |
+          set TEST_PYTHON_VERSION=3.10
+          ${{ steps.matrixpy.outputs.python-path }} bin/fades -v --python=python3.10 -d pytest -x pytest -v tests/integtest.py
+
+  native-generic:
     strategy:
       matrix:
         # just a selection otherwise it's too much
         # - latest OSes
         # - oldest and newest Python
-        os: [ubuntu-22.04, macos-12, windows-2022]
+        os: [ubuntu-22.04, macos-12]
         python-version: [3.8, "3.12"]
 
     runs-on: ${{ matrix.os }}

From 366864969ee1021de3b77df877b85aa28be80423 Mon Sep 17 00:00:00 2001
From: Facundo Batista <facundo@taniquetil.com.ar>
Date: Sat, 15 Jun 2024 10:00:23 -0400
Subject: [PATCH 6/6] Specified the full path to other python in windows.

---
 .github/workflows/integtests.yaml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/integtests.yaml b/.github/workflows/integtests.yaml
index 5fa2fdf..e2a1d0b 100644
--- a/.github/workflows/integtests.yaml
+++ b/.github/workflows/integtests.yaml
@@ -78,6 +78,7 @@ jobs:
 
       - name: Also set up Python 3.10 for cross-Python test
         uses: actions/setup-python@v5
+        id: otherpy
         with:
           python-version: "3.10"
 
@@ -91,7 +92,7 @@ jobs:
       - name: Using a different Python
         run: |
           set TEST_PYTHON_VERSION=3.10
-          ${{ steps.matrixpy.outputs.python-path }} bin/fades -v --python=python3.10 -d pytest -x pytest -v tests/integtest.py
+          ${{ steps.matrixpy.outputs.python-path }} bin/fades -v --python=${{ steps.otherpy.outputs.python-path }} -d pytest -x pytest -v tests/integtest.py
 
   native-generic:
     strategy: