Ejemplo n.º 1
0
    def test_lint_file_with_config(self) -> None:
        source = b"obj.attr.another_attr\n"
        config = LintConfig(
            rule_config={"ParenthesizeAttributeLintRule": {
                "disabled": True
            }})

        reports = rule_lint_engine.lint_file(
            Path("dummy_file.py"),
            source,
            config=config,
            rules={ParenthesizeAttributeLintRule},
        )
        # Expect no reports cause disabled set to True
        self.assertEqual(len(reports), 0)

        config = LintConfig(
            rule_config={"ParenthesizeAttributeLintRule": {
                "disabled": False
            }})
        reports = rule_lint_engine.lint_file(
            Path("dummy_file.py"),
            source,
            config=config,
            rules={ParenthesizeAttributeLintRule},
        )
        self.assertEqual(len(reports), 2)
Ejemplo n.º 2
0
def get_formatted_reports_for_path(
    path: Path,
    opts: LintOpts,
    metadata_cache: Optional[Mapping["ProviderT", object]] = None,
) -> Iterable[str]:
    with open(path, "rb") as f:
        source = f.read()

    try:
        cst_wrapper = None
        if metadata_cache is not None:
            cst_wrapper = MetadataWrapper(parse_module(source), True, metadata_cache)
        raw_reports = lint_file(
            path,
            source,
            rules=opts.rules,
            use_ignore_byte_markers=opts.use_ignore_byte_markers,
            use_ignore_comments=opts.use_ignore_comments,
            cst_wrapper=cst_wrapper,
            find_unused_suppressions=True,
        )
    except (SyntaxError, ParserSyntaxError) as e:
        print_red(
            f"Encountered the following error while parsing source code in file {path}:"
        )
        print(e)
        return []

    # linter completed successfully
    return [opts.formatter.format(rr) for rr in raw_reports]
Ejemplo n.º 3
0
def get_file_lint_result_json(
    path: Path,
    opts: LintOpts,
    metadata_cache: Optional[Mapping["ProviderT", object]] = None,
) -> Sequence[str]:
    try:
        with open(path, "rb") as f:
            source = f.read()
        cst_wrapper = None
        if metadata_cache is not None:
            cst_wrapper = MetadataWrapper(
                cst.parse_module(source),
                True,
                metadata_cache,
            )
        results = opts.success_report.create_reports(
            path,
            lint_file(
                path,
                source,
                rules=opts.rules,
                config=opts.config,
                cst_wrapper=cst_wrapper,
            ),
            **opts.extra,
        )
    except Exception:
        tb_str = traceback.format_exc()
        results = opts.failure_report.create_reports(path, tb_str,
                                                     **opts.extra)
    return [json.dumps(asdict(r)) for r in results]
Ejemplo n.º 4
0
    def _test_method(
        self,
        test_case: Union[ValidTestCase, InvalidTestCase],
        rule: Type[CstLintRule],
        fixture_file: Optional[Path] = None,
    ) -> None:
        cst_wrapper: Optional[MetadataWrapper] = None
        if fixture_file is not None:
            cst_wrapper = gen_type_inference_wrapper(test_case.code,
                                                     fixture_file)
        reports = lint_file(
            Path(test_case.filename),
            _dedent(test_case.code).encode("utf-8"),
            config=test_case.config,
            rules={rule},
            cst_wrapper=cst_wrapper,
        )
        if isinstance(test_case, ValidTestCase):
            self.assertEqual(
                len(reports),
                0,
                'Expected zero reports for this "valid" test case. Instead, found:\n'
                + "\n".join(str(e) for e in reports),
            )
        else:
            self.assertGreater(
                len(reports),
                0,
                'Expected a report for this "invalid" test case but `self.report` was '
                + "not called:\n" + test_case.code,
            )
            self.assertLessEqual(
                len(reports),
                1,
                'Expected one report from this "invalid" test case. Found multiple:\n'
                + "\n".join(str(e) for e in reports),
            )

            # pyre-fixme[16]: `Collection` has no attribute `__getitem__`.
            report = reports[0]

            if not (test_case.line is None or test_case.line == report.line):
                raise AssertionError(
                    f"Expected line: {test_case.line} but found line: {report.line}"
                )

            if not (test_case.column is None
                    or test_case.column == report.column):
                raise AssertionError(
                    f"Expected column: {test_case.column} but found column: {report.column}"
                )
            kind = test_case.kind if test_case.kind is not None else rule.__name__
            if kind != report.code:
                raise AssertionError(
                    f"Expected:\n    {test_case.expected_str}\nBut found:\n    {report}"
                )

            validate_patch(report, test_case)
Ejemplo n.º 5
0
 def test_lint_ignore_with_framework(self) -> None:
     results = list(
         lint_file(
             file_path=Path("dummy/file/path.py"),
             source=
             b"# lint-ignore: F821: testing ignores\nundefined_fn()\n",
             rules={Flake8PseudoLintRule},
             config=LintConfig(),
         ))
     self.assertEqual(results, [])
Ejemplo n.º 6
0
 def test_lint_file_with_framework(self) -> None:
     results = list(
         lint_file(
             file_path=Path("dummy/file/path.py"),
             source=b"undefined_fn()\n",
             rules={Flake8PseudoLintRule},
             config=LintConfig(),
         ))
     self.assertEqual(len(results), 1)
     self.assertEqual(results[0].code, "F821")  # undefined name
Ejemplo n.º 7
0
def get_formatted_reports_for_path(
    path: Path,
    opts: InsertSuppressionsOpts,
    metadata_cache: Optional[Mapping["ProviderT", object]] = None,
) -> Iterable[str]:
    with open(path, "rb") as f:
        source = f.read()

    try:
        cst_wrapper = None
        if metadata_cache is not None:
            cst_wrapper = MetadataWrapper(
                parse_module(source),
                True,
                metadata_cache,
            )
        raw_reports = lint_file(
            path, source, rules={opts.rule}, cst_wrapper=cst_wrapper
        )
    except (SyntaxError, ParserSyntaxError) as e:
        print_red(
            f"Encountered the following error while parsing source code in file {path}:"
        )
        print(e)
        return []

    opts_message = opts.message
    comments = []
    for rr in raw_reports:
        if isinstance(opts_message, str):
            message = opts_message
        elif opts_message == MessageKind.USE_LINT_REPORT:
            message = rr.message
        else:  # opts_message == MessageKind.NO_MESSAGE
            message = None
        comments.append(
            SuppressionComment(opts.kind, rr.line, rr.code, message, opts.max_lines)
        )
    insert_suppressions_result = insert_suppressions(source, comments)
    updated_source = insert_suppressions_result.updated_source
    assert (
        not insert_suppressions_result.failed_insertions
    ), "Failed to insert some comments. This should not be possible."

    if updated_source != source:
        if not opts.skip_autoformatter:
            # Format the code using the config file's formatter.
            updated_source = invoke_formatter(
                get_lint_config().formatter, updated_source
            )
        with open(path, "wb") as f:
            f.write(updated_source)

    # linter completed successfully
    return [opts.formatter.format(rr) for rr in raw_reports]
Ejemplo n.º 8
0
def mock_operation(
    path: Path,
    opts: LintOpts,
    _=None,
) -> Sequence[FakeLintSuccessReport]:
    results = opts.success_report.create_reports(
        path,
        lint_file(path, b"test", rules=opts.rules, config=LintConfig()),
        **opts.extra,
    )
    return cast(Sequence[FakeLintSuccessReport], results)
Ejemplo n.º 9
0
 def parse(self, file: File, source: bytes) -> None:
     """Run the lint engine on the given *source* for the *file*."""
     try:
         reports = lint_file(
             file.path,
             source,
             use_ignore_byte_markers=False,
             use_ignore_comments=False,
             config=DEFAULT_CONFIG,
             rules=self._rules,
         )
         self._pr_record.add_comments(reports, file.name)
     except (SyntaxError, ParserSyntaxError) as exc:
         self._pr_record.add_error(exc, file.name)
         logger.info("Invalid Python code for the file: [%s] %s", file.name,
                     self.pr_html_url)
Ejemplo n.º 10
0
 def test_lint_file(
     self,
     *,
     source: bytes,
     use_ignore_byte_markers: bool,
     use_ignore_comments: bool,
     expected_report_count: int,
 ) -> None:
     reports = rule_lint_engine.lint_file(
         Path("dummy_filename.py"),
         source,
         use_ignore_byte_markers=use_ignore_byte_markers,
         use_ignore_comments=use_ignore_comments,
         config=LintConfig(),
         rules={BadCallCstLintRule},
     )
     self.assertEqual(len(reports), expected_report_count)
Ejemplo n.º 11
0
def map_paths_operation(
    path: Path,
    rules: Set[LintRuleT],
    type_cache: Optional[Mapping[ProviderT, object]],
) -> Union[str, Collection[BaseLintRuleReport]]:
    # A top-level function to be accessible by `map_paths` from `fixit.cli`.
    cst_wrapper = None
    try:
        if type_cache is not None:
            cst_wrapper = MetadataWrapper(
                cst.parse_module(SOURCE_CODE),
                True,
                type_cache,
            )
        return lint_file(
            file_path=path,
            source=SOURCE_CODE,
            rules=rules,
            cst_wrapper=cst_wrapper,
            config=LintConfig(),
        )
    except Exception as e:
        return str(e)
Ejemplo n.º 12
0
    def test_pseudo_lint_rule(self) -> None:
        class DummyLintRuleReport(BaseLintRuleReport):
            pass

        dummy_report = DummyLintRuleReport(
            file_path=DUMMY_FILE_PATH,
            code=DUMMY_LINT_CODE,
            message=DUMMY_LINT_MESSAGE,
            line=1,
            column=0,
        )

        class DummyPseudoLintRule(PseudoLintRule):
            def lint_file(self) -> Iterable[BaseLintRuleReport]:
                return [dummy_report]

        reports = lint_file(
            DUMMY_FILE_PATH,
            DUMMY_SOURCE,
            config=LintConfig(),
            rules={DummyPseudoLintRule},
        )
        self.assertEqual(reports, [dummy_report])
Ejemplo n.º 13
0
    def _test_method(
        self,
        test_case: Union[ValidTestCase, InvalidTestCase],
        rule: Type[CstLintRule],
        fixture_file: Optional[Path] = None,
    ) -> None:
        cst_wrapper: Optional[MetadataWrapper] = None
        if fixture_file is not None:
            cst_wrapper = gen_type_inference_wrapper(test_case.code, fixture_file)
        reports = lint_file(
            Path(test_case.filename),
            _dedent(test_case.code).encode("utf-8"),
            config=test_case.config,
            rules={rule},
            cst_wrapper=cst_wrapper,
        )
        if isinstance(test_case, ValidTestCase):
            self.assertEqual(
                len(reports),
                0,
                'Expected zero reports for this "valid" test case. Instead, found:\n'
                + "\n".join(str(e) for e in reports),
            )
        else:

            self.assertGreater(
                len(reports),
                0,
                'Expected a report for this "invalid" test case but `self.report` was '
                + "not called:\n"
                + test_case.code,
            )
            positions = test_case.positions
            if positions:
                self.assertEqual(
                    len(reports),
                    len(positions),
                    f'Expected report count to match positions count for this "invalid" test case, Reports : {len(reports)}, Positions: {len(positions)}.\n'
                    + "\n".join(str(e) for e in reports),
                )
                # We assert above that reports and positions are the same length
                for report, position in zip(reports, positions):
                    if not (position.line is None or position.line == report.line):
                        raise AssertionError(
                            f"Expected line: {position.line} but found line: {report.line}"
                        )

                    if not (
                        position.column is None or position.column == report.column
                    ):
                        raise AssertionError(
                            f"Expected column: {position.column} but found column: {report.column}"
                        )
                    kind = (
                        test_case.kind if test_case.kind is not None else rule.__name__
                    )
                    if kind != report.code:
                        raise AssertionError(
                            f"Expected:\n    {test_case.expected_str}\nBut found:\n    {report}"
                        )
                    if (
                        test_case.expected_message is not None
                        and test_case.expected_message != report.message
                    ):
                        raise AssertionError(
                            f"Expected message:\n    {test_case.expected_message}\nBut got:\n    {report.message}"
                        )

                validate_patch(reports, test_case)
            else:
                self.assertEqual(
                    len(reports),
                    0,
                    f'Expected report count to match positions count for this "invalid" test case, Reports : {len(reports)}, Positions: 0.\n'
                    + "\n".join(str(e) for e in reports),
                )
Ejemplo n.º 14
0
def test_rules(
    rule: Type[CstLintRule],
    test_case: Union[ValidTestCase, InvalidTestCase],
    test_case_id: str,
) -> None:
    """Test all the rules with the generated test cases.

    All the test cases comes directly from the `VALID` and `INVALID` attributes for the
    provided rules. Some of the points to keep in mind:

    - Invalid test case should be written so as to generate only one report.
    - Attributes should be in all caps: `INVALID` and `VALID`
    - The code can be written in triple quoted string with indented blocks, they will
      be removed with the helper function: ``_dedent``

    The logic of the code is the same as that of ``fixit.common.testing`` but this has
    been converted to using ``pytest`` and removed the fixture feature. This might be
    added if there's any need for that in the future.
    """
    reports = lint_file(
        Path(test_case.filename),
        _dedent(test_case.code).encode("utf-8"),
        config=test_case.config,
        rules={rule},
    )

    if isinstance(test_case, ValidTestCase):
        assert len(reports) == 0, (
            'Expected zero reports for this "valid" test case. Instead, found:\n'
            + "\n".join(str(e) for e in reports),
        )
    else:
        assert len(reports) > 0, (
            'Expected a report for this "invalid" test case but `self.report` was '
            + "not called:\n"
            + test_case.code,
        )
        assert len(reports) <= 1, (
            'Expected one report from this "invalid" test case. Found multiple:\n'
            + "\n".join(str(e) for e in reports),
        )

        report = reports[0]  # type: ignore

        if test_case.line is not None:
            assert (
                test_case.line == report.line
            ), f"Expected line: {test_case.line} but found line: {report.line}"
        if test_case.column is not None:
            assert (
                test_case.column == report.column
            ), f"Expected column: {test_case.column} but found column: {report.column}"

        kind = test_case.kind if test_case.kind is not None else rule.__name__
        assert (
            kind == report.code
        ), f"Expected:\n    {test_case.expected_str}\nBut found:\n    {report}"

        if test_case.expected_message is not None:
            assert test_case.expected_message == report.message, (
                f"Expected message:\n    {test_case.expected_message}\n"
                + f"But got:\n    {report.message}"
            )

        patch = report.patch
        expected_replacement = test_case.expected_replacement

        if patch is None:
            assert expected_replacement is None, (
                "The rule for this test case has no auto-fix, but expected source was "
                + "specified."
            )
            return

        assert expected_replacement is not None, (
            "The rule for this test case has an auto-fix, but no expected source was "
            + "specified."
        )

        expected_replacement = _dedent(expected_replacement)
        patched_code = patch.apply(_dedent(test_case.code))

        assert patched_code == expected_replacement, (
            "Auto-fix did not produce expected result.\n"
            + f"Expected:\n{expected_replacement}\n"
            + f"But found:\n{patched_code}"
        )