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. # See https://github.com/PyCQA/pylint/issues/1498.
# 3) Same as point 2 for no-value-for-parameter. # 3) Same as point 2 for no-value-for-parameter.
# See https://github.com/PyCQA/pylint/issues/2820. # 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] [REPORTS]
@@ -220,13 +220,14 @@ def _get_runtime_cfg(command):
""" """
try: try:
proc = subprocess.Popen( proc = subprocess.run(
command, command,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, universal_newlines=True,
check=False,
env=util.env_no_snap_for_external_calls()) env=util.env_no_snap_for_external_calls())
stdout, stderr = proc.communicate() stdout, stderr = proc.stdout, proc.stderr
except (OSError, ValueError): except (OSError, ValueError):
logger.error( logger.error(
@@ -136,6 +136,6 @@ def assertEqualPathsList(first, second): # pragma: no cover
if any(isPass(path) for path in second): if any(isPass(path) for path in second):
return return
for fpath in first: 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: 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 vhost.enabled = True
return 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. """Enables module in Apache.
Both enables and reloads Apache so module is active. 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.run_script")
@mock.patch("certbot.util.exe_exists") @mock.patch("certbot.util.exe_exists")
@mock.patch("certbot_apache._internal.apache_util.subprocess.Popen") @mock.patch("certbot_apache._internal.apache_util.subprocess.run")
def test_enable_mod(self, mock_popen, mock_exe_exists, mock_run_script): def test_enable_mod(self, mock_run, mock_exe_exists, mock_run_script):
mock_popen().communicate.return_value = ("Define: DUMP_RUN_CFG", "") mock_run.return_value.stdout = "Define: DUMP_RUN_CFG"
mock_popen().returncode = 0 mock_run.return_value.stderr = ""
mock_run.return_value.returncode = 0
mock_exe_exists.return_value = True mock_exe_exists.return_value = True
self.config.enable_mod("ssl") self.config.enable_mod("ssl")
+9 -7
View File
@@ -305,17 +305,19 @@ class BasicParserTest(util.ParserTest):
self.assertRaises( self.assertRaises(
errors.PluginError, self.parser.update_runtime_variables) errors.PluginError, self.parser.update_runtime_variables)
@mock.patch("certbot_apache._internal.apache_util.subprocess.Popen") @mock.patch("certbot_apache._internal.apache_util.subprocess.run")
def test_update_runtime_vars_bad_ctl(self, mock_popen): def test_update_runtime_vars_bad_ctl(self, mock_run):
mock_popen.side_effect = OSError mock_run.side_effect = OSError
self.assertRaises( self.assertRaises(
errors.MisconfigurationError, errors.MisconfigurationError,
self.parser.update_runtime_variables) self.parser.update_runtime_variables)
@mock.patch("certbot_apache._internal.apache_util.subprocess.Popen") @mock.patch("certbot_apache._internal.apache_util.subprocess.run")
def test_update_runtime_vars_bad_exit(self, mock_popen): def test_update_runtime_vars_bad_exit(self, mock_run):
mock_popen().communicate.return_value = ("", "") mock_proc = mock_run.return_value
mock_popen.returncode = -1 mock_proc.stdout = ""
mock_proc.stderr = ""
mock_proc.returncode = -1
self.assertRaises( self.assertRaises(
errors.MisconfigurationError, errors.MisconfigurationError,
self.parser.update_runtime_variables) self.parser.update_runtime_variables)
@@ -51,6 +51,7 @@ class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
with open(self.nginx_config_path, 'w') as file: with open(self.nginx_config_path, 'w') as file:
file.write(self.nginx_config) file.write(self.nginx_config)
# pylint: disable=consider-using-with
process = subprocess.Popen(['nginx', '-c', self.nginx_config_path, '-g', 'daemon off;']) process = subprocess.Popen(['nginx', '-c', self.nginx_config_path, '-g', 'daemon off;'])
assert process.poll() is None assert process.poll() is None
@@ -240,6 +240,7 @@ class ACMEServer:
if not env: if not env:
env = os.environ env = os.environ
stdout = sys.stderr if force_stderr else self._stdout stdout = sys.stderr if force_stderr else self._stdout
# pylint: disable=consider-using-with
process = subprocess.Popen( process = subprocess.Popen(
command, stdout=stdout, stderr=subprocess.STDOUT, cwd=cwd, env=env command, stdout=stdout, stderr=subprocess.STDOUT, cwd=cwd, env=env
) )
@@ -83,6 +83,7 @@ class DNSServer:
def _start_bind(self): def _start_bind(self):
"""Launch the BIND9 server as a Docker container""" """Launch the BIND9 server as a Docker container"""
addr_str = "{}:{}".format(BIND_BIND_ADDRESS[0], BIND_BIND_ADDRESS[1]) addr_str = "{}:{}".format(BIND_BIND_ADDRESS[0], BIND_BIND_ADDRESS[1])
# pylint: disable=consider-using-with
self.process = subprocess.Popen( self.process = subprocess.Popen(
[ [
"docker", "docker",
@@ -985,13 +985,14 @@ class NginxConfigurator(common.Installer):
Unable to run Nginx version command Unable to run Nginx version command
""" """
try: try:
proc = subprocess.Popen( proc = subprocess.run(
[self.conf('ctl'), "-c", self.nginx_conf, "-V"], [self.conf('ctl'), "-c", self.nginx_conf, "-V"],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, universal_newlines=True,
check=False,
env=util.env_no_snap_for_external_calls()) 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: except (OSError, ValueError) as error:
logger.debug(str(error), exc_info=True) logger.debug(str(error), exc_info=True)
raise errors.PluginError( raise errors.PluginError(
@@ -1224,10 +1225,9 @@ def nginx_restart(nginx_ctl, nginx_conf, sleep_duration):
try: try:
reload_output: Text = u"" reload_output: Text = u""
with tempfile.TemporaryFile() as out: with tempfile.TemporaryFile() as out:
proc = subprocess.Popen([nginx_ctl, "-c", nginx_conf, "-s", "reload"], proc = subprocess.run([nginx_ctl, "-c", nginx_conf, "-s", "reload"],
env=util.env_no_snap_for_external_calls(), env=util.env_no_snap_for_external_calls(),
stdout=out, stderr=out) stdout=out, stderr=out, check=False)
proc.communicate()
out.seek(0) out.seek(0)
reload_output = out.read().decode("utf-8") 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 # Write to temporary files instead of piping because of communication issues on Arch
# https://github.com/certbot/certbot/issues/4324 # https://github.com/certbot/certbot/issues/4324
with tempfile.TemporaryFile() as out: with tempfile.TemporaryFile() as out:
nginx_proc = subprocess.Popen([nginx_ctl, "-c", nginx_conf], nginx_proc = subprocess.run([nginx_ctl, "-c", nginx_conf],
stdout=out, stderr=out, env=util.env_no_snap_for_external_calls()) stdout=out, stderr=out, env=util.env_no_snap_for_external_calls(), check=False)
nginx_proc.communicate()
if nginx_proc.returncode != 0: if nginx_proc.returncode != 0:
out.seek(0) out.seek(0)
# Enter recovery routine... # Enter recovery routine...
+68 -59
View File
@@ -42,15 +42,16 @@ class NginxConfiguratorTest(util.NginxTest):
self.assertEqual(13, len(self.config.parser.parsed)) self.assertEqual(13, len(self.config.parser.parsed))
@mock.patch("certbot_nginx._internal.configurator.util.exe_exists") @mock.patch("certbot_nginx._internal.configurator.util.exe_exists")
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen") @mock.patch("certbot_nginx._internal.configurator.subprocess.run")
def test_prepare_initializes_version(self, mock_popen, mock_exe_exists): def test_prepare_initializes_version(self, mock_run, mock_exe_exists):
mock_popen().communicate.return_value = ( mock_run.return_value.stdout = ""
"", "\n".join(["nginx version: nginx/1.6.2", mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/1.6.2",
"built by clang 6.0 (clang-600.0.56)" "built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)", " (based on LLVM 3.5svn)",
"TLS SNI support enabled", "TLS SNI support enabled",
"configure arguments: --prefix=/usr/local/Cellar/" "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 mock_exe_exists.return_value = True
@@ -347,141 +348,149 @@ class NginxConfiguratorTest(util.NginxTest):
self.assertEqual(mock_revert.call_count, 1) self.assertEqual(mock_revert.call_count, 1)
self.assertEqual(mock_restart.call_count, 2) self.assertEqual(mock_restart.call_count, 2)
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen") @mock.patch("certbot_nginx._internal.configurator.subprocess.run")
def test_get_version(self, mock_popen): def test_get_version(self, mock_run):
mock_popen().communicate.return_value = ( mock_run.return_value.stdout = ""
"", "\n".join(["nginx version: nginx/1.4.2", mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/1.4.2",
"built by clang 6.0 (clang-600.0.56)" "built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)", " (based on LLVM 3.5svn)",
"TLS SNI support enabled", "TLS SNI support enabled",
"configure arguments: --prefix=/usr/local/Cellar/" "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)) self.assertEqual(self.config.get_version(), (1, 4, 2))
mock_popen().communicate.return_value = ( mock_run.return_value.stdout = ""
"", "\n".join(["nginx version: nginx/0.9", mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/0.9",
"built by clang 6.0 (clang-600.0.56)" "built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)", " (based on LLVM 3.5svn)",
"TLS SNI support enabled", "TLS SNI support enabled",
"configure arguments: --with-http_ssl_module"])) "configure arguments: --with-http_ssl_module"])
self.assertEqual(self.config.get_version(), (0, 9)) self.assertEqual(self.config.get_version(), (0, 9))
mock_popen().communicate.return_value = ( mock_run.return_value.stdout = ""
"", "\n".join(["blah 0.0.1", mock_run.return_value.stderr = "\n".join(
["blah 0.0.1",
"built by clang 6.0 (clang-600.0.56)" "built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)", " (based on LLVM 3.5svn)",
"TLS SNI support enabled", "TLS SNI support enabled",
"configure arguments: --with-http_ssl_module"])) "configure arguments: --with-http_ssl_module"])
self.assertRaises(errors.PluginError, self.config.get_version) self.assertRaises(errors.PluginError, self.config.get_version)
mock_popen().communicate.return_value = ( mock_run.return_value.stdout = ""
"", "\n".join(["nginx version: nginx/1.4.2", mock_run.return_value.stderr = "\n".join(
"TLS SNI support enabled"])) ["nginx version: nginx/1.4.2",
"TLS SNI support enabled"])
self.assertRaises(errors.PluginError, self.config.get_version) self.assertRaises(errors.PluginError, self.config.get_version)
mock_popen().communicate.return_value = ( mock_run.return_value.stdout = ""
"", "\n".join(["nginx version: nginx/1.4.2", mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/1.4.2",
"built by clang 6.0 (clang-600.0.56)" "built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)", " (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) self.assertRaises(errors.PluginError, self.config.get_version)
mock_popen().communicate.return_value = ( mock_run.return_value.stdout = ""
"", "\n".join(["nginx version: nginx/0.8.1", mock_run.return_value.stderr = "\n".join(
["nginx version: nginx/0.8.1",
"built by clang 6.0 (clang-600.0.56)" "built by clang 6.0 (clang-600.0.56)"
" (based on LLVM 3.5svn)", " (based on LLVM 3.5svn)",
"TLS SNI support enabled", "TLS SNI support enabled",
"configure arguments: --with-http_ssl_module"])) "configure arguments: --with-http_ssl_module"])
self.assertRaises(errors.NotSupportedError, self.config.get_version) 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) self.assertRaises(errors.PluginError, self.config.get_version)
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen") @mock.patch("certbot_nginx._internal.configurator.subprocess.run")
def test_get_openssl_version(self, mock_popen): def test_get_openssl_version(self, mock_run):
# pylint: disable=protected-access # 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 nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9) built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
built with OpenSSL 1.0.2g 1 Mar 2016 built with OpenSSL 1.0.2g 1 Mar 2016
TLS SNI support enabled TLS SNI support enabled
configure arguments: configure arguments:
""") """
self.assertEqual(self.config._get_openssl_version(), "1.0.2g") 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 nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9) 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 built with OpenSSL 1.0.2-beta1 1 Mar 2016
TLS SNI support enabled TLS SNI support enabled
configure arguments: configure arguments:
""") """
self.assertEqual(self.config._get_openssl_version(), "1.0.2-beta1") 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 nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9) built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
built with OpenSSL 1.0.2 1 Mar 2016 built with OpenSSL 1.0.2 1 Mar 2016
TLS SNI support enabled TLS SNI support enabled
configure arguments: configure arguments:
""") """
self.assertEqual(self.config._get_openssl_version(), "1.0.2") 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 nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9) 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) built with OpenSSL 1.0.2g 1 Mar 2016 (running with OpenSSL 1.0.2a 1 Mar 2016)
TLS SNI support enabled TLS SNI support enabled
configure arguments: configure arguments:
""") """
self.assertEqual(self.config._get_openssl_version(), "1.0.2a") 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 nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9) built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
built with LibreSSL 2.2.2 built with LibreSSL 2.2.2
TLS SNI support enabled TLS SNI support enabled
configure arguments: configure arguments:
""") """
self.assertEqual(self.config._get_openssl_version(), "") 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 nginx version: nginx/1.15.5
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9) built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
TLS SNI support enabled TLS SNI support enabled
configure arguments: configure arguments:
""") """
self.assertEqual(self.config._get_openssl_version(), "") 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") @mock.patch("certbot_nginx._internal.configurator.time")
def test_nginx_restart(self, mock_time, mock_popen): def test_nginx_restart(self, mock_time, mock_run):
mocked = mock_popen() mocked = mock_run.return_value
mocked.communicate.return_value = ('', '') mocked.stdout = ''
mocked.stderr = ''
mocked.returncode = 0 mocked.returncode = 0
self.config.restart() 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_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") @mock.patch("certbot_nginx._internal.configurator.logger.debug")
def test_nginx_restart_fail(self, mock_log_debug, mock_popen): def test_nginx_restart_fail(self, mock_log_debug, mock_run):
mocked = mock_popen() mocked = mock_run.return_value
mocked.communicate.return_value = ('', '') mocked.stdout = ''
mocked.stderr = ''
mocked.returncode = 1 mocked.returncode = 1
self.assertRaises(errors.MisconfigurationError, self.config.restart) 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_log_debug.assert_called_once_with("nginx reload failed:\n%s", "")
@mock.patch("certbot_nginx._internal.configurator.subprocess.Popen") @mock.patch("certbot_nginx._internal.configurator.subprocess.run")
def test_no_nginx_start(self, mock_popen): def test_no_nginx_start(self, mock_run):
mock_popen.side_effect = OSError("Can't find program") mock_run.side_effect = OSError("Can't find program")
self.assertRaises(errors.MisconfigurationError, self.config.restart) self.assertRaises(errors.MisconfigurationError, self.config.restart)
@mock.patch("certbot.util.run_script") @mock.patch("certbot.util.run_script")
+1 -1
View File
@@ -1361,7 +1361,7 @@ def make_displayer(config: configuration.NamespaceConfig
if config.quiet: if config.quiet:
config.noninteractive_mode = True config.noninteractive_mode = True
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w") # pylint: disable=consider-using-with
displayer = display_util.NoninteractiveDisplay(devnull) displayer = display_util.NoninteractiveDisplay(devnull)
elif config.noninteractive_mode: elif config.noninteractive_mode:
displayer = display_util.NoninteractiveDisplay(sys.stdout) 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.") "webroot when using the webroot plugin.")
return None if index == 0 else known_webroots[index - 1] # code == display_util.OK 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( code, webroot = ops.validated_directory(
_validate_webroot, _validate_webroot,
"Input the webroot for {0}:".format(domain), "Input the webroot for {0}:".format(domain),
@@ -192,7 +192,7 @@ to serve all files under specified web root ({0})."""
finally: finally:
filesystem.umask(old_umask) 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")) return os.path.join(root_path, achall.chall.encode("token"))
def _perform_single(self, achall): 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 ec
from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import load_pem_private_key from cryptography.hazmat.primitives.serialization import load_pem_private_key
import OpenSSL
import zope.component import zope.component
from certbot import crypto_util from certbot import crypto_util
@@ -306,13 +305,6 @@ def should_renew(config, lineage):
def _avoid_invalidating_lineage(config, lineage, original_server): def _avoid_invalidating_lineage(config, lineage, original_server):
"""Do not renew a valid cert with one from a staging 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 util.is_staging(config.server):
if not util.is_staging(original_server): if not util.is_staging(original_server):
if not config.break_my_certs: 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]: def execute_command(cmd_name: str, shell_cmd: str, env: Optional[dict] = None) -> Tuple[str, str]:
""" """
Run a command: 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 - 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 cmd_name: the user facing name of the hook being run
:param str shell_cmd: shell command to execute :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) :returns: `tuple` (`str` stderr, `str` stdout)
""" """
logger.info("Running %s command: %s", cmd_name, shell_cmd) logger.info("Running %s command: %s", cmd_name, shell_cmd)
if POSIX_MODE: if POSIX_MODE:
cmd = subprocess.Popen(shell_cmd, shell=True, stdout=subprocess.PIPE, proc = subprocess.run(shell_cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, universal_newlines=True, stderr=subprocess.PIPE, universal_newlines=True,
env=env) check=False, env=env)
else: else:
line = ['powershell.exe', '-Command', shell_cmd] line = ['powershell.exe', '-Command', shell_cmd]
cmd = subprocess.Popen(line, stdout=subprocess.PIPE, stderr=subprocess.PIPE, proc = subprocess.run(line, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, env=env) universal_newlines=True, check=False, env=env)
# universal_newlines causes Popen.communicate() # universal_newlines causes stdout and stderr to be str objects instead of
# to return str objects instead of bytes in Python 3 # bytes in Python 3
out, err = cmd.communicate() out, err = proc.stdout, proc.stderr
base_cmd = os.path.basename(shell_cmd.split(None, 1)[0]) base_cmd = os.path.basename(shell_cmd.split(None, 1)[0])
if out: if out:
logger.info('Output from %s command %s:\n%s', cmd_name, base_cmd, 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', logger.error('%s command "%s" returned error code %d',
cmd_name, shell_cmd, cmd.returncode) cmd_name, shell_cmd, proc.returncode)
if err: if err:
logger.error('Error output from %s command %s:\n%s', cmd_name, base_cmd, err) logger.error('Error output from %s command %s:\n%s', cmd_name, base_cmd, err)
return err, out return err, out
+4 -5
View File
@@ -3,8 +3,8 @@ from datetime import datetime
from datetime import timedelta from datetime import timedelta
import logging import logging
import re import re
import subprocess
from subprocess import PIPE from subprocess import PIPE
from subprocess import Popen
from typing import Optional from typing import Optional
from typing import Tuple from typing import Tuple
@@ -50,11 +50,10 @@ class RevocationChecker:
return return
# New versions of openssl want -header var=val, old ones want -header var val # 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, stdout=PIPE, stderr=PIPE, universal_newlines=True,
env=util.env_no_snap_for_external_calls()) check=False, env=util.env_no_snap_for_external_calls())
_out, err = test_host_format.communicate() if "Missing =" in test_host_format.stderr:
if "Missing =" in err:
self.host_args = lambda host: ["Host=" + host] self.host_args = lambda host: ["Host=" + host]
else: else:
self.host_args = lambda host: ["Host", host] 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. 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 # Open up filepath differently depending on if it already exists
if os.path.isfile(filepath): if os.path.isfile(filepath):
op_fd = open(filepath, "r+") op_fd = open(filepath, "r+")
@@ -348,26 +349,18 @@ class Reverter:
""" """
commands_fp = os.path.join(self._get_cp_dir(temporary), "COMMANDS") 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, # It is strongly advised to set newline = '' on Python 3 with CSV,
# and it fixes problems on Windows. # and it fixes problems on Windows.
kwargs = {'newline': ''} kwargs = {'newline': ''}
try: try:
if os.path.isfile(commands_fp): mode = "a" if os.path.isfile(commands_fp) else "w"
command_file = open(commands_fp, "a", **kwargs) # type: ignore with open(commands_fp, mode, **kwargs) as f: # type: ignore
else: csvwriter = csv.writer(f)
command_file = open(commands_fp, "w", **kwargs) # type: ignore csvwriter.writerow(command)
csvwriter = csv.writer(command_file)
csvwriter.writerow(command)
except (IOError, OSError): except (IOError, OSError):
logger.error("Unable to register undo command") logger.error("Unable to register undo command")
raise errors.ReverterError( raise errors.ReverterError(
"Unable to register undo command.") "Unable to register undo command.")
finally:
if command_file is not None:
command_file.close()
def _get_cp_dir(self, temporary): def _get_cp_dir(self, temporary):
"""Return the proper reverter directory.""" """Return the proper reverter directory."""
+2 -2
View File
@@ -259,7 +259,7 @@ class FreezableMock:
def _create_get_utility_mock(): def _create_get_utility_mock():
display = FreezableMock() display = FreezableMock()
# Use pylint code for disable to keep on single line under line length limit # 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': if name != 'notification':
frozen_mock = FreezableMock(frozen=True, func=_assert_valid_call) frozen_mock = FreezableMock(frozen=True, func=_assert_valid_call)
setattr(display, name, frozen_mock) setattr(display, name, frozen_mock)
@@ -284,7 +284,7 @@ def _create_get_utility_mock_with_stdout(stdout):
display = FreezableMock() display = FreezableMock()
# Use pylint code for disable to keep on single line under line length limit # 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': if name == 'notification':
frozen_mock = FreezableMock(frozen=True, frozen_mock = FreezableMock(frozen=True,
func=_write_msg) 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): def run_script(params, log=logger.error):
"""Run the script with the given params. """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 :param callable log: Logger method to use for errors
""" """
try: try:
proc = subprocess.Popen(params, proc = subprocess.run(params,
stdout=subprocess.PIPE, check=False,
stderr=subprocess.PIPE, stdout=subprocess.PIPE,
universal_newlines=True, stderr=subprocess.PIPE,
env=env_no_snap_for_external_calls()) universal_newlines=True,
env=env_no_snap_for_external_calls())
except (OSError, ValueError): except (OSError, ValueError):
msg = "Unable to run the command: %s" % " ".join(params) msg = "Unable to run the command: %s" % " ".join(params)
log(msg) log(msg)
raise errors.SubprocessError(msg) raise errors.SubprocessError(msg)
stdout, stderr = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
msg = "Error while running %s.\n%s\n%s" % ( msg = "Error while running %s.\n%s\n%s" % (
" ".join(params), stdout, stderr) " ".join(params), proc.stdout, proc.stderr)
# Enter recovery routine... # Enter recovery routine...
log(msg) log(msg)
raise errors.SubprocessError(msg) raise errors.SubprocessError(msg)
return stdout, stderr return proc.stdout, proc.stderr
def exe_exists(exe): def exe_exists(exe):
@@ -399,20 +398,20 @@ def get_python_os_info(pretty=False):
os_ver = info[1] os_ver = info[1]
elif os_type.startswith('darwin'): elif os_type.startswith('darwin'):
try: try:
proc = subprocess.Popen( proc = subprocess.run(
["/usr/bin/sw_vers", "-productVersion"], ["/usr/bin/sw_vers", "-productVersion"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, check=False, universal_newlines=True,
env=env_no_snap_for_external_calls(), env=env_no_snap_for_external_calls(),
) )
except OSError: except OSError:
proc = subprocess.Popen( proc = subprocess.run(
["sw_vers", "-productVersion"], ["sw_vers", "-productVersion"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, check=False, universal_newlines=True,
env=env_no_snap_for_external_calls(), 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'): elif os_type.startswith('freebsd'):
# eg "9.3-RC3-p1" # eg "9.3-RC3-p1"
os_ver = os_ver.partition("-")[0] 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): def _test_common(self, returncode, stdout, stderr):
given_command = "foo" given_command = "foo"
given_name = "foo-hook" given_name = "foo-hook"
with mock.patch("certbot.compat.misc.subprocess.Popen") as mock_popen: with mock.patch("certbot.compat.misc.subprocess.run") as mock_run:
mock_popen.return_value.communicate.return_value = (stdout, stderr) mock_run.return_value.stdout = stdout
mock_popen.return_value.returncode = returncode mock_run.return_value.stderr = stderr
mock_run.return_value.returncode = returncode
with mock.patch("certbot.compat.misc.logger") as mock_logger: with mock.patch("certbot.compat.misc.logger") as mock_logger:
self.assertEqual(self._call(given_name, given_command), (stderr, stdout)) self.assertEqual(self._call(given_name, given_command), (stderr, stdout))
executed_command = mock_popen.call_args[1].get( executed_command = mock_run.call_args[1].get(
"args", mock_popen.call_args[0][0]) "args", mock_run.call_args[0][0])
if os.name == 'nt': if os.name == 'nt':
expected_command = ['powershell.exe', '-Command', given_command] expected_command = ['powershell.exe', '-Command', given_command]
else: else:
+19 -23
View File
@@ -1078,29 +1078,25 @@ class MainTest(test_util.ConfigTestCase):
with test_util.patch_get_utility() as mock_get_utility: with test_util.patch_get_utility() as mock_get_utility:
if not quiet_mode: if not quiet_mode:
mock_get_utility().notification.side_effect = write_msg mock_get_utility().notification.side_effect = write_msg
with mock.patch('certbot._internal.main.renewal.OpenSSL') as mock_ssl: with mock.patch('certbot._internal.main.renewal.crypto_util') \
mock_latest = mock.MagicMock() as mock_crypto_util:
mock_latest.get_issuer.return_value = "Artificial pretend" mock_crypto_util.notAfter.return_value = expiry_date
mock_ssl.crypto.load_certificate.return_value = mock_latest with mock.patch('certbot._internal.eff.handle_subscription'):
with mock.patch('certbot._internal.main.renewal.crypto_util') \ if not args:
as mock_crypto_util: args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly']
mock_crypto_util.notAfter.return_value = expiry_date if extra_args:
with mock.patch('certbot._internal.eff.handle_subscription'): args += extra_args
if not args: try:
args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly'] ret, stdout, _, _ = self._call(args, stdout)
if extra_args: if ret:
args += extra_args print("Returned", ret)
try: raise AssertionError(ret)
ret, stdout, _, _ = self._call(args, stdout) assert not error_expected, "renewal should have errored"
if ret: except: # pylint: disable=bare-except
print("Returned", ret) if not error_expected:
raise AssertionError(ret) raise AssertionError(
assert not error_expected, "renewal should have errored" "Unexpected renewal error:\n" +
except: # pylint: disable=bare-except traceback.format_exc())
if not error_expected:
raise AssertionError(
"Unexpected renewal error:\n" +
traceback.format_exc())
if should_renew: if should_renew:
if reuse_key: if reuse_key:
+9 -13
View File
@@ -40,11 +40,9 @@ class OCSPTestOpenSSL(unittest.TestCase):
def setUp(self): def setUp(self):
from certbot import ocsp 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: with mock.patch('certbot.util.exe_exists') as mock_exists:
mock_communicate = mock.MagicMock() mock_run.stderr = out
mock_communicate.communicate.return_value = (None, out)
mock_popen.return_value = mock_communicate
mock_exists.return_value = True mock_exists.return_value = True
self.checker = ocsp.RevocationChecker(enforce_openssl_binary_usage=True) self.checker = ocsp.RevocationChecker(enforce_openssl_binary_usage=True)
@@ -52,28 +50,26 @@ class OCSPTestOpenSSL(unittest.TestCase):
pass pass
@mock.patch('certbot.ocsp.logger.info') @mock.patch('certbot.ocsp.logger.info')
@mock.patch('certbot.ocsp.Popen') @mock.patch('certbot.ocsp.subprocess.run')
@mock.patch('certbot.util.exe_exists') @mock.patch('certbot.util.exe_exists')
def test_init(self, mock_exists, mock_popen, mock_log): def test_init(self, mock_exists, mock_run, mock_log):
mock_communicate = mock.MagicMock() mock_run.return_value.stderr = out
mock_communicate.communicate.return_value = (None, out)
mock_popen.return_value = mock_communicate
mock_exists.return_value = True mock_exists.return_value = True
from certbot import ocsp from certbot import ocsp
checker = ocsp.RevocationChecker(enforce_openssl_binary_usage=True) 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"]) 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) checker = ocsp.RevocationChecker(enforce_openssl_binary_usage=True)
self.assertEqual(checker.host_args("x"), ["Host", "x"]) self.assertEqual(checker.host_args("x"), ["Host", "x"])
self.assertIs(checker.broken, False) self.assertIs(checker.broken, False)
mock_exists.return_value = False mock_exists.return_value = False
mock_popen.call_count = 0 mock_run.call_count = 0
checker = ocsp.RevocationChecker(enforce_openssl_binary_usage=True) 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.assertEqual(mock_log.call_count, 1)
self.assertIs(checker.broken, True) self.assertIs(checker.broken, True)
+13 -17
View File
@@ -59,26 +59,26 @@ class RunScriptTest(unittest.TestCase):
from certbot.util import run_script from certbot.util import run_script
return run_script(params) return run_script(params)
@mock.patch("certbot.util.subprocess.Popen") @mock.patch("certbot.util.subprocess.run")
def test_default(self, mock_popen): def test_default(self, mock_run):
"""These will be changed soon enough with reload.""" """These will be changed soon enough with reload."""
mock_popen().returncode = 0 mock_run().returncode = 0
mock_popen().communicate.return_value = ("stdout", "stderr") mock_run().stdout = "stdout"
mock_run().stderr = "stderr"
out, err = self._call(["test"]) out, err = self._call(["test"])
self.assertEqual(out, "stdout") self.assertEqual(out, "stdout")
self.assertEqual(err, "stderr") self.assertEqual(err, "stderr")
@mock.patch("certbot.util.subprocess.Popen") @mock.patch("certbot.util.subprocess.run")
def test_bad_process(self, mock_popen): def test_bad_process(self, mock_run):
mock_popen.side_effect = OSError mock_run.side_effect = OSError
self.assertRaises(errors.SubprocessError, self._call, ["test"]) self.assertRaises(errors.SubprocessError, self._call, ["test"])
@mock.patch("certbot.util.subprocess.Popen") @mock.patch("certbot.util.subprocess.run")
def test_failure(self, mock_popen): def test_failure(self, mock_run):
mock_popen().communicate.return_value = ("", "") mock_run().returncode = 1
mock_popen().returncode = 1
self.assertRaises(errors.SubprocessError, self._call, ["test"]) self.assertRaises(errors.SubprocessError, self._call, ["test"])
@@ -557,12 +557,8 @@ class OsInfoTest(unittest.TestCase):
with mock.patch('platform.system_alias', with mock.patch('platform.system_alias',
return_value=('darwin', '', '')): return_value=('darwin', '', '')):
with mock.patch("subprocess.Popen") as popen_mock: with mock.patch("subprocess.run") as run_mock:
comm_mock = mock.Mock() run_mock().stdout = '42.42.42'
comm_attrs = {'communicate.return_value':
('42.42.42', 'error')}
comm_mock.configure_mock(**comm_attrs)
popen_mock.return_value = comm_mock
self.assertEqual(cbutil.get_python_os_info()[0], 'darwin') self.assertEqual(cbutil.get_python_os_info()[0], 'darwin')
self.assertEqual(cbutil.get_python_os_info()[1], '42.42.42') 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 :rtype: tuple
""" """
process = subprocess.Popen(args, stdout=subprocess.PIPE, process = subprocess.run(args, stdout=subprocess.PIPE, check=False,
stderr=subprocess.PIPE, universal_newlines=True) stderr=subprocess.PIPE, universal_newlines=True)
out, err = process.communicate() out, err = process.stdout, process.stderr
logger.debug('Return code was %d', process.returncode) logger.debug('Return code was %d', process.returncode)
log_output(logging.DEBUG, out, err) log_output(logging.DEBUG, out, err)
return process.returncode, 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 # needed. This dependency can be removed here once Certbot's support for the
# 3rd party mock library has been dropped. # 3rd party mock library has been dropped.
mock = "*" mock = "*"
# Upgrading coverage, pylint, pytest, and some of pytest's plugins causes many # Upgrading coverage, pytest, and some of pytest's plugins causes many test
# test failures so let's pin these packages back for now. # failures so let's pin these packages back for now.
coverage = "4.5.4" coverage = "4.5.4"
pylint = "2.4.3"
pytest = "3.2.5" pytest = "3.2.5"
pytest-forked = "0.2" pytest-forked = "0.2"
# We were originally pinning back python-augeas for certbot-auto because we # 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" 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" 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 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" 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" 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" 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 backcall==0.2.0
bcrypt==3.2.0; python_version >= "3.6" bcrypt==3.2.0; python_version >= "3.6"
beautifulsoup4==4.9.3; python_version >= "3.6" and python_version < "4.0" 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" 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" 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.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.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" 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" 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") 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" 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" 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") 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" 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==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") 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" 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") 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" 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" 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" 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" 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" 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" 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-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.3.0; 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-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.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" 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" 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" 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.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" invoke==1.5.0; python_version >= "3.6"
ipdb==0.13.7; python_version >= "3.6" ipdb==0.13.7; python_version >= "3.6"
ipython-genutils==0.2.0; python_version == "3.6" ipython-genutils==0.2.0; python_version == "3.6"
ipython==7.16.1; python_version == "3.6" ipython==7.16.1; python_version == "3.6"
ipython==7.23.1; python_version >= "3.7" ipython==7.23.1; python_version >= "3.7"
isodate==0.6.0; python_version >= "3.6" 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 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" 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" 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" josepy==1.8.0; python_version >= "3.6"
jsonlines==2.0.0; python_version >= "3.6" jsonlines==2.0.0; python_version >= "3.6"
jsonpickle==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" 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") 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 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" 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" 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" 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" 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-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" 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 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" 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" 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" 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 pygments==2.9.0
pyjwt==2.1.0; python_version >= "3.6" 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" 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" 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" 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" 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" 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" 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" 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" 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.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" 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" 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.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 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" 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" 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" 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" 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.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" 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"