Initial Python 3.7 drop

This commit is contained in:
Florian Bruhin 2023-06-26 14:24:11 +02:00
parent b9855b9104
commit 93c7fdd60c
27 changed files with 54 additions and 137 deletions

View File

@ -58,7 +58,7 @@ ignore =
PT004,
PT011,
PT012
min-version = 3.7.0
min-version = 3.8.0
max-complexity = 12
per-file-ignores =
qutebrowser/api/hook.py : N801

View File

@ -128,10 +128,10 @@ jobs:
fail-fast: false
matrix:
include:
### PyQt 5.15.2 (Python 3.7)
### PyQt 5.15.2 (Python 3.8)
- testenv: py37-pyqt5152
os: ubuntu-20.04
python: "3.7"
python: "3.8"
### PyQt 5.15 (Python 3.10, with coverage)
# FIXME:qt6
# - testenv: py310-pyqt515-cov
@ -141,10 +141,10 @@ jobs:
- testenv: py311-pyqt515
os: ubuntu-20.04
python: "3.11"
### PyQt 6.2 (Python 3.7)
### PyQt 6.2 (Python 3.8)
- testenv: py37-pyqt62
os: ubuntu-20.04
python: "3.7"
python: "3.8"
### PyQt 6.3 (Python 3.8)
- testenv: py38-pyqt63
os: ubuntu-20.04

View File

@ -21,10 +21,6 @@ jobs:
- uses: actions/checkout@v3
with:
persist-credentials: false
- name: Set up Python 3.7
uses: actions/setup-python@v4
with:
python-version: '3.7'
- name: Set up Python 3.8
uses: actions/setup-python@v4
with:

View File

@ -1,5 +1,5 @@
[mypy]
python_version = 3.7
python_version = 3.8
### --strict
warn_unused_configs = True

View File

@ -20,7 +20,7 @@ load-plugins=qute_pylint.config,
pylint.extensions.dunder
persistent=n
py-version=3.7
py-version=3.8
[MESSAGES CONTROL]
enable=all
@ -52,6 +52,7 @@ disable=locally-disabled,
too-few-public-methods,
import-outside-toplevel,
consider-using-f-string,
consider-using-assignment-expr,
logging-fstring-interpolation,
raise-missing-from,
consider-using-tuple,

View File

@ -82,7 +82,7 @@ Requirements
The following software and libraries are required to run qutebrowser:
* https://www.python.org/[Python] 3.7 or newer
* https://www.python.org/[Python] 3.8 or newer
* https://www.qt.io/[Qt], either 6.2.0 or newer, or 5.15.0 or newer, with the following modules:
- QtCore / qtbase
- QtQuick (part of qtbase or qtdeclarative in some distributions)
@ -103,7 +103,7 @@ websites and using it for transmission of sensitive data._
* https://palletsprojects.com/p/jinja/[jinja2]
* https://github.com/yaml/pyyaml[PyYAML]
On older Python versions (3.7/3.8), the following backports are also required:
On Python 3.8, the following backport is also required:
* https://importlib-resources.readthedocs.io/[importlib_resources]
@ -119,10 +119,6 @@ The following libraries are optional:
QtWebEngine backend.
* On Windows, https://pypi.python.org/pypi/colorama/[colorama] for colored log
output.
* https://importlib-metadata.readthedocs.io/[importlib_metadata] on Python 3.7,
to improve QtWebEngine version detection when PyQtWebEngine is
installed via pip (thus, this dependency usually isn't relevant for
packagers).
* https://asciidoc.org/[asciidoc] to generate the documentation for the `:help`
command, when using the git repository (rather than a release).

View File

@ -57,9 +57,9 @@ Added
Removed
~~~~~~~
- Support for Python 3.6 is dropped, as it's been
https://discuss.python.org/t/python-3-6-rides-into-the-sunset/12964[end-of-life upstream]
since December 2021. Python 3.7.0 or newer is now required.
- Python 3.8.0 or newer is now required.
- Support for Python 3.6 and 3.7 is dropped, as they both reached
their [end of life] in December 2021 and June 2023, respectively.
- Support for Qt/PyQt before 5.15.0 and QtWebEngine before 5.15.2 are now
dropped, as older Qt versions are
https://endoflife.date/qt[end-of-life upstream] since mid/late 2020

View File

@ -111,9 +111,9 @@ unittests and several linters/checkers.
Currently, the following tox environments are available:
* Tests using https://www.pytest.org[pytest]:
- `py37`, `py38`, ...: Run pytest for python 3.7/3.8/... with the system-wide PyQt.
- `py37-pyqt512`, ..., `py37-pyqt515`: Run pytest with the given PyQt version (`py37-*` also works).
- `py37-pyqt515-cov`: Run with coverage support (other Python/PyQt versions work too).
- `py38`, `py39`, ...: Run pytest for python 3.8/3.9/... with the system-wide PyQt.
- `py38-pyqt515`, ..., `py38-pyqt65`: Run pytest with the given PyQt version (`py39-*` etc. also works).
- `py38-pyqt515-cov`: Run with coverage support (other Python/PyQt versions work too).
* `flake8`: Run various linting checks via https://pypi.python.org/pypi/flake8[flake8].
* `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find
unused code portions.
@ -171,16 +171,16 @@ Examples:
----
# run only pytest tests which failed in last run:
tox -e py37 -- --lf
tox -e py38 -- --lf
# run only the end2end feature tests:
tox -e py37 -- tests/end2end/features
tox -e py38 -- tests/end2end/features
# run everything with undo in the generated name, based on the scenario text
tox -e py37 -- tests/end2end/features/test_tabs_bdd.py -k undo
tox -e py38 -- tests/end2end/features/test_tabs_bdd.py -k undo
# run coverage test for specific file (updates htmlcov/index.html)
tox -e py37-cov -- tests/unit/browser/test_webelem.py
tox -e py38-cov -- tests/unit/browser/test_webelem.py
----
Specifying the backend for tests

View File

@ -38,7 +38,7 @@ version (Qt 5.7, based on a Chromium from March 2016). Furthermore, it packages
Ubuntu 16.04 doesn't come with an up-to-date engine (a new enough QtWebKit, or
QtWebEngine) and also comes with Python 3.5.
You should be able to install a newer Python (3.7+) using the
You should be able to install a newer Python (3.8+) using the
https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa[deadsnakes PPA] or
https://github.com/pyenv/pyenv[pyenv], and then proceed to
<<tox,install qutebrowser in a virtualenv>>. However, this is currently untested. If you
@ -452,7 +452,7 @@ This installs all needed Python dependencies in a `.venv` subfolder
This comes with an up-to-date Qt/PyQt including a pre-compiled QtWebEngine
binary, but has a few caveats:
- Make sure your `python3` is Python 3.7 or newer, otherwise you'll get a "No
- Make sure your `python3` is Python 3.8 or newer, otherwise you'll get a "No
matching distribution found" error and/or qutebrowser will not run.
- It only works on 64-bit x86 systems, with other architectures you'll get the
same error.

View File

@ -7,4 +7,3 @@ types-PyYAML
# So stubs are available even on newer Python versions
importlib_resources
importlib_metadata

View File

@ -8,7 +8,7 @@ cryptography==41.0.1
dill==0.3.6
github3.py==4.0.1
idna==3.4
isort==5.11.5
isort==5.12.0
lazy-object-proxy==1.9.0
mccabe==0.7.0
pefile==2023.2.7
@ -20,10 +20,7 @@ python-dateutil==2.8.2
./scripts/dev/pylint_checkers
requests==2.31.0
six==1.16.0
tomli==2.0.1
tomlkit==0.11.8
typed-ast==1.5.4 ; python_version<"3.8"
typing_extensions==4.6.3
uritemplate==4.1.1
# urllib3==2.0.3
wrapt==1.15.0

View File

@ -20,10 +20,4 @@ Pygments # For :view-source --pygments or on QtWebKit
colorama # Colored log output on Windows
adblock # Improved adblocking
# Optional, only relevant when installing PyQt5/PyQtWebEngine via pip.
importlib-metadata # Determining PyQt version
typing_extensions # from importlib-metadata
#@ markers: importlib-resources python_version=="3.7.*" or python_version=="3.8.*"
#@ markers: importlib-metadata python_version=="3.7.*"
#@ markers: typing_extensions python_version<"3.8"
#@ markers: importlib-resources python_version=="3.8.*"

View File

@ -8,17 +8,15 @@ charset-normalizer==3.1.0
cheroot==10.0.0
click==8.1.3
coverage==7.2.7
exceptiongroup==1.1.1
execnet==1.9.0
filelock==3.12.2
Flask==2.3.2 ; python_version>="3.8"
Flask==2.3.2
hunter==3.6.1
hypothesis==6.79.3
idna==3.4
importlib-metadata==6.7.0
iniconfig==2.0.0
itsdangerous==2.1.2
jaraco.functools==3.8.0 ; python_version>="3.8"
jaraco.functools==3.8.0
# Jinja2==3.1.2
Mako==1.2.4
manhole==1.8.0
@ -49,12 +47,7 @@ sortedcontainers==2.4.0
soupsieve==2.4.1
tldextract==3.4.4
toml==0.10.2
tomli==2.0.1
typing_extensions==4.6.3
urllib3==2.0.3
vulture==2.7
Werkzeug==2.3.6 ; python_version>="3.8"
zipp==3.15.0
Flask==2.2.5 ; python_version=="3.7.*"
Werkzeug==2.2.3 ; python_version=="3.7.*"
jaraco.functools<3.8.0 ; python_version=="3.7.*"
Werkzeug==2.3.6

View File

@ -32,10 +32,3 @@ pytest-xdist
tldextract
#@ ignore: Jinja2, MarkupSafe, colorama
# Python 3.7
#@ markers: Flask python_version>="3.8"
#@ add: Flask==2.2.5 ; python_version=="3.7.*"
#@ markers: Werkzeug python_version>="3.8"
#@ add: Werkzeug==2.2.3 ; python_version=="3.7.*"
#@ markers: jaraco.functools python_version>="3.8"
#@ add: jaraco.functools<3.8.0 ; python_version=="3.7.*"

View File

@ -403,21 +403,12 @@ class Command:
raise TypeError("{}: Legacy tuple type annotation!".format(
self.name))
try:
origin = typing.get_origin(typ) # type: ignore[attr-defined]
except AttributeError:
# typing.get_origin was added in Python 3.8
origin = getattr(typ, '__origin__', None)
origin = typing.get_origin(typ)
if origin is Union:
try:
types = list(typing.get_args(typ)) # type: ignore[attr-defined]
except AttributeError:
# typing.get_args was added in Python 3.8
types = list(typ.__args__)
types = list(typing.get_args(typ))
if param.default is not inspect.Parameter.empty:
types.append(type(param.default))
choices = self.get_arg_info(param).choices
value = argparser.multitype_conv(param, types, value,
str_choices=choices)

View File

@ -43,11 +43,11 @@ except ImportError: # pragma: no cover
# to stderr.
def check_python_version():
"""Check if correct python version is run."""
if sys.hexversion < 0x03070000:
if sys.hexversion < 0x03080000:
# We don't use .format() and print_function here just in case someone
# still has < 2.6 installed.
version_str = '.'.join(map(str, sys.version_info[:3]))
text = ("At least Python 3.7 is required to run qutebrowser, but " +
text = ("At least Python 3.8 is required to run qutebrowser, but " +
"it's running with " + version_str + ".\n")
show_errors = '--no-err-windows' not in sys.argv

View File

@ -19,7 +19,7 @@
"""Things which need to be done really early (e.g. before importing Qt).
At this point we can be sure we have all python 3.7 features available.
At this point we can be sure we have all python 3.8 features available.
"""
try:

View File

@ -27,7 +27,7 @@ import pathlib
from typing import Iterator, Iterable, Union
# We cannot use the stdlib version on 3.7-3.8 because we need the files() API.
# We cannot use the stdlib version on 3.8 because we need the files() API.
if sys.version_info >= (3, 11): # pragma: no cover
# https://github.com/python/cpython/issues/90276
import importlib.resources as importlib_resources

View File

@ -33,6 +33,7 @@ import datetime
import getpass
import functools
import dataclasses
import importlib.metadata
from typing import (Mapping, Optional, Sequence, Tuple, ClassVar, Dict, cast,
TYPE_CHECKING)
@ -520,19 +521,9 @@ def _get_pyqt_webengine_qt_version() -> Optional[str]:
PyQtWebEngine 5.15.4 renamed it to PyQtWebEngine-Qt5...:
https://www.riverbankcomputing.com/pipermail/pyqt/2021-March/043699.html
Here, we try to use importlib.metadata or its backport (optional dependency) to
figure out that version number. If PyQtWebEngine is installed via pip, this will
give us an accurate answer.
Here, we try to use importlib.metadata to figure out that version number.
If PyQtWebEngine is installed via pip, this will give us an accurate answer.
"""
try:
import importlib.metadata as importlib_metadata # type: ignore[import]
except ImportError:
try:
import importlib_metadata
except ImportError:
log.misc.debug("Neither importlib.metadata nor backport available")
return None
names = (
['PyQt6-WebEngine-Qt6']
if machinery.IS_QT6 else
@ -541,8 +532,8 @@ def _get_pyqt_webengine_qt_version() -> Optional[str]:
for name in names:
try:
return importlib_metadata.version(name)
except importlib_metadata.PackageNotFoundError:
return importlib.metadata.version(name)
except importlib.metadata.PackageNotFoundError:
log.misc.debug(f"{name} not found")
return None

View File

@ -2,14 +2,11 @@
adblock==0.6.0
colorama==0.4.6
importlib-metadata==6.7.0 ; python_version=="3.7.*"
importlib-resources==5.12.0 ; python_version=="3.7.*" or python_version=="3.8.*"
importlib-resources==5.12.0 ; python_version=="3.8.*"
Jinja2==3.1.2
MarkupSafe==2.1.3
Pygments==2.15.1
PyYAML==6.0
typing_extensions==4.6.3 ; python_version<"3.8"
zipp==3.15.0
# Unpinned due to recompile_requirements.py limitations
pyobjc-core ; sys_platform=="darwin"
pyobjc-framework-Cocoa ; sys_platform=="darwin"

View File

@ -23,7 +23,6 @@
"Flask": "https://flask.palletsprojects.com/en/latest/changes/",
"Mako": "https://docs.makotemplates.org/en/latest/changelog.html",
"hypothesis": "https://hypothesis.readthedocs.io/en/latest/changes.html",
"exceptiongroup": "https://github.com/agronholm/exceptiongroup/blob/main/CHANGES.rst",
"mypy": "https://mypy-lang.blogspot.com/",
"types-PyYAML": "https://github.com/python/typeshed/commits/main/stubs/PyYAML",
"pytest": "https://docs.pytest.org/en/latest/changelog.html",
@ -84,7 +83,6 @@
"pyinstaller": "https://pyinstaller.readthedocs.io/en/stable/CHANGES.html",
"pyinstaller-hooks-contrib": "https://github.com/pyinstaller/pyinstaller-hooks-contrib/blob/master/CHANGELOG.rst",
"pytest-benchmark": "https://pytest-benchmark.readthedocs.io/en/stable/changelog.html",
"typed-ast": "https://github.com/python/typed_ast/commits/master",
"docutils": "https://docutils.sourceforge.io/RELEASE-NOTES.html",
"bump2version": "https://github.com/c4urself/bump2version/blob/master/CHANGELOG.md",
"six": "https://github.com/benjaminp/six/blob/master/CHANGES",

View File

@ -144,11 +144,11 @@ def run_pip(venv_dir, *args, quiet=False, **kwargs):
return subprocess.run([venv_python, '-m', 'pip'] + args, check=True, **kwargs)
def init_venv(host_python, venv_dir, requirements, pre=False, pip_args=None):
def init_venv(venv_dir, requirements, pre=False, pip_args=None):
"""Initialize a new virtualenv and install the given packages."""
with utils.gha_group('Creating virtualenv'):
utils.print_col('$ python3 -m venv {}'.format(venv_dir), 'blue')
subprocess.run([host_python, '-m', 'venv', venv_dir], check=True)
subprocess.run([sys.executable, '-m', 'venv', venv_dir], check=True)
run_pip(venv_dir, 'install', '-U', 'pip', quiet=not utils.ON_CI)
run_pip(venv_dir, 'install', '-U', 'setuptools', 'wheel', quiet=not utils.ON_CI)
@ -347,17 +347,6 @@ def print_changed_files():
print('::set-output name=diff::' + diff_table)
def get_host_python(name):
"""Get the Python to use for a given requirement name.
pylint installs typed_ast on < 3.8 only
"""
if name == 'pylint':
return 'python3.7'
else:
return sys.executable
def get_venv_python(venv_dir):
"""Get the path to Python inside a virtualenv."""
subdir = 'Scripts' if os.name == 'nt' else 'bin'
@ -375,14 +364,12 @@ def build_requirements(name):
"""Build a requirements file."""
utils.print_subtitle("Building")
filename = os.path.join(REQ_DIR, 'requirements-{}.txt-raw'.format(name))
host_python = get_host_python(name)
with open(filename, 'r', encoding='utf-8') as f:
comments = read_comments(f)
with tempfile.TemporaryDirectory() as tmpdir:
init_venv(host_python=host_python,
venv_dir=tmpdir,
init_venv(venv_dir=tmpdir,
requirements=filename,
pre=comments['pre'],
pip_args=comments['pip_args'])
@ -411,14 +398,13 @@ def build_requirements(name):
def test_tox():
"""Test requirements via tox."""
host_python = get_host_python('tox')
req_path = os.path.join(REQ_DIR, 'requirements-tox.txt')
with tempfile.TemporaryDirectory() as tmpdir:
venv_dir = os.path.join(tmpdir, 'venv')
tox_workdir = os.path.join(tmpdir, 'tox-workdir')
venv_python = get_venv_python(venv_dir)
init_venv(host_python, venv_dir, req_path)
init_venv(venv_dir, req_path)
list_proc = subprocess.run([venv_python, '-m', 'tox', '--listenvs'],
check=True,
stdout=subprocess.PIPE,
@ -448,9 +434,8 @@ def test_requirements(name, outfile, *, force=False):
with open(in_file, 'r', encoding='utf-8') as f:
comments = read_comments(f)
host_python = get_host_python(name)
with tempfile.TemporaryDirectory() as tmpdir:
init_venv(host_python, tmpdir, outfile, pip_args=comments['pip_args'])
init_venv(tmpdir, outfile, pip_args=comments['pip_args'])
def cleanup_pylint_build():

View File

@ -73,7 +73,7 @@ try:
zip_safe=True,
install_requires=['jinja2', 'PyYAML',
'importlib_resources>=1.1.0; python_version < "3.9"'],
python_requires='>=3.7',
python_requires='>=3.8',
name='qutebrowser',
version=_get_constant('version'),
description=_get_constant('description'),
@ -95,9 +95,10 @@ try:
'Operating System :: MacOS',
'Operating System :: POSIX :: BSD',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Topic :: Internet',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: Browsers',

View File

@ -336,7 +336,7 @@ def test_command_on_start(request, quteproc_new):
quteproc_new.wait_for_quit()
@pytest.mark.parametrize('python', ['python2', 'python3.6'])
@pytest.mark.parametrize('python', ['python2', 'python3.6', 'python3.7'])
def test_launching_with_old_python(python):
try:
proc = subprocess.run(
@ -346,7 +346,7 @@ def test_launching_with_old_python(python):
except FileNotFoundError:
pytest.skip(f"{python} not found")
assert proc.returncode == 1
error = "At least Python 3.7 is required to run qutebrowser"
error = "At least Python 3.8 is required to run qutebrowser"
assert proc.stderr.decode('ascii').startswith(error)

View File

@ -28,7 +28,7 @@ import pytest
from qutebrowser.misc import checkpyver
TEXT = (r"At least Python 3.7 is required to run qutebrowser, but it's "
TEXT = (r"At least Python 3.8 is required to run qutebrowser, but it's "
r"running with \d+\.\d+\.\d+.")

View File

@ -29,6 +29,7 @@ import logging
import textwrap
import datetime
import dataclasses
import importlib.metadata
import pytest
import hypothesis
@ -1106,24 +1107,10 @@ class TestChromiumVersion:
"""Simulate parsing the version from ELF to fail."""
monkeypatch.setattr(elf, 'parse_webenginecore', lambda: None)
@pytest.fixture
def patch_no_importlib(self, monkeypatch, stubs):
"""Simulate missing importlib modules."""
import_fake = stubs.ImportFake({
'importlib_metadata': False,
'importlib.metadata': False,
}, monkeypatch)
import_fake.patch()
@pytest.fixture
def importlib_patcher(self, monkeypatch):
"""Patch the importlib module."""
def _patch(*, qt, qt5, qt6):
try:
import importlib.metadata as importlib_metadata
except ImportError:
importlib_metadata = pytest.importorskip("importlib_metadata")
def _fake_version(name):
if name == 'PyQtWebEngine-Qt':
outcome = qt
@ -1135,10 +1122,10 @@ class TestChromiumVersion:
raise utils.Unreachable(name)
if outcome is None:
raise importlib_metadata.PackageNotFoundError(name)
raise importlib.metadata.PackageNotFoundError(name)
return outcome
monkeypatch.setattr(importlib_metadata, 'version', _fake_version)
monkeypatch.setattr(importlib.metadata, 'version', _fake_version)
return _patch
@ -1150,7 +1137,6 @@ class TestChromiumVersion:
@pytest.mark.parametrize('patches, sources', [
(['no_api'], ['ELF', 'importlib', 'PyQt', 'Qt']),
(['no_api', 'elf_fail'], ['importlib', 'PyQt', 'Qt']),
(['no_api', 'elf_fail', 'no_importlib'], ['PyQt', 'Qt']),
(['no_api', 'elf_fail', 'importlib_no_package'], ['PyQt', 'Qt']),
], ids=','.join)
def test_simulated(self, request, patches, sources):

View File

@ -35,7 +35,6 @@ passenv =
basepython =
py: {env:PYTHON:python3}
py3: {env:PYTHON:python3}
py37: {env:PYTHON:python3.7}
py38: {env:PYTHON:python3.8}
py39: {env:PYTHON:python3.9}
py310: {env:PYTHON:python3.10}