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, PT004,
PT011, PT011,
PT012 PT012
min-version = 3.7.0 min-version = 3.8.0
max-complexity = 12 max-complexity = 12
per-file-ignores = per-file-ignores =
qutebrowser/api/hook.py : N801 qutebrowser/api/hook.py : N801

View File

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

View File

@ -21,10 +21,6 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
persist-credentials: false 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 - name: Set up Python 3.8
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:

View File

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

View File

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

View File

@ -82,7 +82,7 @@ Requirements
The following software and libraries are required to run qutebrowser: 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: * https://www.qt.io/[Qt], either 6.2.0 or newer, or 5.15.0 or newer, with the following modules:
- QtCore / qtbase - QtCore / qtbase
- QtQuick (part of qtbase or qtdeclarative in some distributions) - 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://palletsprojects.com/p/jinja/[jinja2]
* https://github.com/yaml/pyyaml[PyYAML] * 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] * https://importlib-resources.readthedocs.io/[importlib_resources]
@ -119,10 +119,6 @@ The following libraries are optional:
QtWebEngine backend. QtWebEngine backend.
* On Windows, https://pypi.python.org/pypi/colorama/[colorama] for colored log * On Windows, https://pypi.python.org/pypi/colorama/[colorama] for colored log
output. 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` * https://asciidoc.org/[asciidoc] to generate the documentation for the `:help`
command, when using the git repository (rather than a release). command, when using the git repository (rather than a release).

View File

@ -57,9 +57,9 @@ Added
Removed Removed
~~~~~~~ ~~~~~~~
- Support for Python 3.6 is dropped, as it's been - Python 3.8.0 or newer is now required.
https://discuss.python.org/t/python-3-6-rides-into-the-sunset/12964[end-of-life upstream] - Support for Python 3.6 and 3.7 is dropped, as they both reached
since December 2021. Python 3.7.0 or newer is now required. 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 - Support for Qt/PyQt before 5.15.0 and QtWebEngine before 5.15.2 are now
dropped, as older Qt versions are dropped, as older Qt versions are
https://endoflife.date/qt[end-of-life upstream] since mid/late 2020 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: Currently, the following tox environments are available:
* Tests using https://www.pytest.org[pytest]: * Tests using https://www.pytest.org[pytest]:
- `py37`, `py38`, ...: Run pytest for python 3.7/3.8/... with the system-wide PyQt. - `py38`, `py39`, ...: Run pytest for python 3.8/3.9/... with the system-wide PyQt.
- `py37-pyqt512`, ..., `py37-pyqt515`: Run pytest with the given PyQt version (`py37-*` also works). - `py38-pyqt515`, ..., `py38-pyqt65`: Run pytest with the given PyQt version (`py39-*` etc. also works).
- `py37-pyqt515-cov`: Run with coverage support (other Python/PyQt versions work too). - `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]. * `flake8`: Run various linting checks via https://pypi.python.org/pypi/flake8[flake8].
* `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find * `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find
unused code portions. unused code portions.
@ -171,16 +171,16 @@ Examples:
---- ----
# run only pytest tests which failed in last run: # run only pytest tests which failed in last run:
tox -e py37 -- --lf tox -e py38 -- --lf
# run only the end2end feature tests: # 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 # 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) # 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 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 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. 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://launchpad.net/~deadsnakes/+archive/ubuntu/ppa[deadsnakes PPA] or
https://github.com/pyenv/pyenv[pyenv], and then proceed to https://github.com/pyenv/pyenv[pyenv], and then proceed to
<<tox,install qutebrowser in a virtualenv>>. However, this is currently untested. If you <<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 This comes with an up-to-date Qt/PyQt including a pre-compiled QtWebEngine
binary, but has a few caveats: 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. 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 - It only works on 64-bit x86 systems, with other architectures you'll get the
same error. same error.

View File

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

View File

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

View File

@ -20,10 +20,4 @@ Pygments # For :view-source --pygments or on QtWebKit
colorama # Colored log output on Windows colorama # Colored log output on Windows
adblock # Improved adblocking adblock # Improved adblocking
# Optional, only relevant when installing PyQt5/PyQtWebEngine via pip. #@ markers: importlib-resources python_version=="3.8.*"
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"

View File

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

View File

@ -32,10 +32,3 @@ pytest-xdist
tldextract tldextract
#@ ignore: Jinja2, MarkupSafe, colorama #@ 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( raise TypeError("{}: Legacy tuple type annotation!".format(
self.name)) self.name))
try: origin = typing.get_origin(typ)
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)
if origin is Union: if origin is Union:
try: types = list(typing.get_args(typ))
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__)
if param.default is not inspect.Parameter.empty: if param.default is not inspect.Parameter.empty:
types.append(type(param.default)) types.append(type(param.default))
choices = self.get_arg_info(param).choices choices = self.get_arg_info(param).choices
value = argparser.multitype_conv(param, types, value, value = argparser.multitype_conv(param, types, value,
str_choices=choices) str_choices=choices)

View File

@ -43,11 +43,11 @@ except ImportError: # pragma: no cover
# to stderr. # to stderr.
def check_python_version(): def check_python_version():
"""Check if correct python version is run.""" """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 # We don't use .format() and print_function here just in case someone
# still has < 2.6 installed. # still has < 2.6 installed.
version_str = '.'.join(map(str, sys.version_info[:3])) 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") "it's running with " + version_str + ".\n")
show_errors = '--no-err-windows' not in sys.argv 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). """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: try:

View File

@ -27,7 +27,7 @@ import pathlib
from typing import Iterator, Iterable, Union 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 if sys.version_info >= (3, 11): # pragma: no cover
# https://github.com/python/cpython/issues/90276 # https://github.com/python/cpython/issues/90276
import importlib.resources as importlib_resources import importlib.resources as importlib_resources

View File

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

View File

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

View File

@ -23,7 +23,6 @@
"Flask": "https://flask.palletsprojects.com/en/latest/changes/", "Flask": "https://flask.palletsprojects.com/en/latest/changes/",
"Mako": "https://docs.makotemplates.org/en/latest/changelog.html", "Mako": "https://docs.makotemplates.org/en/latest/changelog.html",
"hypothesis": "https://hypothesis.readthedocs.io/en/latest/changes.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/", "mypy": "https://mypy-lang.blogspot.com/",
"types-PyYAML": "https://github.com/python/typeshed/commits/main/stubs/PyYAML", "types-PyYAML": "https://github.com/python/typeshed/commits/main/stubs/PyYAML",
"pytest": "https://docs.pytest.org/en/latest/changelog.html", "pytest": "https://docs.pytest.org/en/latest/changelog.html",
@ -84,7 +83,6 @@
"pyinstaller": "https://pyinstaller.readthedocs.io/en/stable/CHANGES.html", "pyinstaller": "https://pyinstaller.readthedocs.io/en/stable/CHANGES.html",
"pyinstaller-hooks-contrib": "https://github.com/pyinstaller/pyinstaller-hooks-contrib/blob/master/CHANGELOG.rst", "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", "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", "docutils": "https://docutils.sourceforge.io/RELEASE-NOTES.html",
"bump2version": "https://github.com/c4urself/bump2version/blob/master/CHANGELOG.md", "bump2version": "https://github.com/c4urself/bump2version/blob/master/CHANGELOG.md",
"six": "https://github.com/benjaminp/six/blob/master/CHANGES", "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) 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.""" """Initialize a new virtualenv and install the given packages."""
with utils.gha_group('Creating virtualenv'): with utils.gha_group('Creating virtualenv'):
utils.print_col('$ python3 -m venv {}'.format(venv_dir), 'blue') 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', 'pip', quiet=not utils.ON_CI)
run_pip(venv_dir, 'install', '-U', 'setuptools', 'wheel', 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) 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): def get_venv_python(venv_dir):
"""Get the path to Python inside a virtualenv.""" """Get the path to Python inside a virtualenv."""
subdir = 'Scripts' if os.name == 'nt' else 'bin' subdir = 'Scripts' if os.name == 'nt' else 'bin'
@ -375,14 +364,12 @@ def build_requirements(name):
"""Build a requirements file.""" """Build a requirements file."""
utils.print_subtitle("Building") utils.print_subtitle("Building")
filename = os.path.join(REQ_DIR, 'requirements-{}.txt-raw'.format(name)) 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: with open(filename, 'r', encoding='utf-8') as f:
comments = read_comments(f) comments = read_comments(f)
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
init_venv(host_python=host_python, init_venv(venv_dir=tmpdir,
venv_dir=tmpdir,
requirements=filename, requirements=filename,
pre=comments['pre'], pre=comments['pre'],
pip_args=comments['pip_args']) pip_args=comments['pip_args'])
@ -411,14 +398,13 @@ def build_requirements(name):
def test_tox(): def test_tox():
"""Test requirements via tox.""" """Test requirements via tox."""
host_python = get_host_python('tox')
req_path = os.path.join(REQ_DIR, 'requirements-tox.txt') req_path = os.path.join(REQ_DIR, 'requirements-tox.txt')
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
venv_dir = os.path.join(tmpdir, 'venv') venv_dir = os.path.join(tmpdir, 'venv')
tox_workdir = os.path.join(tmpdir, 'tox-workdir') tox_workdir = os.path.join(tmpdir, 'tox-workdir')
venv_python = get_venv_python(venv_dir) 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'], list_proc = subprocess.run([venv_python, '-m', 'tox', '--listenvs'],
check=True, check=True,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -448,9 +434,8 @@ def test_requirements(name, outfile, *, force=False):
with open(in_file, 'r', encoding='utf-8') as f: with open(in_file, 'r', encoding='utf-8') as f:
comments = read_comments(f) comments = read_comments(f)
host_python = get_host_python(name)
with tempfile.TemporaryDirectory() as tmpdir: 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(): def cleanup_pylint_build():

View File

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

View File

@ -336,7 +336,7 @@ def test_command_on_start(request, quteproc_new):
quteproc_new.wait_for_quit() 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): def test_launching_with_old_python(python):
try: try:
proc = subprocess.run( proc = subprocess.run(
@ -346,7 +346,7 @@ def test_launching_with_old_python(python):
except FileNotFoundError: except FileNotFoundError:
pytest.skip(f"{python} not found") pytest.skip(f"{python} not found")
assert proc.returncode == 1 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) assert proc.stderr.decode('ascii').startswith(error)

View File

@ -28,7 +28,7 @@ import pytest
from qutebrowser.misc import checkpyver 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+.") r"running with \d+\.\d+\.\d+.")

View File

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

View File

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