Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 139 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,49 @@ The source code of **hyperstyle** is distributed under the Apache 2.0 License.

The 3rd party software we use in this project has its own licenses.

* Checkstyle [GNU LGPL v2.1]
* [Site and docs](https://checkstyle.sourceforge.io/)
* [Repository](https://github.com/checkstyle/checkstyle)

* Detekt [Apache 2.0]
* [Site and docs](https://detekt.github.io/detekt/)
* [Repository](https://github.com/detekt/detekt)
Python language:

* ESlint [MIT]
* [Site and docs](https://eslint.org/)
* [Repository](https://github.com/eslint/eslint)

* flake8 [MIT]
- [x] flake8 [MIT]
* [Site and docs](https://flake8.pycqa.org/en/latest/)
* [Repository](https://github.com/PyCQA/flake8)

* PMD [BSD]
* [Site and docs](https://pmd.github.io/)
* [Repository](https://github.com/pmd/pmd)

* Pylint [GNU LGPL v2]

- [x] Pylint [GNU LGPL v2]
* [Site and docs](https://www.pylint.org/)
* [Repository](https://github.com/PyCQA/pylint)


Java language:

* SpotBugs [GNU LGPL v2.1]
- [x] PMD [BSD]
* [Site and docs](https://pmd.github.io/)
* [Repository](https://github.com/pmd/pmd)

- [x] Checkstyle [GNU LGPL v2.1]
* [Site and docs](https://checkstyle.sourceforge.io/)
* [Repository](https://github.com/checkstyle/checkstyle)

- [ ] SpotBugs [GNU LGPL v2.1]
* [Site and docs](https://spotbugs.github.io/)
* [Repository](https://github.com/spotbugs/spotbugs)

* SpringLint
- [ ] SpringLint
* [Repository](https://github.com/mauricioaniche/springlint)



Kotlin language:

- [x] Detekt [Apache 2.0]
* [Site and docs](https://detekt.github.io/detekt/)
* [Repository](https://github.com/detekt/detekt)



JavaScript language:

- [x] ESlint [MIT]
* [Site and docs](https://eslint.org/)
* [Repository](https://github.com/eslint/eslint)

---

Expand All @@ -64,6 +77,111 @@ Simply clone the repository and run the following commands:
1. `pip install -r requirements.txt`
2. `pip install -r requirements-test.txt` for tests

## Usage

Run the [run_tool.py](./src/python/review/run_tool.py) with the arguments.

A simple configuration: `python run_tool.py <path>`.

**Required arguments:**
1. **path** — path to file or directory to inspect.

Optional arguments:

Argument | Description
--- | ---
**&#8209;h**, **&#8209;&#8209;help** | show the help message and exit.
**&#8209;v**, **&#8209;&#8209;verbosity** | choose logging level according [this](https://docs.python.org/3/library/logging.html#levels) list: `1` - **ERROR**; `2` - **INFO**; `3` - **DEBUG**; `0` - disable logging (**CRITICAL** value); default value is `0` (**CRITICAL**).
**&#8209;d**, **&#8209;&#8209;disable** | disable inspectors. Available values: for **Python** language: `pylint` for [Pylint](https://github.com/PyCQA/pylint), `flake8` for [flake8](https://flake8.pycqa.org/en/latest/), `python_ast` to check different measures providing by AST; for **Java** language: `checkstyle` for the [Checkstyle](https://checkstyle.sourceforge.io/), `pmd` for [PMD](https://pmd.github.io/); for `Kotlin` language: detekt for [Detekt](https://detekt.github.io/detekt/); for **JavaScript** language: `eslint` for [ESlint](https://eslint.org/). Example: `-d pylint,flake8`.
**&#8209;&#8209;allow_duplicates** | allow duplicate issues found by different linters. By default, duplicates are skipped.
**&#8209;&#8209;language_version** | specify the language version for JAVA inspectors. Available values: `java7`, `java8`, `java9`, `java11`.
**&#8209;&#8209;n_cpu** | specify number of _cpu_ that can be used to run inspectors
**&#8209;f**, **&#8209;&#8209;format** | the output format. Available values: `json`, `text`. Default value is `json`.
**&#8209;s**, **&#8209;&#8209;start_line**| the first line to be analyzed. By default it starts from `1`.
**&#8209;e**, **&#8209;&#8209;end_line** | the end line to be analyzed. The default value is `None`, which meant to handle file by the end.
**&#8209;&#8209;new_format** | the argument determines whether the tool should use the _new format_. _New format_ means separating the result by the files to allow getting quality and observed issues for each file separately. The default value is `False`.

The output examples:

(_New format_ means separating the result by the files to allow getting quality and observed issues for each file separately)

1. Json `old format` (without **&#8209;&#8209;new_format** argument):

```json
{
"quality": {
"code": "BAD",
"text": "Code quality (beta): BAD"
},
"issues": [
{
"code": "C002",
"text": "Too long function. Try to split it into smaller functions / methods.It will make your code easy to understand and less error prone.",
"line": "<the code line>",
"line_number": 54,
"column_number": 0,
"category": "FUNC_LEN"
},
...
]
}
```

2. Json `new format` (with **&#8209;&#8209;new_format** argument):

```json
{
"quality": {
"code": "BAD",
"text": "Code quality (beta): BAD"
},
"file_review_results": [
{
"file_name": "<your file>",
"quality": {
"code": "BAD",
"text": "Code quality (beta): BAD"
},
"issues": [
{
"code": "W0703",
"text": "Catching too general exception Exception",
"line": "<the code line>",
"line_number": 174,
"column_number": 12,
"category": "BEST_PRACTICES"
},
...
]
}
]
}
```

3. Text format:

```text
Review of <path to your file or project> (N violations)
***********************************************************************************************************
File <file_name>
-----------------------------------------------------------------------------------------------------------
Line № : Column № : Type : Inspector : Origin : Description : Line : Path
54 : 0 : FUNC_LEN : PYTHON_AST : C002 : <Description> : <code line > : <path to the file>
...
-----------------------------------------------------------------------------------------------------------
Code quality (beta): BAD
Next level: EXCELLENT
Next level requirements:
FUNC_LEN: 12

***********************************************************************************************************
General quality:
Code quality (beta): BAD
Next level: EXCELLENT
Next level requirements:
FUNC_LEN: 12
```

---

## Tests running
Expand Down
6 changes: 5 additions & 1 deletion src/python/review/application_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass
from enum import Enum, unique
from typing import Optional, Set
from typing import Optional, Set, List

from src.python.review.inspectors.inspector_type import InspectorType

Expand All @@ -22,3 +22,7 @@ class LanguageVersion(Enum):
JAVA_8 = 'java8'
JAVA_9 = 'java9'
JAVA_11 = 'java11'

@classmethod
def values(cls) -> List[str]:
return [member.value for _, member in LanguageVersion.__members__.items()]
33 changes: 30 additions & 3 deletions src/python/review/inspectors/inspector_type.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
from enum import Enum, unique
from typing import List


@unique
class InspectorType(Enum):
# Python language
PYLINT = 'PYLINT'
FLAKE8 = 'FLAKE8'
PYTHON_AST = 'PYTHON_AST'
CHECKSTYLE = 'CHECKSTYLE'
FLAKE8 = 'FLAKE8'

# Java language
PMD = 'PMD'
CHECKSTYLE = 'CHECKSTYLE'
SPOTBUGS = 'SPOTBUGS'
SPRINGLINT = 'SPRINGLINT'

# Kotlin language
DETEKT = 'DETEKT'
INTELLIJ = 'INTELLIJ'
SPRINGLINT = 'SPRINGLINT'

# JavaScript language
ESLINT = 'ESLINT'

@classmethod
def available_values(cls) -> List[str]:
return [
# Python language
InspectorType.PYLINT.value,
InspectorType.FLAKE8.value,
InspectorType.PYTHON_AST.value,

# Java language
InspectorType.PMD.value,
InspectorType.CHECKSTYLE.value,

# Kotlin language
InspectorType.DETEKT.value,

# JavaScript language
InspectorType.ESLINT.value,
]
6 changes: 5 additions & 1 deletion src/python/review/reviewers/perform_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
from functools import partial
from pathlib import Path
from typing import Final
from typing import Final, List

from src.python.review.application_config import ApplicationConfig
from src.python.review.common.language import Language
Expand Down Expand Up @@ -45,6 +45,10 @@ def __str__(self):
def __repr__(self):
return str(self)

@classmethod
def values(cls) -> List[str]:
return [member.value for _, member in OutputFormat.__members__.items()]


def perform_and_print_review(path: Path,
output_format: OutputFormat,
Expand Down
44 changes: 24 additions & 20 deletions src/python/review/run_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import traceback
from enum import Enum, unique
from pathlib import Path
from typing import Set
from typing import Set, List

sys.path.append('')
sys.path.append('../../..')
Expand All @@ -29,6 +29,10 @@ class VerbosityLevel(Enum):
ERROR = '1'
DISABLE = '0'

@classmethod
def values(cls) -> List[str]:
return [member.value for _, member in VerbosityLevel.__members__.items()]


def parse_disabled_inspectors(value: str) -> Set[InspectorType]:
passed_names = value.upper().split(',')
Expand All @@ -50,17 +54,17 @@ def positive_int(value: str) -> int:
def configure_arguments(parser: argparse.ArgumentParser) -> None:
parser.add_argument('-v', '--verbosity',
help='Choose logging level: '
f'{VerbosityLevel.ERROR.value} - >= ERROR; '
f'{VerbosityLevel.INFO.value} - >= INFO; '
f'{VerbosityLevel.DEBUG.value} - >= DEBUG; '
f'{VerbosityLevel.DISABLE.value} - disable logging'
f'{VerbosityLevel.ERROR.value} - ERROR; '
f'{VerbosityLevel.INFO.value} - INFO; '
f'{VerbosityLevel.DEBUG.value} - DEBUG; '
f'{VerbosityLevel.DISABLE.value} - disable logging; '
'default is 0',
default=VerbosityLevel.DISABLE.value,
choices=list(VerbosityLevel),
type=VerbosityLevel)
choices=VerbosityLevel.values(),
type=str)

# Usage example: -d Flake8,Intelli
inspectors = [inspector.value.lower() for inspector in InspectorType]
inspectors = [inspector.lower() for inspector in InspectorType.available_values()]
example = f'-d {inspectors[0].lower()},{inspectors[1].lower()}'

parser.add_argument('-d', '--disable',
Expand All @@ -70,15 +74,15 @@ def configure_arguments(parser: argparse.ArgumentParser) -> None:
type=parse_disabled_inspectors,
default=set())

parser.add_argument('--allow-duplicates', action='store_true',
parser.add_argument('--allow_duplicates', action='store_true',
help='Allow duplicate issues found by different linters. '
'By default, duplicates are skipped.')

parser.add_argument('--language_version',
help='Specify the language version for inspectors.',
help='Specify the language version for JAVA inspectors.',
default=None,
choices=list(LanguageVersion),
type=LanguageVersion)
choices=LanguageVersion.values(),
type=str)

parser.add_argument('--n_cpu',
help='Specify number of cpu that can be used to run inspectors',
Expand All @@ -91,21 +95,21 @@ def configure_arguments(parser: argparse.ArgumentParser) -> None:

parser.add_argument('-f', '--format',
default=OutputFormat.JSON,
choices=list(OutputFormat),
type=OutputFormat,
choices=OutputFormat.values(),
type=str,
help='The output format. Default is JSON.')

parser.add_argument('-s', '--start-line',
parser.add_argument('-s', '--start_line',
default=1,
type=positive_int,
help='The first line to be analyzed. It starts from 1.')

parser.add_argument('-e', '--end-line',
parser.add_argument('-e', '--end_line',
default=None,
type=positive_int,
help='The end line to be analyzed or None.')

parser.add_argument('--new-format',
parser.add_argument('--new_format',
action='store_true',
help='The argument determines whether the tool '
'should use the new format')
Expand All @@ -130,7 +134,7 @@ def main() -> int:

try:
args = parser.parse_args()
configure_logging(args.verbosity)
configure_logging(VerbosityLevel(args.verbosity))

n_cpu = args.n_cpu
max_n_cpu = os.cpu_count()
Expand All @@ -144,7 +148,7 @@ def main() -> int:
start_line = 1

inspectors_config = {
'language_version': args.language_version,
'language_version': LanguageVersion(args.language_version) if args.language_version is not None else None,
'n_cpu': n_cpu
}

Expand All @@ -156,7 +160,7 @@ def main() -> int:
new_format=args.new_format,
)

n_issues = perform_and_print_review(args.path, args.format, config)
n_issues = perform_and_print_review(args.path, OutputFormat(args.format), config)
if not n_issues:
return 0

Expand Down
Loading