Compare commits

...

48 Commits

Author SHA1 Message Date
Will Greenberg
e9225d1cc2 Release 2.8.0 2023-12-05 11:14:38 -08:00
Will Greenberg
3dd1f0eea9 Update changelog for 2.8.0 release 2023-12-05 11:13:52 -08:00
Brad Warren
917e3aba6b add pkg_resources changelog (#9851) 2023-12-05 10:33:49 -08:00
Brad Warren
3833255980 update dependencies (#9848) 2023-12-05 10:33:31 -08:00
Francesco Colista
619654f317 Add support for Alpine Linux (#9834)
Signed-off-by: Francesco Colista <fcolista@alpinelinux.org>
2023-11-22 13:53:31 +01:00
Brad Warren
76f9a33e45 Upgrade the pinned version of pylint (#9839)
* upgrade pylint

* fix upgraded pylint

* downgrade pyopenssl

* remove unneeded ignores

* stop using text

* update sphinx-rtd-theme
2023-11-15 09:52:37 +01:00
Adrien Ferrand
5f67bb99a8 Full cleanup of pkg_resources (#9797)
Fixes #9606

This PRs removes some elements that were related to pkg_resources dependency and its deprecation.
2023-11-13 15:50:32 -08:00
Will Greenberg
d8392bf394 Merge pull request #9832 from certbot/candidate-2.7.4
Update master from 2.7.4 release
2023-11-01 11:36:29 -07:00
Brad Warren
6a89fcbc56 Merge branch 'master' into candidate-2.7.4 2023-11-01 07:50:54 -07:00
Brad Warren
2adaacab82 Bump version to 2.8.0 2023-11-01 06:24:20 -07:00
Brad Warren
2ae810c45a Add contents to certbot/CHANGELOG.md for next version 2023-11-01 06:24:19 -07:00
Brad Warren
b62133e3e1 Release 2.7.4 2023-11-01 06:24:18 -07:00
Brad Warren
a92bb44ff9 Update changelog for 2.7.4 release 2023-11-01 06:23:12 -07:00
Brad Warren
9650c25968 Fix change detection on mutable values (#9829) (#9830)
* handle mutable values

* add unit test

* add changelog entry

* fix typo

(cherry picked from commit c3c29afdca)
2023-11-01 00:10:11 +00:00
Brad Warren
c3c29afdca Fix change detection on mutable values (#9829)
* handle mutable values

* add unit test

* add changelog entry

* fix typo
2023-10-31 16:28:16 -07:00
Brad Warren
dca4ddd3d8 Prep for 2.7.4 (#9823)
* Set the delegated field in Lexicon config to bypass subdomain resolution (#9821)

The Lexicon-based DNS plugins use a mechanism to determine which actual segment of the input domain is actually the DNS zone in which the DNS-01 challenge has to be initiated (eg. `subdomain.domain.com` or `domain.com` for input `subdomain.domain.com`): they tries recursively to configure Lexicon and initiate authentication from the most specific to most generic domain segment, and select the first segment where Lexicon stop erroring out.

This mechanism broke with #9746 because now the plugins call Lexicon client instead of the underlying providers, and the client makes guess on the actual domain requested. Typically for `subdomain.domain.com` it will actually try to authenticate against `domain.com`, and so the mechanism above does not work anymore.

This PR fixes the issue by using the `delegated` field in Lexicon config each time the plugin needs it. This field is designed for this kind of purpose: it will instruct Lexicon what is the actual DNS zone domain instead of guessing it.

I tested the change with one of my OVH account. The expected behavior is re-established and the plugin is able to test `subdomain.domain.com` then `domain.com` as before.

Fixes #9791
Fixes #9818

(cherry picked from commit cf4f07d17e)

* add changelog entry for 9821 (#9822)

(cherry picked from commit 7bb85f8440)

---------

Co-authored-by: Adrien Ferrand <adferrand@users.noreply.github.com>
2023-10-30 10:34:30 -07:00
Brad Warren
7bb85f8440 add changelog entry for 9821 (#9822) 2023-10-28 00:04:11 +02:00
Adrien Ferrand
cf4f07d17e Set the delegated field in Lexicon config to bypass subdomain resolution (#9821)
The Lexicon-based DNS plugins use a mechanism to determine which actual segment of the input domain is actually the DNS zone in which the DNS-01 challenge has to be initiated (eg. `subdomain.domain.com` or `domain.com` for input `subdomain.domain.com`): they tries recursively to configure Lexicon and initiate authentication from the most specific to most generic domain segment, and select the first segment where Lexicon stop erroring out.

This mechanism broke with #9746 because now the plugins call Lexicon client instead of the underlying providers, and the client makes guess on the actual domain requested. Typically for `subdomain.domain.com` it will actually try to authenticate against `domain.com`, and so the mechanism above does not work anymore.

This PR fixes the issue by using the `delegated` field in Lexicon config each time the plugin needs it. This field is designed for this kind of purpose: it will instruct Lexicon what is the actual DNS zone domain instead of guessing it.

I tested the change with one of my OVH account. The expected behavior is re-established and the plugin is able to test `subdomain.domain.com` then `domain.com` as before.

Fixes #9791
Fixes #9818
2023-10-27 10:04:40 -07:00
Will Greenberg
36c78b3717 Merge pull request #9819 from certbot/candidate-2.7.3
Update master from 2.7.3 release
2023-10-26 14:01:31 -07:00
Will Greenberg
bf5475fa74 Merge pull request #9820 from certbot/2.7.3-update
Update 2.7.x from 2.7.3 release
2023-10-26 14:00:37 -07:00
Brad Warren
9bfc9dda5c Merge branch 'master' into candidate-2.7.3 2023-10-25 08:27:20 -07:00
Brad Warren
e904bd4e29 Bump version to 2.8.0 2023-10-24 13:43:22 -07:00
Brad Warren
d140a7df52 Add contents to certbot/CHANGELOG.md for next version 2023-10-24 13:43:22 -07:00
Brad Warren
bd550c09c2 Release 2.7.3 2023-10-24 13:43:20 -07:00
Brad Warren
01405a8fa6 Update changelog for 2.7.3 release 2023-10-24 13:42:05 -07:00
Brad Warren
5bf833fe28 2.7.3 prep (#9817)
* Update changelog for 2.7.2 release

* Release 2.7.2

* helpful: Add an edge case for arguments w/ contained spaces (#9813)

Fixes #9811

(cherry picked from commit 3ae9d7e03a)

* fixes #9805 (#9816)

(cherry picked from commit d1577280ad)

---------

Co-authored-by: Will Greenberg <willg@eff.org>
2023-10-24 12:49:04 -07:00
Brad Warren
d1577280ad fixes #9805 (#9816) 2023-10-24 12:27:19 -07:00
Will Greenberg
3ae9d7e03a helpful: Add an edge case for arguments w/ contained spaces (#9813)
Fixes #9811
2023-10-24 08:26:00 -07:00
Will Greenberg
5594ac20e0 Merge pull request #9809 from certbot/candidate-2.7.2
Candidate 2.7.2
2023-10-19 17:49:02 -07:00
Brad Warren
7f6000f1d4 Merge branch 'master' into candidate-2.7.2 2023-10-19 17:35:05 -07:00
Will Greenberg
1863c66179 Bump version to 2.8.0 2023-10-19 15:34:19 -07:00
Will Greenberg
185c20c71b Add contents to certbot/CHANGELOG.md for next version 2023-10-19 15:34:19 -07:00
Will Greenberg
a1b773cbdc Release 2.7.2 2023-10-19 15:34:18 -07:00
Will Greenberg
937eaef621 Update changelog for 2.7.2 release 2023-10-19 15:33:34 -07:00
Brad Warren
e40741955f Prep for 2.7.2 (#9808)
* helpful: fix handling of abbreviated ConfigArgparse arguments (#9796)

* helpful: fix handling of abbreviated ConfigArgparse arguments

ConfigArgparse allows for "abbreviated" arguments, i.e. just the prefix
of an argument, but it doesn't set the argument sources in these cases.
This commit checks for those cases and sets the sources appropriately.

* failing to find an action raises an error instead of logging

* Update changelog

* Add handling for short arguments, fix equals sign handling

These were silently being dropped before, possibly leading to instances
of `NamespaceConfig.set_by_user()` returning false negatives.

(cherry picked from commit 11e17ef77b)

* Fix finish_release.py (#9800)

* response is value

* rename vars

(cherry picked from commit a96fb4b6ce)

* Merge pull request #9762 from certbot/docs/yaml-config

Add YAML files for Readthedocs requirements

(cherry picked from commit 44046c70c3)

* Update Lexicon requirements to stabilize certbot-dns-ovh behavior (#9802)

* Update minimum Lexicon version required for certbot-dns-ovh

* Add types

* FIx mypy

* Fix lint

* Fix BOTH lint and mypy

(cherry picked from commit 5cf5f36f19)

* simplify code (#9807)

(cherry picked from commit 6f7b5ab1cd)

* Include linting fixes from 8a95c03

---------

Co-authored-by: Will Greenberg <willg@eff.org>
Co-authored-by: Alexis <alexis@eff.org>
Co-authored-by: Adrien Ferrand <adferrand@users.noreply.github.com>
2023-10-19 11:27:21 -07:00
Brad Warren
6f7b5ab1cd simplify code (#9807) 2023-10-18 14:32:07 -07:00
Adrien Ferrand
5cf5f36f19 Update Lexicon requirements to stabilize certbot-dns-ovh behavior (#9802)
* Update minimum Lexicon version required for certbot-dns-ovh

* Add types

* FIx mypy

* Fix lint

* Fix BOTH lint and mypy
2023-10-18 13:19:26 -07:00
Brad Warren
a96fb4b6ce Fix finish_release.py (#9800)
* response is value

* rename vars
2023-10-16 17:54:24 -07:00
Will Greenberg
11e17ef77b helpful: fix handling of abbreviated ConfigArgparse arguments (#9796)
* helpful: fix handling of abbreviated ConfigArgparse arguments

ConfigArgparse allows for "abbreviated" arguments, i.e. just the prefix
of an argument, but it doesn't set the argument sources in these cases.
This commit checks for those cases and sets the sources appropriately.

* failing to find an action raises an error instead of logging

* Update changelog

* Add handling for short arguments, fix equals sign handling

These were silently being dropped before, possibly leading to instances
of `NamespaceConfig.set_by_user()` returning false negatives.
2023-10-13 12:02:01 -07:00
Adrien Ferrand
8a95c030e6 Drop Python 3.7 support (#9792)
* Drop Python 3.7 support

* Fix lint and test

* Check for venv generation

* Update requirements

* Update oldest constaints and compatibility tests runtime
2023-10-13 06:57:42 -07:00
Brad Warren
d9d825ac50 Merge pull request #9794 from certbot/candidate-2.7.1
Update master from 2.7.1 release
2023-10-11 16:37:57 -07:00
Brad Warren
07b1b0d8b2 Merge pull request #9795 from certbot/candidate-2.7.1-2.7.x
Update 2.7.x from 2.7.1 release
2023-10-11 16:37:48 -07:00
Brad Warren
beec975379 Merge branch 'master' into candidate-2.7.1 2023-10-10 08:50:31 -07:00
Brad Warren
8bf21cad25 Bump version to 2.8.0 2023-10-10 06:40:53 -07:00
Brad Warren
dcac5ed8f0 Add contents to certbot/CHANGELOG.md for next version 2023-10-10 06:40:53 -07:00
Brad Warren
228e3f2a8d Release 2.7.1 2023-10-10 06:40:52 -07:00
Brad Warren
6624e0b65c Update changelog for 2.7.1 release 2023-10-10 06:39:19 -07:00
Brad Warren
21113d17c7 Prep for 2.7.1 (#9790)
* Bump setup.py's ConfigArgParse version (#9784)

I neglected to do this during #9678, so looks like some pip installs
are failing to get the minimum required version.

(cherry picked from commit 02efc8c5ca)

* Fix dnsimple typo (#9787)

Fixes https://github.com/certbot/certbot/issues/9786.

(cherry picked from commit 4e60a0d03a)

* update pinned dependencies (#9788)

This fixes the security alerts those with access can see at https://github.com/certbot/certbot/security/dependabot.

(cherry picked from commit 5849ff73fb)

* update changelog for configargparse (#9789)

I'd like to do a bug fix release for https://github.com/certbot/certbot/issues/9786. If we're doing one, I figure we may as well flag this change from https://github.com/certbot/certbot/pull/9784 too.

(cherry picked from commit 61773be971)

---------

Co-authored-by: Will Greenberg <willg@eff.org>
2023-10-06 18:59:26 +00:00
70 changed files with 676 additions and 524 deletions

View File

@@ -8,9 +8,6 @@ jobs:
- group: certbot-common
strategy:
matrix:
linux-py38:
PYTHON_VERSION: 3.8
TOXENV: py38
linux-py39:
PYTHON_VERSION: 3.9
TOXENV: py39
@@ -20,17 +17,13 @@ jobs:
linux-isolated:
TOXENV: 'isolated-{acme,certbot,apache,cloudflare,digitalocean,dnsimple,dnsmadeeasy,gehirn,google,linode,luadns,nsone,ovh,rfc2136,route53,sakuracloud,nginx}'
linux-boulder-v2-integration-certbot-oldest:
PYTHON_VERSION: 3.7
PYTHON_VERSION: 3.8
TOXENV: integration-certbot-oldest
ACME_SERVER: boulder-v2
linux-boulder-v2-integration-nginx-oldest:
PYTHON_VERSION: 3.7
PYTHON_VERSION: 3.8
TOXENV: integration-nginx-oldest
ACME_SERVER: boulder-v2
linux-boulder-v2-py37-integration:
PYTHON_VERSION: 3.7
TOXENV: integration
ACME_SERVER: boulder-v2
linux-boulder-v2-py38-integration:
PYTHON_VERSION: 3.8
TOXENV: integration

View File

@@ -4,9 +4,9 @@ jobs:
PYTHON_VERSION: 3.11
strategy:
matrix:
macos-py37-cover:
macos-py38-cover:
IMAGE_NAME: macOS-12
PYTHON_VERSION: 3.7
PYTHON_VERSION: 3.8
TOXENV: cover
# As of pip 23.1.0, builds started failing on macOS unless this flag was set.
# See https://github.com/certbot/certbot/pull/9717#issuecomment-1610861794.
@@ -14,11 +14,11 @@ jobs:
macos-cover:
IMAGE_NAME: macOS-12
TOXENV: cover
# See explanation under macos-py37-cover.
# See explanation under macos-py38-cover.
PIP_USE_PEP517: "true"
windows-py37:
windows-py38:
IMAGE_NAME: windows-2019
PYTHON_VERSION: 3.7
PYTHON_VERSION: 3.8
TOXENV: py-win
windows-py39-cover:
IMAGE_NAME: windows-2019
@@ -30,12 +30,12 @@ jobs:
TOXENV: integration-certbot
linux-oldest:
IMAGE_NAME: ubuntu-22.04
PYTHON_VERSION: 3.7
PYTHON_VERSION: 3.8
TOXENV: oldest
linux-py37:
linux-py38:
IMAGE_NAME: ubuntu-22.04
PYTHON_VERSION: 3.7
TOXENV: py37
PYTHON_VERSION: 3.8
TOXENV: py38
linux-cover:
IMAGE_NAME: ubuntu-22.04
TOXENV: cover

View File

@@ -69,7 +69,7 @@ ignored-modules=
# CERTBOT COMMENT
# This is needed for pylint to import linter_plugin.py since
# https://github.com/PyCQA/pylint/pull/3396.
init-hook="import pylint.config, os, sys; sys.path.append(os.path.dirname(pylint.config.PYLINTRC))"
init-hook="import pylint.config, os, sys; sys.path.append(os.path.dirname(next(pylint.config.find_default_config_files())))"
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use, and will cap the count on Windows to
@@ -266,8 +266,8 @@ valid-metaclass-classmethod-first-arg=cls
[EXCEPTIONS]
# Exceptions that will emit a warning when caught.
overgeneral-exceptions=BaseException,
Exception
overgeneral-exceptions=builtins.BaseException,
builtins.Exception
[FORMAT]
@@ -524,7 +524,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace,
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis
ignored-modules=pkg_resources,confargparse,argparse
ignored-modules=confargparse,argparse
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.

View File

@@ -94,6 +94,7 @@ Authors
* [Felix Yan](https://github.com/felixonmars)
* [Filip Ochnik](https://github.com/filipochnik)
* [Florian Klink](https://github.com/flokli)
* [Francesco Colista](https://github.com/fcolista)
* [Francois Marier](https://github.com/fmarier)
* [Frank](https://github.com/Frankkkkk)
* [Frederic BLANC](https://github.com/fblanc)

View File

@@ -6,7 +6,6 @@ This module is an implementation of the `ACME protocol`_.
"""
import sys
import warnings
# This code exists to keep backwards compatibility with people using acme.jose
# before it became the standalone josepy package.
@@ -20,10 +19,3 @@ for mod in list(sys.modules):
# preserved (acme.jose.* is josepy.*)
if mod == 'josepy' or mod.startswith('josepy.'):
sys.modules['acme.' + mod.replace('josepy', 'jose', 1)] = sys.modules[mod]
if sys.version_info[:2] == (3, 7):
warnings.warn(
"Python 3.7 support will be dropped in the next planned release of "
"acme. Please upgrade your Python version.",
PendingDeprecationWarning,
) # pragma: no cover

View File

@@ -12,7 +12,6 @@ from typing import List
from typing import Mapping
from typing import Optional
from typing import Set
from typing import Text
from typing import Tuple
from typing import Union
@@ -517,7 +516,7 @@ class ClientNetwork:
self.account = account
self.alg = alg
self.verify_ssl = verify_ssl
self._nonces: Set[Text] = set()
self._nonces: Set[str] = set()
self.user_agent = user_agent
self.session = requests.Session()
self._default_timeout = timeout

View File

@@ -29,7 +29,7 @@ class Header(jose.Header):
class Signature(jose.Signature):
"""ACME-specific Signature. Uses ACME-specific Header for customer fields."""
__slots__ = jose.Signature._orig_slots # type: ignore[attr-defined] # pylint: disable=protected-access,no-member
__slots__ = jose.Signature._orig_slots # pylint: disable=protected-access,no-member
# TODO: decoder/encoder should accept cls? Otherwise, subclassing
# JSONObjectWithFields is tricky...
@@ -44,7 +44,7 @@ class Signature(jose.Signature):
class JWS(jose.JWS):
"""ACME-specific JWS. Includes none, url, and kid in protected header."""
signature_cls = Signature
__slots__ = jose.JWS._orig_slots # type: ignore[attr-defined] # pylint: disable=protected-access
__slots__ = jose.JWS._orig_slots # pylint: disable=protected-access
@classmethod
# type: ignore[override] # pylint: disable=arguments-differ

View File

@@ -3,6 +3,6 @@ usage: jws [-h] [--compact] {sign,verify} ...
positional arguments:
{sign,verify}
options:
optional arguments:
-h, --help show this help message and exit
--compact

View File

@@ -3,7 +3,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'cryptography>=3.2.1',
@@ -44,14 +44,13 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -4,6 +4,7 @@ from typing import Type
from certbot import util
from certbot_apache._internal import configurator
from certbot_apache._internal import override_alpine
from certbot_apache._internal import override_arch
from certbot_apache._internal import override_centos
from certbot_apache._internal import override_darwin
@@ -14,6 +15,7 @@ from certbot_apache._internal import override_suse
from certbot_apache._internal import override_void
OVERRIDE_CLASSES: Dict[str, Type[configurator.ApacheConfigurator]] = {
"alpine": override_alpine.AlpineConfigurator,
"arch": override_arch.ArchConfigurator,
"cloudlinux": override_centos.CentOSConfigurator,
"darwin": override_darwin.DarwinConfigurator,

View File

@@ -0,0 +1,19 @@
""" Distribution specific override class for Alpine Linux """
from certbot_apache._internal import configurator
from certbot_apache._internal.configurator import OsOptions
class AlpineConfigurator(configurator.ApacheConfigurator):
"""Alpine Linux specific ApacheConfigurator override class"""
OS_DEFAULTS = OsOptions(
server_root="/etc/apache2",
vhost_root="/etc/apache2/conf.d",
vhost_files="*.conf",
logs_root="/var/log/apache2",
ctl="apachectl",
version_cmd=['apachectl', '-v'],
restart_cmd=['apachectl', 'graceful'],
conftest_cmd=['apachectl', 'configtest'],
challenge_location="/etc/apache2/conf.d",
)

View File

@@ -1,7 +1,7 @@
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
# We specify the minimum acme and certbot version as the current plugin
@@ -30,7 +30,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -39,7 +39,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -47,15 +47,8 @@ ECDSA_KEY_TYPE = 'ecdsa'
def _suppress_x509_verification_warnings() -> None:
try:
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
except ImportError:
# Handle old versions of request with vendorized urllib3
# pylint: disable=no-member
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings( # type: ignore[attr-defined]
InsecureRequestWarning)
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def check_until_timeout(url: str, attempts: int = 30) -> None:

View File

@@ -19,9 +19,11 @@ install_requires = [
'pywin32>=300 ; sys_platform == "win32"',
'pyyaml',
'pytz>=2019.3',
'requests',
# requests unvendored its dependencies in version 2.16.0 and this code relies on that for
# calling `urllib3.disable_warnings`.
'requests>=2.16.0',
'setuptools',
'types-python-dateutil'
'types-python-dateutil',
]
setup(
@@ -32,14 +34,13 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -1,5 +1,5 @@
FROM debian:buster
MAINTAINER Brad Warren <bmw@eff.org>
FROM docker.io/python:3.8-buster
LABEL maintainer="Brad Warren <bmw@eff.org>"
# This does not include the dependencies needed to build cryptography. See
# https://cryptography.io/en/latest/installation/#building-cryptography-on-linux

View File

@@ -75,7 +75,7 @@ def _get_server_root(config: str) -> str:
if os.path.isdir(os.path.join(config, name))]
if len(subdirs) != 1:
errors.Error("Malformed configuration directory {0}".format(config))
raise errors.Error("Malformed configuration directory {0}".format(config))
return os.path.join(config, subdirs[0].rstrip())

View File

@@ -1,7 +1,7 @@
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'certbot',
@@ -18,14 +18,13 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'cloudflare>=1.5.1',
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'python-digitalocean>=1.11', # 1.15.0 or newer is recommended for TTL support
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -3,6 +3,7 @@ from unittest import mock
import sys
import pytest
from requests import Response
from requests.exceptions import HTTPError
from certbot.compat import os
@@ -16,7 +17,7 @@ TOKEN = 'foo'
class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common_lexicon.BaseLexiconDNSAuthenticatorTest):
LOGIN_ERROR = HTTPError('401 Client Error: Unauthorized for url: ...')
LOGIN_ERROR = HTTPError('401 Client Error: Unauthorized for url: ...', response=Response())
def setUp(self):
super().setUp()

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
# This version of lexicon is required to address the problem described in
@@ -41,7 +41,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -50,7 +50,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -4,6 +4,7 @@ import sys
from unittest import mock
import pytest
from requests import Response
from requests.exceptions import HTTPError
from certbot.compat import os
@@ -19,8 +20,8 @@ SECRET_KEY = 'bar'
class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common_lexicon.BaseLexiconDNSAuthenticatorTest):
DOMAIN_NOT_FOUND = HTTPError(f'404 Client Error: Not Found for url: {DOMAIN}.')
LOGIN_ERROR = HTTPError(f'403 Client Error: Forbidden for url: {DOMAIN}.')
DOMAIN_NOT_FOUND = HTTPError(f'404 Client Error: Not Found for url: {DOMAIN}.', response=Response())
LOGIN_ERROR = HTTPError(f'403 Client Error: Forbidden for url: {DOMAIN}.', response=Response())
def setUp(self):
super().setUp()

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'dns-lexicon>=3.14.1',
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -1,10 +1,10 @@
"""Tests for certbot_dns_gehirn._internal.dns_gehirn."""
import sys
import unittest
from unittest import mock
import pytest
from requests import Response
from requests.exceptions import HTTPError
from certbot.compat import os
@@ -20,8 +20,8 @@ API_SECRET = 'MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw'
class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common_lexicon.BaseLexiconDNSAuthenticatorTest):
DOMAIN_NOT_FOUND = HTTPError(f'404 Client Error: Not Found for url: {DOMAIN}.')
LOGIN_ERROR = HTTPError(f'401 Client Error: Unauthorized for url: {DOMAIN}.')
DOMAIN_NOT_FOUND = HTTPError(f'404 Client Error: Not Found for url: {DOMAIN}.', response=Response())
LOGIN_ERROR = HTTPError(f'401 Client Error: Unauthorized for url: {DOMAIN}.', response=Response())
def setUp(self):
super().setUp()

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'dns-lexicon>=3.14.1',
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'google-api-python-client>=1.6.5',
@@ -40,7 +40,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -49,7 +49,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'dns-lexicon>=3.14.1',
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -3,6 +3,7 @@ import sys
from unittest import mock
import pytest
from requests import Response
from requests.exceptions import HTTPError
from certbot.compat import os
@@ -17,7 +18,7 @@ TOKEN = 'foo'
class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common_lexicon.BaseLexiconDNSAuthenticatorTest):
LOGIN_ERROR = HTTPError("401 Client Error: Unauthorized for url: ...")
LOGIN_ERROR = HTTPError("401 Client Error: Unauthorized for url: ...", response=Response())
def setUp(self):
super().setUp()

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'dns-lexicon>=3.14.1',
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -3,6 +3,7 @@ import sys
from unittest import mock
import pytest
from requests import Response
from requests.exceptions import HTTPError
from certbot.compat import os
@@ -17,8 +18,8 @@ API_KEY = 'foo'
class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common_lexicon.BaseLexiconDNSAuthenticatorTest):
DOMAIN_NOT_FOUND = HTTPError(f'404 Client Error: Not Found for url: {DOMAIN}.')
LOGIN_ERROR = HTTPError(f'401 Client Error: Unauthorized for url: {DOMAIN}.')
DOMAIN_NOT_FOUND = HTTPError(f'404 Client Error: Not Found for url: {DOMAIN}.', response=Response())
LOGIN_ERROR = HTTPError(f'401 Client Error: Unauthorized for url: {DOMAIN}.', response=Response())
def setUp(self):
super().setUp()

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'dns-lexicon>=3.14.1',
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -3,6 +3,7 @@ from unittest import mock
import sys
import pytest
from requests import Response
from requests.exceptions import HTTPError
from certbot.compat import os
@@ -20,8 +21,8 @@ class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common_lexicon.BaseLexiconDNSAuthenticatorTest):
DOMAIN_NOT_FOUND = Exception('Domain example.com not found')
LOGIN_ERROR = HTTPError('403 Client Error: Forbidden for url: https://eu.api.ovh.com/1.0/...')
LOGIN_ERROR = HTTPError('403 Client Error: Forbidden for url: https://eu.api.ovh.com/1.0/...', response=Response())
def setUp(self):
super().setUp()

View File

@@ -4,10 +4,10 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'dns-lexicon>=3.14.1',
'dns-lexicon>=3.15.1',
'setuptools>=41.6.0',
]
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'dnspython>=1.15.0',
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'boto3>=1.15.15',
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -3,6 +3,7 @@ import sys
from unittest import mock
import pytest
from requests import Response
from requests.exceptions import HTTPError
from certbot.compat import os
@@ -18,8 +19,8 @@ API_SECRET = 'MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw'
class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common_lexicon.BaseLexiconDNSAuthenticatorTest):
DOMAIN_NOT_FOUND = HTTPError(f'404 Client Error: Not Found for url: {DOMAIN}.')
LOGIN_ERROR = HTTPError(f'401 Client Error: Unauthorized for url: {DOMAIN}.')
DOMAIN_NOT_FOUND = HTTPError(f'404 Client Error: Not Found for url: {DOMAIN}.', response=Response())
LOGIN_ERROR = HTTPError(f'401 Client Error: Unauthorized for url: {DOMAIN}.', response=Response())
def setUp(self):
super().setUp()

View File

@@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
'dns-lexicon>=3.14.1',
@@ -39,7 +39,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -48,7 +48,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -18,7 +18,6 @@ from typing import Mapping
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Text
from typing import Tuple
from typing import Type
from typing import Union
@@ -1275,7 +1274,7 @@ def nginx_restart(nginx_ctl: str, nginx_conf: str, sleep_duration: int) -> None:
"""
try:
reload_output: Text = ""
reload_output: str = ""
with tempfile.TemporaryFile() as out:
proc = subprocess.run([nginx_ctl, "-c", nginx_conf, "-s", "reload"],
env=util.env_no_snap_for_external_calls(),

View File

@@ -10,8 +10,8 @@ from typing import Iterable
from typing import Iterator
from typing import List
from typing import overload
from typing import SupportsIndex
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
from pyparsing import Combine
@@ -27,9 +27,6 @@ from pyparsing import stringEnd
from pyparsing import White
from pyparsing import ZeroOrMore
if TYPE_CHECKING:
from typing_extensions import SupportsIndex # typing.SupportsIndex not supported on Python 3.7
logger = logging.getLogger(__name__)

View File

@@ -1,7 +1,7 @@
from setuptools import find_packages
from setuptools import setup
version = '2.8.0.dev0'
version = '2.8.0'
install_requires = [
# We specify the minimum acme and certbot version as the current plugin
@@ -28,7 +28,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -37,7 +37,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -2,15 +2,56 @@
Certbot adheres to [Semantic Versioning](https://semver.org/).
## 2.8.0 - master
## 2.8.0 - 2023-12-05
### Added
*
* Added support for [Alpine Linux](https://www.alpinelinux.org) distribution when is used the apache plugin
### Changed
*
* Support for Python 3.7 was removed.
### Fixed
* Stop using the deprecated `pkg_resources` API included in `setuptools`.
More details about these changes can be found on our GitHub repo.
## 2.7.4 - 2023-11-01
### Fixed
* Fixed a bug introduced in version 2.7.0 that caused interactively entered
webroot plugin values to not be saved for renewal.
* Fixed a bug introduced in version 2.7.0 of our Lexicon based DNS plugins that
caused them to fail to find the DNS zone that needs to be modified in some
cases.
More details about these changes can be found on our GitHub repo.
## 2.7.3 - 2023-10-24
### Fixed
* Fixed a bug where arguments with contained spaces weren't being handled correctly
* Fixed a bug that caused the ACME account to not be properly restored on
renewal causing problems in setups where the user had multiple accounts with
the same ACME server.
More details about these changes can be found on our GitHub repo.
## 2.7.2 - 2023-10-19
### Fixed
* `certbot-dns-ovh` plugin now requires `lexicon>=3.15.1` to ensure a consistent behavior with OVH APIs.
* Fixed a bug where argument sources weren't correctly detected in abbreviated
arguments, short arguments, and some other circumstances
More details about these changes can be found on our GitHub repo.
## 2.7.1 - 2023-10-10
### Fixed

View File

@@ -1,13 +1,4 @@
"""Certbot client."""
import sys
import warnings
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
__version__ = '2.8.0.dev0'
if sys.version_info[:2] == (3, 7):
warnings.warn(
"Python 3.7 support will be dropped in the next planned release of "
"certbot. Please upgrade your Python version.",
PendingDeprecationWarning,
) # pragma: no cover
__version__ = '2.8.0'

View File

@@ -165,6 +165,7 @@ class HelpfulArgumentParser:
def remove_config_file_domains_for_renewal(self, config: NamespaceConfig) -> None:
"""Make "certbot renew" safe if domains are set in cli.ini."""
# Works around https://github.com/certbot/certbot/issues/4096
assert config.argument_sources is not None
if (config.argument_sources['domains'] == ArgumentSource.CONFIG_FILE and
self.verb == "renew"):
config.domains = []
@@ -187,10 +188,12 @@ class HelpfulArgumentParser:
# 2. config files
# 3. env vars (shouldn't be any)
# 4. command line
def update_result(settings_dict: Dict[str, Tuple[configargparse.Action, str]],
source: ArgumentSource) -> None:
actions = [action for _, (action, _) in settings_dict.items()]
result.update({ action.dest: source for action in actions})
actions = [self._find_action_for_arg(arg) if action is None else action
for arg, (action, _) in settings_dict.items()]
result.update({ action.dest: source for action in actions })
# config file sources look like "config_file|<name of file>"
for source_key in source_to_settings_dict:
@@ -203,17 +206,61 @@ class HelpfulArgumentParser:
if 'command_line' in source_to_settings_dict:
settings_dict: Dict[str, Tuple[None, List[str]]]
settings_dict = source_to_settings_dict['command_line'] # type: ignore
(_, args) = settings_dict['']
args = [arg for arg in args if arg.startswith('-')]
(_, unprocessed_args) = settings_dict['']
args = []
for arg in unprocessed_args:
# ignore non-arguments
if not arg.startswith('-'):
continue
# special case for config file argument, which we don't have an action for
if arg in ['-c', '--config']:
result['config_dir'] = ArgumentSource.COMMAND_LINE
continue
if '=' in arg:
arg = arg.split('=')[0]
elif ' ' in arg:
arg = arg.split(' ')[0]
if arg.startswith('--'):
args.append(arg)
# for short args (ones that start with a single hyphen), handle
# the case of multiple short args together, e.g. "-tvm"
else:
for short_arg in arg[1:]:
args.append(f"-{short_arg}")
for arg in args:
# find the action corresponding to this arg
for action in self.actions:
if arg in action.option_strings:
result[action.dest] = ArgumentSource.COMMAND_LINE
continue
action = self._find_action_for_arg(arg)
result[action.dest] = ArgumentSource.COMMAND_LINE
return result
def _find_action_for_arg(self, arg: str) -> configargparse.Action:
# Finds a configargparse Action which matches the given arg, where arg
# can either be preceded by hyphens (as on the command line) or not (as
# in config files)
# if the argument doesn't have leading hypens, prefix it so it can be
# compared directly w/ action option strings
if arg[0] != '-':
arg = '--' + arg
# first, check for exact matches
for action in self.actions:
if arg in action.option_strings:
return action
# now check for abbreviated (i.e. prefix) matches
for action in self.actions:
for option_string in action.option_strings:
if option_string.startswith(arg):
return action
raise AssertionError(f"Action corresponding to argument {arg} is None")
def parse_args(self) -> NamespaceConfig:
"""Parses command line arguments and returns the result.

View File

@@ -21,7 +21,7 @@ SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins"
OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins"
"""Plugins Setuptools entry point before rename."""
CLI_DEFAULTS: Dict[str, Any] = dict( # noqa
CLI_DEFAULTS: Dict[str, Any] = dict( # pylint: disable=use-dict-literal
config_files=[
os.path.join(misc.get_default_folder('config'), 'cli.ini'),
# https://freedesktop.org/wiki/Software/xdg-user-dirs/

View File

@@ -3,12 +3,9 @@ import glob
from types import TracebackType
from typing import Callable
from typing import Iterator
from typing import Literal
from typing import Optional
from typing import Type
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing_extensions import Literal
# readline module is not available on all systems
try:

View File

@@ -1863,10 +1863,6 @@ def main(cli_args: Optional[List[str]] = None) -> Optional[Union[str, int]]:
if config.func != plugins_cmd: # pylint: disable=comparison-with-callable
raise
if sys.version_info[:2] == (3, 7):
logger.warning("Python 3.7 support will be dropped in the next planned release "
"of Certbot - please upgrade your Python version.")
with make_displayer(config) as displayer:
display_obj.set_display(displayer)

View File

@@ -182,9 +182,9 @@ class PluginsRegistry(Mapping):
plugin_paths = plugin_paths_string.split(':') if plugin_paths_string else []
# XXX should ensure this only happens once
sys.path.extend(plugin_paths)
entry_points = list(importlib_metadata.entry_points(
entry_points = list(importlib_metadata.entry_points( # pylint: disable=unexpected-keyword-arg
group=constants.SETUPTOOLS_PLUGINS_ENTRY_POINT))
old_entry_points = list(importlib_metadata.entry_points(
old_entry_points = list(importlib_metadata.entry_points( # pylint: disable=unexpected-keyword-arg
group=constants.OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT))
for entry_point in entry_points + old_entry_points:
try:
@@ -207,6 +207,7 @@ class PluginsRegistry(Mapping):
plugin2_dist = other_ep.entry_point.dist
plugin1 = plugin1_dist.name.lower() if plugin1_dist else "unknown"
plugin2 = plugin2_dist.name.lower() if plugin2_dist else "unknown"
# pylint: disable=broad-exception-raised
raise Exception("Duplicate plugin name {0} from {1} and {2}.".format(
plugin_ep.name, plugin1, plugin2))
if issubclass(plugin_ep.plugin_cls, interfaces.Plugin):

View File

@@ -194,6 +194,7 @@ def restore_required_config_elements(config: configuration.NamespaceConfig,
"""
updated_values = {}
required_items = itertools.chain(
(("pref_challs", _restore_pref_challs),),
zip(BOOL_CONFIG_ITEMS, itertools.repeat(_restore_bool)),
@@ -202,7 +203,9 @@ def restore_required_config_elements(config: configuration.NamespaceConfig,
for item_name, restore_func in required_items:
if item_name in renewalparams and not config.set_by_user(item_name):
value = restore_func(item_name, renewalparams[item_name])
setattr(config, item_name, value)
updated_values[item_name] = value
for key, value in updated_values.items():
setattr(config, key, value)
def _remove_deprecated_config_elements(renewalparams: Mapping[str, Any]) -> Dict[str, Any]:

View File

@@ -552,6 +552,55 @@ class ParseTest(unittest.TestCase):
])
assert_value_and_source(namespace, 'server', COMMAND_LINE_VALUE, ArgumentSource.COMMAND_LINE)
def test_abbreviated_arguments(self):
# Argparse's "allow_abbrev" option (which is True by default) allows
# for unambiguous partial arguments (e.g. "--preferred-chal dns" will be
# interepreted the same as "--preferred-challenges dns")
namespace = self.parse('--preferred-chal dns --no-dir')
assert_set_by_user_with_value(namespace, 'pref_challs', ['dns-01'])
assert_set_by_user_with_value(namespace, 'directory_hooks', False)
with tempfile.NamedTemporaryFile() as tmp_config:
tmp_config.close() # close now because of compatibility issues on Windows
with open(tmp_config.name, 'w') as file_h:
file_h.write('preferred-chal = dns')
namespace = self.parse([
'certonly',
'--config', tmp_config.name,
])
assert_set_by_user_with_value(namespace, 'pref_challs', ['dns-01'])
@mock.patch('certbot._internal.hooks.validate_hooks')
def test_argument_with_equals(self, unsused_mock_validate_hooks):
namespace = self.parse('-d=example.com')
assert_set_by_user_with_value(namespace, 'domains', ['example.com'])
# make sure it doesn't choke on equals signs being present in the argument value
plugins = disco.PluginsRegistry.find_all()
namespace = cli.prepare_and_parse_args(plugins, ['run', '--pre-hook="foo=bar"'])
assert_set_by_user_with_value(namespace, 'pre_hook', '"foo=bar"')
def test_adjacent_short_args(self):
namespace = self.parse('-tv')
assert_set_by_user_with_value(namespace, 'text_mode', True)
assert_set_by_user_with_value(namespace, 'verbose_count', 1)
namespace = self.parse('-tvvv')
assert_set_by_user_with_value(namespace, 'text_mode', True)
assert_set_by_user_with_value(namespace, 'verbose_count', 3)
namespace = self.parse('-tvm foo@example.com')
assert_set_by_user_with_value(namespace, 'text_mode', True)
assert_set_by_user_with_value(namespace, 'verbose_count', 1)
assert_set_by_user_with_value(namespace, 'email', 'foo@example.com')
def test_arg_with_contained_spaces(self):
# This can happen if a user specifies an arg like "-d foo.com" enclosed
# in double quotes, or as its own line in a docker-compose.yml file (as
# in #9811)
namespace = self.parse(['certonly', '-d foo.com'])
assert_set_by_user_with_value(namespace, 'domains', ['foo.com'])
if __name__ == '__main__':
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View File

@@ -165,17 +165,22 @@ class NamespaceConfigTest(test_util.ConfigTestCase):
def test_set_by_user_exception(self):
from certbot.configuration import NamespaceConfig
# a newly created NamespaceConfig has no argument sources dict, so an
# exception is raised
config = NamespaceConfig(self.config.namespace)
with pytest.raises(RuntimeError):
config.set_by_user('whatever')
# now set an argument sources dict
config.set_argument_sources({})
assert not config.set_by_user('whatever')
def test_set_by_user_mutables(self):
assert not self.config.set_by_user('domains')
self.config.domains.append('example.org')
assert self.config.set_by_user('domains')
if __name__ == '__main__':
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View File

@@ -253,6 +253,18 @@ class RestoreRequiredConfigElementsTest(test_util.ConfigTestCase):
self._call(self.config, {'server': constants.V1_URI})
assert self.config.server == constants.CLI_DEFAULTS['server']
def test_related_values(self):
# certbot.configuration.NamespaceConfig.set_by_user considers some values as related to each
# other and considers both set by the user if either is. This test ensures all renewal
# parameters are restored regardless of their restoration order or relation between values.
# See https://github.com/certbot/certbot/issues/9805 for more info.
renewalparams = {
'server': 'https://example.org',
'account': 'somehash',
}
self._call(self.config, renewalparams)
self.assertEqual(self.config.account, renewalparams['account'])
class DescribeResultsTest(unittest.TestCase):
"""Tests for certbot._internal.renewal._renew_describe_results."""

View File

@@ -66,7 +66,8 @@ class NamespaceConfig:
self.namespace: argparse.Namespace
# Avoid recursion loop because of the delegation defined in __setattr__
object.__setattr__(self, 'namespace', namespace)
object.__setattr__(self, 'argument_sources', None)
object.__setattr__(self, '_argument_sources', None)
object.__setattr__(self, '_previously_accessed_mutables', {})
self.namespace.config_dir = os.path.abspath(self.namespace.config_dir)
self.namespace.work_dir = os.path.abspath(self.namespace.work_dir)
@@ -90,7 +91,7 @@ class NamespaceConfig:
"""
# Avoid recursion loop because of the delegation defined in __setattr__
object.__setattr__(self, 'argument_sources', argument_sources)
object.__setattr__(self, '_argument_sources', argument_sources)
def set_by_user(self, var: str) -> bool:
@@ -145,15 +146,48 @@ class NamespaceConfig:
"""
If an argument_sources dict was set, overwrites an argument's source to
be ArgumentSource.RUNTIME. Used when certbot sets an argument's values
at runtime.
at runtime. This also clears the modified value from
_previously_accessed_mutables since it is no longer needed.
"""
if self.argument_sources is not None:
self.argument_sources[name] = ArgumentSource.RUNTIME
if self._argument_sources is not None:
self._argument_sources[name] = ArgumentSource.RUNTIME
if name in self._previously_accessed_mutables:
del self._previously_accessed_mutables[name]
@property
def argument_sources(self) -> Optional[Dict[str, ArgumentSource]]:
"""Returns _argument_sources after handling any changes to accessed mutable values."""
# We keep values in _previously_accessed_mutables until we've detected a modification to try
# to provide up-to-date information when argument_sources is accessed. Once a mutable object
# has been accessed, it can be modified at any time if a reference to it was kept somewhere
# else.
# We copy _previously_accessed_mutables because _mark_runtime_override modifies it.
for name, prev_value in self._previously_accessed_mutables.copy().items():
current_value = getattr(self.namespace, name)
if current_value != prev_value:
self._mark_runtime_override(name)
return self._argument_sources
# Delegate any attribute not explicitly defined to the underlying namespace object.
#
# If any mutable namespace attributes are explicitly defined in the future, you'll probably want
# to take an approach like the one used in __getattr__ and the argument_sources property.
def __getattr__(self, name: str) -> Any:
return getattr(self.namespace, name)
arg_sources = self.argument_sources
value = getattr(self.namespace, name)
if arg_sources is not None:
# If the requested attribute was already modified at runtime, we don't need to track any
# future changes.
if name not in arg_sources or arg_sources[name] != ArgumentSource.RUNTIME:
# If name is already in _previously_accessed_mutables, we don't need to make a copy
# of it again. If its value was changed, this would have been caught while preparing
# the return value of the property self.argument_sources accessed earlier in this
# function.
if name not in self._previously_accessed_mutables and not _is_immutable(value):
self._previously_accessed_mutables[name] = copy.deepcopy(value)
return value
def __setattr__(self, name: str, value: Any) -> None:
self._mark_runtime_override(name)
@@ -425,9 +459,10 @@ class NamespaceConfig:
# Work around https://bugs.python.org/issue1515 for py26 tests :( :(
new_ns = copy.deepcopy(self.namespace)
new_config = type(self)(new_ns)
if self.set_argument_sources is not None:
new_sources = copy.deepcopy(self.argument_sources)
new_config.set_argument_sources(new_sources)
# Avoid recursion loop because of the delegation defined in __setattr__
object.__setattr__(new_config, '_argument_sources', copy.deepcopy(self.argument_sources))
object.__setattr__(new_config, '_previously_accessed_mutables',
copy.deepcopy(self._previously_accessed_mutables))
return new_config
@@ -450,3 +485,15 @@ def _check_config_sanity(config: NamespaceConfig) -> None:
for domain in config.namespace.domains:
# This may be redundant, but let's be paranoid
util.enforce_domain_sanity(domain)
def _is_immutable(value: Any) -> bool:
"""Is value of an immutable type?"""
if isinstance(value, tuple):
# tuples are only immutable if all contained values are immutable.
return all(_is_immutable(subvalue) for subvalue in value)
for immutable_type in (int, float, complex, str, bytes, bool, frozenset,):
if isinstance(value, immutable_type):
return True
# The last case we consider here is None which is also immutable.
return value is None

View File

@@ -30,9 +30,9 @@ try:
from lexicon.config import ConfigResolver
from lexicon.interfaces import Provider
except ImportError: # pragma: no cover
Client = None
ConfigResolver = None
Provider = None
Client = None # type: ignore
ConfigResolver = None # type: ignore
Provider = None # type: ignore
logger = logging.getLogger(__name__)
@@ -148,19 +148,18 @@ def build_lexicon_config(lexicon_provider_name: str,
.. deprecated:: 2.7.0
Please use certbot.plugins.dns_common_lexicon.LexiconDNSAuthenticator instead.
"""
config: Union[ConfigResolver, Dict[str, Any]] = {'provider_name': lexicon_provider_name}
config.update(lexicon_options)
if not ConfigResolver:
config_dict: Dict[str, Any] = {'provider_name': lexicon_provider_name}
config_dict.update(lexicon_options)
if ConfigResolver is None:
# Lexicon 2.x
config.update(provider_options)
config_dict.update(provider_options)
return config_dict
else:
# Lexicon 3.x
provider_config: Dict[str, Any] = {}
provider_config.update(provider_options)
config[lexicon_provider_name] = provider_config
config = ConfigResolver().with_dict(config).with_env()
return config
config_dict[lexicon_provider_name] = provider_config
return ConfigResolver().with_dict(config_dict).with_env()
class LexiconDNSAuthenticator(dns_common.DNSAuthenticator):
@@ -199,6 +198,10 @@ class LexiconDNSAuthenticator(dns_common.DNSAuthenticator):
dict_config = {
'domain': domain,
# We bypass Lexicon subdomain resolution by setting the 'delegated' field in the config
# to the value of the 'domain' field itself. Here we consider that the domain passed to
# _build_lexicon_config() is already the exact subdomain of the actual DNS zone to use.
'delegated': domain,
'provider_name': self._provider_name,
'ttl': self._ttl,
self._provider_name: {item[2]: self._credentials.conf(item[0])

View File

@@ -1,7 +1,7 @@
"""Base test class for DNS authenticators."""
from typing import Any
from typing import Mapping
from typing import TYPE_CHECKING
from typing import Protocol
from unittest import mock
import configobj
@@ -14,12 +14,6 @@ from certbot.plugins.dns_common import DNSAuthenticator
from certbot.tests import acme_util
from certbot.tests import util as test_util
if TYPE_CHECKING:
from typing_extensions import Protocol
else:
Protocol = object
DOMAIN = 'example.com'
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))

View File

@@ -6,13 +6,14 @@ from typing import Any
from typing import cast
from typing import Generator
from typing import List
from typing import Protocol
from typing import Tuple
from typing import TYPE_CHECKING
from unittest import mock
from unittest.mock import MagicMock
import warnings
import josepy as jose
from requests import Response
from requests.exceptions import HTTPError
from requests.exceptions import RequestException
@@ -27,18 +28,13 @@ with warnings.catch_warnings():
from certbot.plugins.dns_test_common import _AuthenticatorCallableTestCase
from certbot.tests import util as test_util
if TYPE_CHECKING: # pragma: no cover
from typing_extensions import Protocol
else:
Protocol = object
DOMAIN = 'example.com'
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
DOMAIN_NOT_FOUND = Exception('No domain found')
GENERIC_ERROR = RequestException
LOGIN_ERROR = HTTPError('400 Client Error: ...')
UNKNOWN_LOGIN_ERROR = HTTPError('500 Surprise! Error: ...')
LOGIN_ERROR = HTTPError('400 Client Error: ...', response=Response())
UNKNOWN_LOGIN_ERROR = HTTPError('500 Surprise! Error: ...', response=Response())
class _AuthenticatorCallableLexiconTestCase(_AuthenticatorCallableTestCase, Protocol):

View File

@@ -1,6 +1,7 @@
"""Test utilities."""
import atexit
from contextlib import ExitStack
import copy
from importlib import reload as reload_module
import io
import logging
@@ -403,7 +404,8 @@ class ConfigTestCase(TempDirTestCase):
def setUp(self) -> None:
super().setUp()
self.config = configuration.NamespaceConfig(
mock.MagicMock(**constants.CLI_DEFAULTS),
# We make a copy here so any mutable values from CLI_DEFAULTS do not get modified.
mock.MagicMock(**copy.deepcopy(constants.CLI_DEFAULTS)),
)
self.config.set_argument_sources({})
self.config.namespace.verb = "certonly"

View File

@@ -122,7 +122,7 @@ options:
case, and to know when to deprecate support for past
Python versions and flags. If you wish to hide this
information from the Let's Encrypt server, set this to
"". (default: CertbotACMEClient/2.7.0 (certbot;
"". (default: CertbotACMEClient/2.8.0 (certbot;
OS_NAME OS_VERSION) Authenticator/XXX Installer/YYY
(SUBCOMMAND; flags: FLAGS) Py/major.minor.patchlevel).
The flags encoded in the user agent are: --duplicate,

View File

@@ -328,8 +328,8 @@ Writing your own plugin
for one example of that.
Certbot client supports dynamic discovery of plugins through the
`setuptools entry points`_ using the `certbot.plugins` group. This
way you can, for example, create a custom implementation of
`importlib.metadata entry points`_ using the `certbot.plugins` group.
This way you can, for example, create a custom implementation of
`~certbot.interfaces.Authenticator` or the
`~certbot.interfaces.Installer` without having to merge it
with the core upstream source code. An example is provided in
@@ -352,8 +352,8 @@ users install it system-wide with `pip install`. Note that this will
only work for users who have Certbot installed from OS packages or via
pip.
.. _`setuptools entry points`:
https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
.. _`importlib.metadata entry points`:
https://importlib-metadata.readthedocs.io/en/latest/using.html#entry-points
Writing your own plugin snap
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -70,8 +70,7 @@ test_extras = [
'coverage',
'mypy',
'pip',
# Our pinned version of pylint requires Python >= 3.7.2.
'pylint ; python_full_version >= "3.7.2"',
'pylint',
'pytest',
'pytest-cov',
'pytest-xdist',
@@ -85,9 +84,6 @@ test_extras = [
'types-requests',
'types-setuptools',
'types-six',
# typing-extensions is required to import typing.Protocol and make the mypy checks
# pass (along with pylint about non-existent objects) on Python 3.7
'typing-extensions',
'wheel',
]
@@ -103,7 +99,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Console',
@@ -113,7 +109,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -9,14 +9,13 @@ setup(
author='Certbot Project',
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',

View File

@@ -10,7 +10,6 @@ import os.path
import re
from pylint.checkers import BaseChecker
from pylint.interfaces import IAstroidChecker
# Modules whose file is matching one of these paths can import the os module.
WHITELIST_PATHS = [
@@ -25,7 +24,6 @@ class ForbidStandardOsModule(BaseChecker):
This checker ensures that standard os module (and submodules) is not imported by certbot
modules. Otherwise an 'os-module-forbidden' error will be registered for the faulty lines.
"""
__implements__ = IAstroidChecker
name = 'forbid-os-module'
msgs = {

View File

@@ -21,26 +21,16 @@
# updated.
# 4) Ignore our own PendingDeprecationWarning about update_symlinks soon to be dropped.
# See https://github.com/certbot/certbot/issues/6284.
# 5) Ignore pkg_resources.declare_namespace used in a large number of Google's Python
# libs. e.g. https://github.com/googleapis/google-auth-library-python/issues/1229.
# It is also is used in sphinxcontrib-devhelp 1.0.2 which as of writing this
# is the latest version of that library. See
# https://github.com/sphinx-doc/sphinxcontrib-devhelp/blob/1.0.2/setup.py#L69.
# 6) Ignore DeprecationWarning from using pkg_resources API
# 7) Ignore our own PendingDeprecationWarning about Python 3.7 soon to be dropped.
# 8) Ignore DeprecationWarning for datetime.utcfromtimestamp() triggered
# 5) Ignore DeprecationWarning for datetime.utcfromtimestamp() triggered
# when importing the pytz.tzinfo module
# https://github.com/stub42/pytz/issues/105
# 9) Boto3 is dropping support for Python 3.7 by end of 2023. Let's ignore the associated
# deprecation warning since we will also drop Python 3.7 soon.
# 6) Ignoring this allows us to continue to update pyOpenSSL (one of our crypto
# dependencies) until https://github.com/certbot/certbot/issues/9828 is resolved.
filterwarnings =
error
ignore:decodestring\(\) is a deprecated alias:DeprecationWarning:dns
ignore:.*rsyncdir:DeprecationWarning
ignore:'urllib3.contrib.pyopenssl:DeprecationWarning:requests_toolbelt
ignore:update_symlinks is deprecated:PendingDeprecationWarning
ignore:.*declare_namespace\(':DeprecationWarning
ignore:pkg_resources is deprecated as an API:DeprecationWarning
ignore:Python 3.7 support will be dropped:PendingDeprecationWarning
ignore:.*datetime.utcfromtimestamp\(\) is deprecated:DeprecationWarning:pytz.tzinfo
ignore:Boto3 will no longer support Python 3.7
ignore:X509Extension support in pyOpenSSL is deprecated:DeprecationWarning

View File

@@ -73,7 +73,7 @@ def parse_args(args):
parser.add_argument('--css', type=str, required=True, help='hostname of code signing server')
return parser.parse_args(args)
def publish_windows(css):
"""SSH into CSS and trigger downloading Azure Pipeline assets, sign, and upload to Github
@@ -83,7 +83,7 @@ def publish_windows(css):
username = input("CSS username (usually EFF username): ")
host = css
command = "ssh -t {}@{} bash /opt/certbot-misc/css/venv.sh".format(username,host)
print("SSH into CSS to trigger signing and uploading of Windows installer...")
subprocess.run(command, check=True, universal_newlines=True, shell=True)
@@ -182,16 +182,16 @@ def fetch_version_number(major_version=None):
:type major_version: str or None
:returns: version number
"""
# Create a connection to the azure org
organization_url = 'https://dev.azure.com/certbot'
connection = Connection(base_url=organization_url)
# Find the build artifacts
build_client = connection.clients.get_build_client()
get_builds_response = build_client.get_builds('certbot', definitions='3')
for build in get_builds_response.value:
builds = build_client.get_builds('certbot', definitions='3')
for build in builds:
version = build_client.get_build('certbot', build.id).source_branch.split('v')[1]
if major_version is None or version.split('.')[0] == major_version:
return version
@@ -206,7 +206,7 @@ def main(args):
# Once the GitHub release has been published, trying to publish it
# again fails. Publishing the snaps can be done multiple times though
# so we do that first to make it easier to run the script again later
# if something goes wrong.
# if something goes wrong.
promote_snaps(ALL_SNAPS, 'beta', version)
publish_windows(css)

View File

@@ -1,102 +1,99 @@
# This file was generated by tools/pinning/oldest/repin.sh and can be updated using
# that script.
apacheconfig==0.3.2 ; python_version >= "3.7" and python_version < "3.8"
appdirs==1.4.4 ; python_version >= "3.7" and python_version < "3.8"
asn1crypto==0.24.0 ; python_version >= "3.7" and python_version < "3.8"
astroid==2.15.6 ; python_full_version >= "3.7.2" and python_version < "3.8"
beautifulsoup4==4.12.2 ; python_version >= "3.7" and python_version < "3.8"
boto3==1.15.15 ; python_version >= "3.7" and python_version < "3.8"
botocore==1.18.15 ; python_version >= "3.7" and python_version < "3.8"
cachetools==5.3.1 ; python_version >= "3.7" and python_version < "3.8"
certifi==2023.7.22 ; python_version >= "3.7" and python_version < "3.8"
cffi==1.11.5 ; python_version >= "3.7" and python_version < "3.8"
chardet==3.0.4 ; python_version >= "3.7" and python_version < "3.8"
cloudflare==1.5.1 ; python_version >= "3.7" and python_version < "3.8"
colorama==0.4.6 ; python_version < "3.8" and sys_platform == "win32" and python_version >= "3.7"
configargparse==1.5.3 ; python_version >= "3.7" and python_version < "3.8"
configobj==5.0.6 ; python_version >= "3.7" and python_version < "3.8"
coverage==7.2.7 ; python_version >= "3.7" and python_version < "3.8"
cryptography==3.2.1 ; python_version >= "3.7" and python_version < "3.8"
cython==0.29.36 ; python_version >= "3.7" and python_version < "3.8"
dill==0.3.7 ; python_full_version >= "3.7.2" and python_version < "3.8"
distlib==0.3.7 ; python_version >= "3.7" and python_version < "3.8"
distro==1.0.1 ; python_version >= "3.7" and python_version < "3.8"
dns-lexicon==3.14.1 ; python_version >= "3.7" and python_version < "3.8"
dnspython==1.15.0 ; python_version >= "3.7" and python_version < "3.8"
exceptiongroup==1.1.3 ; python_version >= "3.7" and python_version < "3.8"
execnet==2.0.2 ; python_version >= "3.7" and python_version < "3.8"
filelock==3.12.2 ; python_version >= "3.7" and python_version < "3.8"
funcsigs==0.4 ; python_version >= "3.7" and python_version < "3.8"
future==0.18.3 ; python_version >= "3.7" and python_version < "3.8"
google-api-python-client==1.6.5 ; python_version >= "3.7" and python_version < "3.8"
google-auth==2.16.0 ; python_version >= "3.7" and python_version < "3.8"
httplib2==0.9.2 ; python_version >= "3.7" and python_version < "3.8"
idna==2.6 ; python_version >= "3.7" and python_version < "3.8"
importlib-metadata==4.6.4 ; python_version >= "3.7" and python_version < "3.8"
importlib-resources==5.12.0 ; python_version >= "3.7" and python_version < "3.8"
iniconfig==2.0.0 ; python_version >= "3.7" and python_version < "3.8"
ipaddress==1.0.16 ; python_version >= "3.7" and python_version < "3.8"
isort==5.11.5 ; python_full_version >= "3.7.2" and python_version < "3.8"
jmespath==0.10.0 ; python_version >= "3.7" and python_version < "3.8"
josepy==1.13.0 ; python_version >= "3.7" and python_version < "3.8"
lazy-object-proxy==1.9.0 ; python_full_version >= "3.7.2" and python_version < "3.8"
logger==1.4 ; python_version >= "3.7" and python_version < "3.8"
mccabe==0.7.0 ; python_full_version >= "3.7.2" and python_version < "3.8"
mypy-extensions==1.0.0 ; python_version >= "3.7" and python_version < "3.8"
mypy==1.4.1 ; python_version >= "3.7" and python_version < "3.8"
ndg-httpsclient==0.3.2 ; python_version >= "3.7" and python_version < "3.8"
oauth2client==4.1.3 ; python_version >= "3.7" and python_version < "3.8"
packaging==23.1 ; python_version >= "3.7" and python_version < "3.8"
parsedatetime==2.4 ; python_version >= "3.7" and python_version < "3.8"
pbr==1.8.0 ; python_version >= "3.7" and python_version < "3.8"
pip==23.2.1 ; python_version >= "3.7" and python_version < "3.8"
platformdirs==3.10.0 ; python_full_version >= "3.7.2" and python_version < "3.8"
pluggy==1.2.0 ; python_version >= "3.7" and python_version < "3.8"
ply==3.4 ; python_version >= "3.7" and python_version < "3.8"
py==1.11.0 ; python_version >= "3.7" and python_version < "3.8"
pyasn1-modules==0.3.0 ; python_version >= "3.7" and python_version < "3.8"
pyasn1==0.4.8 ; python_version >= "3.7" and python_version < "3.8"
pycparser==2.14 ; python_version >= "3.7" and python_version < "3.8"
pylint==2.17.5 ; python_full_version >= "3.7.2" and python_version < "3.8"
pyopenssl==17.5.0 ; python_version >= "3.7" and python_version < "3.8"
pyparsing==2.2.1 ; python_version >= "3.7" and python_version < "3.8"
pyrfc3339==1.0 ; python_version >= "3.7" and python_version < "3.8"
pytest-cov==4.1.0 ; python_version >= "3.7" and python_version < "3.8"
pytest-xdist==3.3.1 ; python_version >= "3.7" and python_version < "3.8"
pytest==7.4.2 ; python_version >= "3.7" and python_version < "3.8"
python-augeas==0.5.0 ; python_version >= "3.7" and python_version < "3.8"
python-dateutil==2.8.2 ; python_version >= "3.7" and python_version < "3.8"
python-digitalocean==1.11 ; python_version >= "3.7" and python_version < "3.8"
pytz==2019.3 ; python_version >= "3.7" and python_version < "3.8"
pywin32==306 ; python_version >= "3.7" and python_version < "3.8" and sys_platform == "win32"
pyyaml==6.0.1 ; python_version >= "3.7" and python_version < "3.8"
requests-file==1.5.1 ; python_version >= "3.7" and python_version < "3.8"
requests==2.20.0 ; python_version >= "3.7" and python_version < "3.8"
rsa==4.9 ; python_version >= "3.7" and python_version < "3.8"
s3transfer==0.3.7 ; python_version >= "3.7" and python_version < "3.8"
setuptools==41.6.0 ; python_version >= "3.7" and python_version < "3.8"
six==1.11.0 ; python_version >= "3.7" and python_version < "3.8"
soupsieve==2.4.1 ; python_version >= "3.7" and python_version < "3.8"
tldextract==3.5.0 ; python_version >= "3.7" and python_version < "3.8"
tomli==2.0.1 ; python_version < "3.8" and python_version >= "3.7"
tomlkit==0.12.1 ; python_full_version >= "3.7.2" and python_version < "3.8"
tox==1.9.2 ; python_version >= "3.7" and python_version < "3.8"
typed-ast==1.5.5 ; python_version < "3.8" and python_version >= "3.7"
types-cryptography==3.3.23.2 ; python_version >= "3.7" and python_version < "3.8"
types-httplib2==0.22.0.2 ; python_version >= "3.7" and python_version < "3.8"
types-pyopenssl==23.0.0.0 ; python_version >= "3.7" and python_version < "3.8"
types-pyrfc3339==1.1.1.5 ; python_version >= "3.7" and python_version < "3.8"
types-python-dateutil==2.8.19.14 ; python_version >= "3.7" and python_version < "3.8"
types-pytz==2023.3.0.1 ; python_version >= "3.7" and python_version < "3.8"
types-pywin32==306.0.0.4 ; python_version >= "3.7" and python_version < "3.8"
types-requests==2.31.0.2 ; python_version >= "3.7" and python_version < "3.8"
types-setuptools==68.2.0.0 ; python_version >= "3.7" and python_version < "3.8"
types-six==1.16.21.9 ; python_version >= "3.7" and python_version < "3.8"
types-urllib3==1.26.25.14 ; python_version >= "3.7" and python_version < "3.8"
typing-extensions==4.7.1 ; python_version < "3.8" and python_version >= "3.7"
uritemplate==3.0.1 ; python_version >= "3.7" and python_version < "3.8"
urllib3==1.24.2 ; python_version >= "3.7" and python_version < "3.8"
virtualenv==20.4.7 ; python_version >= "3.7" and python_version < "3.8"
wheel==0.33.6 ; python_version >= "3.7" and python_version < "3.8"
wrapt==1.15.0 ; python_full_version >= "3.7.2" and python_version < "3.8"
zipp==3.15.0 ; python_version >= "3.7" and python_version < "3.8"
apacheconfig==0.3.2 ; python_version >= "3.8" and python_version < "3.9"
asn1crypto==0.24.0 ; python_version >= "3.8" and python_version < "3.9"
astroid==3.0.1 ; python_version >= "3.8" and python_version < "3.9"
beautifulsoup4==4.12.2 ; python_version >= "3.8" and python_version < "3.9"
boto3==1.15.15 ; python_version >= "3.8" and python_version < "3.9"
botocore==1.18.15 ; python_version >= "3.8" and python_version < "3.9"
cachetools==5.3.2 ; python_version >= "3.8" and python_version < "3.9"
certifi==2023.11.17 ; python_version >= "3.8" and python_version < "3.9"
cffi==1.12.3 ; python_version >= "3.8" and python_version < "3.9"
chardet==3.0.4 ; python_version >= "3.8" and python_version < "3.9"
cloudflare==1.5.1 ; python_version >= "3.8" and python_version < "3.9"
colorama==0.4.6 ; python_version >= "3.8" and python_version < "3.9" and sys_platform == "win32"
configargparse==1.5.3 ; python_version >= "3.8" and python_version < "3.9"
configobj==5.0.6 ; python_version >= "3.8" and python_version < "3.9"
coverage==7.3.2 ; python_version >= "3.8" and python_version < "3.9"
cryptography==3.2.1 ; python_version >= "3.8" and python_version < "3.9"
cython==0.29.36 ; python_version >= "3.8" and python_version < "3.9"
dill==0.3.7 ; python_version >= "3.8" and python_version < "3.9"
distlib==0.3.7 ; python_version >= "3.8" and python_version < "3.9"
distro==1.0.1 ; python_version >= "3.8" and python_version < "3.9"
dns-lexicon==3.15.1 ; python_version >= "3.8" and python_version < "3.9"
dnspython==1.15.0 ; python_version >= "3.8" and python_version < "3.9"
exceptiongroup==1.2.0 ; python_version >= "3.8" and python_version < "3.9"
execnet==2.0.2 ; python_version >= "3.8" and python_version < "3.9"
filelock==3.13.1 ; python_version >= "3.8" and python_version < "3.9"
funcsigs==0.4 ; python_version >= "3.8" and python_version < "3.9"
future==0.18.3 ; python_version >= "3.8" and python_version < "3.9"
google-api-python-client==1.6.5 ; python_version >= "3.8" and python_version < "3.9"
google-auth==2.16.0 ; python_version >= "3.8" and python_version < "3.9"
httplib2==0.9.2 ; python_version >= "3.8" and python_version < "3.9"
idna==2.6 ; python_version >= "3.8" and python_version < "3.9"
importlib-metadata==4.6.4 ; python_version >= "3.8" and python_version < "3.9"
importlib-resources==6.1.1 ; python_version >= "3.8" and python_version < "3.9"
iniconfig==2.0.0 ; python_version >= "3.8" and python_version < "3.9"
ipaddress==1.0.16 ; python_version >= "3.8" and python_version < "3.9"
isort==5.12.0 ; python_version >= "3.8" and python_version < "3.9"
jmespath==0.10.0 ; python_version >= "3.8" and python_version < "3.9"
josepy==1.14.0 ; python_version >= "3.8" and python_version < "3.9"
logger==1.4 ; python_version >= "3.8" and python_version < "3.9"
mccabe==0.7.0 ; python_version >= "3.8" and python_version < "3.9"
mypy-extensions==1.0.0 ; python_version >= "3.8" and python_version < "3.9"
mypy==1.7.1 ; python_version >= "3.8" and python_version < "3.9"
ndg-httpsclient==0.3.2 ; python_version >= "3.8" and python_version < "3.9"
oauth2client==4.1.3 ; python_version >= "3.8" and python_version < "3.9"
packaging==23.2 ; python_version >= "3.8" and python_version < "3.9"
parsedatetime==2.4 ; python_version >= "3.8" and python_version < "3.9"
pbr==1.8.0 ; python_version >= "3.8" and python_version < "3.9"
pip==23.3.1 ; python_version >= "3.8" and python_version < "3.9"
platformdirs==4.0.0 ; python_version >= "3.8" and python_version < "3.9"
pluggy==1.3.0 ; python_version >= "3.8" and python_version < "3.9"
ply==3.4 ; python_version >= "3.8" and python_version < "3.9"
py==1.11.0 ; python_version >= "3.8" and python_version < "3.9"
pyasn1-modules==0.3.0 ; python_version >= "3.8" and python_version < "3.9"
pyasn1==0.4.8 ; python_version >= "3.8" and python_version < "3.9"
pycparser==2.14 ; python_version >= "3.8" and python_version < "3.9"
pylint==3.0.2 ; python_version >= "3.8" and python_version < "3.9"
pyopenssl==17.5.0 ; python_version >= "3.8" and python_version < "3.9"
pyotp==2.9.0 ; python_version >= "3.8" and python_version < "3.9"
pyparsing==2.2.1 ; python_version >= "3.8" and python_version < "3.9"
pyrfc3339==1.0 ; python_version >= "3.8" and python_version < "3.9"
pytest-cov==4.1.0 ; python_version >= "3.8" and python_version < "3.9"
pytest-xdist==3.5.0 ; python_version >= "3.8" and python_version < "3.9"
pytest==7.4.3 ; python_version >= "3.8" and python_version < "3.9"
python-augeas==0.5.0 ; python_version >= "3.8" and python_version < "3.9"
python-dateutil==2.8.2 ; python_version >= "3.8" and python_version < "3.9"
python-digitalocean==1.11 ; python_version >= "3.8" and python_version < "3.9"
pytz==2019.3 ; python_version >= "3.8" and python_version < "3.9"
pywin32==306 ; python_version >= "3.8" and python_version < "3.9" and sys_platform == "win32"
pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "3.9"
requests-file==1.5.1 ; python_version >= "3.8" and python_version < "3.9"
requests==2.20.0 ; python_version >= "3.8" and python_version < "3.9"
rsa==4.9 ; python_version >= "3.8" and python_version < "3.9"
s3transfer==0.3.7 ; python_version >= "3.8" and python_version < "3.9"
setuptools==41.6.0 ; python_version >= "3.8" and python_version < "3.9"
six==1.11.0 ; python_version >= "3.8" and python_version < "3.9"
soupsieve==2.5 ; python_version >= "3.8" and python_version < "3.9"
tldextract==5.1.1 ; python_version >= "3.8" and python_version < "3.9"
tomli==2.0.1 ; python_version >= "3.8" and python_version < "3.9"
tomlkit==0.12.3 ; python_version >= "3.8" and python_version < "3.9"
tox==1.9.2 ; python_version >= "3.8" and python_version < "3.9"
types-cryptography==3.3.23.2 ; python_version >= "3.8" and python_version < "3.9"
types-httplib2==0.22.0.2 ; python_version >= "3.8" and python_version < "3.9"
types-pyopenssl==23.0.0.0 ; python_version >= "3.8" and python_version < "3.9"
types-pyrfc3339==1.1.1.5 ; python_version >= "3.8" and python_version < "3.9"
types-python-dateutil==2.8.19.14 ; python_version >= "3.8" and python_version < "3.9"
types-pytz==2023.3.1.1 ; python_version >= "3.8" and python_version < "3.9"
types-pywin32==306.0.0.6 ; python_version >= "3.8" and python_version < "3.9"
types-requests==2.31.0.6 ; python_version >= "3.8" and python_version < "3.9"
types-setuptools==69.0.0.0 ; python_version >= "3.8" and python_version < "3.9"
types-six==1.16.21.9 ; python_version >= "3.8" and python_version < "3.9"
types-urllib3==1.26.25.14 ; python_version >= "3.8" and python_version < "3.9"
typing-extensions==4.8.0 ; python_version >= "3.8" and python_version < "3.9"
uritemplate==3.0.1 ; python_version >= "3.8" and python_version < "3.9"
urllib3==1.24.2 ; python_version >= "3.8" and python_version < "3.9"
virtualenv==20.25.0 ; python_version >= "3.8" and python_version < "3.9"
wheel==0.33.6 ; python_version >= "3.8" and python_version < "3.9"
zipp==3.17.0 ; python_version >= "3.8" and python_version < "3.9"

View File

@@ -6,7 +6,7 @@ authors = ["Certbot Project"]
license = "Apache License 2.0"
[tool.poetry.dependencies]
python = "^3.7"
python = "^3.8"
# Local dependencies
# Any local packages that have dependencies on other local packages must be
@@ -58,7 +58,7 @@ setuptools-rust = "*"
#
# If this pinning is removed, we may still need to add a lower bound for the
# pylint version. See https://github.com/certbot/certbot/pull/9229.
pylint = { version="2.15.5", python = ">=3.7.2" }
pylint = "3.0.2"
# Bug in poetry, where still installes yanked versions from pypi (source: https://github.com/python-poetry/poetry/issues/2453)
# this version of cryptography introduced a security vulnrability.
@@ -70,10 +70,11 @@ cryptography = "!= 37.0.3"
# https://github.com/python-poetry/poetry-plugin-export/issues/168 is resolved.
poetry = "<1.3.0"
# setuptools 67.5.0 deprecated pkg_resources which we still use. Let's pin it
# back until this is fixed. Doing this work is being tracked by
# https://github.com/certbot/certbot/issues/9606.
setuptools = "<67.5.0"
# Branch 4.x of tox introduces backward incompatibility changes. The tox.ini
# file in the project must be adapted accordingly before moving out of the 3.x
# branch. Once done, the following constraint should become tox >= 4 to keep
# deterministic builds.
tox = "<4"
[tool.poetry.dev-dependencies]

View File

@@ -10,7 +10,7 @@ license = "Apache License 2.0"
[tool.poetry.dependencies]
# The Python version here should be kept in sync with the one used in our
# oldest tests in tox.ini.
python = "<3.8 >= 3.7"
python = "<3.9 >= 3.8"
# Local dependencies
# Any local packages that have dependencies on other local packages must be
@@ -48,13 +48,13 @@ apacheconfig = "0.3.2"
asn1crypto = "0.24.0"
boto3 = "1.15.15"
botocore = "1.18.15"
cffi = "1.11.5"
cffi = "1.12.3"
chardet = "3.0.4"
cloudflare = "1.5.1"
configobj = "5.0.6"
cryptography = "3.2.1"
distro = "1.0.1"
dns-lexicon = "3.14.1"
dns-lexicon = "3.15.1"
dnspython = "1.15.0"
funcsigs = "0.4"
google-api-python-client = "1.6.5"

View File

@@ -5,182 +5,186 @@
# requirements.txt so that is scanned by GitHub. See
# https://docs.github.com/en/github/visualizing-repository-data-with-graphs/about-the-dependency-graph#supported-package-ecosystems
# for more info.
alabaster==0.7.13 ; python_version >= "3.7" and python_version < "4.0"
apacheconfig==0.3.2 ; python_version >= "3.7" and python_version < "4.0"
appnope==0.1.3 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "darwin"
astroid==2.13.5 ; python_full_version >= "3.7.2" and python_version < "4.0"
attrs==23.1.0 ; python_version >= "3.7" and python_version < "4.0"
azure-core==1.29.4 ; python_version >= "3.7" and python_version < "4.0"
azure-devops==7.1.0b3 ; python_version >= "3.7" and python_version < "4.0"
babel==2.13.0 ; python_version >= "3.7" and python_version < "4.0"
backcall==0.2.0 ; python_version >= "3.7" and python_version < "4.0"
backports-cached-property==1.0.2 ; python_version >= "3.7" and python_version < "3.8"
bcrypt==4.0.1 ; python_version >= "3.7" and python_version < "4.0"
beautifulsoup4==4.12.2 ; python_version >= "3.7" and python_version < "4.0"
bleach==6.0.0 ; python_version >= "3.7" and python_version < "4.0"
boto3==1.28.60 ; python_version >= "3.7" and python_version < "4.0"
botocore==1.31.60 ; python_version >= "3.7" and python_version < "4.0"
cachecontrol==0.12.14 ; python_version >= "3.7" and python_version < "4.0"
cachetools==5.3.1 ; python_version >= "3.7" and python_version < "4.0"
cachy==0.3.0 ; python_version >= "3.7" and python_version < "4.0"
certifi==2023.7.22 ; python_version >= "3.7" and python_version < "4.0"
cffi==1.15.1 ; python_version >= "3.7" and python_version < "4.0"
charset-normalizer==3.3.0 ; python_version >= "3.7" and python_version < "4.0"
cleo==1.0.0a5 ; python_version >= "3.7" and python_version < "4.0"
cloudflare==2.12.4 ; python_version >= "3.7" and python_version < "4.0"
colorama==0.4.6 ; python_version < "4.0" and sys_platform == "win32" and python_version >= "3.7" or python_version >= "3.7" and python_version < "4.0" and platform_system == "Windows"
configargparse==1.7 ; python_version >= "3.7" and python_version < "4.0"
configobj==5.0.8 ; python_version >= "3.7" and python_version < "4.0"
coverage==7.2.7 ; python_version >= "3.7" and python_version < "4.0"
crashtest==0.3.1 ; python_version >= "3.7" and python_version < "4.0"
cryptography==41.0.4 ; python_version >= "3.7" and python_version < "4.0"
cython==0.29.36 ; python_version >= "3.7" and python_version < "4.0"
decorator==5.1.1 ; python_version >= "3.7" and python_version < "4.0"
deprecated==1.2.14 ; python_version >= "3.7" and python_version < "4.0"
dill==0.3.7 ; python_full_version >= "3.7.2" and python_version < "4.0"
distlib==0.3.7 ; python_version >= "3.7" and python_version < "4.0"
distro==1.8.0 ; python_version >= "3.7" and python_version < "4.0"
dns-lexicon==3.14.1 ; python_version >= "3.7" and python_version < "4.0"
dnspython==2.3.0 ; python_version >= "3.7" and python_version < "4.0"
docutils==0.18.1 ; python_version >= "3.7" and python_version < "4.0"
dulwich==0.20.50 ; python_version >= "3.7" and python_version < "4.0"
exceptiongroup==1.1.3 ; python_version >= "3.7" and python_version < "3.11"
execnet==2.0.2 ; python_version >= "3.7" and python_version < "4.0"
fabric==3.2.2 ; python_version >= "3.7" and python_version < "4.0"
filelock==3.12.2 ; python_version >= "3.7" and python_version < "4.0"
google-api-core==2.12.0 ; python_version >= "3.7" and python_version < "4.0"
google-api-python-client==2.102.0 ; python_version >= "3.7" and python_version < "4.0"
google-auth-httplib2==0.1.1 ; python_version >= "3.7" and python_version < "4.0"
google-auth==2.23.2 ; python_version >= "3.7" and python_version < "4.0"
googleapis-common-protos==1.60.0 ; python_version >= "3.7" and python_version < "4.0"
html5lib==1.1 ; python_version >= "3.7" and python_version < "4.0"
httplib2==0.22.0 ; python_version >= "3.7" and python_version < "4.0"
idna==3.4 ; python_version >= "3.7" and python_version < "4.0"
imagesize==1.4.1 ; python_version >= "3.7" and python_version < "4.0"
importlib-metadata==4.13.0 ; python_version >= "3.7" and python_version < "4.0"
importlib-resources==5.12.0 ; python_version >= "3.7" and python_version < "4.0"
iniconfig==2.0.0 ; python_version >= "3.7" and python_version < "4.0"
invoke==2.2.0 ; python_version >= "3.7" and python_version < "4.0"
ipdb==0.13.13 ; python_version >= "3.7" and python_version < "4.0"
ipython==7.34.0 ; python_version >= "3.7" and python_version < "4.0"
isodate==0.6.1 ; python_version >= "3.7" and python_version < "4.0"
isort==5.11.5 ; python_full_version >= "3.7.2" and python_version < "4.0"
jaraco-classes==3.2.3 ; python_version >= "3.7" and python_version < "4.0"
jedi==0.19.1 ; python_version >= "3.7" and python_version < "4.0"
jeepney==0.8.0 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "linux"
jinja2==3.1.2 ; python_version >= "3.7" and python_version < "4.0"
jmespath==1.0.1 ; python_version >= "3.7" and python_version < "4.0"
josepy==1.13.0 ; python_version >= "3.7" and python_version < "4.0"
jsonlines==3.1.0 ; python_version >= "3.7" and python_version < "4.0"
jsonpickle==3.0.2 ; python_version >= "3.7" and python_version < "4.0"
jsonschema==4.17.3 ; python_version >= "3.7" and python_version < "4.0"
keyring==24.1.1 ; python_version >= "3.7" and python_version < "4.0"
lazy-object-proxy==1.9.0 ; python_full_version >= "3.7.2" and python_version < "4.0"
lockfile==0.12.2 ; python_version >= "3.7" and python_version < "4.0"
markdown-it-py==2.2.0 ; python_version >= "3.7" and python_version < "4.0"
markupsafe==2.1.3 ; python_version >= "3.7" and python_version < "4.0"
matplotlib-inline==0.1.6 ; python_version >= "3.7" and python_version < "4.0"
mccabe==0.7.0 ; python_full_version >= "3.7.2" and python_version < "4.0"
mdurl==0.1.2 ; python_version >= "3.7" and python_version < "4.0"
more-itertools==9.1.0 ; python_version >= "3.7" and python_version < "4.0"
msgpack==1.0.5 ; python_version >= "3.7" and python_version < "4.0"
msrest==0.7.1 ; python_version >= "3.7" and python_version < "4.0"
mypy-extensions==1.0.0 ; python_version >= "3.7" and python_version < "4.0"
mypy==1.4.1 ; python_version >= "3.7" and python_version < "4.0"
oauthlib==3.2.2 ; python_version >= "3.7" and python_version < "4.0"
packaging==23.2 ; python_version >= "3.7" and python_version < "4.0"
paramiko==3.3.1 ; python_version >= "3.7" and python_version < "4.0"
parsedatetime==2.6 ; python_version >= "3.7" and python_version < "4.0"
parso==0.8.3 ; python_version >= "3.7" and python_version < "4.0"
pexpect==4.8.0 ; python_version >= "3.7" and python_version < "4.0"
pickleshare==0.7.5 ; python_version >= "3.7" and python_version < "4.0"
pip==23.2.1 ; python_version >= "3.7" and python_version < "4.0"
pkginfo==1.9.6 ; python_version >= "3.7" and python_version < "4.0"
pkgutil-resolve-name==1.3.10 ; python_version >= "3.7" and python_version < "3.9"
platformdirs==2.6.2 ; python_version < "4.0" and python_version >= "3.7"
pluggy==1.2.0 ; python_version >= "3.7" and python_version < "4.0"
ply==3.11 ; python_version >= "3.7" and python_version < "4.0"
poetry-core==1.3.2 ; python_version >= "3.7" and python_version < "4.0"
poetry-plugin-export==1.2.0 ; python_version >= "3.7" and python_version < "4.0"
poetry==1.2.2 ; python_version >= "3.7" and python_version < "4.0"
prompt-toolkit==3.0.39 ; python_version >= "3.7" and python_version < "4.0"
protobuf==4.24.4 ; python_version >= "3.7" and python_version < "4.0"
ptyprocess==0.7.0 ; python_version >= "3.7" and python_version < "4.0"
py==1.11.0 ; python_version >= "3.7" and python_version < "4.0"
pyasn1-modules==0.3.0 ; python_version >= "3.7" and python_version < "4.0"
pyasn1==0.5.0 ; python_version >= "3.7" and python_version < "4.0"
pycparser==2.21 ; python_version >= "3.7" and python_version < "4.0"
pygments==2.16.1 ; python_version >= "3.7" and python_version < "4.0"
pylev==1.4.0 ; python_version >= "3.7" and python_version < "4.0"
pylint==2.15.5 ; python_full_version >= "3.7.2" and python_version < "4.0"
pynacl==1.5.0 ; python_version >= "3.7" and python_version < "4.0"
pynsist==2.7 ; python_version >= "3.7" and python_version < "4.0"
pyopenssl==23.2.0 ; python_version >= "3.7" and python_version < "4.0"
pyparsing==3.1.1 ; python_version >= "3.7" and python_version < "4.0"
pyrfc3339==1.1 ; python_version >= "3.7" and python_version < "4.0"
pyrsistent==0.19.3 ; python_version >= "3.7" and python_version < "4.0"
pytest-cov==4.1.0 ; python_version >= "3.7" and python_version < "4.0"
pytest-xdist==3.3.1 ; python_version >= "3.7" and python_version < "4.0"
pytest==7.4.2 ; python_version >= "3.7" and python_version < "4.0"
python-augeas==1.1.0 ; python_version >= "3.7" and python_version < "4.0"
python-dateutil==2.8.2 ; python_version >= "3.7" and python_version < "4.0"
python-digitalocean==1.17.0 ; python_version >= "3.7" and python_version < "4.0"
pytz==2023.3.post1 ; python_version >= "3.7" and python_version < "4.0"
pywin32-ctypes==0.2.2 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32"
pywin32==306 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32"
pyyaml==6.0.1 ; python_version >= "3.7" and python_version < "4.0"
readme-renderer==37.3 ; python_version >= "3.7" and python_version < "4.0"
requests-download==0.1.2 ; python_version >= "3.7" and python_version < "4.0"
requests-file==1.5.1 ; python_version >= "3.7" and python_version < "4.0"
requests-oauthlib==1.3.1 ; python_version >= "3.7" and python_version < "4.0"
requests-toolbelt==0.9.1 ; python_version >= "3.7" and python_version < "4.0"
requests==2.31.0 ; python_version >= "3.7" and python_version < "4.0"
rfc3986==2.0.0 ; python_version >= "3.7" and python_version < "4.0"
rich==13.6.0 ; python_version >= "3.7" and python_version < "4.0"
rsa==4.9 ; python_version >= "3.7" and python_version < "4"
s3transfer==0.7.0 ; python_version >= "3.7" and python_version < "4.0"
secretstorage==3.3.3 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "linux"
semantic-version==2.10.0 ; python_version >= "3.7" and python_version < "4.0"
setuptools-rust==1.7.0 ; python_version >= "3.7" and python_version < "4.0"
setuptools==67.4.0 ; python_version >= "3.7" and python_version < "4.0"
shellingham==1.5.3 ; python_version >= "3.7" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.7" and python_version < "4.0"
snowballstemmer==2.2.0 ; python_version >= "3.7" and python_version < "4.0"
soupsieve==2.4.1 ; python_version >= "3.7" and python_version < "4.0"
sphinx-rtd-theme==1.3.0 ; python_version >= "3.7" and python_version < "4.0"
sphinx==5.3.0 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-applehelp==1.0.2 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-devhelp==1.0.2 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-htmlhelp==2.0.0 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-jquery==4.1 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-jsmath==1.0.1 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-qthelp==1.0.3 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-serializinghtml==1.1.5 ; python_version >= "3.7" and python_version < "4.0"
tldextract==3.6.0 ; python_version >= "3.7" and python_version < "4.0"
tomli==2.0.1 ; python_version >= "3.7" and python_full_version <= "3.11.0a6"
tomlkit==0.12.1 ; python_version < "4.0" and python_version >= "3.7"
tox==3.28.0 ; python_version >= "3.7" and python_version < "4.0"
traitlets==5.9.0 ; python_version >= "3.7" and python_version < "4.0"
twine==4.0.2 ; python_version >= "3.7" and python_version < "4.0"
typed-ast==1.5.5 ; python_version < "3.8" and python_version >= "3.7"
types-httplib2==0.22.0.2 ; python_version >= "3.7" and python_version < "4.0"
types-pyopenssl==23.2.0.2 ; python_version >= "3.7" and python_version < "4.0"
types-pyrfc3339==1.1.1.5 ; python_version >= "3.7" and python_version < "4.0"
types-python-dateutil==2.8.19.14 ; python_version >= "3.7" and python_version < "4.0"
types-pytz==2023.3.1.1 ; python_version >= "3.7" and python_version < "4.0"
types-pywin32==306.0.0.4 ; python_version >= "3.7" and python_version < "4.0"
types-requests==2.31.0.2 ; python_version >= "3.7" and python_version < "4.0"
types-setuptools==68.2.0.0 ; python_version >= "3.7" and python_version < "4.0"
types-six==1.16.21.9 ; python_version >= "3.7" and python_version < "4.0"
types-urllib3==1.26.25.14 ; python_version >= "3.7" and python_version < "4.0"
typing-extensions==4.7.1 ; python_version >= "3.7" and python_version < "4.0"
uritemplate==4.1.1 ; python_version >= "3.7" and python_version < "4.0"
urllib3==1.26.17 ; python_version >= "3.7" and python_version < "4.0"
virtualenv==20.21.1 ; python_version >= "3.7" and python_version < "4.0"
wcwidth==0.2.8 ; python_version >= "3.7" and python_version < "4.0"
webencodings==0.5.1 ; python_version >= "3.7" and python_version < "4.0"
wheel==0.41.2 ; python_version >= "3.7" and python_version < "4.0"
wrapt==1.15.0 ; python_version < "4.0" and python_version >= "3.7"
xattr==0.9.9 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "darwin"
yarg==0.1.9 ; python_version >= "3.7" and python_version < "4.0"
zipp==3.15.0 ; python_version >= "3.7" and python_version < "4.0"
alabaster==0.7.13 ; python_version >= "3.8" and python_version < "4.0"
apacheconfig==0.3.2 ; python_version >= "3.8" and python_version < "4.0"
appnope==0.1.3 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "darwin"
astroid==3.0.1 ; python_version >= "3.8" and python_version < "4.0"
asttokens==2.4.1 ; python_version >= "3.8" and python_version < "4.0"
attrs==23.1.0 ; python_version >= "3.8" and python_version < "4.0"
azure-core==1.29.5 ; python_version >= "3.8" and python_version < "4.0"
azure-devops==7.1.0b4 ; python_version >= "3.8" and python_version < "4.0"
babel==2.13.1 ; python_version >= "3.8" and python_version < "4.0"
backcall==0.2.0 ; python_version >= "3.8" and python_version < "4.0"
bcrypt==4.1.1 ; python_version >= "3.8" and python_version < "4.0"
beautifulsoup4==4.12.2 ; python_version >= "3.8" and python_version < "4.0"
boto3==1.33.6 ; python_version >= "3.8" and python_version < "4.0"
botocore==1.33.6 ; python_version >= "3.8" and python_version < "4.0"
cachecontrol==0.12.14 ; python_version >= "3.8" and python_version < "4.0"
cachetools==5.3.2 ; python_version >= "3.8" and python_version < "4.0"
cachy==0.3.0 ; python_version >= "3.8" and python_version < "4.0"
certifi==2023.11.17 ; python_version >= "3.8" and python_version < "4.0"
cffi==1.16.0 ; python_version >= "3.8" and python_version < "4.0"
charset-normalizer==3.3.2 ; python_version >= "3.8" and python_version < "4.0"
cleo==1.0.0a5 ; python_version >= "3.8" and python_version < "4.0"
cloudflare==2.14.2 ; python_version >= "3.8" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.8" and python_version < "4.0" and platform_system == "Windows"
configargparse==1.7 ; python_version >= "3.8" and python_version < "4.0"
configobj==5.0.8 ; python_version >= "3.8" and python_version < "4.0"
coverage==7.3.2 ; python_version >= "3.8" and python_version < "4.0"
crashtest==0.3.1 ; python_version >= "3.8" and python_version < "4.0"
cryptography==41.0.7 ; python_version >= "3.8" and python_version < "4.0"
cython==0.29.36 ; python_version >= "3.8" and python_version < "4.0"
decorator==5.1.1 ; python_version >= "3.8" and python_version < "4.0"
deprecated==1.2.14 ; python_version >= "3.8" and python_version < "4.0"
dill==0.3.7 ; python_version < "4.0" and python_version >= "3.8"
distlib==0.3.7 ; python_version >= "3.8" and python_version < "4.0"
distro==1.8.0 ; python_version >= "3.8" and python_version < "4.0"
dns-lexicon==3.17.0 ; python_version >= "3.8" and python_version < "4.0"
dnspython==2.4.2 ; python_version >= "3.8" and python_version < "4.0"
docutils==0.20.1 ; python_version >= "3.8" and python_version < "4.0"
dulwich==0.20.50 ; python_version >= "3.8" and python_version < "4.0"
exceptiongroup==1.2.0 ; python_version >= "3.8" and python_version < "3.11"
execnet==2.0.2 ; python_version >= "3.8" and python_version < "4.0"
executing==2.0.1 ; python_version >= "3.8" and python_version < "4.0"
fabric==3.2.2 ; python_version >= "3.8" and python_version < "4.0"
filelock==3.13.1 ; python_version >= "3.8" and python_version < "4.0"
google-api-core==2.14.0 ; python_version >= "3.8" and python_version < "4.0"
google-api-python-client==2.109.0 ; python_version >= "3.8" and python_version < "4.0"
google-auth-httplib2==0.1.1 ; python_version >= "3.8" and python_version < "4.0"
google-auth==2.24.0 ; python_version >= "3.8" and python_version < "4.0"
googleapis-common-protos==1.61.0 ; python_version >= "3.8" and python_version < "4.0"
html5lib==1.1 ; python_version >= "3.8" and python_version < "4.0"
httplib2==0.22.0 ; python_version >= "3.8" and python_version < "4.0"
idna==3.6 ; python_version >= "3.8" and python_version < "4.0"
imagesize==1.4.1 ; python_version >= "3.8" and python_version < "4.0"
importlib-metadata==4.13.0 ; python_version >= "3.8" and python_version < "4.0"
importlib-resources==6.1.1 ; python_version >= "3.8" and python_version < "4.0"
iniconfig==2.0.0 ; python_version >= "3.8" and python_version < "4.0"
invoke==2.2.0 ; python_version >= "3.8" and python_version < "4.0"
ipdb==0.13.13 ; python_version >= "3.8" and python_version < "4.0"
ipython==8.12.3 ; python_version >= "3.8" and python_version < "4.0"
isodate==0.6.1 ; python_version >= "3.8" and python_version < "4.0"
isort==5.12.0 ; python_version >= "3.8" and python_version < "4.0"
jaraco-classes==3.3.0 ; python_version >= "3.8" and python_version < "4.0"
jedi==0.19.1 ; python_version >= "3.8" and python_version < "4.0"
jeepney==0.8.0 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "linux"
jinja2==3.1.2 ; python_version >= "3.8" and python_version < "4.0"
jmespath==1.0.1 ; python_version >= "3.8" and python_version < "4.0"
josepy==1.14.0 ; python_version >= "3.8" and python_version < "4.0"
jsonlines==4.0.0 ; python_version >= "3.8" and python_version < "4.0"
jsonpickle==3.0.2 ; python_version >= "3.8" and python_version < "4.0"
jsonschema-specifications==2023.11.2 ; python_version >= "3.8" and python_version < "4.0"
jsonschema==4.20.0 ; python_version >= "3.8" and python_version < "4.0"
keyring==24.3.0 ; python_version >= "3.8" and python_version < "4.0"
lockfile==0.12.2 ; python_version >= "3.8" and python_version < "4.0"
markdown-it-py==3.0.0 ; python_version >= "3.8" and python_version < "4.0"
markupsafe==2.1.3 ; python_version >= "3.8" and python_version < "4.0"
matplotlib-inline==0.1.6 ; python_version >= "3.8" and python_version < "4.0"
mccabe==0.7.0 ; python_version >= "3.8" and python_version < "4.0"
mdurl==0.1.2 ; python_version >= "3.8" and python_version < "4.0"
more-itertools==10.1.0 ; python_version >= "3.8" and python_version < "4.0"
msgpack==1.0.7 ; python_version >= "3.8" and python_version < "4.0"
msrest==0.7.1 ; python_version >= "3.8" and python_version < "4.0"
mypy-extensions==1.0.0 ; python_version >= "3.8" and python_version < "4.0"
mypy==1.7.1 ; python_version >= "3.8" and python_version < "4.0"
nh3==0.2.14 ; python_version >= "3.8" and python_version < "4.0"
oauthlib==3.2.2 ; python_version >= "3.8" and python_version < "4.0"
packaging==23.2 ; python_version >= "3.8" and python_version < "4.0"
paramiko==3.3.1 ; python_version >= "3.8" and python_version < "4.0"
parsedatetime==2.6 ; python_version >= "3.8" and python_version < "4.0"
parso==0.8.3 ; python_version >= "3.8" and python_version < "4.0"
pexpect==4.9.0 ; python_version >= "3.8" and python_version < "4.0"
pickleshare==0.7.5 ; python_version >= "3.8" and python_version < "4.0"
pip==23.3.1 ; python_version >= "3.8" and python_version < "4.0"
pkginfo==1.9.6 ; python_version >= "3.8" and python_version < "4.0"
pkgutil-resolve-name==1.3.10 ; python_version >= "3.8" and python_version < "3.9"
platformdirs==2.6.2 ; python_version >= "3.8" and python_version < "4.0"
pluggy==1.3.0 ; python_version >= "3.8" and python_version < "4.0"
ply==3.11 ; python_version >= "3.8" and python_version < "4.0"
poetry-core==1.3.2 ; python_version >= "3.8" and python_version < "4.0"
poetry-plugin-export==1.2.0 ; python_version >= "3.8" and python_version < "4.0"
poetry==1.2.2 ; python_version >= "3.8" and python_version < "4.0"
prompt-toolkit==3.0.41 ; python_version >= "3.8" and python_version < "4.0"
protobuf==4.25.1 ; python_version >= "3.8" and python_version < "4.0"
ptyprocess==0.7.0 ; python_version >= "3.8" and python_version < "4.0"
pure-eval==0.2.2 ; python_version >= "3.8" and python_version < "4.0"
py==1.11.0 ; python_version >= "3.8" and python_version < "4.0"
pyasn1-modules==0.3.0 ; python_version >= "3.8" and python_version < "4.0"
pyasn1==0.5.1 ; python_version >= "3.8" and python_version < "4.0"
pycparser==2.21 ; python_version >= "3.8" and python_version < "4.0"
pygments==2.17.2 ; python_version >= "3.8" and python_version < "4.0"
pylev==1.4.0 ; python_version >= "3.8" and python_version < "4.0"
pylint==3.0.2 ; python_version >= "3.8" and python_version < "4.0"
pynacl==1.5.0 ; python_version >= "3.8" and python_version < "4.0"
pynsist==2.7 ; python_version >= "3.8" and python_version < "4.0"
pyopenssl==23.3.0 ; python_version >= "3.8" and python_version < "4.0"
pyotp==2.9.0 ; python_version >= "3.8" and python_version < "4.0"
pyparsing==3.1.1 ; python_version >= "3.8" and python_version < "4.0"
pyrfc3339==1.1 ; python_version >= "3.8" and python_version < "4.0"
pytest-cov==4.1.0 ; python_version >= "3.8" and python_version < "4.0"
pytest-xdist==3.5.0 ; python_version >= "3.8" and python_version < "4.0"
pytest==7.4.3 ; python_version >= "3.8" and python_version < "4.0"
python-augeas==1.1.0 ; python_version >= "3.8" and python_version < "4.0"
python-dateutil==2.8.2 ; python_version >= "3.8" and python_version < "4.0"
python-digitalocean==1.17.0 ; python_version >= "3.8" and python_version < "4.0"
pytz==2023.3.post1 ; python_version >= "3.8" and python_version < "4.0"
pywin32-ctypes==0.2.2 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "win32"
pywin32==306 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "win32"
pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "4.0"
readme-renderer==42.0 ; python_version >= "3.8" and python_version < "4.0"
referencing==0.31.1 ; python_version >= "3.8" and python_version < "4.0"
requests-download==0.1.2 ; python_version >= "3.8" and python_version < "4.0"
requests-file==1.5.1 ; python_version >= "3.8" and python_version < "4.0"
requests-oauthlib==1.3.1 ; python_version >= "3.8" and python_version < "4.0"
requests-toolbelt==0.9.1 ; python_version >= "3.8" and python_version < "4.0"
requests==2.31.0 ; python_version >= "3.8" and python_version < "4.0"
rfc3986==2.0.0 ; python_version >= "3.8" and python_version < "4.0"
rich==13.7.0 ; python_version >= "3.8" and python_version < "4.0"
rpds-py==0.13.2 ; python_version >= "3.8" and python_version < "4.0"
rsa==4.9 ; python_version >= "3.8" and python_version < "4"
s3transfer==0.8.2 ; python_version >= "3.8" and python_version < "4.0"
secretstorage==3.3.3 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "linux"
semantic-version==2.10.0 ; python_version >= "3.8" and python_version < "4.0"
setuptools-rust==1.8.1 ; python_version >= "3.8" and python_version < "4.0"
setuptools==69.0.2 ; python_version < "4.0" and python_version >= "3.8"
shellingham==1.5.4 ; python_version >= "3.8" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.8" and python_version < "4.0"
snowballstemmer==2.2.0 ; python_version >= "3.8" and python_version < "4.0"
soupsieve==2.5 ; python_version >= "3.8" and python_version < "4.0"
sphinx-rtd-theme==2.0.0 ; python_version >= "3.8" and python_version < "4.0"
sphinx==7.1.2 ; python_version >= "3.8" and python_version < "4.0"
sphinxcontrib-applehelp==1.0.4 ; python_version >= "3.8" and python_version < "4.0"
sphinxcontrib-devhelp==1.0.2 ; python_version >= "3.8" and python_version < "4.0"
sphinxcontrib-htmlhelp==2.0.1 ; python_version >= "3.8" and python_version < "4.0"
sphinxcontrib-jquery==4.1 ; python_version >= "3.8" and python_version < "4.0"
sphinxcontrib-jsmath==1.0.1 ; python_version >= "3.8" and python_version < "4.0"
sphinxcontrib-qthelp==1.0.3 ; python_version >= "3.8" and python_version < "4.0"
sphinxcontrib-serializinghtml==1.1.5 ; python_version >= "3.8" and python_version < "4.0"
stack-data==0.6.3 ; python_version >= "3.8" and python_version < "4.0"
tldextract==5.1.1 ; python_version >= "3.8" and python_version < "4.0"
tomli==2.0.1 ; python_version >= "3.8" and python_full_version <= "3.11.0a6"
tomlkit==0.12.3 ; python_version >= "3.8" and python_version < "4.0"
tox==3.28.0 ; python_version >= "3.8" and python_version < "4.0"
traitlets==5.14.0 ; python_version >= "3.8" and python_version < "4.0"
twine==4.0.2 ; python_version >= "3.8" and python_version < "4.0"
types-httplib2==0.22.0.2 ; python_version >= "3.8" and python_version < "4.0"
types-pyopenssl==23.3.0.0 ; python_version >= "3.8" and python_version < "4.0"
types-pyrfc3339==1.1.1.5 ; python_version >= "3.8" and python_version < "4.0"
types-python-dateutil==2.8.19.14 ; python_version >= "3.8" and python_version < "4.0"
types-pytz==2023.3.1.1 ; python_version >= "3.8" and python_version < "4.0"
types-pywin32==306.0.0.6 ; python_version >= "3.8" and python_version < "4.0"
types-requests==2.31.0.6 ; python_version >= "3.8" and python_version < "4.0"
types-setuptools==69.0.0.0 ; python_version >= "3.8" and python_version < "4.0"
types-six==1.16.21.9 ; python_version >= "3.8" and python_version < "4.0"
types-urllib3==1.26.25.14 ; python_version >= "3.8" and python_version < "4.0"
typing-extensions==4.8.0 ; python_version >= "3.8" and python_version < "4.0"
uritemplate==4.1.1 ; python_version >= "3.8" and python_version < "4.0"
urllib3==1.26.18 ; python_version < "4.0" and python_version >= "3.8"
virtualenv==20.21.1 ; python_version >= "3.8" and python_version < "4.0"
wcwidth==0.2.12 ; python_version >= "3.8" and python_version < "4.0"
webencodings==0.5.1 ; python_version >= "3.8" and python_version < "4.0"
wheel==0.42.0 ; python_version >= "3.8" and python_version < "4.0"
wrapt==1.16.0 ; python_version >= "3.8" and python_version < "4.0"
xattr==0.9.9 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "darwin"
yarg==0.1.9 ; python_version >= "3.8" and python_version < "4.0"
zipp==3.17.0 ; python_version >= "3.8" and python_version < "4.0"

View File

@@ -117,7 +117,7 @@ def _check_version(version_str):
version = (int(search.group(1)), int(search.group(2)))
if version >= (3, 7):
if version >= (3, 8):
return True
print('Incompatible python version for Certbot found: {0}'.format(version_str))

View File

@@ -56,10 +56,10 @@ commands =
commands =
{[testenv:py-win]commands} certbot-apache
[testenv:py3{,7,8,9,10,11}]
[testenv:py3{,8,9,10,11}]
commands = {[testenv:py]commands}
[testenv:py3.{7,8,9,10,11}]
[testenv:py3.{8,9,10,11}]
commands = {[testenv:py]commands}
[testenv:oldest]
@@ -69,7 +69,7 @@ commands = {[testenv:py]commands}
#
# This version should be kept in sync with the one declared in
# tools/pinning/oldest/pyproject.toml.
basepython = python3.7
basepython = python3.8
setenv = CERTBOT_OLDEST=1
commands = {[testenv:py]commands}

View File

@@ -11,14 +11,13 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.7',
python_requires='>=3.8',
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',