mirror of
https://github.com/certbot/certbot.git
synced 2026-05-28 04:34:11 -04:00
Merge branch 'master' of https://github.com/certbot/certbot into docs/yaml-config
This commit is contained in:
commit
4178e8ffc4
31 changed files with 485 additions and 357 deletions
|
|
@ -4,20 +4,25 @@
|
|||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
import josepy as jose
|
||||
from josepy.util import ComparableECKey
|
||||
from OpenSSL import crypto
|
||||
import pkg_resources
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
|
||||
def load_vector(*names):
|
||||
"""Load contents of a test vector."""
|
||||
# luckily, resource_string opens file in binary mode
|
||||
return pkg_resources.resource_string(
|
||||
__name__, os.path.join('testdata', *names))
|
||||
vector_ref = importlib_resources.files(__package__).joinpath('testdata', *names)
|
||||
return vector_ref.read_bytes()
|
||||
|
||||
|
||||
def _guess_loader(filename, loader_pem, loader_der):
|
||||
|
|
|
|||
|
|
@ -22,6 +22,15 @@ docs_extras = [
|
|||
]
|
||||
|
||||
test_extras = [
|
||||
# In theory we could scope importlib_resources to env marker 'python_version<"3.9"'. But this
|
||||
# makes the pinning mechanism emit warnings when running `poetry lock` because in the corner
|
||||
# case of an extra dependency with env marker coming from a setup.py file, it generate the
|
||||
# invalid requirement 'importlib_resource>=1.3.1;python<=3.9;extra=="test"'.
|
||||
# To fix the issue, we do not pass the env marker. This is fine because:
|
||||
# - importlib_resources can be applied to any Python version,
|
||||
# - this is a "test" extra dependency for limited audience,
|
||||
# - it does not change anything at the end for the generated requirement files.
|
||||
'importlib_resources>=1.3.1',
|
||||
'pytest',
|
||||
'pytest-xdist',
|
||||
'typing-extensions',
|
||||
|
|
|
|||
|
|
@ -1,21 +1,28 @@
|
|||
""" Utility functions for certbot-apache plugin """
|
||||
import atexit
|
||||
import binascii
|
||||
import fnmatch
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from contextlib import ExitStack
|
||||
from typing import Dict
|
||||
from typing import Iterable
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from certbot import errors
|
||||
from certbot import util
|
||||
from certbot.compat import os
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -248,6 +255,8 @@ def find_ssl_apache_conf(prefix: str) -> str:
|
|||
:return: the path the TLS Apache config file
|
||||
:rtype: str
|
||||
"""
|
||||
return pkg_resources.resource_filename(
|
||||
"certbot_apache",
|
||||
os.path.join("_internal", "tls_configs", "{0}-options-ssl-apache.conf".format(prefix)))
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
ref = importlib_resources.files("certbot_apache").joinpath(
|
||||
"_internal", "tls_configs", "{0}-options-ssl-apache.conf".format(prefix))
|
||||
return str(file_manager.enter_context(importlib_resources.as_file(ref)))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
"""Apache plugin constants."""
|
||||
import atexit
|
||||
import sys
|
||||
from contextlib import ExitStack
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from certbot.compat import os
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
MOD_SSL_CONF_DEST = "options-ssl-apache.conf"
|
||||
"""Name of the mod_ssl config file as saved
|
||||
|
|
@ -37,8 +41,15 @@ ALL_SSL_OPTIONS_HASHES: List[str] = [
|
|||
]
|
||||
"""SHA256 hashes of the contents of previous versions of all versions of MOD_SSL_CONF_SRC"""
|
||||
|
||||
AUGEAS_LENS_DIR = pkg_resources.resource_filename(
|
||||
"certbot_apache", os.path.join("_internal", "augeas_lens"))
|
||||
def _generate_augeas_lens_dir_static() -> str:
|
||||
# This code ensures that the resource is accessible as file for the lifetime of current
|
||||
# Python process, and will be automatically cleaned up on exit.
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
augeas_lens_dir_ref = importlib_resources.files("certbot_apache") / "_internal" / "augeas_lens"
|
||||
return str(file_manager.enter_context(importlib_resources.as_file(augeas_lens_dir_ref)))
|
||||
|
||||
AUGEAS_LENS_DIR = _generate_augeas_lens_dir_static()
|
||||
"""Path to the Augeas lens directory"""
|
||||
|
||||
REWRITE_HTTPS_ARGS: List[str] = [
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import shutil
|
|||
import socket
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
|
@ -1659,20 +1658,23 @@ class InstallSslOptionsConfTest(util.ApacheTest):
|
|||
file has been manually edited by the user, and will refuse to update it.
|
||||
This test ensures that all necessary hashes are present.
|
||||
"""
|
||||
import pkg_resources
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
from certbot_apache._internal.constants import ALL_SSL_OPTIONS_HASHES
|
||||
|
||||
tls_configs_dir = pkg_resources.resource_filename(
|
||||
"certbot_apache", os.path.join("_internal", "tls_configs"))
|
||||
all_files = [os.path.join(tls_configs_dir, name) for name in os.listdir(tls_configs_dir)
|
||||
if name.endswith('options-ssl-apache.conf')]
|
||||
assert len(all_files) >= 1
|
||||
for one_file in all_files:
|
||||
file_hash = crypto_util.sha256sum(one_file)
|
||||
assert file_hash in ALL_SSL_OPTIONS_HASHES, \
|
||||
f"Constants.ALL_SSL_OPTIONS_HASHES must be appended with the sha256 " \
|
||||
f"hash of {one_file} when it is updated."
|
||||
ref = importlib_resources.files("certbot_apache") / "_internal" / "tls_configs"
|
||||
with importlib_resources.as_file(ref) as tls_configs_dir:
|
||||
all_files = [os.path.join(tls_configs_dir, name) for name in os.listdir(tls_configs_dir)
|
||||
if name.endswith('options-ssl-apache.conf')]
|
||||
assert len(all_files) >= 1
|
||||
for one_file in all_files:
|
||||
file_hash = crypto_util.sha256sum(one_file)
|
||||
assert file_hash in ALL_SSL_OPTIONS_HASHES, \
|
||||
f"Constants.ALL_SSL_OPTIONS_HASHES must be appended with the sha256 " \
|
||||
f"hash of {one_file} when it is updated."
|
||||
|
||||
def test_openssl_version(self):
|
||||
self.config._openssl_version = None
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ApacheTest(unittest.TestCase):
|
|||
# pylint: disable=arguments-differ
|
||||
self.temp_dir, self.config_dir, self.work_dir = common.dir_setup(
|
||||
test_dir=test_dir,
|
||||
pkg=__name__)
|
||||
pkg=__package__)
|
||||
|
||||
self.config_path = os.path.join(self.temp_dir, config_root)
|
||||
self.vhost_path = os.path.join(self.temp_dir, vhost_root)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ install_requires = [
|
|||
# https://github.com/certbot/certbot/issues/8761 for more info.
|
||||
f'acme>={version}',
|
||||
f'certbot>={version}',
|
||||
'importlib_resources>=1.3.1; python_version < "3.9"',
|
||||
'python-augeas',
|
||||
'setuptools>=41.6.0',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""General purpose nginx test configuration generator."""
|
||||
import atexit
|
||||
import getpass
|
||||
import sys
|
||||
from contextlib import ExitStack
|
||||
from typing import Optional
|
||||
|
||||
import pkg_resources
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
|
||||
def construct_nginx_config(nginx_root: str, nginx_webroot: str, http_port: int, https_port: int,
|
||||
|
|
@ -23,10 +29,18 @@ def construct_nginx_config(nginx_root: str, nginx_webroot: str, http_port: int,
|
|||
:return: a string containing the full nginx configuration
|
||||
:rtype: str
|
||||
"""
|
||||
key_path = key_path if key_path \
|
||||
else pkg_resources.resource_filename('certbot_integration_tests', 'assets/key.pem')
|
||||
cert_path = cert_path if cert_path \
|
||||
else pkg_resources.resource_filename('certbot_integration_tests', 'assets/cert.pem')
|
||||
if not key_path:
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
ref = importlib_resources.files('certbot_integration_tests').joinpath('assets', 'key.pem')
|
||||
key_path = str(file_manager.enter_context(importlib_resources.as_file(ref)))
|
||||
|
||||
if not cert_path:
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
ref = importlib_resources.files('certbot_integration_tests').joinpath('assets', 'cert.pem')
|
||||
cert_path = str(file_manager.enter_context(importlib_resources.as_file(ref)))
|
||||
|
||||
return '''\
|
||||
# This error log will be written regardless of server scope error_log
|
||||
# definitions, so we have to set this here in the main scope.
|
||||
|
|
|
|||
|
|
@ -1,17 +1,21 @@
|
|||
"""Module to handle the context of RFC2136 integration tests."""
|
||||
|
||||
from contextlib import contextmanager
|
||||
import sys
|
||||
import tempfile
|
||||
from typing import Generator
|
||||
from typing import Iterable
|
||||
from typing import Tuple
|
||||
|
||||
from pkg_resources import resource_filename
|
||||
import pytest
|
||||
|
||||
from certbot_integration_tests.certbot_tests import context as certbot_context
|
||||
from certbot_integration_tests.utils import certbot_call
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
|
||||
class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
|
||||
"""Integration test context for certbot-dns-rfc2136"""
|
||||
|
|
@ -44,15 +48,15 @@ class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
|
|||
:yields: Path to credentials file
|
||||
:rtype: str
|
||||
"""
|
||||
src_file = resource_filename('certbot_integration_tests',
|
||||
'assets/bind-config/rfc2136-credentials-{}.ini.tpl'
|
||||
.format(label))
|
||||
|
||||
with open(src_file, 'r') as f:
|
||||
contents = f.read().format(
|
||||
server_address=self._dns_xdist['address'],
|
||||
server_port=self._dns_xdist['port']
|
||||
)
|
||||
src_ref_file = importlib_resources.files('certbot_integration_tests').joinpath(
|
||||
'assets', 'bind-config', f'rfc2136-credentials-{label}.ini.tpl'
|
||||
)
|
||||
with importlib_resources.as_file(src_ref_file) as src_file:
|
||||
with open(src_file, 'r') as f:
|
||||
contents = f.read().format(
|
||||
server_address=self._dns_xdist['address'],
|
||||
server_port=self._dns_xdist['port']
|
||||
)
|
||||
|
||||
with tempfile.NamedTemporaryFile('w+', prefix='rfc2136-creds-{}'.format(label),
|
||||
suffix='.ini', dir=self.workspace) as fp:
|
||||
|
|
|
|||
|
|
@ -6,11 +6,8 @@ import subprocess
|
|||
import sys
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Mapping
|
||||
from typing import Tuple
|
||||
|
||||
import pkg_resources
|
||||
|
||||
import certbot_integration_tests
|
||||
# pylint: disable=wildcard-import,unused-wildcard-import
|
||||
from certbot_integration_tests.utils.constants import *
|
||||
|
|
@ -84,29 +81,14 @@ def _prepare_environ(workspace: str) -> Dict[str, str]:
|
|||
return new_environ
|
||||
|
||||
|
||||
def _compute_additional_args(workspace: str, environ: Mapping[str, str],
|
||||
force_renew: bool) -> List[str]:
|
||||
additional_args = []
|
||||
output = subprocess.check_output(['certbot', '--version'],
|
||||
universal_newlines=True, stderr=subprocess.STDOUT,
|
||||
cwd=workspace, env=environ)
|
||||
# Typical response is: output = 'certbot 0.31.0.dev0'
|
||||
version_str = output.split(' ')[1].strip()
|
||||
if pkg_resources.parse_version(version_str) >= pkg_resources.parse_version('0.30.0'):
|
||||
additional_args.append('--no-random-sleep-on-renew')
|
||||
|
||||
if force_renew:
|
||||
additional_args.append('--renew-by-default')
|
||||
|
||||
return additional_args
|
||||
|
||||
|
||||
def _prepare_args_env(certbot_args: List[str], directory_url: str, http_01_port: int,
|
||||
tls_alpn_01_port: int, config_dir: str, workspace: str,
|
||||
force_renew: bool) -> Tuple[List[str], Dict[str, str]]:
|
||||
|
||||
new_environ = _prepare_environ(workspace)
|
||||
additional_args = _compute_additional_args(workspace, new_environ, force_renew)
|
||||
additional_args = ['--no-random-sleep-on-renew']
|
||||
if force_renew:
|
||||
additional_args.append('--renew-by-default')
|
||||
|
||||
command = [
|
||||
'certbot',
|
||||
|
|
|
|||
|
|
@ -15,10 +15,13 @@ from typing import List
|
|||
from typing import Optional
|
||||
from typing import Type
|
||||
|
||||
from pkg_resources import resource_filename
|
||||
|
||||
from certbot_integration_tests.utils import constants
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
BIND_DOCKER_IMAGE = "internetsystemsconsortium/bind9:9.16"
|
||||
BIND_BIND_ADDRESS = ("127.0.0.1", 45953)
|
||||
|
||||
|
|
@ -80,13 +83,12 @@ class DNSServer:
|
|||
|
||||
def _configure_bind(self) -> None:
|
||||
"""Configure the BIND9 server based on the prebaked configuration"""
|
||||
bind_conf_src = resource_filename(
|
||||
"certbot_integration_tests", "assets/bind-config"
|
||||
)
|
||||
for directory in ("conf", "zones"):
|
||||
shutil.copytree(
|
||||
os.path.join(bind_conf_src, directory), os.path.join(self.bind_root, directory)
|
||||
)
|
||||
ref = importlib_resources.files("certbot_integration_tests") / "assets" / "bind-config"
|
||||
with importlib_resources.as_file(ref) as path:
|
||||
for directory in ("conf", "zones"):
|
||||
shutil.copytree(
|
||||
os.path.join(path, directory), os.path.join(self.bind_root, directory)
|
||||
)
|
||||
|
||||
def _start_bind(self) -> None:
|
||||
"""Launch the BIND9 server as a Docker container"""
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
Misc module contains stateless functions that could be used during pytest execution,
|
||||
or outside during setup/teardown of the integration tests environment.
|
||||
"""
|
||||
import atexit
|
||||
import contextlib
|
||||
import errno
|
||||
import functools
|
||||
|
|
@ -30,13 +31,17 @@ from cryptography.hazmat.primitives.serialization import PrivateFormat
|
|||
from cryptography.x509 import Certificate
|
||||
from cryptography.x509 import load_pem_x509_certificate
|
||||
from OpenSSL import crypto
|
||||
import pkg_resources
|
||||
import requests
|
||||
|
||||
from certbot_integration_tests.certbot_tests.context import IntegrationTestsContext
|
||||
from certbot_integration_tests.utils.constants import PEBBLE_ALTERNATE_ROOTS
|
||||
from certbot_integration_tests.utils.constants import PEBBLE_MANAGEMENT_URL
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
RSA_KEY_TYPE = 'rsa'
|
||||
ECDSA_KEY_TYPE = 'ecdsa'
|
||||
|
||||
|
|
@ -125,7 +130,11 @@ def generate_test_file_hooks(config_dir: str, hook_probe: str) -> None:
|
|||
:param str config_dir: current certbot config directory
|
||||
:param str hook_probe: path to the hook probe to test hook scripts execution
|
||||
"""
|
||||
hook_path = pkg_resources.resource_filename('certbot_integration_tests', 'assets/hook.py')
|
||||
file_manager = contextlib.ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
hook_path_ref = importlib_resources.files('certbot_integration_tests').joinpath(
|
||||
'assets', 'hook.py')
|
||||
hook_path = str(file_manager.enter_context(importlib_resources.as_file(hook_path_ref)))
|
||||
|
||||
for hook_dir in list_renewal_hooks_dirs(config_dir):
|
||||
# We want an equivalent of bash `chmod -p $HOOK_DIR, that does not fail if one folder of
|
||||
|
|
@ -260,9 +269,12 @@ def load_sample_data_path(workspace: str) -> str:
|
|||
:returns: the path to the loaded sample data directory
|
||||
:rtype: str
|
||||
"""
|
||||
original = pkg_resources.resource_filename('certbot_integration_tests', 'assets/sample-config')
|
||||
copied = os.path.join(workspace, 'sample-config')
|
||||
shutil.copytree(original, copied, symlinks=True)
|
||||
original_ref = importlib_resources.files('certbot_integration_tests').joinpath(
|
||||
'assets', 'sample-config'
|
||||
)
|
||||
with importlib_resources.as_file(original_ref) as original:
|
||||
copied = os.path.join(workspace, 'sample-config')
|
||||
shutil.copytree(original, copied, symlinks=True)
|
||||
|
||||
if os.name == 'nt':
|
||||
# Fix the symlinks on Windows if GIT is not configured to create them upon checkout
|
||||
|
|
|
|||
|
|
@ -1,33 +1,43 @@
|
|||
# pylint: disable=missing-module-docstring
|
||||
|
||||
import atexit
|
||||
import json
|
||||
import os
|
||||
import stat
|
||||
import sys
|
||||
from contextlib import ExitStack
|
||||
from typing import Tuple
|
||||
|
||||
import pkg_resources
|
||||
import requests
|
||||
|
||||
from certbot_integration_tests.utils.constants import DEFAULT_HTTP_01_PORT
|
||||
from certbot_integration_tests.utils.constants import MOCK_OCSP_SERVER_PORT
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
PEBBLE_VERSION = 'v2.3.1'
|
||||
ASSETS_PATH = pkg_resources.resource_filename('certbot_integration_tests', 'assets')
|
||||
|
||||
|
||||
def fetch(workspace: str, http_01_port: int = DEFAULT_HTTP_01_PORT) -> Tuple[str, str, str]:
|
||||
# pylint: disable=missing-function-docstring
|
||||
suffix = 'linux-amd64' if os.name != 'nt' else 'windows-amd64.exe'
|
||||
|
||||
pebble_path = _fetch_asset('pebble', suffix)
|
||||
challtestsrv_path = _fetch_asset('pebble-challtestsrv', suffix)
|
||||
pebble_config_path = _build_pebble_config(workspace, http_01_port)
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
pebble_path_ref = importlib_resources.files('certbot_integration_tests') / 'assets'
|
||||
assets_path = str(file_manager.enter_context(importlib_resources.as_file(pebble_path_ref)))
|
||||
|
||||
pebble_path = _fetch_asset('pebble', suffix, assets_path)
|
||||
challtestsrv_path = _fetch_asset('pebble-challtestsrv', suffix, assets_path)
|
||||
pebble_config_path = _build_pebble_config(workspace, http_01_port, assets_path)
|
||||
|
||||
return pebble_path, challtestsrv_path, pebble_config_path
|
||||
|
||||
|
||||
def _fetch_asset(asset: str, suffix: str) -> str:
|
||||
asset_path = os.path.join(ASSETS_PATH, '{0}_{1}_{2}'.format(asset, PEBBLE_VERSION, suffix))
|
||||
def _fetch_asset(asset: str, suffix: str, assets_path: str) -> str:
|
||||
asset_path = os.path.join(assets_path, '{0}_{1}_{2}'.format(asset, PEBBLE_VERSION, suffix))
|
||||
if not os.path.exists(asset_path):
|
||||
asset_url = ('https://github.com/letsencrypt/pebble/releases/download/{0}/{1}_{2}'
|
||||
.format(PEBBLE_VERSION, asset, suffix))
|
||||
|
|
@ -40,15 +50,15 @@ def _fetch_asset(asset: str, suffix: str) -> str:
|
|||
return asset_path
|
||||
|
||||
|
||||
def _build_pebble_config(workspace: str, http_01_port: int) -> str:
|
||||
def _build_pebble_config(workspace: str, http_01_port: int, assets_path: str) -> str:
|
||||
config_path = os.path.join(workspace, 'pebble-config.json')
|
||||
with open(config_path, 'w') as file_h:
|
||||
file_h.write(json.dumps({
|
||||
'pebble': {
|
||||
'listenAddress': '0.0.0.0:14000',
|
||||
'managementListenAddress': '0.0.0.0:15000',
|
||||
'certificate': os.path.join(ASSETS_PATH, 'cert.pem'),
|
||||
'privateKey': os.path.join(ASSETS_PATH, 'key.pem'),
|
||||
'certificate': os.path.join(assets_path, 'cert.pem'),
|
||||
'privateKey': os.path.join(assets_path, 'key.pem'),
|
||||
'httpPort': http_01_port,
|
||||
'tlsPort': 5001,
|
||||
'ocspResponderURL': 'http://127.0.0.1:{0}'.format(MOCK_OCSP_SERVER_PORT),
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
from pkg_resources import parse_version
|
||||
from setuptools import __version__ as setuptools_version
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '0.32.0.dev0'
|
||||
|
||||
# setuptools 36.2+ is needed for support for environment markers
|
||||
min_setuptools_version='36.2'
|
||||
# This conditional isn't necessary, but it provides better error messages to
|
||||
# people who try to install this package with older versions of setuptools.
|
||||
if parse_version(setuptools_version) < parse_version(min_setuptools_version):
|
||||
raise RuntimeError(f'setuptools {min_setuptools_version}+ is required')
|
||||
|
||||
install_requires = [
|
||||
'coverage',
|
||||
'cryptography',
|
||||
'importlib_resources>=1.3.1; python_version < "3.9"',
|
||||
'pyopenssl',
|
||||
'pytest',
|
||||
'pytest-cov',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
# pylint: disable=too-many-lines
|
||||
"""Nginx Configuration"""
|
||||
import atexit
|
||||
from contextlib import ExitStack
|
||||
import logging
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
from typing import Any
|
||||
|
|
@ -21,7 +24,6 @@ from typing import Type
|
|||
from typing import Union
|
||||
|
||||
import OpenSSL
|
||||
import pkg_resources
|
||||
|
||||
from acme import challenges
|
||||
from acme import crypto_util as acme_crypto_util
|
||||
|
|
@ -39,6 +41,11 @@ from certbot_nginx._internal import nginxparser
|
|||
from certbot_nginx._internal import obj
|
||||
from certbot_nginx._internal import parser
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
NAME_RANK = 0
|
||||
START_WILDCARD_RANK = 1
|
||||
END_WILDCARD_RANK = 2
|
||||
|
|
@ -163,8 +170,12 @@ class NginxConfigurator(common.Configurator):
|
|||
else:
|
||||
config_filename = "options-ssl-nginx-old.conf"
|
||||
|
||||
return pkg_resources.resource_filename(
|
||||
"certbot_nginx", os.path.join("_internal", "tls_configs", config_filename))
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
ref = importlib_resources.files("certbot_nginx").joinpath(
|
||||
"_internal", "tls_configs", config_filename)
|
||||
|
||||
return str(file_manager.enter_context(importlib_resources.as_file(ref)))
|
||||
|
||||
@property
|
||||
def mod_ssl_conf(self) -> str:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
"""Test for certbot_nginx._internal.configurator."""
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import OpenSSL
|
||||
|
|
@ -1075,22 +1074,21 @@ class InstallSslOptionsConfTest(util.NginxTest):
|
|||
file has been manually edited by the user, and will refuse to update it.
|
||||
This test ensures that all necessary hashes are present.
|
||||
"""
|
||||
import pkg_resources
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
from certbot_nginx._internal.constants import ALL_SSL_OPTIONS_HASHES
|
||||
all_files = [
|
||||
pkg_resources.resource_filename("certbot_nginx",
|
||||
os.path.join("_internal", "tls_configs", x))
|
||||
for x in ("options-ssl-nginx.conf",
|
||||
"options-ssl-nginx-old.conf",
|
||||
"options-ssl-nginx-tls12-only.conf")
|
||||
]
|
||||
assert all_files
|
||||
for one_file in all_files:
|
||||
file_hash = crypto_util.sha256sum(one_file)
|
||||
assert file_hash in ALL_SSL_OPTIONS_HASHES, \
|
||||
f"Constants.ALL_SSL_OPTIONS_HASHES must be appended with the sha256 " \
|
||||
f"hash of {one_file} when it is updated."
|
||||
|
||||
tls_configs_ref = importlib_resources.files("certbot_nginx").joinpath(
|
||||
"_internal", "tls_configs")
|
||||
with importlib_resources.as_file(tls_configs_ref) as tls_configs_dir:
|
||||
for tls_config_file in os.listdir(tls_configs_dir):
|
||||
file_hash = crypto_util.sha256sum(os.path.join(tls_configs_dir, tls_config_file))
|
||||
assert file_hash in ALL_SSL_OPTIONS_HASHES, \
|
||||
f"Constants.ALL_SSL_OPTIONS_HASHES must be appended with the sha256 " \
|
||||
f"hash of {tls_config_file} when it is updated."
|
||||
|
||||
def test_nginx_version_uses_correct_config(self):
|
||||
self.config.version = (1, 5, 8)
|
||||
|
|
|
|||
|
|
@ -70,50 +70,53 @@ class TestRawNginxParser(unittest.TestCase):
|
|||
' image/jpeg jpg;}}}'.split('\n')
|
||||
|
||||
def test_parse_from_file(self):
|
||||
with open(util.get_data_filename('foo.conf')) as handle:
|
||||
parsed = util.filter_comments(load(handle))
|
||||
with util.get_data_filename('foo.conf') as path:
|
||||
with open(path) as handle:
|
||||
parsed = util.filter_comments(load(handle))
|
||||
assert parsed == \
|
||||
[['user', 'www-data'],
|
||||
[['http'],
|
||||
[[['server'], [
|
||||
['listen', '*:80', 'default_server', 'ssl'],
|
||||
['server_name', '*.www.foo.com', '*.www.example.com'],
|
||||
['root', '/home/ubuntu/sites/foo/'],
|
||||
[['location', '/status'], [
|
||||
[['types'], [['image/jpeg', 'jpg']]],
|
||||
]],
|
||||
[['location', '~', r'case_sensitive\.php$'], [
|
||||
['index', 'index.php'],
|
||||
['root', '/var/root'],
|
||||
]],
|
||||
[['location', '~*', r'case_insensitive\.php$'], []],
|
||||
[['location', '=', r'exact_match\.php$'], []],
|
||||
[['location', '^~', r'ignore_regex\.php$'], []]
|
||||
]]]]]
|
||||
[['http'],
|
||||
[[['server'], [
|
||||
['listen', '*:80', 'default_server', 'ssl'],
|
||||
['server_name', '*.www.foo.com', '*.www.example.com'],
|
||||
['root', '/home/ubuntu/sites/foo/'],
|
||||
[['location', '/status'], [
|
||||
[['types'], [['image/jpeg', 'jpg']]],
|
||||
]],
|
||||
[['location', '~', r'case_sensitive\.php$'], [
|
||||
['index', 'index.php'],
|
||||
['root', '/var/root'],
|
||||
]],
|
||||
[['location', '~*', r'case_insensitive\.php$'], []],
|
||||
[['location', '=', r'exact_match\.php$'], []],
|
||||
[['location', '^~', r'ignore_regex\.php$'], []]
|
||||
]]]]]
|
||||
|
||||
def test_parse_from_file2(self):
|
||||
with open(util.get_data_filename('edge_cases.conf')) as handle:
|
||||
parsed = util.filter_comments(load(handle))
|
||||
with util.get_data_filename('edge_cases.conf') as path:
|
||||
with open(path) as handle:
|
||||
parsed = util.filter_comments(load(handle))
|
||||
assert parsed == \
|
||||
[[['server'], [['server_name', 'simple']]],
|
||||
[['server'],
|
||||
[['server_name', 'with.if'],
|
||||
[['location', '~', '^/services/.+$'],
|
||||
[['server'],
|
||||
[['server_name', 'with.if'],
|
||||
[['location', '~', '^/services/.+$'],
|
||||
[[['if', '($request_filename', '~*', '\\.(ttf|woff)$)'],
|
||||
[['add_header', 'Access-Control-Allow-Origin', '"*"']]]]]]],
|
||||
[['server'],
|
||||
[['server_name', 'with.complicated.headers'],
|
||||
[['location', '~*', '\\.(?:gif|jpe?g|png)$'],
|
||||
[['add_header', 'Access-Control-Allow-Origin', '"*"']]]]]]],
|
||||
[['server'],
|
||||
[['server_name', 'with.complicated.headers'],
|
||||
[['location', '~*', '\\.(?:gif|jpe?g|png)$'],
|
||||
[['add_header', 'Pragma', 'public'],
|
||||
['add_header',
|
||||
'Cache-Control', '\'public, must-revalidate, proxy-revalidate\'',
|
||||
'"test,;{}"', 'foo'],
|
||||
['blah', '"hello;world"'],
|
||||
['try_files', '$uri', '@rewrites']]]]]]
|
||||
['add_header',
|
||||
'Cache-Control', '\'public, must-revalidate, proxy-revalidate\'',
|
||||
'"test,;{}"', 'foo'],
|
||||
['blah', '"hello;world"'],
|
||||
['try_files', '$uri', '@rewrites']]]]]]
|
||||
|
||||
def test_parse_from_file3(self):
|
||||
with open(util.get_data_filename('multiline_quotes.conf')) as handle:
|
||||
parsed = util.filter_comments(load(handle))
|
||||
with util.get_data_filename('multiline_quotes.conf') as path:
|
||||
with open(path) as handle:
|
||||
parsed = util.filter_comments(load(handle))
|
||||
assert parsed == \
|
||||
[[['http'],
|
||||
[[['server'],
|
||||
|
|
@ -129,13 +132,15 @@ class TestRawNginxParser(unittest.TestCase):
|
|||
' end\'']]]]]]]]
|
||||
|
||||
def test_abort_on_parse_failure(self):
|
||||
with open(util.get_data_filename('broken.conf')) as handle:
|
||||
with pytest.raises(ParseException):
|
||||
load(handle)
|
||||
with util.get_data_filename('broken.conf') as path:
|
||||
with open(path) as handle:
|
||||
with pytest.raises(ParseException):
|
||||
load(handle)
|
||||
|
||||
def test_dump_as_file(self):
|
||||
with open(util.get_data_filename('nginx.conf')) as handle:
|
||||
parsed = load(handle)
|
||||
with util.get_data_filename('nginx.conf') as path:
|
||||
with open(path) as handle:
|
||||
parsed = load(handle)
|
||||
parsed[-1][-1].append(UnspacedList([['server'],
|
||||
[['listen', ' ', '443', ' ', 'ssl'],
|
||||
['server_name', ' ', 'localhost'],
|
||||
|
|
@ -155,8 +160,9 @@ class TestRawNginxParser(unittest.TestCase):
|
|||
assert parsed == parsed_new
|
||||
|
||||
def test_comments(self):
|
||||
with open(util.get_data_filename('minimalistic_comments.conf')) as handle:
|
||||
parsed = load(handle)
|
||||
with util.get_data_filename('minimalistic_comments.conf') as path:
|
||||
with open(path) as handle:
|
||||
parsed = load(handle)
|
||||
|
||||
with tempfile.TemporaryFile(mode='w+t') as f:
|
||||
dump(parsed, f)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
import copy
|
||||
import shutil
|
||||
import tempfile
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from unittest import mock
|
||||
|
||||
import josepy as jose
|
||||
import pkg_resources
|
||||
|
||||
from certbot import util
|
||||
from certbot.compat import os
|
||||
|
|
@ -14,6 +15,10 @@ from certbot.tests import util as test_util
|
|||
from certbot_nginx._internal import configurator
|
||||
from certbot_nginx._internal import nginxparser
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
class NginxTest(test_util.ConfigTestCase):
|
||||
|
||||
|
|
@ -24,7 +29,7 @@ class NginxTest(test_util.ConfigTestCase):
|
|||
self.config = None
|
||||
|
||||
self.temp_dir, self.config_dir, self.work_dir = common.dir_setup(
|
||||
"etc_nginx", __name__)
|
||||
"etc_nginx", __package__)
|
||||
self.logs_dir = tempfile.mkdtemp('logs')
|
||||
|
||||
self.config_path = os.path.join(self.temp_dir, "etc_nginx")
|
||||
|
|
@ -78,11 +83,12 @@ class NginxTest(test_util.ConfigTestCase):
|
|||
return config
|
||||
|
||||
|
||||
@contextmanager
|
||||
def get_data_filename(filename):
|
||||
"""Gets the filename of a test data file."""
|
||||
return pkg_resources.resource_filename(
|
||||
__name__, os.path.join(
|
||||
"testdata", "etc_nginx", filename))
|
||||
ref = importlib_resources.files(__package__) / "testdata" / "etc_nginx"/ filename
|
||||
with importlib_resources.as_file(ref) as path:
|
||||
yield path
|
||||
|
||||
|
||||
def filter_comments(tree):
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ install_requires = [
|
|||
# https://github.com/certbot/certbot/issues/8761 for more info.
|
||||
f'acme>={version}',
|
||||
f'certbot>={version}',
|
||||
'importlib_resources>=1.3.1; python_version < "3.9"',
|
||||
# pyOpenSSL 23.1.0 is a bad release: https://github.com/pyca/pyopenssl/issues/1199
|
||||
'PyOpenSSL>=17.5.0,!=23.1.0',
|
||||
'pyparsing>=2.2.1',
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
"""Certbot constants."""
|
||||
import atexit
|
||||
import logging
|
||||
import sys
|
||||
from contextlib import ExitStack
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from acme import challenges
|
||||
from certbot.compat import misc
|
||||
from certbot.compat import os
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins"
|
||||
"""Setuptools entry point group name for plugins."""
|
||||
|
||||
|
|
@ -220,8 +226,15 @@ SSL_DHPARAMS_DEST = "ssl-dhparams.pem"
|
|||
"""Name of the ssl_dhparams file as saved
|
||||
in `certbot.configuration.NamespaceConfig.config_dir`."""
|
||||
|
||||
SSL_DHPARAMS_SRC = pkg_resources.resource_filename(
|
||||
"certbot", "ssl-dhparams.pem")
|
||||
def _generate_ssl_dhparams_src_static() -> str:
|
||||
# This code ensures that the resource is accessible as file for the lifetime of current
|
||||
# Python process, and will be automatically cleaned up on exit.
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
ssl_dhparams_src_ref = importlib_resources.files("certbot") / "ssl-dhparams.pem"
|
||||
return str(file_manager.enter_context(importlib_resources.as_file(ssl_dhparams_src_ref)))
|
||||
|
||||
SSL_DHPARAMS_SRC = _generate_ssl_dhparams_src_static()
|
||||
"""Path to the nginx ssl_dhparams file found in the Certbot distribution."""
|
||||
|
||||
UPDATED_SSL_DHPARAMS_DIGEST = ".updated-ssl-dhparams-pem-digest.txt"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
"""Utilities for plugins discovery and selection."""
|
||||
import itertools
|
||||
import logging
|
||||
import sys
|
||||
from typing import Callable
|
||||
|
|
@ -13,8 +12,6 @@ from typing import Optional
|
|||
from typing import Type
|
||||
from typing import Union
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from certbot import configuration
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
|
|
@ -22,6 +19,11 @@ from certbot._internal import constants
|
|||
from certbot.compat import os
|
||||
from certbot.errors import Error
|
||||
|
||||
if sys.version_info >= (3, 10): # pragma: no cover
|
||||
import importlib.metadata as importlib_metadata
|
||||
else:
|
||||
import importlib_metadata
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -35,7 +37,7 @@ class PluginEntryPoint:
|
|||
# this object is mutable, don't allow it to be hashed!
|
||||
__hash__ = None # type: ignore
|
||||
|
||||
def __init__(self, entry_point: pkg_resources.EntryPoint) -> None:
|
||||
def __init__(self, entry_point: importlib_metadata.EntryPoint) -> None:
|
||||
self.name = self.entry_point_to_plugin_name(entry_point)
|
||||
self.plugin_cls: Type[interfaces.Plugin] = entry_point.load()
|
||||
self.entry_point = entry_point
|
||||
|
|
@ -50,7 +52,7 @@ class PluginEntryPoint:
|
|||
return False
|
||||
|
||||
@classmethod
|
||||
def entry_point_to_plugin_name(cls, entry_point: pkg_resources.EntryPoint) -> str:
|
||||
def entry_point_to_plugin_name(cls, entry_point: importlib_metadata.EntryPoint) -> str:
|
||||
"""Unique plugin name for an ``entry_point``"""
|
||||
return entry_point.name
|
||||
|
||||
|
|
@ -75,7 +77,7 @@ class PluginEntryPoint:
|
|||
return getattr(self.plugin_cls, "hidden", False)
|
||||
|
||||
def ifaces(self, *ifaces_groups: Iterable[Type]) -> bool:
|
||||
"""Does plugin implements specified interface groups?"""
|
||||
"""Does plugin implement specified interface groups?"""
|
||||
return not ifaces_groups or any(
|
||||
all(issubclass(self.plugin_cls, iface)
|
||||
for iface in ifaces)
|
||||
|
|
@ -89,7 +91,6 @@ class PluginEntryPoint:
|
|||
def init(self, config: Optional[configuration.NamespaceConfig] = None) -> interfaces.Plugin:
|
||||
"""Memoized plugin initialization."""
|
||||
if not self._initialized:
|
||||
self.entry_point.require() # fetch extras!
|
||||
# For plugins implementing ABCs Plugin, Authenticator or Installer, the following
|
||||
# line will raise an exception if some implementations of abstract methods are missing.
|
||||
self._initialized = self.plugin_cls(config, self.name)
|
||||
|
|
@ -181,32 +182,31 @@ 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)
|
||||
for plugin_path in plugin_paths:
|
||||
pkg_resources.working_set.add_entry(plugin_path)
|
||||
entry_points = itertools.chain(
|
||||
pkg_resources.iter_entry_points(
|
||||
constants.SETUPTOOLS_PLUGINS_ENTRY_POINT),
|
||||
pkg_resources.iter_entry_points(
|
||||
constants.OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT),)
|
||||
for entry_point in entry_points:
|
||||
entry_points = list(importlib_metadata.entry_points(
|
||||
group=constants.SETUPTOOLS_PLUGINS_ENTRY_POINT))
|
||||
old_entry_points = list(importlib_metadata.entry_points(
|
||||
group=constants.OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT))
|
||||
for entry_point in entry_points + old_entry_points:
|
||||
try:
|
||||
cls._load_entry_point(entry_point, plugins)
|
||||
except Exception as e:
|
||||
raise errors.PluginError(
|
||||
f"The '{entry_point.module_name}' plugin errored while loading: {e}. "
|
||||
"You may need to remove or update this plugin. The Certbot log will "
|
||||
"contain the full error details and this should be reported to the "
|
||||
"plugin developer.") from e
|
||||
f"The '{entry_point.module}' plugin errored while loading: {e}. "
|
||||
"You may need to remove or update this plugin. The Certbot log will "
|
||||
"contain the full error details and this should be reported to the "
|
||||
"plugin developer.") from e
|
||||
return cls(plugins)
|
||||
|
||||
@classmethod
|
||||
def _load_entry_point(cls, entry_point: pkg_resources.EntryPoint,
|
||||
def _load_entry_point(cls, entry_point: importlib_metadata.EntryPoint,
|
||||
plugins: Dict[str, PluginEntryPoint]) -> None:
|
||||
plugin_ep = PluginEntryPoint(entry_point)
|
||||
if plugin_ep.name in plugins:
|
||||
other_ep = plugins[plugin_ep.name]
|
||||
plugin1 = plugin_ep.entry_point.dist.key if plugin_ep.entry_point.dist else "unknown"
|
||||
plugin2 = other_ep.entry_point.dist.key if other_ep.entry_point.dist else "unknown"
|
||||
plugin1_dist = plugin_ep.entry_point.dist
|
||||
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"
|
||||
raise Exception("Duplicate plugin name {0} from {1} and {2}.".format(
|
||||
plugin_ep.name, plugin1, plugin2))
|
||||
if issubclass(plugin_ep.plugin_cls, interfaces.Plugin):
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
|
|||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
||||
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
||||
import parsedatetime
|
||||
import pkg_resources
|
||||
import pytz
|
||||
|
||||
import certbot
|
||||
|
|
@ -38,12 +37,13 @@ from certbot._internal.plugins import disco as plugins_disco
|
|||
from certbot.compat import filesystem
|
||||
from certbot.compat import os
|
||||
from certbot.plugins import common as plugins_common
|
||||
from certbot.util import parse_loose_version
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ALL_FOUR = ("cert", "privkey", "chain", "fullchain")
|
||||
README = "README"
|
||||
CURRENT_VERSION = pkg_resources.parse_version(certbot.__version__)
|
||||
CURRENT_VERSION = parse_loose_version(certbot.__version__)
|
||||
BASE_PRIVKEY_MODE = 0o600
|
||||
|
||||
# pylint: disable=too-many-lines
|
||||
|
|
@ -492,7 +492,7 @@ class RenewableCert(interfaces.RenewableCert):
|
|||
|
||||
conf_version = self.configuration.get("version")
|
||||
if (conf_version is not None and
|
||||
pkg_resources.parse_version(conf_version) > CURRENT_VERSION):
|
||||
parse_loose_version(conf_version) > CURRENT_VERSION):
|
||||
logger.info(
|
||||
"Attempting to parse the version %s renewal configuration "
|
||||
"file found at %s with version %s of Certbot. This might not "
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ from typing import List
|
|||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import pkg_resources
|
||||
import pytest
|
||||
|
||||
from certbot import errors
|
||||
|
|
@ -15,30 +14,55 @@ from certbot._internal.plugins import null
|
|||
from certbot._internal.plugins import standalone
|
||||
from certbot._internal.plugins import webroot
|
||||
|
||||
EP_SA = pkg_resources.EntryPoint(
|
||||
"sa", "certbot._internal.plugins.standalone",
|
||||
attrs=("Authenticator",),
|
||||
dist=mock.MagicMock(key="certbot"))
|
||||
EP_WR = pkg_resources.EntryPoint(
|
||||
"wr", "certbot._internal.plugins.webroot",
|
||||
attrs=("Authenticator",),
|
||||
dist=mock.MagicMock(key="certbot"))
|
||||
if sys.version_info >= (3, 10): # pragma: no cover
|
||||
import importlib.metadata as importlib_metadata
|
||||
else:
|
||||
import importlib_metadata
|
||||
|
||||
|
||||
class _EntryPointLoadFail(importlib_metadata.EntryPoint):
|
||||
def load(self):
|
||||
raise RuntimeError("Loading failure")
|
||||
|
||||
|
||||
EP_SA = importlib_metadata.EntryPoint(
|
||||
name="sa",
|
||||
value="certbot._internal.plugins.standalone:Authenticator",
|
||||
group="certbot.plugins")
|
||||
|
||||
EP_WR = importlib_metadata.EntryPoint(
|
||||
name="wr",
|
||||
value="certbot._internal.plugins.webroot:Authenticator",
|
||||
group="certbot.plugins")
|
||||
|
||||
EP_SA_LOADFAIL = _EntryPointLoadFail(
|
||||
name="sa",
|
||||
value="certbot._internal.plugins.standalone:Authenticator",
|
||||
group="certbot.plugins")
|
||||
|
||||
|
||||
class PluginEntryPointTest(unittest.TestCase):
|
||||
"""Tests for certbot._internal.plugins.disco.PluginEntryPoint."""
|
||||
|
||||
def setUp(self):
|
||||
self.ep1 = pkg_resources.EntryPoint(
|
||||
"ep1", "p1.ep1", dist=mock.MagicMock(key="p1"))
|
||||
self.ep1prim = pkg_resources.EntryPoint(
|
||||
"ep1", "p2.ep2", dist=mock.MagicMock(key="p2"))
|
||||
self.ep1 = importlib_metadata.EntryPoint(
|
||||
name="ep1",
|
||||
value="p1.ep1:Authenticator",
|
||||
group="certbot.plugins")
|
||||
self.ep1prim = importlib_metadata.EntryPoint(
|
||||
name="ep1",
|
||||
value="p2.pe2:Authenticator",
|
||||
group="certbot.plugins")
|
||||
# nested
|
||||
self.ep2 = pkg_resources.EntryPoint(
|
||||
"ep2", "p2.foo.ep2", dist=mock.MagicMock(key="p2"))
|
||||
self.ep2 = importlib_metadata.EntryPoint(
|
||||
name="ep2",
|
||||
value="p2.foo.ep2:Authenticator",
|
||||
group="certbot.plugins")
|
||||
# project name != top-level package name
|
||||
self.ep3 = pkg_resources.EntryPoint(
|
||||
"ep3", "a.ep3", dist=mock.MagicMock(key="p3"))
|
||||
self.ep3 = importlib_metadata.EntryPoint(
|
||||
name="ep3",
|
||||
value="a.ep3:Authenticator",
|
||||
group="certbot.plugins")
|
||||
|
||||
from certbot._internal.plugins.disco import PluginEntryPoint
|
||||
self.plugin_ep = PluginEntryPoint(EP_SA)
|
||||
|
|
@ -172,16 +196,18 @@ class PluginsRegistryTest(unittest.TestCase):
|
|||
self.plugin_ep.__hash__.side_effect = TypeError
|
||||
self.plugins = {self.plugin_ep.name: self.plugin_ep}
|
||||
self.reg = self._create_new_registry(self.plugins)
|
||||
self.ep1 = pkg_resources.EntryPoint(
|
||||
"ep1", "p1.ep1", dist=mock.MagicMock(key="p1"))
|
||||
self.ep1 = importlib_metadata.EntryPoint(
|
||||
name="ep1",
|
||||
value="p1.ep1",
|
||||
group="certbot.plugins")
|
||||
|
||||
def test_find_all(self):
|
||||
from certbot._internal.plugins.disco import PluginsRegistry
|
||||
with mock.patch("certbot._internal.plugins.disco.pkg_resources") as mock_pkg:
|
||||
mock_pkg.iter_entry_points.side_effect = [
|
||||
iter([EP_SA]), iter([EP_WR, self.ep1])
|
||||
with mock.patch("certbot._internal.plugins.disco.importlib_metadata") as mock_meta:
|
||||
mock_meta.entry_points.side_effect = [
|
||||
[EP_SA], [EP_WR, self.ep1],
|
||||
]
|
||||
with mock.patch.object(pkg_resources.EntryPoint, 'load') as mock_load:
|
||||
with mock.patch.object(importlib_metadata.EntryPoint, 'load') as mock_load:
|
||||
mock_load.side_effect = [
|
||||
standalone.Authenticator, webroot.Authenticator,
|
||||
null.Installer, null.Installer]
|
||||
|
|
@ -196,10 +222,10 @@ class PluginsRegistryTest(unittest.TestCase):
|
|||
|
||||
def test_find_all_error_message(self):
|
||||
from certbot._internal.plugins.disco import PluginsRegistry
|
||||
with mock.patch("certbot._internal.plugins.disco.pkg_resources") as mock_pkg:
|
||||
EP_SA.load = None # This triggers a TypeError when the entrypoint loads
|
||||
mock_pkg.iter_entry_points.side_effect = [
|
||||
iter([EP_SA]), iter([EP_WR, self.ep1])
|
||||
with mock.patch("certbot._internal.plugins.disco.importlib_metadata") as mock_meta:
|
||||
#EP_SA.load = None # This triggers a TypeError when the entrypoint loads
|
||||
mock_meta.entry_points.side_effect = [
|
||||
[EP_SA_LOADFAIL], [EP_WR, self.ep1],
|
||||
]
|
||||
with self.assertRaises(errors.PluginError) as cm:
|
||||
PluginsRegistry.find_all()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import argparse
|
|||
import logging
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
|
|
@ -16,8 +17,6 @@ from typing import Tuple
|
|||
from typing import Type
|
||||
from typing import TypeVar
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from acme import challenges
|
||||
from certbot import achallenges
|
||||
from certbot import configuration
|
||||
|
|
@ -32,6 +31,11 @@ from certbot.interfaces import Installer as AbstractInstaller
|
|||
from certbot.interfaces import Plugin as AbstractPlugin
|
||||
from certbot.plugins.storage import PluginStorage
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -461,10 +465,9 @@ def dir_setup(test_dir: str, pkg: str) -> Tuple[str, str, str]: # pragma: no co
|
|||
filesystem.chmod(config_dir, constants.CONFIG_DIRS_MODE)
|
||||
filesystem.chmod(work_dir, constants.CONFIG_DIRS_MODE)
|
||||
|
||||
test_configs = pkg_resources.resource_filename(
|
||||
pkg, os.path.join("testdata", test_dir))
|
||||
|
||||
shutil.copytree(
|
||||
test_configs, os.path.join(temp_dir, test_dir), symlinks=True)
|
||||
test_dir_ref = importlib_resources.files(pkg).joinpath("testdata", test_dir)
|
||||
with importlib_resources.as_file(test_dir_ref) as path:
|
||||
shutil.copytree(
|
||||
path, os.path.join(temp_dir, test_dir), symlinks=True)
|
||||
|
||||
return temp_dir, config_dir, work_dir
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
"""Test utilities."""
|
||||
import atexit
|
||||
from contextlib import ExitStack
|
||||
from importlib import reload as reload_module
|
||||
import io
|
||||
import logging
|
||||
|
|
@ -23,7 +25,6 @@ from cryptography.hazmat.primitives import serialization
|
|||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
||||
import josepy as jose
|
||||
from OpenSSL import crypto
|
||||
import pkg_resources
|
||||
|
||||
from certbot import configuration
|
||||
from certbot import util
|
||||
|
|
@ -36,6 +37,11 @@ from certbot.compat import os
|
|||
from certbot.display import util as display_util
|
||||
from certbot.plugins import common
|
||||
|
||||
if sys.version_info >= (3, 9): # pragma: no cover
|
||||
import importlib.resources as importlib_resources
|
||||
else: # pragma: no cover
|
||||
import importlib_resources
|
||||
|
||||
|
||||
class DummyInstaller(common.Installer):
|
||||
"""Dummy installer plugin for test purpose."""
|
||||
|
|
@ -75,15 +81,17 @@ class DummyInstaller(common.Installer):
|
|||
|
||||
def vector_path(*names: str) -> str:
|
||||
"""Path to a test vector."""
|
||||
return pkg_resources.resource_filename(
|
||||
__name__, os.path.join('testdata', *names))
|
||||
_file_manager = ExitStack()
|
||||
atexit.register(_file_manager.close)
|
||||
vector_ref = importlib_resources.files(__package__).joinpath('testdata', *names)
|
||||
path = _file_manager.enter_context(importlib_resources.as_file(vector_ref))
|
||||
return str(path)
|
||||
|
||||
|
||||
def load_vector(*names: str) -> bytes:
|
||||
"""Load contents of a test vector."""
|
||||
# luckily, resource_string opens file in binary mode
|
||||
data = pkg_resources.resource_string(
|
||||
__name__, os.path.join('testdata', *names))
|
||||
vector_ref = importlib_resources.files(__package__).joinpath('testdata', *names)
|
||||
data = vector_ref.read_bytes()
|
||||
# Try at most to convert CRLF to LF when data is text
|
||||
try:
|
||||
return data.decode().replace('\r\n', '\n').encode()
|
||||
|
|
|
|||
|
|
@ -1,19 +1,10 @@
|
|||
import codecs
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pkg_resources import parse_version
|
||||
from setuptools import __version__ as setuptools_version
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
min_setuptools_version='41.6.0'
|
||||
# This conditional isn't necessary, but it provides better error messages to
|
||||
# people who try to install this package with older versions of setuptools.
|
||||
if parse_version(setuptools_version) < parse_version(min_setuptools_version):
|
||||
raise RuntimeError(f'setuptools {min_setuptools_version}+ is required')
|
||||
|
||||
def read_file(filename, encoding='utf8'):
|
||||
"""Read unicode from given file."""
|
||||
with codecs.open(filename, encoding=encoding) as fd:
|
||||
|
|
@ -44,6 +35,8 @@ install_requires = [
|
|||
'configobj>=5.0.6',
|
||||
'cryptography>=3.2.1',
|
||||
'distro>=1.0.1',
|
||||
'importlib_resources>=1.3.1; python_version < "3.9"',
|
||||
'importlib_metadata>=4.6; python_version < "3.10"',
|
||||
'josepy>=1.13.0',
|
||||
'parsedatetime>=2.4',
|
||||
'pyrfc3339',
|
||||
|
|
@ -51,7 +44,7 @@ install_requires = [
|
|||
# This dependency needs to be added using environment markers to avoid its
|
||||
# installation on Linux.
|
||||
'pywin32>=300 ; sys_platform == "win32"',
|
||||
f'setuptools>={min_setuptools_version}',
|
||||
'setuptools>=41.6.0',
|
||||
]
|
||||
|
||||
dev_extras = [
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
# 8) 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.
|
||||
filterwarnings =
|
||||
error
|
||||
ignore:decodestring\(\) is a deprecated alias:DeprecationWarning:dns
|
||||
|
|
@ -38,6 +40,7 @@ filterwarnings =
|
|||
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:pkg_resources
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,98 +1,100 @@
|
|||
# This file was generated by tools/pinning/oldest/repin.sh and can be updated using
|
||||
# that script.
|
||||
apacheconfig==0.3.2 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
asn1crypto==0.24.0 ; python_full_version >= "3.7.0" and python_full_version < "3.8.0"
|
||||
astroid==2.15.6 ; python_full_version >= "3.7.2" and python_full_version < "3.8.0"
|
||||
boto3==1.15.15 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
botocore==1.18.15 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
cachetools==5.3.1 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
certifi==2023.7.22 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
cffi==1.11.5 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
chardet==3.0.4 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
cloudflare==1.5.1 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
colorama==0.4.6 ; python_full_version < "3.8.0" and sys_platform == "win32" and python_version >= "3.7"
|
||||
configargparse==1.5.3 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
configobj==5.0.6 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
coverage==7.2.7 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
cryptography==3.2.1 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
cython==0.29.36 ; python_full_version >= "3.7.0" and python_full_version < "3.8.0"
|
||||
dill==0.3.7 ; python_full_version >= "3.7.2" and python_full_version < "3.8.0"
|
||||
distlib==0.3.7 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
distro==1.0.1 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
dns-lexicon==3.2.1 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
dnspython==1.15.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
exceptiongroup==1.1.3 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
execnet==2.0.2 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
filelock==3.12.2 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
funcsigs==0.4 ; python_full_version >= "3.7.0" and python_full_version < "3.8.0"
|
||||
future==0.18.3 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
google-api-python-client==1.6.5 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
google-auth==2.16.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
httplib2==0.9.2 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
idna==2.6 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
importlib-metadata==6.7.0 ; python_version >= "3.7" and python_version < "3.8"
|
||||
iniconfig==2.0.0 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
ipaddress==1.0.16 ; python_full_version >= "3.7.0" and python_full_version < "3.8.0"
|
||||
isort==5.11.5 ; python_full_version >= "3.7.2" and python_full_version < "3.8.0"
|
||||
jmespath==0.10.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
josepy==1.13.0 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
lazy-object-proxy==1.9.0 ; python_full_version >= "3.7.2" and python_full_version < "3.8.0"
|
||||
logger==1.4 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
mccabe==0.7.0 ; python_full_version >= "3.7.2" and python_full_version < "3.8.0"
|
||||
mypy-extensions==1.0.0 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
mypy==1.4.1 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
ndg-httpsclient==0.3.2 ; python_full_version >= "3.7.0" and python_full_version < "3.8.0"
|
||||
oauth2client==4.1.3 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
packaging==23.1 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
parsedatetime==2.4 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pbr==1.8.0 ; python_full_version >= "3.7.0" and python_full_version < "3.8.0"
|
||||
pip==23.2.1 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
platformdirs==3.10.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pluggy==1.2.0 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
ply==3.4 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
py==1.11.0 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
pyasn1-modules==0.3.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pyasn1==0.4.8 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pycparser==2.14 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pylint==2.17.5 ; python_full_version >= "3.7.2" and python_full_version < "3.8.0"
|
||||
pyopenssl==17.5.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pyparsing==2.2.1 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pyrfc3339==1.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pytest-cov==4.1.0 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
pytest-xdist==3.3.1 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
pytest==7.4.0 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
python-augeas==0.5.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
python-dateutil==2.8.2 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
python-digitalocean==1.11 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pytz==2019.3 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
pywin32==306 ; python_version >= "3.7" and python_full_version < "3.8.0" and sys_platform == "win32"
|
||||
pyyaml==6.0.1 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
requests-file==1.5.1 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
requests==2.20.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
rsa==4.9 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
s3transfer==0.3.7 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
setuptools==41.6.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
six==1.11.0 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
tldextract==3.4.4 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
tomli==2.0.1 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
tomlkit==0.12.1 ; python_full_version >= "3.7.2" and python_full_version < "3.8.0"
|
||||
tox==1.9.2 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
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_full_version < "3.8.0"
|
||||
types-httplib2==0.22.0.2 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
types-pyopenssl==23.0.0.0 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
types-pyrfc3339==1.1.1.5 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
types-python-dateutil==2.8.19.14 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
types-pytz==2023.3.0.1 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
types-pywin32==306.0.0.4 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
types-requests==2.31.0.2 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
types-setuptools==68.1.0.0 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
types-six==1.16.21.9 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
types-urllib3==1.26.25.14 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
typing-extensions==4.7.1 ; python_version < "3.8" and python_version >= "3.7"
|
||||
uritemplate==3.0.1 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
urllib3==1.24.2 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
virtualenv==20.24.3 ; python_version >= "3.7" and python_full_version < "3.8.0"
|
||||
wheel==0.33.6 ; python_full_version < "3.8.0" and python_version >= "3.7"
|
||||
wrapt==1.15.0 ; python_full_version >= "3.7.2" and python_full_version < "3.8.0"
|
||||
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"
|
||||
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.7" and python_version < "3.8" and sys_platform == "win32"
|
||||
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.2.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"
|
||||
tldextract==3.5.0 ; python_version >= "3.7" and python_version < "3.8"
|
||||
tomli==2.0.1 ; python_version >= "3.7" and python_version < "3.8"
|
||||
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.7" and python_version < "3.8"
|
||||
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.7" and python_version < "3.8"
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -75,6 +75,11 @@ poetry = "<1.3.0"
|
|||
# https://github.com/certbot/certbot/issues/9606.
|
||||
setuptools = "<67.5.0"
|
||||
|
||||
# Lexicon 3.14+ deprecates several private APIs and create stubs packages, leading
|
||||
# to several warnings and lint/mypy errors. Let's pin it to 3.13 until this is fixed
|
||||
# with https://github.com/certbot/certbot/pull/9746.
|
||||
dns-lexicon = "<3.14"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[build-system]
|
||||
|
|
|
|||
|
|
@ -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.7.*"
|
||||
python = "<3.8 >= 3.7"
|
||||
|
||||
# Local dependencies
|
||||
# Any local packages that have dependencies on other local packages must be
|
||||
|
|
@ -61,6 +61,7 @@ google-api-python-client = "1.6.5"
|
|||
google-auth = "2.16.0"
|
||||
httplib2 = "0.9.2"
|
||||
idna = "2.6"
|
||||
importlib-metadata = "4.6.4"
|
||||
ipaddress = "1.0.16"
|
||||
ndg-httpsclient = "0.3.2"
|
||||
parsedatetime = "2.4"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ 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.3 ; 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.12.1 ; python_version >= "3.7" and python_version < "4.0"
|
||||
backcall==0.2.0 ; python_version >= "3.7" and python_version < "4.0"
|
||||
|
|
@ -18,8 +18,8 @@ backports-cached-property==1.0.2 ; python_version >= "3.7" and python_version <
|
|||
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.27.1 ; python_version >= "3.7" and python_version < "4.0"
|
||||
botocore==1.30.1 ; python_version >= "3.7" and python_version < "4.0"
|
||||
boto3==1.28.43 ; python_version >= "3.7" and python_version < "4.0"
|
||||
botocore==1.31.43 ; 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"
|
||||
|
|
@ -28,7 +28,7 @@ cffi==1.15.1 ; python_version >= "3.7" and python_version < "4.0"
|
|||
charset-normalizer==3.2.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.11.7 ; 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"
|
||||
colorama==0.4.6 ; python_version >= "3.7" and python_version < "4.0" and (sys_platform == "win32" or 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"
|
||||
|
|
@ -42,14 +42,14 @@ 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.13.0 ; 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"
|
||||
docutils==0.19 ; 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.1 ; 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.11.1 ; python_version >= "3.7" and python_version < "4.0"
|
||||
google-api-python-client==2.97.0 ; python_version >= "3.7" and python_version < "4.0"
|
||||
google-api-python-client==2.98.0 ; python_version >= "3.7" and python_version < "4.0"
|
||||
google-auth-httplib2==0.1.0 ; python_version >= "3.7" and python_version < "4.0"
|
||||
google-auth==2.22.0 ; python_version >= "3.7" and python_version < "4.0"
|
||||
googleapis-common-protos==1.60.0 ; python_version >= "3.7" and python_version < "4.0"
|
||||
|
|
@ -58,7 +58,7 @@ 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 < "3.9"
|
||||
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"
|
||||
|
|
@ -97,14 +97,14 @@ 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"
|
||||
platformdirs==2.6.2 ; python_version >= "3.7" and python_version < "4.0"
|
||||
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.1 ; python_version >= "3.7" and python_version < "4.0"
|
||||
protobuf==4.24.3 ; 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"
|
||||
|
|
@ -121,11 +121,11 @@ 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.0 ; 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 ; 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"
|
||||
|
|
@ -147,22 +147,21 @@ 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-rtd-theme==0.5.1 ; 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.4.4 ; python_version >= "3.7" and python_version < "4.0"
|
||||
tldextract==3.5.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"
|
||||
tomlkit==0.12.1 ; python_version >= "3.7" and python_version < "4.0"
|
||||
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"
|
||||
typed-ast==1.5.5 ; python_version >= "3.7" and python_version < "3.8"
|
||||
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"
|
||||
|
|
@ -170,7 +169,7 @@ types-python-dateutil==2.8.19.14 ; python_version >= "3.7" and python_version <
|
|||
types-pytz==2023.3.0.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.1.0.0 ; 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"
|
||||
|
|
@ -180,7 +179,7 @@ virtualenv==20.21.1 ; python_version >= "3.7" and python_version < "4.0"
|
|||
wcwidth==0.2.6 ; 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"
|
||||
wrapt==1.15.0 ; python_version >= "3.7" and python_version < "4.0"
|
||||
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"
|
||||
|
|
|
|||
Loading…
Reference in a new issue