diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index e6a272e97..e45a4e4eb 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -34,7 +34,6 @@ jobs:
- pypy2
include:
- { os: macos-latest, py: brew@py3 }
- - { os: "ubuntu-18.04", py: 3.4.10 }
steps:
- name: Install OS dependencies
run: |
diff --git a/docs/changelog/2141.removal.rst b/docs/changelog/2141.removal.rst
new file mode 100644
index 000000000..900864276
--- /dev/null
+++ b/docs/changelog/2141.removal.rst
@@ -0,0 +1 @@
+Drop python ``3.4`` support as it has been over 2 years since EOL - by :user:`gaborbernat`.
diff --git a/docs/installation.rst b/docs/installation.rst
index d3c44eba2..19a37dfe8 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -88,8 +88,8 @@ Python and OS Compatibility
virtualenv works with the following Python interpreter implementations:
-- `CPython `_ versions 2.7, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9
-- `PyPy `_ 2.7 and 3.4+.
+- `CPython `_ versions 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10
+- `PyPy `_ 2.7 and 3.5+.
This means virtualenv works on the latest patch version of each of these minor versions. Previous patch versions are
supported on a best effort approach.
diff --git a/docs/user_guide.rst b/docs/user_guide.rst
index ca33617b8..bb8ae48f0 100644
--- a/docs/user_guide.rst
+++ b/docs/user_guide.rst
@@ -106,7 +106,7 @@ at the moment has two types of virtual environments:
- ``venv`` - this delegates the creation process towards the ``venv`` module, as described in
`PEP 405 `_. This is only available on Python interpreters having version
- ``3.4`` or later, and also has the downside that virtualenv **must** create a process to invoke that module (unless
+ ``3.5`` or later, and also has the downside that virtualenv **must** create a process to invoke that module (unless
virtualenv is installed in the system python), which can be an expensive operation (especially true on Windows).
- ``builtin`` - this means ``virtualenv`` is able to do the creation operation itself (by knowing exactly what files to
diff --git a/setup.cfg b/setup.cfg
index 5b845d565..4144e8ce3 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -21,7 +21,6 @@ classifiers =
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
@@ -48,7 +47,7 @@ install_requires =
importlib-metadata>=0.12;python_version<"3.8"
importlib-resources>=1.0;python_version<"3.7"
pathlib2>=2.3.3,<3;python_version < '3.4' and sys.platform != 'win32'
-python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
package_dir =
=src
zip_safe = True
diff --git a/src/virtualenv/discovery/cached_py_info.py b/src/virtualenv/discovery/cached_py_info.py
index d16a8e298..31beff52f 100644
--- a/src/virtualenv/discovery/cached_py_info.py
+++ b/src/virtualenv/discovery/cached_py_info.py
@@ -37,7 +37,7 @@ def from_exe(cls, app_data, exe, env=None, raise_on_error=True, ignore_cache=Fal
def _get_from_cache(cls, app_data, exe, env, ignore_cache=True):
# note here we cannot resolve symlinks, as the symlink may trigger different prefix information if there's a
- # pyenv.cfg somewhere alongside on python3.4+
+ # pyenv.cfg somewhere alongside on python3.5+
exe_path = Path(exe)
if not ignore_cache and exe_path in _CACHE: # check in the in-memory cache
result = _CACHE[exe_path]
diff --git a/src/virtualenv/seed/wheels/embed/__init__.py b/src/virtualenv/seed/wheels/embed/__init__.py
index 10615ea6c..f47c962c0 100644
--- a/src/virtualenv/seed/wheels/embed/__init__.py
+++ b/src/virtualenv/seed/wheels/embed/__init__.py
@@ -35,11 +35,6 @@
"setuptools": "setuptools-50.3.2-py3-none-any.whl",
"wheel": "wheel-0.36.2-py2.py3-none-any.whl",
},
- "3.4": {
- "pip": "pip-19.1.1-py2.py3-none-any.whl",
- "setuptools": "setuptools-43.0.0-py2.py3-none-any.whl",
- "wheel": "wheel-0.33.6-py2.py3-none-any.whl",
- },
"2.7": {
"pip": "pip-20.3.4-py2.py3-none-any.whl",
"setuptools": "setuptools-44.1.1-py2.py3-none-any.whl",
diff --git a/src/virtualenv/seed/wheels/embed/pip-19.1.1-py2.py3-none-any.whl b/src/virtualenv/seed/wheels/embed/pip-19.1.1-py2.py3-none-any.whl
deleted file mode 100644
index 8476c1193..000000000
Binary files a/src/virtualenv/seed/wheels/embed/pip-19.1.1-py2.py3-none-any.whl and /dev/null differ
diff --git a/src/virtualenv/seed/wheels/embed/setuptools-43.0.0-py2.py3-none-any.whl b/src/virtualenv/seed/wheels/embed/setuptools-43.0.0-py2.py3-none-any.whl
deleted file mode 100644
index 733faa6a5..000000000
Binary files a/src/virtualenv/seed/wheels/embed/setuptools-43.0.0-py2.py3-none-any.whl and /dev/null differ
diff --git a/src/virtualenv/seed/wheels/embed/wheel-0.33.6-py2.py3-none-any.whl b/src/virtualenv/seed/wheels/embed/wheel-0.33.6-py2.py3-none-any.whl
deleted file mode 100644
index 2a71896be..000000000
Binary files a/src/virtualenv/seed/wheels/embed/wheel-0.33.6-py2.py3-none-any.whl and /dev/null differ
diff --git a/src/virtualenv/util/path/_pathlib/__init__.py b/src/virtualenv/util/path/_pathlib/__init__.py
index 6bb045c2d..746c8aed2 100644
--- a/src/virtualenv/util/path/_pathlib/__init__.py
+++ b/src/virtualenv/util/path/_pathlib/__init__.py
@@ -6,52 +6,6 @@
if six.PY3:
from pathlib import Path
-
- if sys.version_info[0:2] == (3, 4):
- # no read/write text on python3.4
- BuiltinPath = Path
-
- class Path(type(BuiltinPath())):
- def read_text(self, encoding=None, errors=None):
- """
- Open the file in text mode, read it, and close the file.
- """
- with self.open(mode="r", encoding=encoding, errors=errors) as f:
- return f.read()
-
- def read_bytes(self):
- """
- Open the file in bytes mode, read it, and close the file.
- """
- with self.open(mode="rb") as f:
- return f.read()
-
- def write_text(self, data, encoding=None, errors=None):
- """
- Open the file in text mode, write to it, and close the file.
- """
- if not isinstance(data, str):
- raise TypeError("data must be str, not %s" % data.__class__.__name__)
- with self.open(mode="w", encoding=encoding, errors=errors) as f:
- return f.write(data)
-
- def write_bytes(self, data):
- """
- Open the file in bytes mode, write to it, and close the file.
- """
- # type-check for the buffer interface before truncating the file
- view = memoryview(data)
- with self.open(mode="wb") as f:
- return f.write(view)
-
- def mkdir(self, mode=0o777, parents=False, exist_ok=False):
- try:
- super(type(BuiltinPath()), self).mkdir(mode, parents)
- except FileExistsError as exception:
- if not exist_ok:
- raise exception
-
-
else:
if sys.platform == "win32":
# workaround for https://github.com/mcmtroffaes/pathlib2/issues/56
diff --git a/tasks/make_zipapp.py b/tasks/make_zipapp.py
index 7eceb50c1..aa6f62509 100644
--- a/tasks/make_zipapp.py
+++ b/tasks/make_zipapp.py
@@ -20,7 +20,7 @@
HERE = Path(__file__).parent.absolute()
-VERSIONS = ["3.{}".format(i) for i in range(9, 3, -1)] + ["2.7"]
+VERSIONS = ["3.{}".format(i) for i in range(10, 4, -1)] + ["2.7"]
def main():
diff --git a/tasks/upgrade_wheels.py b/tasks/upgrade_wheels.py
index fe0010ab8..78e5ffe28 100644
--- a/tasks/upgrade_wheels.py
+++ b/tasks/upgrade_wheels.py
@@ -16,7 +16,7 @@
STRICT = "UPGRADE_ADVISORY" not in os.environ
BUNDLED = ["pip", "setuptools", "wheel"]
-SUPPORT = list(reversed([(2, 7)] + [(3, i) for i in range(4, 11)]))
+SUPPORT = list(reversed([(2, 7)] + [(3, i) for i in range(5, 11)]))
DEST = Path(__file__).resolve().parents[1] / "src" / "virtualenv" / "seed" / "wheels" / "embed"
diff --git a/tests/unit/create/test_creator.py b/tests/unit/create/test_creator.py
index e51833fc8..424a9b385 100644
--- a/tests/unit/create/test_creator.py
+++ b/tests/unit/create/test_creator.py
@@ -129,8 +129,6 @@ def system(session_app_data):
ids=lambda i: "-".join(i) if isinstance(i, tuple) else i,
)
def test_create_no_seed(python, creator, isolated, system, coverage_env, special_name_dir):
- if creator[0] == "venv" and sys.version_info[0:2] == (3, 4): # venv on python3.4 only supports ascii chars
- special_name_dir = special_name_dir.with_name(special_name_dir.name.encode("ascii", errors="ignore").decode())
dest = special_name_dir
creator_key, method = creator
cmd = [
diff --git a/tests/unit/seed/embed/test_pip_invoke.py b/tests/unit/seed/embed/test_pip_invoke.py
index c033aa19d..f2e7a33ef 100644
--- a/tests/unit/seed/embed/test_pip_invoke.py
+++ b/tests/unit/seed/embed/test_pip_invoke.py
@@ -25,7 +25,7 @@ def test_base_bootstrap_via_pip_invoke(tmp_path, coverage_env, mocker, current_f
def _load_embed_wheel(app_data, distribution, for_py_version, version):
return load_embed_wheel(app_data, distribution, old_ver, version)
- old_ver = "3.4"
+ old_ver = "2.7"
old = BUNDLE_SUPPORT[old_ver]
mocker.patch("virtualenv.seed.wheels.bundle.load_embed_wheel", side_effect=_load_embed_wheel)
@@ -36,7 +36,9 @@ def _execute(cmd, env):
continue
if with_version == "embed":
expected.add(BUNDLE_FOLDER)
- elif old[dist] != new[dist]:
+ elif old[distribution] == new[distribution]:
+ expected.add(BUNDLE_FOLDER)
+ else:
expected.add(extra_search_dir)
expected_list = list(
itertools.chain.from_iterable(["--find-links", str(e)] for e in sorted(expected, key=lambda x: str(x))),
diff --git a/tests/unit/seed/wheels/test_periodic_update.py b/tests/unit/seed/wheels/test_periodic_update.py
index 2ea473277..f308aee17 100644
--- a/tests/unit/seed/wheels/test_periodic_update.py
+++ b/tests/unit/seed/wheels/test_periodic_update.py
@@ -67,7 +67,7 @@ def _do_update(distribution, for_py_version, embed_filename, app_data, search_di
def test_pick_periodic_update(tmp_path, session_app_data, mocker, for_py_version):
- embed, current = get_embed_wheel("setuptools", "3.4"), get_embed_wheel("setuptools", for_py_version)
+ embed, current = get_embed_wheel("setuptools", "3.5"), get_embed_wheel("setuptools", for_py_version)
mocker.patch("virtualenv.seed.wheels.bundle.load_embed_wheel", return_value=embed)
completed = datetime.now() - timedelta(days=29)
u_log = UpdateLog(
diff --git a/tox.ini b/tox.ini
index f7cbd3869..e50a0d232 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,7 +6,6 @@ envlist =
py37
py36
py35
- py34
py27
pypy3
pypy2
@@ -30,21 +29,22 @@ setenv =
COVERAGE_PROCESS_START = {toxinidir}/.coveragerc
PYTHONIOENCODING = utf-8
_COVERAGE_SRC = {envsitepackagesdir}/virtualenv
- {py34,py27,pypy2, upgrade}: PYTHONWARNINGS = ignore:DEPRECATION::pip._internal.cli.base_command
+ {py27,pypy2, upgrade}: PYTHONWARNINGS = ignore:DEPRECATION::pip._internal.cli.base_command
extras =
testing
commands =
python -m coverage erase
- python -m coverage run -m pytest \
+ python -m coverage run -m pytest {tty:--color=yes} \
--junitxml {toxworkdir}/junit.{envname}.xml \
{posargs:tests --int --timeout 600}
python -m coverage combine
python -m coverage report --skip-covered --show-missing
python -m coverage xml -o {toxworkdir}/coverage.{envname}.xml
- python -m coverage html -d {envtmpdir}/htmlcov \
- !py34: --show-contexts \
+ python -m coverage html -d {envtmpdir}/htmlcov --show-contexts \
--title virtualenv-{envname}-coverage
install_command = python -m pip install {opts} {packages} --disable-pip-version-check
+package = wheel
+wheel_build_env = .pkg
[testenv:fix_lint]
description = format the code base to adhere to our styles, and complain about what we cannot do automatically
@@ -83,7 +83,6 @@ depends =
py37
py36
py35
- py34
py27
pypy
pypy3