Skip to content

Commit

Permalink
Added integration with eradicate to highlight dead code
Browse files Browse the repository at this point in the history
There's an awesome Python project called `eradicate` (https://github.com/PyCQA/eradicate), but it doesn't seem to have a PyLint plugin. This PR adds this integration.
  • Loading branch information
nfx committed Apr 22, 2024
1 parent 9e5388d commit 82a6fd9
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ jobs:

- name: Publish test coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}

fmt:
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
<!-- TOC -->
Expand Down Expand Up @@ -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)]
Expand Down
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions scripts/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,6 +24,7 @@ def do_something():
NotebookChecker(linter),
SparkChecker(linter),
MockingChecker(linter),
EradicateChecker(linter),
]:
out.append(f"## `{checker.name}` checker")
out.append(
Expand Down
2 changes: 2 additions & 0 deletions src/databricks/labs/pylint/all.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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))
32 changes: 32 additions & 0 deletions src/databricks/labs/pylint/eradicate.py
Original file line number Diff line number Diff line change
@@ -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))
5 changes: 5 additions & 0 deletions tests/samples/d/dead_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# pylint: disable=missing-module-docstring
print("Hello, world!")
# +1: [dead-code]
# print('Goodbye, world!')
print("...")
1 change: 1 addition & 0 deletions tests/samples/d/dead_code.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dead-code:4:0:None:None::"Remove commented out code: # print('Goodbye, world!')":HIGH
2 changes: 1 addition & 1 deletion tests/samples/m/many_cells.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Databricks notebook source
# pylint: disable=missing-module-docstring
# pylint: disable=missing-module-docstring,dead-code

print("Hello, World!")

Expand Down

0 comments on commit 82a6fd9

Please sign in to comment.