Upgrade pylint (#8855)

This is part of https://github.com/certbot/certbot/issues/8782. I took it on now because the currently pinned version of `pylint` doesn't work with newer versions of `poetry` which I wanted to upgrade as part of https://github.com/certbot/certbot/issues/8787.

To say a bit more about the specific changes in this PR:

* Newer versions of `pylint` complain if `Popen` isn't used as a context manager. Instead of making this change, I switched to using `subprocess.run` which is simpler and [recommended in the Python docs](https://docs.python.org/3/library/subprocess.html#using-the-subprocess-module). I also disabled this check in a few places where no longer using `Popen` would require significant refactoring.
* The deleted code in `certbot/certbot/_internal/renewal.py` is cruft since https://github.com/certbot/certbot/pull/8685.
* The unused argument to `enable_mod` in the Apache plugin is used in some over the override classes that subclass that class.

* unpin pylint and repin dependencies

* disable raise-missing-from

* disable wrong-input-order

* remove unused code

* misc lint fixes

* remove unused import

* various lint fixes
This commit is contained in:
Brad Warren
2021-05-24 10:02:55 -07:00
committed by GitHub
parent 2df279bc5b
commit 315ddb247f
26 changed files with 223 additions and 225 deletions
+12 -1
View File
@@ -56,7 +56,18 @@ extension-pkg-whitelist=pywintypes,win32api,win32file,win32security
# See https://github.com/PyCQA/pylint/issues/1498.
# 3) Same as point 2 for no-value-for-parameter.
# See https://github.com/PyCQA/pylint/issues/2820.
disable=fixme,locally-disabled,locally-enabled,bad-continuation,no-self-use,invalid-name,cyclic-import,duplicate-code,design,import-outside-toplevel,useless-object-inheritance,unsubscriptable-object,no-value-for-parameter,no-else-return,no-else-raise,no-else-break,no-else-continue
# 4) raise-missing-from makes it an error to raise an exception from except
# block without using explicit exception chaining. While explicit exception
# chaining results in a slightly more informative traceback, I don't think
# it's beneficial enough for us to change all of our current instances and
# give Certbot developers errors about this when they're working on new code
# in the future. You can read more about exception chaining and this pylint
# check at
# https://blog.ram.rachum.com/post/621791438475296768/improving-python-exception-chaining-with.
# 5) wrong-import-order generates false positives and a pylint developer
# suggests that people using isort should disable this check at
# https://github.com/PyCQA/pylint/issues/3817#issuecomment-687892090.
disable=fixme,locally-disabled,locally-enabled,bad-continuation,no-self-use,invalid-name,cyclic-import,duplicate-code,design,import-outside-toplevel,useless-object-inheritance,unsubscriptable-object,no-value-for-parameter,no-else-return,no-else-raise,no-else-break,no-else-continue,raise-missing-from,wrong-import-order
[REPORTS]
@@ -220,13 +220,14 @@ def _get_runtime_cfg(command):
"""
try:
proc = subprocess.Popen(
proc = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
check=False,
env=util.env_no_snap_for_external_calls())
stdout, stderr = proc.communicate()
stdout, stderr = proc.stdout, proc.stderr
except (OSError, ValueError):
logger.error(
@@ -136,6 +136,6 @@ def assertEqualPathsList(first, second): # pragma: no cover
if any(isPass(path) for path in second):
return
for fpath in first:
assert any([fnmatch.fnmatch(fpath, spath) for spath in second])
assert any(fnmatch.fnmatch(fpath, spath) for spath in second)
for spath in second:
assert any([fnmatch.fnmatch(fpath, spath) for fpath in first])
assert any(fnmatch.fnmatch(fpath, spath) for fpath in first)
@@ -2396,7 +2396,7 @@ class ApacheConfigurator(common.Installer):
vhost.enabled = True
return
def enable_mod(self, mod_name, temp=False):
def enable_mod(self, mod_name, temp=False): # pylint: disable=unused-argument
"""Enables module in Apache.
Both enables and reloads Apache so module is active.
+5 -4
View File
@@ -49,10 +49,11 @@ class MultipleVhostsTestDebian(util.ApacheTest):
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@mock.patch("certbot_apache._internal.apache_util.subprocess.Popen")
def test_enable_mod(self, mock_popen, mock_exe_exists, mock_run_script):
mock_popen().communicate.return_value = ("Define: DUMP_RUN_CFG", "")
mock_popen().returncode = 0
@mock.patch("certbot_apache._internal.apache_util.subprocess.run")
def test_enable_mod(self, mock_run, mock_exe_exists, mock_run_script):
mock_run.return_value.stdout = "Define: DUMP_RUN_CFG"
mock_run.return_value.stderr = ""
mock_run.return_value.returncode = 0
mock_exe_exists.return_value = True
self.config.enable_mod("ssl")
+9 -7
View File
@@ -305,17 +305,19 @@ class BasicParserTest(util.ParserTest):
self.assertRaises(
errors.PluginError, self.parser.update_runtime_variables)
@mock.patch("certbot_apache._internal.apache_util.subprocess.Popen")
def test_update_runtime_vars_bad_ctl(self, mock_popen):
mock_popen.side_effect = OSError
@mock.patch("certbot_apache._internal.apache_util.subprocess.run")
def test_update_runtime_vars_bad_ctl(self, mock_run):
mock_run.side_effect = OSError
self.assertRaises(
errors.MisconfigurationError,
self.parser.update_runtime_variables)
@mock.patch("certbot_apache._internal.apache_util.subprocess.Popen")
def test_update_runtime_vars_bad_exit(self, mock_popen):
mock_popen().communicate.return_value = ("", "")
mock_popen.returncode = -1
@mock.patch("certbot_apache._internal.apache_util.subprocess.run")
def test_update_runtime_vars_bad_exit(self, mock_run):
mock_proc = mock_run.return_value
mock_proc.stdout = ""
mock_proc.stderr = ""
mock_proc.returncode = -1
self.assertRaises(
errors.MisconfigurationError,
self.parser.update_runtime_variables)
@@ -51,6 +51,7 @@ class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
with open(self.nginx_config_path, 'w') as file:
file.write(self.nginx_config)
# pylint: disable=consider-using-with
process = subprocess.Popen(['nginx', '-c', self.nginx_config_path, '-g', 'daemon off;'])
assert process.poll() is None
@@ -240,6 +240,7 @@ class ACMEServer:
if not env:
env = os.environ
stdout = sys.stderr if force_stderr else self._stdout
# pylint: disable=consider-using-with
process = subprocess.Popen(
command, stdout=stdout, stderr=subprocess.STDOUT, cwd=cwd, env=env
)
@@ -83,6 +83,7 @@ class DNSServer:
def _start_bind(self):
"""Launch the BIND9 server as a Docker container"""
addr_str = "{}:{}".format(BIND_BIND_ADDRESS[0], BIND_BIND_ADDRESS[1])
# pylint: disable=consider-using-with
self.process = subprocess.Popen(
[
"docker",
@@ -985,13 +985,14 @@ class NginxConfigurator(common.Installer):
Unable to run Nginx version command
"""
try:
proc = subprocess.Popen(
proc = subprocess.run(
[self.conf('ctl'), "-c", self.nginx_conf, "-V"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
check=False,
env=util.env_no_snap_for_external_calls())
text = proc.communicate()[1] # nginx prints output to stderr
text = proc.stderr # nginx prints output to stderr
except (OSError, ValueError) as error:
logger.debug(str(error), exc_info=True)
raise errors.PluginError(
@@ -1224,10 +1225,9 @@ def nginx_restart(nginx_ctl, nginx_conf, sleep_duration):
try:
reload_output: Text = u""
with tempfile.TemporaryFile() as out:
proc = subprocess.Popen([nginx_ctl, "-c", nginx_conf, "-s", "reload"],
env=util.env_no_snap_for_external_calls(),
stdout=out, stderr=out)
proc.communicate()
proc = subprocess.run([nginx_ctl, "-c", nginx_conf, "-s", "reload"],
env=util.env_no_snap_for_external_calls(),
stdout=out, stderr=out, check=False)
out.seek(0)
reload_output = out.read().decode("utf-8")
@@ -1237,9 +1237,8 @@ def nginx_restart(nginx_ctl, nginx_conf, sleep_duration):
# Write to temporary files instead of piping because of communication issues on Arch
# https://github.com/certbot/certbot/issues/4324
with tempfile.TemporaryFile() as out:
nginx_proc = subprocess.Popen([nginx_ctl, "-c", nginx_conf],
stdout=out, stderr=out, env=util.env_no_snap_for_external_calls())
nginx_proc.communicate()
nginx_proc = subprocess.run([nginx_ctl, "-c", nginx_conf],
stdout=out, stderr=out, env=util.env_no_snap_for_external_calls(), check=False)
if nginx_proc.returncode != 0:
out.seek(0)
# Enter recovery routine...
+68 -59
View File
@@ -42,15 +42,16 @@ class NginxConfiguratorTest(util.NginxTest):
self.assertEqual(13, len(self.config.parser.parsed))
@mock.patch("certbot_nginx._internal.configurator.util.exe_exists")
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
def test_prepare_initializes_version(self, mock_popen, mock_exe_exists):
mock_popen().communicate.return_value = (
"", "\n".join(["nginx version: nginx/1.6.2",
@mock.patch("certbot_nginx._internal.configurator.subprocess.run")
def test_prepare_initializes_version(self, mock_run, mock_exe_exists):
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/1.6.2",
"built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)",
"TLS SNI support enabled",
"configure arguments: --prefix=/usr/local/Cellar/"
"nginx/1.6.2 --with-http_ssl_module"]))
"nginx/1.6.2 --with-http_ssl_module"])
mock_exe_exists.return_value = True
@@ -347,141 +348,149 @@ class NginxConfiguratorTest(util.NginxTest):
self.assertEqual(mock_revert.call_count, 1)
self.assertEqual(mock_restart.call_count, 2)
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
def test_get_version(self, mock_popen):
mock_popen().communicate.return_value = (
"", "\n".join(["nginx version: nginx/1.4.2",
@mock.patch("certbot_nginx._internal.configurator.subprocess.run")
def test_get_version(self, mock_run):
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/1.4.2",
"built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)",
"TLS SNI support enabled",
"configure arguments: --prefix=/usr/local/Cellar/"
"nginx/1.6.2 --with-http_ssl_module"]))
"nginx/1.6.2 --with-http_ssl_module"])
self.assertEqual(self.config.get_version(), (1, 4, 2))
mock_popen().communicate.return_value = (
"", "\n".join(["nginx version: nginx/0.9",
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/0.9",
"built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)",
"TLS SNI support enabled",
"configure arguments: --with-http_ssl_module"]))
"configure arguments: --with-http_ssl_module"])
self.assertEqual(self.config.get_version(), (0, 9))
mock_popen().communicate.return_value = (
"", "\n".join(["blah 0.0.1",
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = "\n".join(
["blah 0.0.1",
"built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)",
"TLS SNI support enabled",
"configure arguments: --with-http_ssl_module"]))
"configure arguments: --with-http_ssl_module"])
self.assertRaises(errors.PluginError, self.config.get_version)
mock_popen().communicate.return_value = (
"", "\n".join(["nginx version: nginx/1.4.2",
"TLS SNI support enabled"]))
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/1.4.2",
"TLS SNI support enabled"])
self.assertRaises(errors.PluginError, self.config.get_version)
mock_popen().communicate.return_value = (
"", "\n".join(["nginx version: nginx/1.4.2",
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/1.4.2",
"built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)",
"configure arguments: --with-http_ssl_module"]))
"configure arguments: --with-http_ssl_module"])
self.assertRaises(errors.PluginError, self.config.get_version)
mock_popen().communicate.return_value = (
"", "\n".join(["nginx version: nginx/0.8.1",
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/0.8.1",
"built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)",
"TLS SNI support enabled",
"configure arguments: --with-http_ssl_module"]))
"configure arguments: --with-http_ssl_module"])
self.assertRaises(errors.NotSupportedError, self.config.get_version)
mock_popen.side_effect = OSError("Can't find program")
mock_run.side_effect = OSError("Can't find program")
self.assertRaises(errors.PluginError, self.config.get_version)
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
def test_get_openssl_version(self, mock_popen):
@mock.patch("certbot_nginx._internal.configurator.subprocess.run")
def test_get_openssl_version(self, mock_run):
# pylint: disable=protected-access
mock_popen().communicate.return_value = (
"", """
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = """
nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
built with OpenSSL 1.0.2g 1 Mar 2016
TLS SNI support enabled
configure arguments:
""")
"""
self.assertEqual(self.config._get_openssl_version(), "1.0.2g")
mock_popen().communicate.return_value = (
"", """
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = """
nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
built with OpenSSL 1.0.2-beta1 1 Mar 2016
TLS SNI support enabled
configure arguments:
""")
"""
self.assertEqual(self.config._get_openssl_version(), "1.0.2-beta1")
mock_popen().communicate.return_value = (
"", """
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = """
nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
built with OpenSSL 1.0.2 1 Mar 2016
TLS SNI support enabled
configure arguments:
""")
"""
self.assertEqual(self.config._get_openssl_version(), "1.0.2")
mock_popen().communicate.return_value = (
"", """
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = """
nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
built with OpenSSL 1.0.2g 1 Mar 2016 (running with OpenSSL 1.0.2a 1 Mar 2016)
TLS SNI support enabled
configure arguments:
""")
"""
self.assertEqual(self.config._get_openssl_version(), "1.0.2a")
mock_popen().communicate.return_value = (
"", """
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = """
nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
built with LibreSSL 2.2.2
TLS SNI support enabled
configure arguments:
""")
"""
self.assertEqual(self.config._get_openssl_version(), "")
mock_popen().communicate.return_value = (
"", """
mock_run.return_value.stdout = ""
mock_run.return_value.stderr = """
nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
TLS SNI support enabled
configure arguments:
""")
"""
self.assertEqual(self.config._get_openssl_version(), "")
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
@mock.patch("certbot_nginx._internal.configurator.subprocess.run")
@mock.patch("certbot_nginx._internal.configurator.time")
def test_nginx_restart(self, mock_time, mock_popen):
mocked = mock_popen()
mocked.communicate.return_value = ('', '')
def test_nginx_restart(self, mock_time, mock_run):
mocked = mock_run.return_value
mocked.stdout = ''
mocked.stderr = ''
mocked.returncode = 0
self.config.restart()
self.assertEqual(mocked.communicate.call_count, 1)
self.assertEqual(mock_run.call_count, 1)
mock_time.sleep.assert_called_once_with(0.1234)
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
@mock.patch("certbot_nginx._internal.configurator.subprocess.run")
@mock.patch("certbot_nginx._internal.configurator.logger.debug")
def test_nginx_restart_fail(self, mock_log_debug, mock_popen):
mocked = mock_popen()
mocked.communicate.return_value = ('', '')
def test_nginx_restart_fail(self, mock_log_debug, mock_run):
mocked = mock_run.return_value
mocked.stdout = ''
mocked.stderr = ''
mocked.returncode = 1
self.assertRaises(errors.MisconfigurationError, self.config.restart)
self.assertEqual(mocked.communicate.call_count, 2)
self.assertEqual(mock_run.call_count, 2)
mock_log_debug.assert_called_once_with("nginx reload failed:\n%s", "")
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
def test_no_nginx_start(self, mock_popen):
mock_popen.side_effect = OSError("Can't find program")
@mock.patch("certbot_nginx._internal.configurator.subprocess.run")
def test_no_nginx_start(self, mock_run):
mock_run.side_effect = OSError("Can't find program")
self.assertRaises(errors.MisconfigurationError, self.config.restart)
@mock.patch("certbot.util.run_script")
+1 -1
View File
@@ -1361,7 +1361,7 @@ def make_displayer(config: configuration.NamespaceConfig
if config.quiet:
config.noninteractive_mode = True
devnull = open(os.devnull, "w")
devnull = open(os.devnull, "w") # pylint: disable=consider-using-with
displayer = display_util.NoninteractiveDisplay(devnull)
elif config.noninteractive_mode:
displayer = display_util.NoninteractiveDisplay(sys.stdout)
+2 -2
View File
@@ -134,7 +134,7 @@ to serve all files under specified web root ({0})."""
"webroot when using the webroot plugin.")
return None if index == 0 else known_webroots[index - 1] # code == display_util.OK
def _prompt_for_new_webroot(self, domain, allowraise=False): # pylint: no-self-use
def _prompt_for_new_webroot(self, domain, allowraise=False):
code, webroot = ops.validated_directory(
_validate_webroot,
"Input the webroot for {0}:".format(domain),
@@ -192,7 +192,7 @@ to serve all files under specified web root ({0})."""
finally:
filesystem.umask(old_umask)
def _get_validation_path(self, root_path, achall): # pylint: no-self-use
def _get_validation_path(self, root_path, achall):
return os.path.join(root_path, achall.chall.encode("token"))
def _perform_single(self, achall):
-8
View File
@@ -14,7 +14,6 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import OpenSSL
import zope.component
from certbot import crypto_util
@@ -306,13 +305,6 @@ def should_renew(config, lineage):
def _avoid_invalidating_lineage(config, lineage, original_server):
"""Do not renew a valid cert with one from a staging server!"""
# Some lineages may have begun with --staging, but then had production
# certificates added to them
with open(lineage.cert) as the_file:
contents = the_file.read()
latest_cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, contents)
if util.is_staging(config.server):
if not util.is_staging(original_server):
if not config.break_my_certs:
+13 -12
View File
@@ -115,35 +115,36 @@ def underscores_for_unsupported_characters_in_path(path: str) -> str:
def execute_command(cmd_name: str, shell_cmd: str, env: Optional[dict] = None) -> Tuple[str, str]:
"""
Run a command:
- on Linux command will be run by the standard shell selected with Popen(shell=True)
- on Linux command will be run by the standard shell selected with
subprocess.run(shell=True)
- on Windows command will be run in a Powershell shell
:param str cmd_name: the user facing name of the hook being run
:param str shell_cmd: shell command to execute
:param dict env: environ to pass into Popen
:param dict env: environ to pass into subprocess.run
:returns: `tuple` (`str` stderr, `str` stdout)
"""
logger.info("Running %s command: %s", cmd_name, shell_cmd)
if POSIX_MODE:
cmd = subprocess.Popen(shell_cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, universal_newlines=True,
env=env)
proc = subprocess.run(shell_cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, universal_newlines=True,
check=False, env=env)
else:
line = ['powershell.exe', '-Command', shell_cmd]
cmd = subprocess.Popen(line, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, env=env)
proc = subprocess.run(line, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, check=False, env=env)
# universal_newlines causes Popen.communicate()
# to return str objects instead of bytes in Python 3
out, err = cmd.communicate()
# universal_newlines causes stdout and stderr to be str objects instead of
# bytes in Python 3
out, err = proc.stdout, proc.stderr
base_cmd = os.path.basename(shell_cmd.split(None, 1)[0])
if out:
logger.info('Output from %s command %s:\n%s', cmd_name, base_cmd, out)
if cmd.returncode != 0:
if proc.returncode != 0:
logger.error('%s command "%s" returned error code %d',
cmd_name, shell_cmd, cmd.returncode)
cmd_name, shell_cmd, proc.returncode)
if err:
logger.error('Error output from %s command %s:\n%s', cmd_name, base_cmd, err)
return err, out
+4 -5
View File
@@ -3,8 +3,8 @@ from datetime import datetime
from datetime import timedelta
import logging
import re
import subprocess
from subprocess import PIPE
from subprocess import Popen
from typing import Optional
from typing import Tuple
@@ -50,11 +50,10 @@ class RevocationChecker:
return
# New versions of openssl want -header var=val, old ones want -header var val
test_host_format = Popen(["openssl", "ocsp", "-header", "var", "val"],
test_host_format = subprocess.run(["openssl", "ocsp", "-header", "var", "val"],
stdout=PIPE, stderr=PIPE, universal_newlines=True,
env=util.env_no_snap_for_external_calls())
_out, err = test_host_format.communicate()
if "Missing =" in err:
check=False, env=util.env_no_snap_for_external_calls())
if "Missing =" in test_host_format.stderr:
self.host_args = lambda host: ["Host=" + host]
else:
self.host_args = lambda host: ["Host", host]
+5 -12
View File
@@ -198,6 +198,7 @@ class Reverter:
Read the file returning the lines, and a pointer to the end of the file.
"""
# pylint: disable=consider-using-with
# Open up filepath differently depending on if it already exists
if os.path.isfile(filepath):
op_fd = open(filepath, "r+")
@@ -348,26 +349,18 @@ class Reverter:
"""
commands_fp = os.path.join(self._get_cp_dir(temporary), "COMMANDS")
command_file = None
# It is strongly advised to set newline = '' on Python 3 with CSV,
# and it fixes problems on Windows.
kwargs = {'newline': ''}
try:
if os.path.isfile(commands_fp):
command_file = open(commands_fp, "a", **kwargs) # type: ignore
else:
command_file = open(commands_fp, "w", **kwargs) # type: ignore
csvwriter = csv.writer(command_file)
csvwriter.writerow(command)
mode = "a" if os.path.isfile(commands_fp) else "w"
with open(commands_fp, mode, **kwargs) as f: # type: ignore
csvwriter = csv.writer(f)
csvwriter.writerow(command)
except (IOError, OSError):
logger.error("Unable to register undo command")
raise errors.ReverterError(
"Unable to register undo command.")
finally:
if command_file is not None:
command_file.close()
def _get_cp_dir(self, temporary):
"""Return the proper reverter directory."""
+2 -2
View File
@@ -259,7 +259,7 @@ class FreezableMock:
def _create_get_utility_mock():
display = FreezableMock()
# Use pylint code for disable to keep on single line under line length limit
for name in interfaces.IDisplay.names(): # pylint: E1120
for name in interfaces.IDisplay.names():
if name != 'notification':
frozen_mock = FreezableMock(frozen=True, func=_assert_valid_call)
setattr(display, name, frozen_mock)
@@ -284,7 +284,7 @@ def _create_get_utility_mock_with_stdout(stdout):
display = FreezableMock()
# Use pylint code for disable to keep on single line under line length limit
for name in interfaces.IDisplay.names(): # pylint: E1120
for name in interfaces.IDisplay.names():
if name == 'notification':
frozen_mock = FreezableMock(frozen=True,
func=_write_msg)
+14 -15
View File
@@ -89,32 +89,31 @@ def env_no_snap_for_external_calls():
def run_script(params, log=logger.error):
"""Run the script with the given params.
:param list params: List of parameters to pass to Popen
:param list params: List of parameters to pass to subprocess.run
:param callable log: Logger method to use for errors
"""
try:
proc = subprocess.Popen(params,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
env=env_no_snap_for_external_calls())
proc = subprocess.run(params,
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
env=env_no_snap_for_external_calls())
except (OSError, ValueError):
msg = "Unable to run the command: %s" % " ".join(params)
log(msg)
raise errors.SubprocessError(msg)
stdout, stderr = proc.communicate()
if proc.returncode != 0:
msg = "Error while running %s.\n%s\n%s" % (
" ".join(params), stdout, stderr)
" ".join(params), proc.stdout, proc.stderr)
# Enter recovery routine...
log(msg)
raise errors.SubprocessError(msg)
return stdout, stderr
return proc.stdout, proc.stderr
def exe_exists(exe):
@@ -399,20 +398,20 @@ def get_python_os_info(pretty=False):
os_ver = info[1]
elif os_type.startswith('darwin'):
try:
proc = subprocess.Popen(
proc = subprocess.run(
["/usr/bin/sw_vers", "-productVersion"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True,
check=False, universal_newlines=True,
env=env_no_snap_for_external_calls(),
)
except OSError:
proc = subprocess.Popen(
proc = subprocess.run(
["sw_vers", "-productVersion"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True,
check=False, universal_newlines=True,
env=env_no_snap_for_external_calls(),
)
os_ver = proc.communicate()[0].rstrip('\n')
os_ver = proc.stdout.rstrip('\n')
elif os_type.startswith('freebsd'):
# eg "9.3-RC3-p1"
os_ver = os_ver.partition("-")[0]
+6 -5
View File
@@ -25,14 +25,15 @@ class ExecuteTest(unittest.TestCase):
def _test_common(self, returncode, stdout, stderr):
given_command = "foo"
given_name = "foo-hook"
with mock.patch("certbot.compat.misc.subprocess.Popen") as mock_popen:
mock_popen.return_value.communicate.return_value = (stdout, stderr)
mock_popen.return_value.returncode = returncode
with mock.patch("certbot.compat.misc.subprocess.run") as mock_run:
mock_run.return_value.stdout = stdout
mock_run.return_value.stderr = stderr
mock_run.return_value.returncode = returncode
with mock.patch("certbot.compat.misc.logger") as mock_logger:
self.assertEqual(self._call(given_name, given_command), (stderr, stdout))
executed_command = mock_popen.call_args[1].get(
"args", mock_popen.call_args[0][0])
executed_command = mock_run.call_args[1].get(
"args", mock_run.call_args[0][0])
if os.name == 'nt':
expected_command = ['powershell.exe', '-Command', given_command]
else:
+19 -23
View File
@@ -1078,29 +1078,25 @@ class MainTest(test_util.ConfigTestCase):
with test_util.patch_get_utility() as mock_get_utility:
if not quiet_mode:
mock_get_utility().notification.side_effect = write_msg
with mock.patch('certbot._internal.main.renewal.OpenSSL') as mock_ssl:
mock_latest = mock.MagicMock()
mock_latest.get_issuer.return_value = "Artificial pretend"
mock_ssl.crypto.load_certificate.return_value = mock_latest
with mock.patch('certbot._internal.main.renewal.crypto_util') \
as mock_crypto_util:
mock_crypto_util.notAfter.return_value = expiry_date
with mock.patch('certbot._internal.eff.handle_subscription'):
if not args:
args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly']
if extra_args:
args += extra_args
try:
ret, stdout, _, _ = self._call(args, stdout)
if ret:
print("Returned", ret)
raise AssertionError(ret)
assert not error_expected, "renewal should have errored"
except: # pylint: disable=bare-except
if not error_expected:
raise AssertionError(
"Unexpected renewal error:\n" +
traceback.format_exc())
with mock.patch('certbot._internal.main.renewal.crypto_util') \
as mock_crypto_util:
mock_crypto_util.notAfter.return_value = expiry_date
with mock.patch('certbot._internal.eff.handle_subscription'):
if not args:
args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly']
if extra_args:
args += extra_args
try:
ret, stdout, _, _ = self._call(args, stdout)
if ret:
print("Returned", ret)
raise AssertionError(ret)
assert not error_expected, "renewal should have errored"
except: # pylint: disable=bare-except
if not error_expected:
raise AssertionError(
"Unexpected renewal error:\n" +
traceback.format_exc())
if should_renew:
if reuse_key:
+9 -13
View File
@@ -40,11 +40,9 @@ class OCSPTestOpenSSL(unittest.TestCase):
def setUp(self):
from certbot import ocsp
with mock.patch('certbot.ocsp.Popen') as mock_popen:
with mock.patch('certbot.ocsp.subprocess.run') as mock_run:
with mock.patch('certbot.util.exe_exists') as mock_exists:
mock_communicate = mock.MagicMock()
mock_communicate.communicate.return_value = (None, out)
mock_popen.return_value = mock_communicate
mock_run.stderr = out
mock_exists.return_value = True
self.checker = ocsp.RevocationChecker(enforce_openssl_binary_usage=True)
@@ -52,28 +50,26 @@ class OCSPTestOpenSSL(unittest.TestCase):
pass
@mock.patch('certbot.ocsp.logger.info')
@mock.patch('certbot.ocsp.Popen')
@mock.patch('certbot.ocsp.subprocess.run')
@mock.patch('certbot.util.exe_exists')
def test_init(self, mock_exists, mock_popen, mock_log):
mock_communicate = mock.MagicMock()
mock_communicate.communicate.return_value = (None, out)
mock_popen.return_value = mock_communicate
def test_init(self, mock_exists, mock_run, mock_log):
mock_run.return_value.stderr = out
mock_exists.return_value = True
from certbot import ocsp
checker = ocsp.RevocationChecker(enforce_openssl_binary_usage=True)
self.assertEqual(mock_popen.call_count, 1)
self.assertEqual(mock_run.call_count, 1)
self.assertEqual(checker.host_args("x"), ["Host=x"])
mock_communicate.communicate.return_value = (None, out.partition("\n")[2])
mock_run.return_value.stderr = out.partition("\n")[2]
checker = ocsp.RevocationChecker(enforce_openssl_binary_usage=True)
self.assertEqual(checker.host_args("x"), ["Host", "x"])
self.assertIs(checker.broken, False)
mock_exists.return_value = False
mock_popen.call_count = 0
mock_run.call_count = 0
checker = ocsp.RevocationChecker(enforce_openssl_binary_usage=True)
self.assertEqual(mock_popen.call_count, 0)
self.assertEqual(mock_run.call_count, 0)
self.assertEqual(mock_log.call_count, 1)
self.assertIs(checker.broken, True)
+13 -17
View File
@@ -59,26 +59,26 @@ class RunScriptTest(unittest.TestCase):
from certbot.util import run_script
return run_script(params)
@mock.patch("certbot.util.subprocess.Popen")
def test_default(self, mock_popen):
@mock.patch("certbot.util.subprocess.run")
def test_default(self, mock_run):
"""These will be changed soon enough with reload."""
mock_popen().returncode = 0
mock_popen().communicate.return_value = ("stdout", "stderr")
mock_run().returncode = 0
mock_run().stdout = "stdout"
mock_run().stderr = "stderr"
out, err = self._call(["test"])
self.assertEqual(out, "stdout")
self.assertEqual(err, "stderr")
@mock.patch("certbot.util.subprocess.Popen")
def test_bad_process(self, mock_popen):
mock_popen.side_effect = OSError
@mock.patch("certbot.util.subprocess.run")
def test_bad_process(self, mock_run):
mock_run.side_effect = OSError
self.assertRaises(errors.SubprocessError, self._call, ["test"])
@mock.patch("certbot.util.subprocess.Popen")
def test_failure(self, mock_popen):
mock_popen().communicate.return_value = ("", "")
mock_popen().returncode = 1
@mock.patch("certbot.util.subprocess.run")
def test_failure(self, mock_run):
mock_run().returncode = 1
self.assertRaises(errors.SubprocessError, self._call, ["test"])
@@ -557,12 +557,8 @@ class OsInfoTest(unittest.TestCase):
with mock.patch('platform.system_alias',
return_value=('darwin', '', '')):
with mock.patch("subprocess.Popen") as popen_mock:
comm_mock = mock.Mock()
comm_attrs = {'communicate.return_value':
('42.42.42', 'error')}
comm_mock.configure_mock(**comm_attrs)
popen_mock.return_value = comm_mock
with mock.patch("subprocess.run") as run_mock:
run_mock().stdout = '42.42.42'
self.assertEqual(cbutil.get_python_os_info()[0], 'darwin')
self.assertEqual(cbutil.get_python_os_info()[1], '42.42.42')
+3 -3
View File
@@ -264,9 +264,9 @@ def subprocess_call(args):
:rtype: tuple
"""
process = subprocess.Popen(args, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, universal_newlines=True)
out, err = process.communicate()
process = subprocess.run(args, stdout=subprocess.PIPE, check=False,
stderr=subprocess.PIPE, universal_newlines=True)
out, err = process.stdout, process.stderr
logger.debug('Return code was %d', process.returncode)
log_output(logging.DEBUG, out, err)
return process.returncode, out, err
+2 -3
View File
@@ -58,10 +58,9 @@ cython = "*"
# needed. This dependency can be removed here once Certbot's support for the
# 3rd party mock library has been dropped.
mock = "*"
# Upgrading coverage, pylint, pytest, and some of pytest's plugins causes many
# test failures so let's pin these packages back for now.
# 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"
pylint = "2.4.3"
pytest = "3.2.5"
pytest-forked = "0.2"
# We were originally pinning back python-augeas for certbot-auto because we
+19 -19
View File
@@ -10,17 +10,17 @@ apacheconfig==0.3.2; python_version >= "3.6"
apipkg==1.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
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.3.3; python_version >= "3.6"
astroid==2.5.6; python_version >= "3.6" and python_version < "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.69; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0")
awscli==1.19.77; (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.69; 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.69; 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.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"
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")
@@ -32,13 +32,13 @@ cleo==0.8.1; python_version >= "3.6" and python_full_version < "3.0.0" or python
clikit==0.6.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
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; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
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")
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")
decorator==5.0.7
decorator==5.0.9
deprecated==1.2.12; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
distlib==0.3.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
distro==1.5.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
@@ -52,8 +52,8 @@ docutils==0.15.2; (python_version >= "2.6" and python_full_version < "3.0.0") or
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"
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.26.3; 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.3.0; python_version >= "3.6"
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-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"
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"
@@ -62,28 +62,28 @@ 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-resources==5.1.2; 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"
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"
invoke==1.5.0; python_version >= "3.6"
ipdb==0.13.7; 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"
isodate==0.6.0; python_version >= "3.6"
isort==4.3.21; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
isort==5.8.0; python_version >= "3.6" and python_version < "4.0"
jedi==0.18.0
jeepney==0.6.0; 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"
jinja2==2.11.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
jinja2==3.0.1; python_version >= "3.6"
jmespath==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
josepy==1.8.0; python_version >= "3.6"
jsonlines==2.0.0; python_version >= "3.6"
jsonpickle==2.0.0; python_version >= "3.6"
jsonschema==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
keyring==21.8.0; 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")
lazy-object-proxy==1.4.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
lazy-object-proxy==1.6.0; 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.6.0"
lockfile==0.12.2
markupsafe==1.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"
markupsafe==2.0.1; python_version >= "3.6"
matplotlib-inline==0.1.2; python_version >= "3.7"
mccabe==0.6.1; python_version >= "3.6"
mccabe==0.6.1; python_version >= "3.6" and python_version < "4.0"
mock==4.0.3; python_version >= "3.6"
msgpack==1.0.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
msrest==0.6.21; python_version >= "3.6"
@@ -105,7 +105,7 @@ 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.16.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.0; 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"
@@ -115,7 +115,7 @@ pygithub==1.55; python_version >= "3.6"
pygments==2.9.0
pyjwt==2.1.0; python_version >= "3.6"
pylev==1.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"
pylint==2.4.3; python_version >= "3.5"
pylint==2.8.2; python_version >= "3.6" and python_version < "4.0"
pynacl==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
pynsist==2.7; python_version >= "3.6"
pyopenssl==20.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"
@@ -160,8 +160,8 @@ sphinxcontrib-qthelp==1.0.3; python_version >= "3.6"
sphinxcontrib-serializinghtml==1.1.4; 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" or python_version == "3.6" and python_full_version >= "3.5.0" or python_version > "3.6" and python_full_version >= "3.5.0"
tomlkit==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"
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"
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"
traitlets==4.3.3
@@ -174,7 +174,7 @@ virtualenv==20.4.6; python_version >= "3.6" and python_full_version < "3.0.0" or
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.11.2; python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0")
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"
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"