From a12d91aef6c48765b1783f0741f55202343d7a9b Mon Sep 17 00:00:00 2001 From: alexzorin Date: Tue, 6 Apr 2021 08:50:12 +1000 Subject: [PATCH] fix various fd leaks (#8747) * fix various fd leaks * use context manager for display provider --- certbot/certbot/_internal/main.py | 40 ++++++++++++++++++--------- certbot/tests/log_test.py | 3 +- certbot/tests/plugins/webroot_test.py | 3 +- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/certbot/certbot/_internal/main.py b/certbot/certbot/_internal/main.py index ac4d18449..aed265ba3 100644 --- a/certbot/certbot/_internal/main.py +++ b/certbot/certbot/_internal/main.py @@ -4,6 +4,9 @@ import functools import logging.handlers import sys +from contextlib import contextmanager +from typing import Generator +from typing import IO from typing import Iterable from typing import List from typing import Optional @@ -1347,26 +1350,36 @@ def make_or_verify_needed_dirs(config): util.make_or_verify_dir(hook_dir, strict=config.strict_permissions) -def set_displayer(config): - """Set the displayer +@contextmanager +def make_displayer(config: configuration.NamespaceConfig + ) -> Generator[Union[display_util.NoninteractiveDisplay, + display_util.FileDisplay], None, None]: + """Creates a display object appropriate to the flags in the supplied config. :param config: Configuration object - :type config: interfaces.IConfig - :returns: `None` - :rtype: None + :returns: Display object implementing :class:`certbot.interfaces.IDisplay` """ + displayer: Union[None, display_util.NoninteractiveDisplay, + display_util.FileDisplay] = None + devnull: Optional[IO] = None + if config.quiet: config.noninteractive_mode = True - displayer: Union[None, display_util.NoninteractiveDisplay, display_util.FileDisplay] =\ - display_util.NoninteractiveDisplay(open(os.devnull, "w")) + devnull = open(os.devnull, "w") + displayer = display_util.NoninteractiveDisplay(devnull) elif config.noninteractive_mode: displayer = display_util.NoninteractiveDisplay(sys.stdout) else: - displayer = display_util.FileDisplay(sys.stdout, - config.force_interactive) - zope.component.provideUtility(displayer) + displayer = display_util.FileDisplay( + sys.stdout, config.force_interactive) + + try: + yield displayer + finally: + if devnull: + devnull.close() def main(cli_args=None): @@ -1411,11 +1424,12 @@ def main(cli_args=None): if config.func != plugins_cmd: # pylint: disable=comparison-with-callable raise - set_displayer(config) - # Reporter report = reporter.Reporter(config) zope.component.provideUtility(report) util.atexit_register(report.print_messages) - return config.func(config, plugins) + with make_displayer(config) as displayer: + zope.component.provideUtility(displayer) + + return config.func(config, plugins) diff --git a/certbot/tests/log_test.py b/certbot/tests/log_test.py index 7b1aacd5c..5960b37cf 100644 --- a/certbot/tests/log_test.py +++ b/certbot/tests/log_test.py @@ -28,7 +28,8 @@ class PreArgParseSetupTest(unittest.TestCase): @classmethod def _call(cls, *args, **kwargs): # pylint: disable=unused-argument from certbot._internal.log import pre_arg_parse_setup - return pre_arg_parse_setup() + with mock.patch('builtins.open', mock.mock_open()): + return pre_arg_parse_setup() @mock.patch('certbot._internal.log.sys') @mock.patch('certbot._internal.log.pre_arg_parse_except_hook') diff --git a/certbot/tests/plugins/webroot_test.py b/certbot/tests/plugins/webroot_test.py index e6fbd8e88..5792924a9 100644 --- a/certbot/tests/plugins/webroot_test.py +++ b/certbot/tests/plugins/webroot_test.py @@ -141,7 +141,8 @@ class AuthenticatorTest(unittest.TestCase): f.write("thingimy") filesystem.chmod(self.path, 0o000) try: - open(permission_canary, "r") + with open(permission_canary, "r"): + pass print("Warning, running tests as root skips permissions tests...") except IOError: # ok, permissions work, test away...