From 28cb2f349f614e4e3b4fea23bdabbfca925f79ed Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Mon, 4 Apr 2016 16:27:59 -0400 Subject: [PATCH] Support loading closed config file descriptor. Refactored fix supplied in #735 and added tests. --- mkdocs/config/base.py | 9 +++- mkdocs/tests/config/base_tests.py | 75 ++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/mkdocs/config/base.py b/mkdocs/config/base.py index cd8d80b6..9f376a9b 100644 --- a/mkdocs/config/base.py +++ b/mkdocs/config/base.py @@ -106,7 +106,11 @@ def _open_config_file(config_file): if config_file is None: config_file = os.path.abspath('mkdocs.yml') - log.debug("Loading configuration file: %s", config_file) + # If closed file descriptor, get file path to reopen later. + if hasattr(config_file, 'closed') and config_file.closed: + config_file = config_file.name + + log.debug("Loading configuration file: {0}".format(config_file)) # If it is a string, we can assume it is a path and attempt to open it. if isinstance(config_file, utils.string_types): @@ -116,6 +120,9 @@ def _open_config_file(config_file): raise exceptions.ConfigurationError( "Config file '{0}' does not exist.".format(config_file)) + # Ensure file descriptor is at begining + config_file.seek(0) + return config_file diff --git a/mkdocs/tests/config/base_tests.py b/mkdocs/tests/config/base_tests.py index e7298674..8e4a0122 100644 --- a/mkdocs/tests/config/base_tests.py +++ b/mkdocs/tests/config/base_tests.py @@ -35,12 +35,85 @@ class ConfigBaseTests(unittest.TestCase): self.assertEqual(len(warnings), 0) - def test_load_missing_required(self): + def test_load_from_file(self): """ Users can explicitly set the config file using the '--config' option. Allows users to specify a config other than the default `mkdocs.yml`. """ + config_file = tempfile.NamedTemporaryFile('w', delete=False) + try: + config_file.write("site_name: MkDocs Test\n") + config_file.flush() + config_file.close() + + cfg = base.load_config(config_file=config_file.name) + self.assertIsInstance(cfg, base.Config) + self.assertEqual(cfg['site_name'], 'MkDocs Test') + finally: + os.remove(config_file.name) + + def test_load_from_missing_file(self): + + self.assertRaises(exceptions.ConfigurationError, + base.load_config, config_file='missing_file.yml') + + def test_load_from_open_file(self): + """ + `load_config` can accept an open file descriptor. + """ + + config_file = tempfile.NamedTemporaryFile('r+', delete=False) + try: + config_file.write("site_name: MkDocs Test\n") + config_file.flush() + + cfg = base.load_config(config_file=config_file) + self.assertIsInstance(cfg, base.Config) + self.assertEqual(cfg['site_name'], 'MkDocs Test') + # load_config will always close the file + self.assertTrue(config_file.closed) + finally: + os.remove(config_file.name) + + def test_load_from_closed_file(self): + """ + The `serve` command with auto-reload may pass in a closed file descriptor. + Ensure `load_config` reloads the closed file. + """ + + config_file = tempfile.NamedTemporaryFile('w', delete=False) + try: + config_file.write("site_name: MkDocs Test\n") + config_file.flush() + config_file.close() + + cfg = base.load_config(config_file=config_file) + self.assertIsInstance(cfg, base.Config) + self.assertEqual(cfg['site_name'], 'MkDocs Test') + finally: + os.remove(config_file.name) + + def test_load_from_deleted_file(self): + """ + Deleting the config file could trigger a server reload. + """ + + config_file = tempfile.NamedTemporaryFile('w', delete=False) + try: + config_file.write("site_name: MkDocs Test\n") + config_file.flush() + config_file.close() + finally: + os.remove(config_file.name) + self.assertRaises(exceptions.ConfigurationError, + base.load_config, config_file=config_file) + + def test_load_missing_required(self): + """ + `site_name` is a required setting. + """ + config_file = tempfile.NamedTemporaryFile('w', delete=False) try: config_file.write(