A tool for running a set of pre-configured linters and evaluating code quality. It is used on the Hyperskill platform to check the quality of learners' code.
Read more details about the project at Hyperskill Help Center
- Runs linters for several programming languages and parses their output;
- Prints the result using a unified JSON-based format;
- Evaluates the code quality value (EXCELLENT, GOOD, MODERATE, or BAD) based on the linters' output and some heuristics.
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.
Python language (all versions can be found in the requirements.txt file):
-
flake8 [MIT]
-
Pylint [GNU LGPL v2]
-
Radon [MIT]
-
Python IJ Inspections [MIT]
Java language:
-
PMD [BSD] (Version: 6.37.0)
-
Checkstyle [GNU LGPL v2.1] (Version: 8.44)
Kotlin language:
-
Detekt [Apache 2.0] (Version: 1.14.2)
-
Kotlin IJ inspections [MIT]
JavaScript language:
- ESlint [MIT] (Version: 7.5.0)
Go language:
- golangci-lint [GNU GPL v3.0] (Version: 1.49.0)
You have to create a set of environment variables in order to be able to use several linters:
CHECKSTYLE_VERSION
(the value of the variable must be the same with its value in Dockerfile)CHECKSTYLE_DIRECTORY
(the directory withCHECKSTYLE
linter sources)DETEKT_VERSION
(the value of the variable must be the same with its value in Dockerfile)DETEKT_DIRECTORY
(the directory withDETEKT
linter sources)PMD_VERSION
(the value of the variable must be the same with its value in Dockerfile)PMD_DIRECTORY
(the directory withPMD
linter sources)GOLANG_LINT_VERSION
(the value of the variable must be the same with its value in Dockerfile)GOLANG_LINT_DIRECTORY
(the directory withGOLANG_LINT
linter sources)
Just run the following commands to install everything you need to run the tool:
-
Install the latest version of hyperstyle from PyPI:
pip install hyperstyle
You could also install a specific version:
pip install hyperstyle==<VERSION>
where
<VERSION>
is your versions. The list of all available versions you could find here. -
Install (or update) linters specified in the environment variables above:
curl -sSL https://github.com/hyperskill/hyperstyle/blob/main/setup_environment.sh | bash -
This is necessary because the package does not distribute several third-party linters.
For now the script proposes to install development requirements, you should skip this step.
You can also install linters manually. To do this, please refer to this section.
Alternatively, you can build a docker image by Dockerfile and run the tool inside this image. Or use the public docker image, that we use in the build.yml file.
To set up a development environment, you need to run the following commands:
-
Download the repository:
git clone https://github.com/hyperskill/hyperstyle.git && cd hyperstyle
-
Install a virtual environment:
python3 -m venv venv && source venv/bin/activate
-
Install development requirements and install (or update) linters specified in the environment variables above:
./setup_environment.sh
It will install all dependencies from the requirements-dev.txt, which contains runtime, test and build dependencies.
You can also install linters manually. To do this, please refer to this section.
You can download all linters' sources by the following commands:
-
ESLINT
:npm install [email protected] -g && eslint --init
-
CHECKSTYLE
:curl -L https://github.com/checkstyle/checkstyle/releases/download/checkstyle-${CHECKSTYLE_VERSION}/checkstyle-${CHECKSTYLE_VERSION}-all.jar > ${CHECKSTYLE_DIRECTORY}/checkstyle-${CHECKSTYLE_VERSION}-all.jar
-
DETEKT
:curl -sSLO https://github.com/detekt/detekt/releases/download/v${DETEKT_VERSION}/detekt-cli-${DETEKT_VERSION}.zip \ && unzip detekt-cli-${DETEKT_VERSION}.zip -d ${DETEKT_DIRECTORY} \ && curl -H "Accept: application/zip" https://repo.maven.apache.org/maven2/io/gitlab/arturbosch/detekt/detekt-formatting/${DETEKT_VERSION}/detekt-formatting-${DETEKT_VERSION}.jar -o ${DETEKT_DIRECTORY}/detekt-formatting-${DETEKT_VERSION}.jar
-
PMD
:curl -sSLO https://github.com/pmd/pmd/releases/download/pmd_releases/${PMD_VERSION}/pmd-bin-${PMD_VERSION}.zip \ && unzip pmd-bin-${PMD_VERSION}.zip -d ${PMD_DIRECTORY}
-
GOLANG_LINT
:curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ${GOLANG_LINT_DIRECTORY} v${GOLANG_LINT_VERSION}
-
IJ-based linters:
python3 -m grpc_tools.protoc --proto_path=. --python_out=. --pyi_out=. --grpc_python_out=. hyperstyle/src/python/review/inspectors/common/inspector/proto/model.proto
Run the run_tool.py with the arguments.
A simple configuration: python run_tool.py <path>
.
Required arguments:
- path — path to file or directory to inspect.
Optional arguments:
Argument | Description |
---|---|
‑h, ‑‑help | show the help message and exit. |
‑v, ‑‑verbosity | choose logging level according this list: 1 - ERROR; 2 - INFO; 3 - DEBUG; 0 - disable logging (CRITICAL value); default value is 0 (CRITICAL). |
‑d, ‑‑disable | disable inspectors. Available values: for Python language: pylint for Pylint, flake8 for flake8, radon for Radon, python_ast to check different measures providing by AST, ij-python for IJ inspections; for Java language: checkstyle for the Checkstyle, pmd for PMD; for Kotlin language: detekt for Detekt, ij-kotlin for IJ inspections; for JavaScript language: eslint for ESlint; for Go language: golang_lint for golangci-lint. Example: -d pylint,flake8 . |
‑‑allow-duplicates | allow duplicate issues found by different linters. By default, duplicates are skipped. |
‑‑language-version, ‑‑language_version | specify the language version for JAVA inspectors. Available values: java7 , java8 , java9 , java11 , java15 , java17 . Note: ‑‑language_version is deprecated and will be deleted in the future. |
‑‑n-cpu, ‑‑n_cpu | specify number of cpu that can be used to run inspectors. Note: ‑‑n_cpu is deprecated. Will be deleted in the future. |
‑f, ‑‑format | the output format. Available values: json , text . Default value is json . |
‑s, ‑‑start-line | the first line to be analyzed. By default it starts from 1 . |
‑e, ‑‑end-line | the end line to be analyzed. The default value is None , which meant to handle file by the end. |
‑‑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 . |
‑‑history | JSON string with a list of issues for each language. For each issue its class and quantity are specified. Example: --history "{\"python\": [{\"origin_class\": \"SC200\", \"number\": 20}, {\"origin_class\": \"WPS314\", \"number\": 3}]}" |
‑‑with‑all‑categories | Without this flag, all issues will be categorized into 5 main categories: CODE_STYLE , BEST_PRACTICES , ERROR_PRONE , COMPLEXITY , INFO . |
‑‑group‑by‑difficulty | With this flag, the final grade and influence on penalty will be grouped by the issue difficulty. |
‑‑language | Specify the language to inspect. The tool will check all languages by default. The default value is None . |
‑‑ij‑config | JSON string containing information for setting up a connection to the IJ server for each language to be analyzed with the IJ inspector. Example: --ij-config "{\"python\": {\"host\": \"localhost\", \"port\": 8080}, \"kotlin\": {\"host\": \"localhost\", \"port\": 8081}}" |
The output examples:
(New format means separating the result by the files to allow getting quality and observed issues for each file separately)
- Json
old format
(without ‑‑new-format argument):
{
"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",
"difficulty": "EASY",
"influence_on_penalty": 0
},
...
]
}
- Json
new format
(with ‑‑new-format argument):
{
"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",
"difficulty": "MEDIUM",
"influence_on_penalty": 0
},
...
]
}
]
}
- Json
old format
(with ‑‑group‑by‑difficulty argument):
{
"quality": {
"EASY": {
"code": "BAD",
"text": "Code quality (beta): BAD"
},
"MEDIUM": {
"code": "BAD",
"text": "Code quality (beta): BAD"
},
"HARD": {
"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",
"difficulty": "EASY",
"influence_on_penalty": {
"EASY": 0,
"MEDIUM": 0,
"HARD": 0
}
},
...
]
}
- Json
new format
(with ‑‑group‑by‑difficulty argument)
{
"quality": {
"EASY": {
"code": "BAD",
"text": "Code quality (beta): BAD"
},
"MEDIUM": {
"code": "BAD",
"text": "Code quality (beta): BAD"
},
"HARD": {
"code": "BAD",
"text": "Code quality (beta): BAD"
}
},
"file_review_results": [
{
"file_name": "<your file>",
"quality": {
"EASY": {
"code": "BAD",
"text": "Code quality (beta): BAD"
},
"MEDIUM": {
"code": "BAD",
"text": "Code quality (beta): BAD"
},
"HARD": {
"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",
"difficulty": "MEDIUM",
"influence_on_penalty": {
"EASY": 0,
"MEDIUM": 0,
"HARD": 0
}
},
...
]
}
]
}
- Text format:
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
We use pytest
library for tests.
Note: If you have ModuleNotFoundError
while you try to run tests, please call pip install -e .
before using the test system.
Note: We use eslint and open-jdk 11 in the tests. Please, set up the environment before running the tests. You can see en example of the environment configuration in the Dockerfile file.
Use pytest
from the root directory to run ALL tests.