Esempio n. 1
0
File: cli.py Progetto: rhysd/vint
 def _build_env(self, argv):
     """ Build an environment object.
     This method take an argv parameter to make function pure.
     """
     cmdargs = self._build_cmdargs(argv)
     env = build_environment(cmdargs)
     return env
Esempio n. 2
0
def _build_env(argv):
    """ Build an environment object.
    This method take an argv parameter to make function pure.
    """
    cmdargs = _build_cmdargs(argv)
    env = build_environment(cmdargs)
    return env
Esempio n. 3
0
    def test_build_environment(self):
        cwd = Path('path', 'to', 'cwd')
        home = Path('/', 'home', 'user')
        xdg_config_home = Path('/', 'home', 'user', '.config')

        cmdargs = {
            'verbose': True,
            'warning': True,
            'max_violations': 10,
            'files': [str(FIXTURE_PATH)],
        }

        expected_env = {
            'cmdargs': {
                'files': [str(FIXTURE_PATH)],
                'verbose': True,
                'warning': True,
                'max_violations': 10,
            },
            'file_paths':
            set([
                Path(FIXTURE_PATH, '1.vim'),
                Path(FIXTURE_PATH, '2.vim'),
                Path(FIXTURE_PATH, 'sub', '3.vim'),
                Path(FIXTURE_PATH, 'sub', '4.vim'),
            ]),
            'home_path':
            home,
            'xdg_config_home':
            xdg_config_home,
            'cwd':
            cwd,
        }

        # we should mock os.getcwd() because env get the cwd by os.getcwd()
        with mock.patch('os.getcwd') as mocked_getcwd:
            mocked_getcwd.return_value = str(cwd)

            with mock.patch('os.path.expanduser') as mocked_expanduser:
                mocked_expanduser.return_value = str(home)

                with mock.patch.dict(
                        'os.environ',
                    {'XDG_CONFIG_HOME': '/home/user/.config'}):
                    env = build_environment(cmdargs)

        self.maxDiff = 1000
        self.assertEqual(env, expected_env)
Esempio n. 4
0
    def test_build_environment(self):
        cwd = Path('path', 'to', 'cwd')
        home = Path('/', 'home', 'user')
        xdg_config_home = Path('/', 'home', 'user', '.config')

        cmdargs = {
            'verbose': True,
            'warning': True,
            'max_violations': 10,
            'files': [str(FIXTURE_PATH)],
        }

        expected_env = {
            'cmdargs': {
                'files': [str(FIXTURE_PATH)],
                'verbose': True,
                'warning': True,
                'max_violations': 10,
            },
            'file_paths': [
                Path(FIXTURE_PATH, '1.vim'),
                Path(FIXTURE_PATH, '2.vim'),
                Path(FIXTURE_PATH, 'sub', '3.vim'),
                Path(FIXTURE_PATH, 'sub', '4.vim'),
            ],
            'home_path': home,
            'xdg_config_home': xdg_config_home,
            'cwd': cwd,
        }

        # we should mock os.getcwd() because env get the cwd by os.getcwd()
        with mock.patch('os.getcwd') as mocked_getcwd:
            mocked_getcwd.return_value = str(cwd)

            with mock.patch('os.path.expanduser') as mocked_expanduser:
                mocked_expanduser.return_value = str(home)

                with mock.patch.dict('os.environ', {'XDG_CONFIG_HOME': '/home/user/.config'}):
                    env = build_environment(cmdargs)

        self.maxDiff = 1000
        self.assertEqual(env, expected_env)
 def _build_linter(self):
     env = build_environment({})
     vint_config = self._build_config_dict(env)
     policies = PolicySet()
     return Linter(policies, vint_config)
 def _build_linter(self):
     env = build_environment({})
     vint_config = self._build_config_dict(env)
     policies = PolicySet()
     return Linter(policies, vint_config)
Esempio n. 7
0
    def rpc_lsp_formatter_vint(self, args: List[Any]) -> Any:
        buf_path: Optional[str]
        buf_root_dir: str
        buf_lines: List[str]
        buf_path, buf_root_dir, buf_lines = args

        try:
            from vint.bootstrap import init_linter
            from vint.linting.env import build_environment
            from vint.linting.config.config_container import ConfigContainer
            from vint.linting.config.config_cmdargs_source import ConfigCmdargsSource
            from vint.linting.config.config_default_source import ConfigDefaultSource
            from vint.linting.config.config_global_source import ConfigGlobalSource
            from vint.linting.config.config_project_source import ConfigProjectSource
            from vint.linting.policy_set import PolicySet
            from vint.linting.linter import Linter
        except ModuleNotFoundError as err:
            raise pynvim.ErrorResponse('vint is not installed: {}'.format(err))

        init_linter()

        lint_file_path = Path(buf_path if buf_path is not None else os.devnull)
        # The codebase seems to always check for presence of keys in `cmdargs`,
        # possibly because they can be specified as an dictionary in a config file,
        # as such construction of a realistic dictionary is not necessary.
        # lint_cmdargs = vars(vint.linting.cli.CLI()._build_argparser().parse_args([]))
        lint_cmdargs = {
            # Stub for v0.4a1 and later, see below.
            'stdin_display_name': str(lint_file_path)
        }
        lint_env: Dict[str, Any] = build_environment(lint_cmdargs)
        lint_env['cwd'] = Path(buf_root_dir)
        lint_env['file_paths'] = ['-'
                                  ]  # Basically a stub but is not read at all.

        config = ConfigContainer(
            ConfigDefaultSource(lint_env),
            ConfigGlobalSource(lint_env),
            ConfigProjectSource(lint_env),
            ConfigCmdargsSource(lint_env),
        )
        config_dict: Any = config.get_config_dict()

        # TODO: same comment
        buf_text = '\n'.join(buf_lines) + '\n'

        # Alright, so, here's the deal... We can't use `lint_text` directly. That
        # is because in all versions since 0.2.0 there is this policy
        # `MissingScriptEncoding` which attempts to read the file to check its
        # encoding. `lint_text` is normally called when the linted file is supplied
        # via stdin, then the `Linter` class sets the path of the file to the
        # special string `stdin` (Note: relax, this will not lead to hilarious bugs
        # when linting a legitimate file named `stdin`, this is not Javascript with
        # its lazy comparison rules, real paths are instances of the `pathlib.Path`
        # class), which the aforementioned policy checks to determine where to read
        # the file contents from. In our case, however, the file is supplied from a
        # string and not from a real file, and stdin is used for PRC with Nvim, so
        # this leads to a crash.
        #
        # ...However, the above problem occurs just in my bizarre Vim setup, but do
        # you know what's even better? There is another policy, `NoAbortFunction`,
        # which ensures that all functions defined in files in the `autoload`
        # directory have a `!` and `abort`... and, because it treats the path
        # parameter as a `pathlib.Path`, it will ALWAYS crash when the file is
        # supplied via stdin (like `vint - < init.vim`, which is entirely normal
        # usage of the program)! Because, of course, the special value `stdin` is
        # used. This rule was already implemented in version 0.0.0.
        #
        # Now, I can only imagine why this blatant bug has gone unnoticed for so
        # frikin long, although stdin support has not always been present, it was
        # actually added only relatively recently as part of 0.3.19:
        # <https://github.com/Vimjas/vint/commit/cac0cf731dc34ed0a3ffcf1191774d91fbfa8f89>.
        # However, what this means for me is that I have to either write the buffer
        # text to disk into a temporary file, or use the knowledge of internals and
        # write code which essentially combines both `lint_text` and `lint_file`.
        #
        # Thankfully, 0.4a1 and onwards implement a LintTarget abstraction which
        # solves this problem, but currently this version is not available in
        # Arch's repos, so there. See
        # <https://github.com/Vimjas/vint/commit/3a2729eb6a5eb809054a1357ae1e1f32bc59cb1c>.

        violations: List[Dict[str, Any]] = []

        if hasattr(Linter, 'lint'):
            # For >=0.4
            from vint.linting.policy_registry import get_policy_classes
            from vint.linting.lint_target import AbstractLintTarget

            class LintTargetInMemory(AbstractLintTarget):
                def __init__(self, path: Path, contents: bytes) -> None:
                    super().__init__(path)
                    self.contents = contents

                def read(self) -> bytes:
                    return self.contents

            policy_set = PolicySet(get_policy_classes())
            linter = Linter(policy_set, config_dict)
            violations = linter.lint(
                LintTargetInMemory(lint_file_path, buf_text.encode('utf8')))

        elif hasattr(Linter, 'lint_text'):
            # For >=0.3.19 <0.4
            from vint._bundles import vimlparser
            from vint.encodings.decoder import EncodingDetectionError
            from vint.linting.policy.prohibit_missing_scriptencoding import ProhibitMissingScriptEncoding

            policy_set = PolicySet()
            linter = Linter(policy_set, config_dict)
            linter._log_file_path_to_lint(lint_file_path)

            # However, additionally I still patch out the function which is
            # responsible for reading the file in `MissingScriptEncoding`, this is to
            # ensure that it can work on unsaved files and if the file doesn't even
            # exist. I also think that this is the cleanest way to do the
            # monkey-patch because only one instance of the policy is affected, and
            # they are always newly created in the `PolicySet`'s constructor.
            broken_policy: Any = policy_set._all_policies_map.get(
                'ProhibitMissingScriptEncoding')
            if type(broken_policy) == ProhibitMissingScriptEncoding:

                def check_script_has_multibyte_char(lint_context: Any) -> bool:
                    # <https://stackoverflow.com/a/51141941/12005228>
                    return not buf_text.isascii()

                broken_policy._check_script_has_multibyte_char = check_script_has_multibyte_char

            root_ast = None
            have_parsing_error = False
            try:
                root_ast = linter._parser.parse(buf_text)
            except vimlparser.VimLParserException as err:
                violations = [
                    linter._create_parse_error(lint_file_path, str(err))
                ]
                have_parsing_error = True
            except EncodingDetectionError as err:
                # NOTE: EncodingDetectionError can only be thrown when calling
                # `linter._parser.parse_file`, but I handle it for completeness.
                violations = [
                    linter._create_decoding_error(lint_file_path, str(err))
                ]
                have_parsing_error = True

            if not have_parsing_error:
                linter._traverse(root_ast, lint_file_path)
                violations = linter._violations

        else:
            # For <0.3.19
            policy_set = PolicySet()
            linter = Linter(policy_set, config_dict)

            # Poor man's method involving a temporary file.
            tmp_dir: Optional[str] = None
            tmp_prefix: Optional[str] = None
            tmp_suffix: Optional[str] = '.vim'
            if buf_path is not None:
                tmp_dir = os.path.dirname(buf_path)
                if not os.path.isdir(tmp_dir):
                    tmp_dir = None
                tmp_prefix, tmp_suffix = os.path.splitext(
                    os.path.basename(buf_path))

            with tempfile.NamedTemporaryFile(
                    dir=tmp_dir, prefix=tmp_prefix,
                    suffix=tmp_suffix) as tmp_buf_file:
                tmp_buf_file.write(buf_text.encode('utf8'))
                tmp_buf_file.flush()
                violations = linter.lint_file(Path(tmp_buf_file.name))

        rpc_result: List[List[Any]] = []
        for idx, violation in enumerate(violations):
            rpc_result.append([
                violation['position']['line'],
                violation['position']['column'],
                violation['level'].value,
                violation['name'],
                violation['description'],
                violation['reference'],
            ])

        return rpc_result