diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index b9b78f7..53cae85 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -43,6 +43,8 @@ jobs: - name: Publish test coverage uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} fmt: runs-on: ubuntu-latest diff --git a/NOTICE b/NOTICE index 7aef7e3..62ee6e1 100644 --- a/NOTICE +++ b/NOTICE @@ -2,6 +2,12 @@ Copyright (2024) Databricks, Inc. This Software includes software developed at Databricks (https://www.databricks.com/) and its use is subject to the included LICENSE file. +This Software contains code from the following open source projects, licensed under the MIT license: + +Eradicate - https://github.com/PyCQA/eradicate +Copyright (C) 2012-2018 Steven Myint +License - https://github.com/PyCQA/eradicate/blob/master/eradicate.py#L1-L20 + This Software contains code from the following open source projects, licensed under the Apache 2.0 license: Databricks SDK for Python - https://github.com/databricks/databricks-sdk-py diff --git a/README.md b/README.md index e008925..09a428f 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ PyLint with checks for common mistakes and issues in Python code specifically in * [`mocking` checker](#mocking-checker) * [`R8918`: `explicit-dependency-required`](#r8918-explicit-dependency-required) * [`R8919`: `obscure-mock`](#r8919-obscure-mock) + * [`eradicate` checker](#eradicate-checker) + * [`C8920`: `dead-code`](#c8920-dead-code) * [Testing in isolation](#testing-in-isolation) * [Project Support](#project-support) @@ -335,11 +337,24 @@ To disable this check on a specific line, add `# pylint: disable=obscure-mock` a [[back to top](#pylint-plugin-for-databricks)] +## `eradicate` checker +To use this checker, add `databricks.labs.pylint.eradicate` to `load-plugins` configuration in your `pylintrc` or `pyproject.toml` file. + +[[back to top](#pylint-plugin-for-databricks)] + +### `C8920`: `dead-code` + +Remove commented out code: XXX. Version control helps with keeping track of code changes. There is no need to keep commented out code in the codebase. Remove it to keep the codebase clean. + +To disable this check on a specific line, add `# pylint: disable=dead-code` at the end of it. + +[[back to top](#pylint-plugin-for-databricks)] + ## Testing in isolation To test this plugin in isolation, you can use the following command: ```bash -pylint --load-plugins=databricks.labs.pylint.all --disable=all --enable=missing-data-security-mode,unsupported-runtime,dbutils-fs-cp,dbutils-fs-head,dbutils-fs-ls,dbutils-fs-mount,dbutils-credentials,dbutils-notebook-run,pat-token-leaked,internal-api,legacy-cli,incompatible-with-uc,notebooks-too-many-cells,notebooks-percent-run,spark-outside-function,use-display-instead-of-show,no-spark-argument-in-function,explicit-dependency-required,obscure-mock . +pylint --load-plugins=databricks.labs.pylint.all --disable=all --enable=missing-data-security-mode,unsupported-runtime,dbutils-fs-cp,dbutils-fs-head,dbutils-fs-ls,dbutils-fs-mount,dbutils-credentials,dbutils-notebook-run,pat-token-leaked,internal-api,legacy-cli,incompatible-with-uc,notebooks-too-many-cells,notebooks-percent-run,spark-outside-function,use-display-instead-of-show,no-spark-argument-in-function,explicit-dependency-required,obscure-mock,dead-code . ``` [[back to top](#pylint-plugin-for-databricks)] diff --git a/pyproject.toml b/pyproject.toml index 8dcb565..027b56f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,12 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", ] -dependencies = ["pylint", "astroid", "databricks-sdk"] +dependencies = [ + "pylint", + "astroid", + "databricks-sdk", + "eradicate~=2.3.0", +] [project.urls] Issues = "https://github.com/databrickslabs/pylint-plugin/issues" diff --git a/scripts/docs.py b/scripts/docs.py index 89344cf..518b736 100644 --- a/scripts/docs.py +++ b/scripts/docs.py @@ -5,6 +5,7 @@ from databricks.labs.pylint.airflow import AirflowChecker from databricks.labs.pylint.dbutils import DbutilsChecker +from databricks.labs.pylint.eradicate import EradicateChecker from databricks.labs.pylint.legacy import LegacyChecker from databricks.labs.pylint.mocking import MockingChecker from databricks.labs.pylint.notebooks import NotebookChecker @@ -23,6 +24,7 @@ def do_something(): NotebookChecker(linter), SparkChecker(linter), MockingChecker(linter), + EradicateChecker(linter), ]: out.append(f"## `{checker.name}` checker") out.append( diff --git a/src/databricks/labs/pylint/all.py b/src/databricks/labs/pylint/all.py index 5942527..40f3f8d 100644 --- a/src/databricks/labs/pylint/all.py +++ b/src/databricks/labs/pylint/all.py @@ -1,5 +1,6 @@ from databricks.labs.pylint.airflow import AirflowChecker from databricks.labs.pylint.dbutils import DbutilsChecker +from databricks.labs.pylint.eradicate import EradicateChecker from databricks.labs.pylint.legacy import LegacyChecker from databricks.labs.pylint.mocking import MockingChecker from databricks.labs.pylint.notebooks import NotebookChecker @@ -13,3 +14,4 @@ def register(linter): linter.register_checker(AirflowChecker(linter)) linter.register_checker(SparkChecker(linter)) linter.register_checker(MockingChecker(linter)) + linter.register_checker(EradicateChecker(linter)) diff --git a/src/databricks/labs/pylint/eradicate.py b/src/databricks/labs/pylint/eradicate.py new file mode 100644 index 0000000..44469f3 --- /dev/null +++ b/src/databricks/labs/pylint/eradicate.py @@ -0,0 +1,32 @@ +import astroid +from eradicate import Eradicator +from pylint.checkers import BaseRawFileChecker +from pylint.interfaces import HIGH + + +class EradicateChecker(BaseRawFileChecker): + name = "eradicate" + + msgs = { + "C8920": ( + "Remove commented out code: %s", + "dead-code", + "Version control helps with keeping track of code changes. There is no need to keep commented out code in " + "the codebase. Remove it to keep the codebase clean.", + ), + } + + def open(self) -> None: + self._eradicator = Eradicator() + + def process_module(self, node: astroid.Module): + with node.stream() as stream: + source = stream.read().decode() + lines = source.splitlines() + for line_no in self._eradicator.commented_out_code_line_numbers(source): + src_line = lines[line_no - 1].strip() + self.add_message("dead-code", line=line_no, confidence=HIGH, args=(src_line,)) + + +def register(linter): + linter.register_checker(EradicateChecker(linter)) diff --git a/tests/samples/d/dead_code.py b/tests/samples/d/dead_code.py new file mode 100644 index 0000000..4b2c7f5 --- /dev/null +++ b/tests/samples/d/dead_code.py @@ -0,0 +1,5 @@ +# pylint: disable=missing-module-docstring +print("Hello, world!") +# +1: [dead-code] +# print('Goodbye, world!') +print("...") diff --git a/tests/samples/d/dead_code.txt b/tests/samples/d/dead_code.txt new file mode 100644 index 0000000..e10b218 --- /dev/null +++ b/tests/samples/d/dead_code.txt @@ -0,0 +1 @@ +dead-code:4:0:None:None::"Remove commented out code: # print('Goodbye, world!')":HIGH \ No newline at end of file diff --git a/tests/samples/m/many_cells.py b/tests/samples/m/many_cells.py index b633100..be8b14b 100644 --- a/tests/samples/m/many_cells.py +++ b/tests/samples/m/many_cells.py @@ -1,5 +1,5 @@ # Databricks notebook source -# pylint: disable=missing-module-docstring +# pylint: disable=missing-module-docstring,dead-code print("Hello, World!")