diff --git a/requirements.txt b/requirements.txt index 06bc16d..d3d84e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ PyYAML ~= 3.11 ansicolor ~= 0.2.4 chardet >= 2.3.0 +typing >= 3.6.2 diff --git a/test/fixture/ast/inline_python.vim b/test/fixture/ast/inline_python.vim new file mode 100644 index 0000000..995074c --- /dev/null +++ b/test/fixture/ast/inline_python.vim @@ -0,0 +1,4 @@ +let s:inline_python = 'python << EOF' +exec s:inline_python +import os +EOF diff --git a/test/fixture/config/xdg_config_home/.vintrc.yaml b/test/fixture/config/xdg_config_home/.vintrc.yaml new file mode 100644 index 0000000..c85cb9b --- /dev/null +++ b/test/fixture/config/xdg_config_home/.vintrc.yaml @@ -0,0 +1,13 @@ +cmdargs: + verbose: true + severity: warning + error-limit: 10 + + +policies: + ProhibitSomethingEvil: + # Some comments + enabled: false + + ProhibitSomethingDengerous: + enabled: true diff --git a/test/unit/vint/linting/config/test_config_global_source.py b/test/unit/vint/linting/config/test_config_global_source.py index ce10a18..837cb38 100644 --- a/test/unit/vint/linting/config/test_config_global_source.py +++ b/test/unit/vint/linting/config/test_config_global_source.py @@ -7,31 +7,45 @@ class TestConfigGlobalSource(ConfigSourceAssertion, unittest.TestCase): def test_get_config_dict(self): env = { - 'home_path': get_fixture_path('dummy_home') + 'home_path': get_fixture_path('dummy_home'), + 'xdg_config_home': get_fixture_path('unexistent_xdg_config_home'), } - - expected_type = { + expected_config_dict = { 'cmdargs': { 'verbose': bool, 'error-limit': int, 'severity': Enum, } } - config_source = self.initialize_config_source_with_env(ConfigGlobalSource, env) - self.assertConfigValueType(config_source, expected_type) + self.assertConfigValueType(config_source, expected_config_dict) def test_get_config_dict_with_no_global_config(self): env = { - 'home_path': get_fixture_path('unexistent_home') + 'home_path': get_fixture_path('unexistent_home'), + 'xdg_config_home': get_fixture_path('unexistent_xdg_config_home'), } - expected_config_dict = {} - config_source = self.initialize_config_source_with_env(ConfigGlobalSource, env) self.assertConfigDict(config_source, expected_config_dict) + def test_get_config_dict_with_default_xdg_config_home(self): + env = { + 'home_path': get_fixture_path('unexistent_home'), + 'xdg_config_home': get_fixture_path('xdg_config_home'), + } + expected_config_dict = { + 'cmdargs': { + 'verbose': bool, + 'error-limit': int, + 'severity': Enum, + } + } + config_source = self.initialize_config_source_with_env(ConfigGlobalSource, env) + self.assertConfigValueType(config_source, expected_config_dict) + + if __name__ == '__main__': unittest.main() diff --git a/test/unit/vint/linting/test_env.py b/test/unit/vint/linting/test_env.py index 210f2d4..089515a 100644 --- a/test/unit/vint/linting/test_env.py +++ b/test/unit/vint/linting/test_env.py @@ -11,6 +11,7 @@ class TestEnv(unittest.TestCase): def test_build_environment(self): cwd = Path('path', 'to', 'cwd') home = Path('/', 'home', 'user') + xdg_config_home = Path('/', 'home', 'user', '.config') cmdargs = { 'verbose': True, @@ -33,6 +34,7 @@ def test_build_environment(self): Path(FIXTURE_PATH, 'sub', '4.vim'), ]), 'home_path': home, + 'xdg_config_home': xdg_config_home, 'cwd': cwd, } @@ -42,7 +44,9 @@ def test_build_environment(self): with mock.patch('os.path.expanduser') as mocked_expanduser: mocked_expanduser.return_value = str(home) - env = build_environment(cmdargs) + + with mock.patch.dict('os.environ', {'XDG_CONFIG_HOME': '/home/user/.config'}): + env = build_environment(cmdargs) self.maxDiff = 1000 self.assertEqual(env, expected_env) diff --git a/test/unit/vint/utils/__init__.py b/test/unit/vint/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/unit/vint/utils/test_array.py b/test/unit/vint/utils/test_array.py new file mode 100644 index 0000000..ab20d1c --- /dev/null +++ b/test/unit/vint/utils/test_array.py @@ -0,0 +1,16 @@ +import unittest +from vint.utils.array import flatten, flat_map + + +class TestUtilsArray(unittest.TestCase): + def test_flatten_empty(self): + result = flatten([]) + self.assertEqual(result, []) + + def test_flatten_not_empty(self): + result = flatten([[0], [1, 2]]) + self.assertEqual(result, [0, 1, 2]) + + def test_flat_map(self): + result = flat_map(lambda x: [x, x * 2], [0, 1, 2]) + self.assertEqual(result, [0, 0, 1, 2, 2, 4]) diff --git a/vint/linting/config/config_filenames.py b/vint/linting/config/config_filenames.py new file mode 100644 index 0000000..c9eb2f2 --- /dev/null +++ b/vint/linting/config/config_filenames.py @@ -0,0 +1,5 @@ +CONFIG_FILENAMES = [ + '.vintrc.yaml', + '.vintrc.yml', + '.vintrc', +] diff --git a/vint/linting/config/config_global_source.py b/vint/linting/config/config_global_source.py index 48b348c..adf6dae 100644 --- a/vint/linting/config/config_global_source.py +++ b/vint/linting/config/config_global_source.py @@ -1,16 +1,38 @@ +from typing import Dict from pathlib import Path +from vint.utils.array import flat_map from vint.asset import get_asset_path +from vint.linting.config.config_filenames import CONFIG_FILENAMES from vint.linting.config.config_file_source import ConfigFileSource -GLOBAL_CONFIG_FILENAME = '.vintrc.yaml' VOID_CONFIG_PATH = get_asset_path('void_config.yaml') class ConfigGlobalSource(ConfigFileSource): def get_file_path(self, env): - global_config_path = Path(env['home_path'], GLOBAL_CONFIG_FILENAME) + global_config_paths = ConfigGlobalSource._get_filenames_candidates(env) + + for global_config_path in global_config_paths: + if global_config_path.is_file(): + return global_config_path + + return VOID_CONFIG_PATH + + @classmethod + def _get_filenames_candidates(cls, env): + # type: (Dict[str, str]) -> [Path] + global_config_dir_candidates = [ + env['xdg_config_home'], + env['home_path'], + ] + + return flat_map( + lambda directory: ConfigGlobalSource._get_filenames_candidates_from_dir(directory), + global_config_dir_candidates + ) + + @classmethod + def _get_filenames_candidates_from_dir(cls, config_dir): + # type: (str) -> [Path] + return [Path(config_dir, filename) for filename in CONFIG_FILENAMES] - if global_config_path.is_file(): - return global_config_path - else: - return VOID_CONFIG_PATH diff --git a/vint/linting/env.py b/vint/linting/env.py index ad8e98b..a935719 100644 --- a/vint/linting/env.py +++ b/vint/linting/env.py @@ -7,17 +7,18 @@ def build_environment(cmdargs): return { 'cmdargs': cmdargs, - 'home_path': _get_home_path(cmdargs), - 'cwd': _get_cwd(cmdargs), + 'home_path': _get_home_path(), + 'xdg_config_home': _get_xdg_config_home(), + 'cwd': _get_cwd(), 'file_paths': _get_file_paths(cmdargs) } -def _get_cwd(cmdargs): +def _get_cwd(): return Path(os.getcwd()) -def _get_home_path(cmdargs): +def _get_home_path(): return Path(os.path.expanduser('~')) @@ -28,3 +29,10 @@ def _get_file_paths(cmdargs): found_file_paths = find_vim_script(map(Path, cmdargs['files'])) return set(found_file_paths) + + +def _get_xdg_config_home(): + return Path(os.environ.get( + "XDG_CONFIG_HOME", + str(_get_home_path().joinpath(".config")) + )) diff --git a/vint/utils/__init__.py b/vint/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vint/utils/array.py b/vint/utils/array.py new file mode 100644 index 0000000..efa58b1 --- /dev/null +++ b/vint/utils/array.py @@ -0,0 +1,16 @@ +from typing import TypeVar, List, Callable +from functools import reduce +from operator import add + +T = TypeVar('T') +S = TypeVar('S') + + +def flatten(l): + # type: (List[List[T]]) -> List[T] + return reduce(add, l, []) + + +def flat_map(f, l): + # type: (Callable[[S], List[T]], List[S]) -> List[T] + return flatten([f(x) for x in l]) \ No newline at end of file