From 315ddb247f3dcf29eaa0bd357e3e88c9e6465eac Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 24 May 2021 10:02:55 -0700 Subject: [PATCH] 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 --- .pylintrc | 13 +- .../certbot_apache/_internal/apache_util.py | 5 +- .../certbot_apache/_internal/assertions.py | 4 +- .../certbot_apache/_internal/configurator.py | 2 +- certbot-apache/tests/debian_test.py | 9 +- certbot-apache/tests/parser_test.py | 16 ++- .../nginx_tests/context.py | 1 + .../utils/acme_server.py | 1 + .../utils/dns_server.py | 1 + .../certbot_nginx/_internal/configurator.py | 17 ++- certbot-nginx/tests/configurator_test.py | 127 ++++++++++-------- certbot/certbot/_internal/main.py | 2 +- certbot/certbot/_internal/plugins/webroot.py | 4 +- certbot/certbot/_internal/renewal.py | 8 -- certbot/certbot/compat/misc.py | 25 ++-- certbot/certbot/ocsp.py | 9 +- certbot/certbot/reverter.py | 17 +-- certbot/certbot/tests/util.py | 4 +- certbot/certbot/util.py | 29 ++-- certbot/tests/compat/misc_test.py | 11 +- certbot/tests/main_test.py | 42 +++--- certbot/tests/ocsp_test.py | 22 ++- certbot/tests/util_test.py | 30 ++--- tests/lock_test.py | 6 +- tools/pinning/pyproject.toml | 5 +- tools/requirements.txt | 38 +++--- 26 files changed, 223 insertions(+), 225 deletions(-) diff --git a/.pylintrc b/.pylintrc index e19077d8d..365413b32 100644 --- a/.pylintrc +++ b/.pylintrc @@ -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] diff --git a/certbot-apache/certbot_apache/_internal/apache_util.py b/certbot-apache/certbot_apache/_internal/apache_util.py index 9b9855a45..424d10fcf 100644 --- a/certbot-apache/certbot_apache/_internal/apache_util.py +++ b/certbot-apache/certbot_apache/_internal/apache_util.py @@ -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( diff --git a/certbot-apache/certbot_apache/_internal/assertions.py b/certbot-apache/certbot_apache/_internal/assertions.py index 53603c526..46bed3265 100644 --- a/certbot-apache/certbot_apache/_internal/assertions.py +++ b/certbot-apache/certbot_apache/_internal/assertions.py @@ -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) diff --git a/certbot-apache/certbot_apache/_internal/configurator.py b/certbot-apache/certbot_apache/_internal/configurator.py index abf199793..95ad514a7 100644 --- a/certbot-apache/certbot_apache/_internal/configurator.py +++ b/certbot-apache/certbot_apache/_internal/configurator.py @@ -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. diff --git a/certbot-apache/tests/debian_test.py b/certbot-apache/tests/debian_test.py index c72b8b6ae..b5a486ff1 100644 --- a/certbot-apache/tests/debian_test.py +++ b/certbot-apache/tests/debian_test.py @@ -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") diff --git a/certbot-apache/tests/parser_test.py b/certbot-apache/tests/parser_test.py index e30eca153..00ca23f7a 100644 --- a/certbot-apache/tests/parser_test.py +++ b/certbot-apache/tests/parser_test.py @@ -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) diff --git a/certbot-ci/certbot_integration_tests/nginx_tests/context.py b/certbot-ci/certbot_integration_tests/nginx_tests/context.py index 3ee1766a1..f273ed9ff 100644 --- a/certbot-ci/certbot_integration_tests/nginx_tests/context.py +++ b/certbot-ci/certbot_integration_tests/nginx_tests/context.py @@ -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 diff --git a/certbot-ci/certbot_integration_tests/utils/acme_server.py b/certbot-ci/certbot_integration_tests/utils/acme_server.py index ceebbe7ed..9fd5fcb39 100755 --- a/certbot-ci/certbot_integration_tests/utils/acme_server.py +++ b/certbot-ci/certbot_integration_tests/utils/acme_server.py @@ -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 ) diff --git a/certbot-ci/certbot_integration_tests/utils/dns_server.py b/certbot-ci/certbot_integration_tests/utils/dns_server.py index 48f5a533e..d9007ef3a 100644 --- a/certbot-ci/certbot_integration_tests/utils/dns_server.py +++ b/certbot-ci/certbot_integration_tests/utils/dns_server.py @@ -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", diff --git a/certbot-nginx/certbot_nginx/_internal/configurator.py b/certbot-nginx/certbot_nginx/_internal/configurator.py index 9fb81efaa..6a239bf36 100644 --- a/certbot-nginx/certbot_nginx/_internal/configurator.py +++ b/certbot-nginx/certbot_nginx/_internal/configurator.py @@ -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... diff --git a/certbot-nginx/tests/configurator_test.py b/certbot-nginx/tests/configurator_test.py index e6ceca344..a3c55b37b 100644 --- a/certbot-nginx/tests/configurator_test.py +++ b/certbot-nginx/tests/configurator_test.py @@ -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") diff --git a/certbot/certbot/_internal/main.py b/certbot/certbot/_internal/main.py index 70da95d20..ceb802733 100644 --- a/certbot/certbot/_internal/main.py +++ b/certbot/certbot/_internal/main.py @@ -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) diff --git a/certbot/certbot/_internal/plugins/webroot.py b/certbot/certbot/_internal/plugins/webroot.py index 3473d3c8e..41ad582ba 100644 --- a/certbot/certbot/_internal/plugins/webroot.py +++ b/certbot/certbot/_internal/plugins/webroot.py @@ -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): diff --git a/certbot/certbot/_internal/renewal.py b/certbot/certbot/_internal/renewal.py index f29709ef4..abc1273d8 100644 --- a/certbot/certbot/_internal/renewal.py +++ b/certbot/certbot/_internal/renewal.py @@ -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: diff --git a/certbot/certbot/compat/misc.py b/certbot/certbot/compat/misc.py index 3d2624906..4a89a9a51 100644 --- a/certbot/certbot/compat/misc.py +++ b/certbot/certbot/compat/misc.py @@ -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 diff --git a/certbot/certbot/ocsp.py b/certbot/certbot/ocsp.py index 0a842e108..deae72007 100644 --- a/certbot/certbot/ocsp.py +++ b/certbot/certbot/ocsp.py @@ -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] diff --git a/certbot/certbot/reverter.py b/certbot/certbot/reverter.py index ebc69fcbe..323f12ce5 100644 --- a/certbot/certbot/reverter.py +++ b/certbot/certbot/reverter.py @@ -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.""" diff --git a/certbot/certbot/tests/util.py b/certbot/certbot/tests/util.py index b892ad0a1..41f412e72 100644 --- a/certbot/certbot/tests/util.py +++ b/certbot/certbot/tests/util.py @@ -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) diff --git a/certbot/certbot/util.py b/certbot/certbot/util.py index 5f4a08dc7..aaf97e7df 100644 --- a/certbot/certbot/util.py +++ b/certbot/certbot/util.py @@ -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] diff --git a/certbot/tests/compat/misc_test.py b/certbot/tests/compat/misc_test.py index e87498cbe..85aad7a72 100644 --- a/certbot/tests/compat/misc_test.py +++ b/certbot/tests/compat/misc_test.py @@ -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: diff --git a/certbot/tests/main_test.py b/certbot/tests/main_test.py index 5689c0281..9b8d9b845 100644 --- a/certbot/tests/main_test.py +++ b/certbot/tests/main_test.py @@ -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: diff --git a/certbot/tests/ocsp_test.py b/certbot/tests/ocsp_test.py index 72a39e2d1..417eec8a7 100644 --- a/certbot/tests/ocsp_test.py +++ b/certbot/tests/ocsp_test.py @@ -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) diff --git a/certbot/tests/util_test.py b/certbot/tests/util_test.py index a78b614cf..c5494e64d 100644 --- a/certbot/tests/util_test.py +++ b/certbot/tests/util_test.py @@ -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') diff --git a/tests/lock_test.py b/tests/lock_test.py index f310b5753..d76ca0c1f 100644 --- a/tests/lock_test.py +++ b/tests/lock_test.py @@ -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 diff --git a/tools/pinning/pyproject.toml b/tools/pinning/pyproject.toml index 4dee88d51..11116f7f3 100644 --- a/tools/pinning/pyproject.toml +++ b/tools/pinning/pyproject.toml @@ -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 diff --git a/tools/requirements.txt b/tools/requirements.txt index 9e442f411..1354861b3 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -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"