diff --git a/tests/test_config.py b/tests/test_config.py index 70a73e0..a696b5d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -14,12 +14,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import importlib from io import StringIO import os import shutil import sys import tempfile import unittest +import unittest.mock from tests.common import build_temp_workspace @@ -475,3 +477,38 @@ class IgnorePathConfigTestCase(unittest.TestCase): './s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:4:17: ' + trailing, './s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:5:5: ' + hyphen, ))) + + +class MissingModulePath(unittest.TestCase): + + @classmethod + def setUpClass(cls): + super(MissingModulePath, cls).setUpClass() + cls.mock_module_path = unittest.mock.patch.dict(config.__dict__, + {'_MODULE_PATH': None}) + + def setUp(self): + if 'importlib.resources' in sys.modules: + del sys.modules['importlib.resources'] + + @unittest.skipIf(sys.version_info < (3, 7), + 'importlib.resources not supported') + def test_missing_module_path(self): + '''When __file__ is undefined, importlib.resources should be used''' + with self.mock_module_path: + importlib.reload(config) + self.assertTrue(config.__dict__['_MODULE_PATH'] is None) + self.assertTrue('importlib.resources' in sys.modules.keys()) + self.assertTrue(config.get_extended_config('default') is not None) + + @unittest.skipIf(sys.version_info < (3, 7), + 'importlib.resources not supported') + def test_missing_importlib_resources(self): + '''When __file__ is undefined and importlib.resources is unavailable, + an ImportError exception is expected.''' + with self.mock_module_path: + sys.modules['importlib.resources'] = None # force an ImportError + error_regex = '.*importlib.resources library is required.*' + with self.assertRaisesRegex(ImportError, error_regex): + importlib.reload(config) + self.assertTrue(config.__dict__['_MODULE_PATH'] is None) diff --git a/yamllint/conf/__init__.py b/yamllint/conf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yamllint/config.py b/yamllint/config.py index a42e744..4f05e4c 100644 --- a/yamllint/config.py +++ b/yamllint/config.py @@ -19,8 +19,35 @@ import os.path import pathspec import yaml +import yamllint.conf import yamllint.rules +try: + _MODULE_PATH # allows unit tests to patch this +except NameError: + try: + _MODULE_PATH = __file__ + except NameError: # pragma: no cover + _MODULE_PATH = None + +if _MODULE_PATH is not None: + def _get_config(name): + module_dir = os.path.dirname(os.path.realpath(_MODULE_PATH)) + config_path = os.path.join(module_dir, 'conf', name) + with open(config_path) as config: + return config.read() +else: + try: + from importlib.resources import read_text + except ImportError as exc: + _IMPORT_ERR_MESSAGE = ( + "The importlib.resources library is required to locate resources " + "when __file__ is not defined") + raise ImportError(_IMPORT_ERR_MESSAGE) from exc + + def _get_config(name): + return read_text(yamllint.conf, name) + class YamlLintConfigError(Exception): pass @@ -90,8 +117,8 @@ class YamlLintConfig(object): # Does this conf override another conf that we need to load? if 'extends' in conf: - path = get_extended_config_file(conf['extends']) - base = YamlLintConfig(file=path) + config = get_extended_config(conf['extends']) + base = YamlLintConfig(content=config) try: self.extend(base) except Exception as e: @@ -200,14 +227,11 @@ def validate_rule_conf(rule, conf): return conf -def get_extended_config_file(name): +def get_extended_config(name): # Is it a standard conf shipped with yamllint... if '/' not in name: - std_conf = os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'conf', name + '.yaml') - - if os.path.isfile(std_conf): - return std_conf - + resource = name + '.yaml' + return _get_config(resource) # or a custom conf on filesystem? - return name + with open(name) as config: + return config.read()