Compare commits

...

11 Commits

Author SHA1 Message Date
Brad Warren
0306161645 update coverage 2021-05-28 14:28:18 -07:00
Brad Warren
c5cc2f9027 fix leaky test 2021-05-28 11:52:07 -07:00
Brad Warren
1ccb331f6d don't use capture_output 2021-05-27 12:20:47 -07:00
Brad Warren
68b0621b19 rename variable 2021-05-27 11:18:13 -07:00
Brad Warren
53d67d46e0 undouble counts 2021-05-27 11:09:34 -07:00
Brad Warren
34deb46625 capture stdout and stderr separately 2021-05-27 11:07:24 -07:00
Brad Warren
7f3817fe8d fix output count 2021-05-26 14:04:27 -07:00
Brad Warren
fecdb242d0 fix test_revoke_mutual_exclusive_flags 2021-05-26 13:29:31 -07:00
Brad Warren
9d57a4aa55 fix assertion 2021-05-26 13:29:31 -07:00
Brad Warren
d40626f76d ignore external mock warnings 2021-05-26 13:29:31 -07:00
Brad Warren
7df08d106f unpin pytest and update pinnings 2021-05-26 13:29:28 -07:00
7 changed files with 68 additions and 55 deletions

View File

@@ -61,7 +61,7 @@ class IntegrationTestsContext:
Execute certbot with given args, not renewing certificates by default.
:param args: args to pass to certbot
:param force_renew: set to False to not renew by default
:return: output of certbot execution
:return: stdout and stderr from certbot execution
"""
command = ['--authenticator', 'standalone', '--installer', 'null']
command.extend(args)

View File

@@ -78,9 +78,9 @@ def test_registration_override(context):
def test_prepare_plugins(context):
"""Test that plugins are correctly instantiated and displayed."""
output = context.certbot(['plugins', '--init', '--prepare'])
stdout, _ = context.certbot(['plugins', '--init', '--prepare'])
assert 'webroot' in output
assert 'webroot' in stdout
def test_http_01(context):
@@ -407,9 +407,9 @@ def test_invalid_domain_with_dns_challenge(context):
'--manual-cleanup-hook', context.manual_dns_cleanup_hook
])
output = context.certbot(['certificates'])
stdout, _ = context.certbot(['certificates'])
assert context.get_domain('fail-dns1') not in output
assert context.get_domain('fail-dns1') not in stdout
def test_reuse_key(context):
@@ -614,11 +614,11 @@ def test_revoke_and_unregister(context):
context.certbot(['unregister'])
output = context.certbot(['certificates'])
stdout, _ = context.certbot(['certificates'])
assert cert1 not in output
assert cert2 not in output
assert cert3 in output
assert cert1 not in stdout
assert cert2 not in stdout
assert cert3 in stdout
def test_revoke_mutual_exclusive_flags(context):
@@ -630,7 +630,7 @@ def test_revoke_mutual_exclusive_flags(context):
'revoke', '--cert-name', cert,
'--cert-path', join(context.config_dir, 'live', cert, 'fullchain.pem')
])
assert 'Exactly one of --cert-path or --cert-name must be specified' in error.out
assert 'Exactly one of --cert-path or --cert-name must be specified' in error.value.stderr
def test_revoke_multiple_lineages(context):
@@ -685,12 +685,12 @@ def test_wildcard_certificates(context):
def test_ocsp_status_stale(context):
"""Test retrieval of OCSP statuses for staled config"""
sample_data_path = misc.load_sample_data_path(context.workspace)
output = context.certbot(['certificates', '--config-dir', sample_data_path])
stdout, _ = context.certbot(['certificates', '--config-dir', sample_data_path])
assert output.count('TEST_CERT') == 2, ('Did not find two test certs as expected ({0})'
.format(output.count('TEST_CERT')))
assert output.count('EXPIRED') == 2, ('Did not find two expired certs as expected ({0})'
.format(output.count('EXPIRED')))
assert stdout.count('TEST_CERT') == 2, ('Did not find two test certs as expected ({0})'
.format(stdout.count('TEST_CERT')))
assert stdout.count('EXPIRED') == 2, ('Did not find two expired certs as expected ({0})'
.format(stdout.count('EXPIRED')))
def test_ocsp_status_live(context):
@@ -699,20 +699,20 @@ def test_ocsp_status_live(context):
# OSCP 1: Check live certificate OCSP status (VALID)
context.certbot(['--domains', cert])
output = context.certbot(['certificates'])
stdout, _ = context.certbot(['certificates'])
assert output.count('VALID') == 1, 'Expected {0} to be VALID'.format(cert)
assert output.count('EXPIRED') == 0, 'Did not expect {0} to be EXPIRED'.format(cert)
assert stdout.count('VALID') == 1, 'Expected {0} to be VALID'.format(cert)
assert stdout.count('EXPIRED') == 0, 'Did not expect {0} to be EXPIRED'.format(cert)
# OSCP 2: Check live certificate OCSP status (REVOKED)
context.certbot(['revoke', '--cert-name', cert, '--no-delete-after-revoke'])
# Sometimes in oldest tests (using openssl binary and not cryptography), the OCSP status is
# not seen immediately by Certbot as invalid. Waiting few seconds solves this transient issue.
time.sleep(5)
output = context.certbot(['certificates'])
stdout, _ = context.certbot(['certificates'])
assert output.count('INVALID') == 1, 'Expected {0} to be INVALID'.format(cert)
assert output.count('REVOKED') == 1, 'Expected {0} to be REVOKED'.format(cert)
assert stdout.count('INVALID') == 1, 'Expected {0} to be INVALID'.format(cert)
assert stdout.count('REVOKED') == 1, 'Expected {0} to be REVOKED'.format(cert)
def test_ocsp_renew(context):

View File

@@ -17,7 +17,7 @@ def certbot_test(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
Invoke the certbot executable available in PATH in a test context for the given args.
The test context consists in running certbot in debug mode, with various flags suitable
for tests (eg. no ssl check, customizable ACME challenge ports and config directory ...).
This command captures stdout and returns it to the caller.
This command captures both stdout and stderr and returns it to the caller.
:param list certbot_args: the arguments to pass to the certbot executable
:param str directory_url: URL of the ACME directory server to use
:param int http_01_port: port for the HTTP-01 challenges
@@ -25,13 +25,19 @@ def certbot_test(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
:param str config_dir: certbot configuration directory to use
:param str workspace: certbot current directory to use
:param bool force_renew: set False to not force renew existing certificates (default: True)
:return: stdout as string
:rtype: str
:return: stdout and stderr as strings
:rtype: `tuple` of `str`
"""
command, env = _prepare_args_env(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
config_dir, workspace, force_renew)
return subprocess.check_output(command, universal_newlines=True, cwd=workspace, env=env)
proc = subprocess.run(command, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, check=False, universal_newlines=True,
cwd=workspace, env=env)
print('--> Certbot log output was:')
print(proc.stderr)
proc.check_returncode()
return proc.stdout, proc.stderr
def _prepare_environ(workspace):

View File

@@ -28,8 +28,13 @@ class PreArgParseSetupTest(unittest.TestCase):
@classmethod
def _call(cls, *args, **kwargs): # pylint: disable=unused-argument
from certbot._internal.log import pre_arg_parse_setup
with mock.patch('builtins.open', mock.mock_open()):
return pre_arg_parse_setup()
return pre_arg_parse_setup()
def tearDown(self):
# We need to call logging.shutdown() at the end of this test to
# properly clean up any resources created by pre_arg_parse_setup.
logging.shutdown()
super().tearDown()
@mock.patch('certbot._internal.log.sys')
@mock.patch('certbot._internal.log.pre_arg_parse_except_hook')

View File

@@ -9,5 +9,10 @@
# deprecation warnings and gives time for plugins that don't use the deprecated
# API to propagate, especially for plugins packaged as an external snap, before
# we release breaking changes.
#
# The current warnings being ignored are:
# 1) The warning raised when importing certbot.tests.util and the external mock
# library is installed.
filterwarnings =
error
ignore:The external mock module:PendingDeprecationWarning

View File

@@ -58,11 +58,6 @@ cython = "*"
# needed. This dependency can be removed here once Certbot's support for the
# 3rd party mock library has been dropped.
mock = "*"
# Upgrading coverage, pytest, and some of pytest's plugins causes many test
# failures so let's pin these packages back for now.
coverage = "4.5.4"
pytest = "3.2.5"
pytest-forked = "0.2"
# We were originally pinning back python-augeas for certbot-auto because we
# found the way older versions of the library linked to Augeas were more
# reliable. That's no longer a concern, however, we continue to pin back the

View File

@@ -11,16 +11,17 @@ apipkg==1.5; python_version >= "3.6" and python_full_version < "3.0.0" or python
appdirs==1.4.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
appnope==0.1.2
astroid==2.5.6; python_version >= "3.6" and python_version < "4.0"
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0"
attrs==21.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
awscli==1.19.77; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0")
awscli==1.19.83; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0")
azure-devops==6.0.0b4; python_version >= "3.6"
babel==2.9.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
backcall==0.2.0
bcrypt==3.2.0; python_version >= "3.6"
beautifulsoup4==4.9.3; python_version >= "3.6" and python_version < "4.0"
bleach==3.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
boto3==1.17.77; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
botocore==1.20.77; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
boto3==1.17.83; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
botocore==1.20.83; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
cachecontrol==0.12.6; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
cached-property==1.5.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
cachetools==4.2.2; python_version >= "3.5" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6")
@@ -34,7 +35,7 @@ cloudflare==2.8.15; python_version >= "3.6"
colorama==0.4.3; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
configargparse==1.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
configobj==5.0.6; python_version >= "3.6"
coverage==4.5.4; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0" and python_version < "4")
coverage==5.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.6"
crashtest==0.3.1; python_version >= "3.6" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0")
cryptography==3.4.7; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0") and sys_platform == "linux" or python_full_version >= "3.5.0" and python_version >= "3.6" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0") and sys_platform == "linux"
cython==0.29.23; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
@@ -49,25 +50,26 @@ docker==4.2.2; python_version >= "3.6" and python_full_version < "3.0.0" or pyth
dockerpty==0.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
docopt==0.6.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
docutils==0.15.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
execnet==1.8.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
execnet==1.8.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
fabric==2.6.0; python_version >= "3.6"
filelock==3.0.12; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "4.0"
google-api-core==1.28.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
google-api-python-client==2.5.0; python_version >= "3.6"
google-api-python-client==2.6.0; python_version >= "3.6"
google-auth-httplib2==0.1.0; python_version >= "3.6"
google-auth==1.30.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
google-auth==1.30.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
googleapis-common-protos==1.53.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
html5lib==1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
httplib2==0.19.1; python_version >= "3.6"
idna==2.10; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_full_version >= "3.5.0" and python_version >= "3.6" and python_version < "4.0"
imagesize==1.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
importlib-metadata==1.7.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0") or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.8" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0")
importlib-metadata==1.7.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0") or python_version < "3.8" and python_version >= "3.6" and python_full_version >= "3.5.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0")
importlib-resources==5.1.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.7"
iniconfig==1.1.1; python_version >= "3.6"
invoke==1.5.0; python_version >= "3.6"
ipdb==0.13.7; python_version >= "3.6"
ipdb==0.13.8; python_version >= "3.6"
ipython-genutils==0.2.0; python_version == "3.6"
ipython==7.16.1; python_version == "3.6"
ipython==7.23.1; python_version >= "3.7"
ipython==7.24.0; python_version >= "3.7"
isodate==0.6.0; python_version >= "3.6"
isort==5.8.0; python_version >= "3.6" and python_version < "4.0"
jedi==0.18.0
@@ -100,12 +102,12 @@ pathlib2==2.3.5; python_version >= "3.6"
pexpect==4.8.0
pickleshare==0.7.5
pkginfo==1.7.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
pluggy==0.13.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
pluggy==0.13.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
ply==3.11; python_version >= "3.6"
poetry-core==1.0.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
poetry==1.1.6; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
prompt-toolkit==3.0.3
protobuf==3.17.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
protobuf==3.17.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
ptyprocess==0.7.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
py==1.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pyasn1-modules==0.2.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
@@ -123,10 +125,10 @@ pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or p
pypiwin32==223; sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
pyrfc3339==1.1; python_version >= "3.6"
pyrsistent==0.17.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pytest-cov==2.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pytest-forked==0.2
pytest-xdist==1.24.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pytest==3.2.5
pytest-cov==2.12.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pytest-forked==1.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pytest-xdist==2.2.1; python_version >= "3.6"
pytest==6.2.4; python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
python-augeas==0.5.0
python-dateutil==2.8.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
python-digitalocean==1.16.0; python_version >= "3.6"
@@ -154,29 +156,29 @@ sphinx-rtd-theme==0.5.2; python_version >= "3.6"
sphinx==3.5.4; python_version >= "3.5"
sphinxcontrib-applehelp==1.0.2; python_version >= "3.6"
sphinxcontrib-devhelp==1.0.2; python_version >= "3.6"
sphinxcontrib-htmlhelp==1.0.3; python_version >= "3.6"
sphinxcontrib-htmlhelp==2.0.0; python_version >= "3.6"
sphinxcontrib-jsmath==1.0.1; python_version >= "3.6"
sphinxcontrib-qthelp==1.0.3; python_version >= "3.6"
sphinxcontrib-serializinghtml==1.1.4; python_version >= "3.6"
sphinxcontrib-serializinghtml==1.1.5; python_version >= "3.6"
texttable==1.6.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
tldextract==3.1.0; python_version >= "3.6" and python_version < "4.0"
toml==0.10.2; python_version == "3.6" and python_full_version < "3.0.0" or python_version > "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version == "3.6" and python_full_version >= "3.5.0" or python_version > "3.6" and python_full_version >= "3.5.0" and python_version < "4.0"
toml==0.10.2; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
tomlkit==0.7.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
tox==3.23.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
tqdm==4.60.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
tqdm==4.61.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
traitlets==4.3.3
twine==3.3.0; python_version >= "3.6"
typed-ast==1.4.3; implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.6"
typing-extensions==3.10.0.0; python_version >= "3.6"
uritemplate==3.0.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
urllib3==1.26.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.6"
virtualenv==20.4.6; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
urllib3==1.26.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.6"
virtualenv==20.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
wcwidth==0.2.5; python_version == "3.6"
webencodings==0.5.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
websocket-client==0.59.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
wrapt==1.12.1; python_version >= "3.6" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0")
yarg==0.1.9; python_version >= "3.6"
zipp==3.4.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.7"
zipp==3.4.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version < "3.7" and python_version >= "3.6" and python_full_version >= "3.5.0"
zope.component==5.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
zope.event==4.5.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
zope.hookable==5.0.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"