Repository: hyperskill/hyperstyle (language: python)

Issue:
ValueError: '.ipynb' is not a valid Extension
I have tried to use Hyperstyle as a linter in a real python project. I ran Hyperstyle on a directory with `.ipynb` files besides `.py`, but failed with exceptions`ValueError: '.ipynb' is not a valid Extension`. Same was with  `.md` files in diectoy: `ValueError: '.md' is not a valid Extension`. The full traceback was following:
```
Traceback (most recent call last):
  File "/Users/tiginamaria1999/PycharmProjects/hyperstyle/hyperstyle/src/python/review/run_tool.py", line 169, in main
    n_issues = perform_and_print_review(args.path, OutputFormat(args.format), config)
  File "/Users/tiginamaria1999/PycharmProjects/hyperstyle/hyperstyle/src/python/review/reviewers/perform_review.py", line 57, in perform_and_print_review
    review_result = perform_review(path, config)
  File "/Users/tiginamaria1999/PycharmProjects/hyperstyle/hyperstyle/src/python/review/reviewers/perform_review.py", line 85, in perform_review
    metadata = explore_project(path)
  File "/Users/tiginamaria1999/PycharmProjects/hyperstyle/hyperstyle/src/python/review/reviewers/utils/metadata_exploration.py", line 75, in explore_project
    inner_files.append(explore_file(file_path))
  File "/Users/tiginamaria1999/PycharmProjects/hyperstyle/hyperstyle/src/python/review/reviewers/utils/metadata_exploration.py", line 65, in explore_file
    language = guess_file_language(path)
  File "/Users/tiginamaria1999/PycharmProjects/hyperstyle/hyperstyle/src/python/review/common/language.py", line 54, in guess_file_language
    return EXTENSION_TO_LANGUAGE.get(Extension.get_extension_from_file(file_path), Language.UNKNOWN)
  File "/Users/tiginamaria1999/PycharmProjects/hyperstyle/hyperstyle/src/python/review/common/file_system.py", line 40, in get_extension_from_file
    return Extension(os.path.splitext(file)[1])
  File "/usr/local/Cellar/python@3.9/3.9.12/Frameworks/Python.framework/Versions/3.9/lib/python3.9/enum.py", line 384, in __call__
    return cls.__new__(cls, value)
  File "/usr/local/Cellar/python@3.9/3.9.12/Frameworks/Python.framework/Versions/3.9/lib/python3.9/enum.py", line 702, in __new__
    raise ve_exc
ValueError: '.ipynb' is not a valid Extension
```

 It seems that Hyperstyle fails when meets file with extension out of enum class `hyperstyle.src.python.review.common.file_system.Extension`. But I expected that Hyperstyle will skip such files and give feedback only on `.py` files it meets. 

Maybe you can do something with it?


Discussion / hints:
I think we can add Extension.UNKNOWN here:
https://github.com/hyperskill/hyperstyle/blob/e10a38c40e1195b12e0af02285163755265731bd/hyperstyle/src/python/review/common/file_system.py#L28-L34

And here a new pair of EXTENSION.UNKNOWN: LANGUAGE.UNKNOWN
https://github.com/hyperskill/hyperstyle/blob/e10a38c40e1195b12e0af02285163755265731bd/hyperstyle/src/python/review/common/language.py#L44-L50

Or we can return None in this function if we encounter a file with an unknown extension:
https://github.com/hyperskill/hyperstyle/blob/e10a38c40e1195b12e0af02285163755265731bd/hyperstyle/src/python/review/common/file_system.py#L39-L40

And then Hyperstyle should not crash in that case. @nbirillo, what do you think?
I think return None is better
Fixed in #157 

Fix patch:
diff --git a/.dockerignore b/.dockerignore
index 3f78c748..cb26562a 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -5,3 +5,9 @@
 .github
 .idea
 node_modules
+venv
+.venv
+build
+**/*.egg-info
+**/model_pb2*
+linters
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a3763b5d..3bd27396 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,79 +1,97 @@
 name: Python build
 
-on: [ push, pull_request ]
+on:
+  push:
+    branches:
+      - main
+      - develop
+  pull_request:
 
-jobs:
-
-  build:
-    runs-on: ubuntu-latest
-    # Consistent with base image in Dockerfile
-    # container: stepik/hyperstyle-base:py3.8.11-java11.0.11-node14.17.3-go1.18.5
-    container: nastyabirillo/hyperstyle:1.4.4
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
 
+jobs:
+  build_image:
+    name: Build Image
+    runs-on: [ self-hosted, small ]
     steps:
-      - name: Install git
-        run: |
-            apt-get update
-            apt-get -y install git
+      - uses: actions/checkout@v4
 
-      - name: Check env variables
-        run: |
-            echo $DETEKT_DIRECTORY && echo $DETEKT_VERSION
-            echo $CHECKSTYLE_DIRECTORY && echo CHECKSTYLE_VERSION
-            echo $PMD_DIRECTORY && echo PMD_VERSION
+      - uses: docker/login-action@v3
+        with:
+          registry: hyperskill.azurecr.io
+          username: ${{ secrets.REGISTRY_USER }}
+          password: ${{ secrets.REGISTRY_PASSWORD }}
 
-      - name: Checkout
-        uses: actions/checkout@v3
+      - uses: docker/setup-buildx-action@v3
 
-      - name: Install requirements
+      - name: Build and push server image
+        uses: docker/build-push-action@v6
+        with:
+          context: .
+          pull: true
+          push: true
+          tags: hyperskill.azurecr.io/hyperstyle:${{ github.sha }}
+          cache-from: |
+            type=gha
+            type=gha,scope=main
+          cache-to: type=gha,mode=max
+
+  tests:
+    name: Tests
+    needs:
+      - build_image
+    runs-on: [ self-hosted, small ]
+    container:
+      image: hyperskill.azurecr.io/hyperstyle:${{ github.sha }}
+      credentials:
+        username: ${{ secrets.REGISTRY_USER }}
+        password: ${{ secrets.REGISTRY_PASSWORD }}
+    steps:
+      - name: Run tests
         run: |
-          pip install --no-cache-dir -r requirements-test.txt -r requirements.txt
+          cd /review
+          /hyperstyle/bin/pytest
 
-      - name: Set up git rights
+  build:
+    runs-on: [ self-hosted, small ]
+    needs:
+      - build_image
+    container:
+      image: hyperskill.azurecr.io/hyperstyle:${{ github.sha }}
+      credentials:
+        username: ${{ secrets.REGISTRY_USER }}
+        password: ${{ secrets.REGISTRY_PASSWORD }}
+    steps:
+      - name: Check env variables
         run: |
-          git config --global --add safe.directory '*'
+          echo $DETEKT_DIRECTORY && echo $DETEKT_VERSION
+          echo $CHECKSTYLE_DIRECTORY && echo $CHECKSTYLE_VERSION
+          echo $PMD_DIRECTORY && echo $PMD_VERSION
+          echo $GOLANG_LINT_DIRECTORY && echo $GOLANG_LINT_VERSION
 
       - name: Lint with flake8
         run: |
+          cd /review
           # stop the build if there are Python syntax errors or undefined names
-          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=.git,__pycache__,docs/source/conf.py,old,build,dist,venv,test/resources,.eggs,review.egg-info,.pytest_cache,node_modules
+          /hyperstyle/bin/flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=.git,__pycache__,docs/source/conf.py,old,build,dist,venv,test/resources,.eggs,review.egg-info,.pytest_cache,node_modules,hyperstyle/src/python/review/inspectors/common/inspector/proto
+
           # TODO: change max-complexity into 10 after refactoring
           # TODO: remove R504, A003, E800, E402, WPS1, WPS2, WPS3, WPS4, WPS5, WPS6, H601
-          flake8 . --count --max-complexity=11 --max-line-length=120 --max-doc-length=120 --ignore=R504,A003,E800,E402,W503,WPS,H601,N400 --statistics --exclude=.git,__pycache__,docs/source/conf.py,old,build,dist,venv,test/resources,.eggs,review.egg-info,.pytest_cache,node_modules
-
-      - name: Sort whitelists
-        run: |
-            for file in "whitelist.txt" "hyperstyle/src/python/review/inspectors/flake8/whitelist.txt"
-            do
-            LC_ALL=C sort $file -o $file
-            done
-      - name: Commit sorted whitelists
-        uses: EndBug/add-and-commit@v7.2.1
-        with:
-          add: "['whitelist.txt', 'hyperstyle/src/python/review/inspectors/flake8/whitelist.txt']"
-          message: 'Sort whitelists (Github Actions)'
-
-      - name: Set up Eslint
-        run: |
-          # Consistent with eslint version in Dockerfile
-          npm install eslint@7.5.0 -g && eslint --init
-
-      - name: Test with pytest
-        run: |
-          pytest -vv
-
-      - name: Install review module
-        run: |
-          pip install .
+          /hyperstyle/bin/flake8 . --count --max-complexity=11 --max-line-length=120 --max-doc-length=120 --ignore=R504,A003,E800,E402,W503,WPS,H601,N400,I100,I201,I202, --statistics --exclude=.git,__pycache__,docs/source/conf.py,old,build,dist,venv,test/resources,.eggs,review.egg-info,.pytest_cache,node_modules,hyperstyle/src/python/review/inspectors/common/inspector/proto
 
       - name: Check installed module can run python linters
         run: |
-            review setup.py
+          cd /review
+          /hyperstyle/bin/python -m hyperstyle.src.python.review.run_tool test/resources/inspectors/python/case39_no_issues.py
 
       - name: Check installed module can run java linters
         run: |
-            review test/resources/inspectors/java/test_algorithm_with_scanner.java
+          cd /review
+          /hyperstyle/bin/python -m hyperstyle.src.python.review.run_tool test/resources/inspectors/java/test_algorithm_with_scanner.java
 
       - name: Check installed module can run js linters
         run: |
-          review test/resources/inspectors/js/case0_no_issues.js
+          cd /review
+          /hyperstyle/bin/python -m hyperstyle.src.python.review.run_tool test/resources/inspectors/js/case0_no_issues.js
diff --git a/.github/workflows/build_base_image.yml b/.github/workflows/build_base_image.yml
index 51ae5774..7f66dab4 100644
--- a/.github/workflows/build_base_image.yml
+++ b/.github/workflows/build_base_image.yml
@@ -7,7 +7,7 @@ on:
         type: string
         description: 'Image tag'
         required: true
-        default: 'py3.12.5-java11.0.11-node14.17.3-go1.18.5'
+        default: 'py3.10.14-java11.0.11-node14.17.3-go1.18.5'
 
 concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
diff --git a/.github/workflows/build_image.yml b/.github/workflows/build_image.yml
new file mode 100644
index 00000000..80f61805
--- /dev/null
+++ b/.github/workflows/build_image.yml
@@ -0,0 +1,43 @@
+name: Build Image after Release
+
+on:
+  workflow_dispatch:
+    inputs:
+      image_tag:
+        description: 'Image tag'
+        required: true
+        default: 'latest'
+  release:
+    types:
+      - released
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: false
+
+jobs:
+  build_image:
+    name: Build Image
+    runs-on: [ self-hosted, small ]
+    steps:
+      - uses: actions/checkout@v4
+
+      - uses: docker/login-action@v3
+        with:
+          registry: hyperskill.azurecr.io
+          username: ${{ secrets.REGISTRY_USER }}
+          password: ${{ secrets.REGISTRY_PASSWORD }}
+
+      - uses: docker/setup-buildx-action@v3
+
+      - name: Build and push server image
+        uses: docker/build-push-action@v6
+        with:
+          context: .
+          pull: true
+          push: true
+          tags: hyperskill.azurecr.io/hyperstyle:${{ inputs.image_tag || github.event.release.tag_name }}
+          cache-from: |
+            type=gha
+            type=gha,scope=main
+          cache-to: type=gha,mode=max
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 47819798..c1cbfafb 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -1,19 +1,23 @@
-# Publish to PyPI in case of releasing
+name: Publish to PyPI in case of releasing
 
 on:
   push:
 
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: false
+
 jobs:
   build-n-publish:
     name: Build and publish Python 🐍 distributions 📦 to PyPI
-    runs-on: ubuntu-latest
+    runs-on: [ self-hosted, small ]
 
     steps:
       - uses: actions/checkout@v2
-      - name: Set up Python 3.8
-        uses: actions/setup-python@v2
+      - name: Set up Python
+        uses: actions/setup-python@v4
         with:
-          python-version: 3.8
+          python-version-file: .python-version
       - name: Install pypa/build
         run: |
           python -m pip install build --user
@@ -24,4 +28,4 @@ jobs:
         if: startsWith(github.ref, 'refs/tags')
         uses: pypa/gh-action-pypi-publish@master
         with:
-          password: ${{ secrets.SECRETS_TEST_PYPI_API_TOKEN }}
\ No newline at end of file
+          password: ${{ secrets.SECRETS_TEST_PYPI_API_TOKEN }}
diff --git a/.github/workflows/sort_whitespaces.yml b/.github/workflows/sort_whitespaces.yml
new file mode 100644
index 00000000..ecbc7a64
--- /dev/null
+++ b/.github/workflows/sort_whitespaces.yml
@@ -0,0 +1,39 @@
+name: Sort whitespaces
+
+on:
+  push:
+    branches:
+      - main
+      - develop
+    paths:
+      - 'whitelist.txt'
+      - 'hyperstyle/src/python/review/inspectors/flake8/whitelist.txt'
+  pull_request:
+    paths:
+      - 'whitelist.txt'
+      - 'hyperstyle/src/python/review/inspectors/flake8/whitelist.txt'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  sort_whitespaces:
+    name: Sort whitespaces
+    runs-on: [ self-hosted, small ]
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Sort whitelists
+        run: |
+          for file in "whitelist.txt" "hyperstyle/src/python/review/inspectors/flake8/whitelist.txt"
+          do
+          LC_ALL=C sort $file -o $file
+          done
+
+      - name: Commit sorted whitelists
+        uses: EndBug/add-and-commit@v7.2.1
+        with:
+          add: "['whitelist.txt', 'hyperstyle/src/python/review/inspectors/flake8/whitelist.txt']"
+          message: 'Sort whitelists (Github Actions)'
diff --git a/.gitignore b/.gitignore
index 63d25e2b..981f14ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,3 +77,11 @@ __pycache__/
 /.intellij_inspector/out/
 
 lightweight.Dockerfile
+linters
+
+node_modules
+.eslintrc.js
+package-lock.json
+
+hyperstyle/src/python/review/inspectors/common/inspector/proto/model_pb2*
+.env
diff --git a/.python-version b/.python-version
new file mode 100644
index 00000000..1445aee8
--- /dev/null
+++ b/.python-version
@@ -0,0 +1,1 @@
+3.10.14
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ce90bf0e..341e2ddb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,9 +10,9 @@ In this document you will find a detailed description of how to add new function
 ## Adding a new inspector for an existing language
 
 If you want to add a new inspector for an existing language, you need to:
-1. Inherit from the [`BaseInspector`](hyperstyle/src/python/review/inspectors/base_inspector.py#L9) class.
-2. Define the [`inspector_type`](hyperstyle/src/python/review/inspectors/base_inspector.py#L29) property. To do this, you need to update the [`InspectorType`](hyperstyle/src/python/review/inspectors/inspector_type.py#L6) class by adding there a name of the inspector.
-3. Implement the [`inspect`](hyperstyle/src/python/review/inspectors/base_inspector.py#L33) function, which should run an analysis of the language and return a list of found issues;
+1. Inherit from the [`BaseInspector`](hyperstyle/src/python/review/inspectors/common/inspector/base_inspector.py#L9) class.
+2. Define the [`inspector_type`](hyperstyle/src/python/review/inspectors/common/inspector/base_inspector.py#L29) property. To do this, you need to update the [`InspectorType`](hyperstyle/src/python/review/inspectors/common/inspector/inspector_type.py#L6) class by adding there a name of the inspector.
+3. Implement the [`inspect`](hyperstyle/src/python/review/inspectors/common/inspector/base_inspector.py#L33) function, which should run an analysis of the language and return a list of found issues;
 
 Usually the inspector runs a third-party linter through a command line and parses its result. In this case it will be useful to implement several auxiliary functions:
 1. `create_command` – creates a command to run the linter via subprocess.
@@ -31,9 +31,9 @@ If you are implementing the inspector that uses the third-party linter, you must
 
 ### Implementation of the `parse` function
 
-Usually, the `parse` function parses the result of the third-party linter line-by-line, then creates a base issue using the [`BaseIssue`](hyperstyle/src/python/review/inspectors/issue.py#L199) dataclass, which is later converted to either [`CodeIssue`](hyperstyle/src/python/review/inspectors/issue.py#L217) or one of the measurable issues using the [`convert_base_issue`](hyperstyle/src/python/review/inspectors/common/base_issue_converter.py#L17) function and an instance of the [`IssueConigsHandler`](hyperstyle/src/python/review/inspectors/issue_configs.py#L117) class. The resulting issue is added to the general list of found issues and this list is returned from the function after the parsing is finished.
+Usually, the `parse` function parses the result of the third-party linter line-by-line, then creates a base issue using the [`BaseIssue`](hyperstyle/src/python/review/inspectors/common/issue/issue.py#L199) dataclass, which is later converted to either [`CodeIssue`](hyperstyle/src/python/review/inspectors/common/issue/issue.py#L217) or one of the measurable issues using the [`convert_base_issue`](hyperstyle/src/python/review/inspectors/common/issue/base_issue_converter.py#L17) function and an instance of the [`IssueConigsHandler`](hyperstyle/src/python/review/inspectors/common/issue/issue_configs.py#L117) class. The resulting issue is added to the general list of found issues and this list is returned from the function after the parsing is finished.
 
-The `IssueConfigHandler` class handles custom issue descriptions and also parses metrics from their descriptions (examples of metrics are: line or function length, cyclomatic complexity, maintainability index).  It receives instances of [`IssueConfig`](hyperstyle/src/python/review/inspectors/issue_configs.py#L46) or [`MeasurableIssueConfig`](hyperstyle/src/python/review/inspectors/issue_configs.py#L85) classes as input, which should be stored in the `ISSUE_CONFIGS` list next to the inspector.
+The `IssueConfigHandler` class handles custom issue descriptions and also parses metrics from their descriptions (examples of metrics are: line or function length, cyclomatic complexity, maintainability index).  It receives instances of [`IssueConfig`](hyperstyle/src/python/review/inspectors/common/issue/issue_configs.py#L46) or [`MeasurableIssueConfig`](hyperstyle/src/python/review/inspectors/common/issue/issue_configs.py#L85) classes as input, which should be stored in the `ISSUE_CONFIGS` list next to the inspector.
 
 Also, if the third-party linter supports output in "Checkstyle" format, you can use the [`parse_xml_file_result`](hyperstyle/src/python/review/inspectors/common/xml_parser.py#L47) function to parse the output file.
 
@@ -42,7 +42,7 @@ Also, if the third-party linter supports output in "Checkstyle" format, you can
 A sample checklist for adding a new inspector looks like this:
 - [ ] I've inherited from the `BaseInspector` class.
 - [ ] I've implemented the `inspect` function and _if needed_ I've added a check for the existence of third-party linter environment variables using the [`check_set_up_env_variable`](hyperstyle/src/python/review/common/file_system.py#L124) function.
-- [ ] I've added a new inspector type to the `InspectorType` class, updated the [`available_values`](hyperstyle/src/python/review/inspectors/inspector_type.py#L27) function and defined the inspector type.
+- [ ] I've added a new inspector type to the `InspectorType` class, updated the [`available_values`](hyperstyle/src/python/review/inspectors/common/inspector/inspector_type.py#L27) function and defined the inspector type.
 - [ ] _If needed_. I've added a config to run the third-party linter.
 - [ ] _If needed_. I've implemented the `create_command` function.
 - [ ] _if needed_. I've implemented the `parse` function and defined the `ISSUES_CONFIGS` list in a separate file.
diff --git a/Dockerfile b/Dockerfile
index 4ba0e5e6..9cedaea3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,64 +1,79 @@
-FROM hyperskill.azurecr.io/hyperstyle-base:py3.8.11-java11.0.11-node14.17.3-go1.18.5
+FROM hyperskill.azurecr.io/hyperstyle-base:py3.10.14-java11.0.11-node14.17.3-go1.18.5
 
-RUN npm install eslint@7.5.0 -g \
-    && eslint --init
+ENV LINTERS_DIRECTORY=/opt/linters
 
-COPY . review
-RUN pip install --no-cache-dir \
-    -r review/requirements-test.txt \
-    -r review/requirements.txt \
-    ./review
+ENV ESLINT_VERSION=7.5.0
 
-ENV LINTERS_DIRECTORY      /opt/linters
+ENV CHECKSTYLE_VERSION=8.44
+ENV CHECKSTYLE_DIRECTORY=${LINTERS_DIRECTORY}/checkstyle
 
-ENV CHECKSTYLE_VERSION  8.44
-ENV CHECKSTYLE_DIRECTORY  ${LINTERS_DIRECTORY}/checkstyle
+ENV DETEKT_VERSION=1.14.2
+ENV DETEKT_DIRECTORY=${LINTERS_DIRECTORY}/detekt
 
-ENV DETEKT_VERSION  1.14.2
-ENV DETEKT_DIRECTORY  ${LINTERS_DIRECTORY}/detekt
+ENV PMD_VERSION=6.37.0
+ENV PMD_DIRECTORY=${LINTERS_DIRECTORY}/pmd
 
-ENV PMD_VERSION  6.37.0
-ENV PMD_DIRECTORY  ${LINTERS_DIRECTORY}/pmd
+ENV GOLANG_LINT_VERSION=1.49.0
+ENV GOLANG_LINT_DIRECTORY=${LINTERS_DIRECTORY}/golangci-lint
 
-ENV GOLANG_LINT_VERSION  1.49.0
-ENV GOLANG_LINT_DIRECTORY  ${LINTERS_DIRECTORY}/golangci-lint
-
-RUN mkdir -p ${CHECKSTYLE_DIRECTORY} &&  \
-    mkdir -p ${DETEKT_DIRECTORY} &&  \
-    mkdir -p ${PMD_DIRECTORY} &&  \
-    mkdir -p ${GOLANG_LINT_DIRECTORY}
+RUN mkdir -p ${CHECKSTYLE_DIRECTORY} \
+  && mkdir -p ${DETEKT_DIRECTORY} \
+  && mkdir -p ${PMD_DIRECTORY} \
+  && mkdir -p ${GOLANG_LINT_DIRECTORY}
 
 # Install Curl and Unzip
-RUN apt -y update &&  \
-    apt -y upgrade && \
-    apt -y install curl unzip
+RUN apt-get update \
+  && apt-get install -y --no-install-recommends curl unzip ca-certificates \
+  && rm -rf /var/lib/apt/lists/*
+
+# Install eslint
+RUN npm install eslint@${ESLINT_VERSION} -g \
+  && eslint --init
 
 # Install Detekt and Detekt-formatting
 RUN 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
+  && unzip detekt-cli-${DETEKT_VERSION}.zip -d ${DETEKT_DIRECTORY} \
+  && rm detekt-cli-${DETEKT_VERSION}.zip \
+  && 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
 
 # Install Checkstyle
 RUN curl -L https://github.com/checkstyle/checkstyle/releases/download/checkstyle-${CHECKSTYLE_VERSION}/checkstyle-${CHECKSTYLE_VERSION}-all.jar > ${CHECKSTYLE_DIRECTORY}/checkstyle-${CHECKSTYLE_VERSION}-all.jar
 
 # Install PMD
 RUN 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}
+  && unzip pmd-bin-${PMD_VERSION}.zip -d ${PMD_DIRECTORY} \
+  && rm pmd-bin-${PMD_VERSION}.zip
 
 # Install golangci-lint
-RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh  \
-    | sh -s -- -b ${GOLANG_LINT_DIRECTORY} v${GOLANG_LINT_VERSION}
+RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
+  | sh -s -- -b ${GOLANG_LINT_DIRECTORY} v${GOLANG_LINT_VERSION}
 
 # Install third party golang libraries and pre-cache them by compiling main.go
 # Taken from: https://github.com/StepicOrg/epicbox-images/blob/a5eadb5211909fc7ef99724ee0b8bf3a758ae1b7/epicbox-go/Dockerfile
-RUN curl -sSfLO https://raw.githubusercontent.com/StepicOrg/epicbox-images/a5eadb5211909fc7ef99724ee0b8bf3a758ae1b7/epicbox-go/go.mod && \
-    curl -sSfLO https://raw.githubusercontent.com/StepicOrg/epicbox-images/a5eadb5211909fc7ef99724ee0b8bf3a758ae1b7/epicbox-go/go.sum && \
-    curl -sSfLO https://raw.githubusercontent.com/StepicOrg/epicbox-images/a5eadb5211909fc7ef99724ee0b8bf3a758ae1b7/epicbox-go/main.go && \
-    go mod download && \
-    go mod verify && \
-    go mod tidy && \
-    go run main.go &&  \
-    rm main.go &&  \
-    chmod ugo-w go.mod go.sum
-
-CMD ["/bin/bash"]
+RUN curl -sSfLO https://raw.githubusercontent.com/StepicOrg/epicbox-images/a5eadb5211909fc7ef99724ee0b8bf3a758ae1b7/epicbox-go/go.mod \
+  && curl -sSfLO https://raw.githubusercontent.com/StepicOrg/epicbox-images/a5eadb5211909fc7ef99724ee0b8bf3a758ae1b7/epicbox-go/go.sum \
+  && curl -sSfLO https://raw.githubusercontent.com/StepicOrg/epicbox-images/a5eadb5211909fc7ef99724ee0b8bf3a758ae1b7/epicbox-go/main.go \
+  && go mod download \
+  && go mod verify \
+  && go mod tidy \
+  && go run main.go \
+  && rm main.go \
+  && chmod ugo-w go.mod go.sum
+
+ARG POETRY_VERSION=1.8.3
+RUN pip install poetry==${POETRY_VERSION} \
+  && poetry config virtualenvs.create false \
+  && python -m venv /hyperstyle
+
+WORKDIR /review
+
+COPY pyproject.toml poetry.lock ./
+RUN . /hyperstyle/bin/activate \
+  && poetry install --no-interaction --no-ansi --no-cache --no-root
+
+COPY . .
+
+RUN PROTO_PATH="hyperstyle/src/python/review/inspectors/common/inspector/proto" \
+  && /hyperstyle/bin/python -m grpc_tools.protoc --proto_path=. --python_out=. --pyi_out=. --grpc_python_out=. ${PROTO_PATH}/model.proto
+
+CMD ["/hyperstyle/bin/pytest"]
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index bdf4942c..00000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,3 +0,0 @@
-include VERSION.md
-include requirements.txt
-recursive-exclude __pycache__ *.pyc *.pyo *.orig
\ No newline at end of file
diff --git a/README.md b/README.md
index 22797696..b6f29a35 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ A tool for running a set of pre-configured linters and evaluating code quality.
 It is used on the [Hyperskill](https://hyperskill.org/) platform 
 to check the quality of learners' code.
 
-[Read more detail about the project at Hyperskill Help Center](https://support.hyperskill.org/hc/en-us/articles/360049582712-Code-style-Code-quality)
+[Read more details about the project at Hyperskill Help Center](https://support.hyperskill.org/hc/en-us/articles/360049582712-Code-style-Code-quality)
 
 [The dockerized version](https://hub.docker.com/r/stepik/hyperstyle/tags)
 
@@ -24,13 +24,14 @@ 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](requirements.txt) file):
 
-- [x]  flake8 [MIT]
+- [x] flake8 [MIT]
     * [Site and docs](https://flake8.pycqa.org/en/latest/)
     * [Repository](https://github.com/PyCQA/flake8)
   
-- [x]  Pylint [GNU LGPL v2]
+- [x] Pylint [GNU LGPL v2]
     * [Site and docs](https://www.pylint.org/)
     * [Repository](https://github.com/PyCQA/pylint)
    
@@ -38,13 +39,19 @@ Python language (all versions can be found in the [requirements.txt](requirement
     * [Site and docs](https://radon.readthedocs.io/en/latest/)
     * [Repository](https://github.com/rubik/radon)
 
+- [x] Python IJ Inspections [MIT]
+    * [Site and docs](https://www.jetbrains.com/help/pycharm/disabling-and-enabling-inspections.html)
+    * [Repository](https://github.com/JetBrains-Research/code-quality-ij-server/tree/master)
+
+
+
 Java language:
 
-- [x]  PMD [BSD] (Version: 6.37.0)
+- [x] PMD [BSD] (Version: 6.37.0)
     * [Site and docs](https://pmd.github.io/)
     * [Repository](https://github.com/pmd/pmd)
   
-- [x]  Checkstyle [GNU LGPL v2.1] (Version: 8.44)
+- [x] Checkstyle [GNU LGPL v2.1] (Version: 8.44)
     * [Site and docs](https://checkstyle.sourceforge.io/)
     * [Repository](https://github.com/checkstyle/checkstyle)
 
@@ -52,15 +59,18 @@ Java language:
 
 Kotlin language:
 
-- [x]  Detekt [Apache 2.0] (Version: 1.14.2)
+- [x] Detekt [Apache 2.0] (Version: 1.14.2)
     * [Site and docs](https://detekt.github.io/detekt/)
     * [Repository](https://github.com/detekt/detekt)
 
+- [x] Kotlin IJ inspections [MIT]
+    * [Site and docs](https://www.jetbrains.com/help/idea/code-inspection.html)
+    * [Repository](https://github.com/JetBrains-Research/code-quality-ij-server/tree/master)
 
 
 JavaScript language:
 
-- [x]  ESlint [MIT] (Version: 7.5.0)
+- [x] ESlint [MIT] (Version: 7.5.0)
     * [Site and docs](https://eslint.org/)
     * [Repository](https://github.com/eslint/eslint)
   
@@ -75,7 +85,9 @@ Go language:
 
 ## Installation
 
-You have to create set of environment variables:
+### Pre-requirements
+
+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](Dockerfile))
 - `CHECKSTYLE_DIRECTORY` (the directory with `CHECKSTYLE` linter sources)
 - `DETEKT_VERSION` (the value of the variable must be the same with its value in [Dockerfile](Dockerfile))
@@ -85,52 +97,97 @@ You have to create set of environment variables:
 - `GOLANG_LINT_VERSION` (the value of the variable must be the same with its value in [Dockerfile](Dockerfile))
 - `GOLANG_LINT_DIRECTORY` (the directory with `GOLANG_LINT` linter sources)
 
-### Using script
+### Using pip
 
-Just run the following command:
-```bash
-./setup_environment.sh
-```
-and install everything the script suggests. 
+Just run the following commands to install everything you need to run the tool:
+
+1. Install the latest version of hyperstyle from [PyPI](https://pypi.org/project/hyperstyle/):
+   ```bash
+   pip install hyperstyle
+   ```
+   
+   You could also install a specific version:
+   ```bash
+   pip install hyperstyle==<VERSION>
+   ```
+   where `<VERSION>` is your versions.
+   The list of all available versions you could find [here](https://pypi.org/project/hyperstyle/#history).
+
+2. Install (or update) linters specified in the environment variables above:
+   ```bash
+   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](#linter-manual-installation) section.
+
+### Using docker
+
+Alternatively, you can build a docker image by [Dockerfile](Dockerfile) and run the tool inside this image.
+Or use the public docker image, that we use in the [build.yml](.github/workflows/build.yml) file.
 
-**Note**: You can also use this script to update linters. To do this, just update the corresponding 
-linter version variables, run the script, and reinstall only the necessary linters. 
+### Manually (for development purposes)
 
-### Manually
+To set up a development environment, you need to run the following commands:
 
-If you don't want to use the script, you can install the environment manually.
+1. Download the repository:
+   ```bash
+   git clone https://github.com/hyperskill/hyperstyle.git && cd hyperstyle
+   ```
 
-Simply clone the repository and run the following commands:
+2. Install a virtual environment:
+   ```bash
+   python3 -m venv venv && source venv/bin/activate
+   ```
 
-1. `pip install -r requirements.txt`
-2. `pip install -r requirements-test.txt` for tests
-3. `npm install eslint@7.5.0 -g && eslint --init`
+3. Install development requirements and install (or update) linters specified in the environment variables above:
+   ```bash
+   ./setup_environment.sh
+   ```
+
+   It will install all dependencies from the [requirements-dev.txt](requirements-dev.txt), 
+   which contains runtime, test and build dependencies.   
+
+   You can also install linters manually. To do this, please refer to [this](#linter-manual-installation) section.
+
+### Linter manual installation
+
+You can download all linters' sources by the following commands:
+
+- `ESLINT`:
+  ```bash
+  npm install eslint@7.5.0 -g && eslint --init
+  ```
 
-You can download all linters sources manually or by the following commands:
 - `CHECKSTYLE`: 
-```bash
-curl -L https://github.com/checkstyle/checkstyle/releases/download/checkstyle-${CHECKSTYLE_VERSION}/checkstyle-${CHECKSTYLE_VERSION}-all.jar > ${CHECKSTYLE_DIRECTORY}/checkstyle-${CHECKSTYLE_VERSION}-all.jar
-```
+  ```bash
+  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`: 
-```bash
-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
-```
+  ```bash
+  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`: 
-```bash
-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`:
-```bash
-curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ${GOLANG_LINT_DIRECTORY} v${GOLANG_LINT_VERSION}
-```
+  ```bash
+  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}
+  ```
 
-### Using docker
+- `GOLANG_LINT`:
+  ```bash
+  curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ${GOLANG_LINT_DIRECTORY} v${GOLANG_LINT_VERSION}
+  ```
 
-Alternatively, you can build a docker image by [Dockerfile](Dockerfile) and run the tool inside this image.
-Or use the public docker image, that we use in the [build.yml](.github/workflows/build.yml) file.
+- IJ-based linters:
+  ```bash
+  python3 -m grpc_tools.protoc --proto_path=. --python_out=. --pyi_out=. --grpc_python_out=. hyperstyle/src/python/review/inspectors/common/inspector/proto/model.proto
+  ```
 
 ## Usage
 
@@ -143,22 +200,23 @@ A simple configuration: `python run_tool.py <path>`.
 
 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/), `radon` for [Radon](https://radon.readthedocs.io/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/); for **Go** language: `golang_lint` for [golangci-lint](https://golangci-lint.run/). Example: `-d pylint,flake8`.
-**&#8209;&#8209;allow-duplicates**        |  allow duplicate issues found by different linters. By default, duplicates are skipped.
-**&#8209;&#8209;language-version**, **&#8209;&#8209;language_version**  |  specify the language version for JAVA inspectors. Available values: `java7`, `java8`, `java9`, `java11`, `java15`, `java17`. **Note**: **&#8209;&#8209;language_version** is deprecated and will be deleted in the future.
-**&#8209;&#8209;n-cpu**, **&#8209;&#8209;n_cpu**  |  specify number of _cpu_ that can be used to run inspectors. **Note**: **&#8209;&#8209;n_cpu** is deprecated. Will be deleted in the future.
-**&#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`.
-**&#8209;&#8209;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}]}"`
-**&#8209;&#8209;with&#8209;all&#8209;categories** | Without this flag, all issues will be categorized into 5 main categories: `CODE_STYLE`, `BEST_PRACTICES`, `ERROR_PRONE`, `COMPLEXITY`, `INFO`.
-**&#8209;&#8209;group&#8209;by&#8209;difficulty** | With this flag, the final grade and influence on penalty will be grouped by the issue difficulty.
-**&#8209;&#8209;language** | Specify the language to inspect. The tool will check all languages by default. The default value is `None`.
+| 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/), `radon` for [Radon](https://radon.readthedocs.io/en/latest/), `python_ast` to check different measures providing by AST, `ij-python` for IJ inspections; 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/), `ij-kotlin` for IJ inspections; for **JavaScript** language: `eslint` for [ESlint](https://eslint.org/); for **Go** language: `golang_lint` for [golangci-lint](https://golangci-lint.run/). Example: `-d pylint,flake8`. |
+| **&#8209;&#8209;allow-duplicates**                                     | allow duplicate issues found by different linters. By default, duplicates are skipped.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
+| **&#8209;&#8209;language-version**, **&#8209;&#8209;language_version** | specify the language version for JAVA inspectors. Available values: `java7`, `java8`, `java9`, `java11`, `java15`, `java17`. **Note**: **&#8209;&#8209;language_version** is deprecated and will be deleted in the future.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
+| **&#8209;&#8209;n-cpu**, **&#8209;&#8209;n_cpu**                       | specify number of _cpu_ that can be used to run inspectors. **Note**: **&#8209;&#8209;n_cpu** is deprecated. Will be deleted in the future.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
+| **&#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`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
+| **&#8209;&#8209;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}]}"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
+| **&#8209;&#8209;with&#8209;all&#8209;categories**                      | Without this flag, all issues will be categorized into 5 main categories: `CODE_STYLE`, `BEST_PRACTICES`, `ERROR_PRONE`, `COMPLEXITY`, `INFO`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
+| **&#8209;&#8209;group&#8209;by&#8209;difficulty**                      | With this flag, the final grade and influence on penalty will be grouped by the issue difficulty.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
+| **&#8209;&#8209;language**                                             | Specify the language to inspect. The tool will check all languages by default. The default value is `None`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
+| **&#8209;&#8209;ij&#8209;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:
 
diff --git a/VERSION.md b/VERSION.md
deleted file mode 100644
index 1c99cf0e..00000000
--- a/VERSION.md
+++ /dev/null
@@ -1,1 +0,0 @@
-1.4.4
diff --git a/base.Dockerfile b/base.Dockerfile
index a7d6c6bb..a9d7c75b 100644
--- a/base.Dockerfile
+++ b/base.Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.12.5-slim
+FROM python:3.10.14-slim
 
 #########
 # Taken from https://github.com/docker-library/openjdk/blob/608f26c5ea63ca34070b439c904cb94a30f6b0c1/11/jdk/slim-buster/Dockerfile
diff --git a/hyperstyle/src/python/common/tool_arguments.py b/hyperstyle/src/python/common/tool_arguments.py
index f258bb11..e4455bb3 100644
--- a/hyperstyle/src/python/common/tool_arguments.py
+++ b/hyperstyle/src/python/common/tool_arguments.py
@@ -4,7 +4,7 @@
 
 from hyperstyle.src.python.review.common.language import Language
 from hyperstyle.src.python.review.common.language_version import LanguageVersion
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
 
 
 @unique
@@ -80,7 +80,7 @@ class RunToolArgument(Enum):
                                'should use the new format')
 
     HISTORY = ArgumentsInfo(None, '--history',
-                            'Json string, which contains lists of issues in the previous submissions '
+                            'JSON string, which contains lists of issues in the previous submissions '
                             'for other tasks for one user.')
 
     WITH_ALL_CATEGORIES = ArgumentsInfo(None, '--with-all-categories',
@@ -89,3 +89,11 @@ class RunToolArgument(Enum):
 
     GROUP_BY_DIFFICULTY = ArgumentsInfo(None, '--group-by-difficulty',
                                         'With this flag, the final grade will be grouped by the issue difficulty.')
+
+    IJ_CONFIG = ArgumentsInfo(
+        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. '
+        'It should be a dictionary of dictionaries where for each language host and port are specified.',
+    )
diff --git a/hyperstyle/src/python/review/application_config.py b/hyperstyle/src/python/review/application_config.py
index 79514c3f..f8315a10 100644
--- a/hyperstyle/src/python/review/application_config.py
+++ b/hyperstyle/src/python/review/application_config.py
@@ -2,7 +2,7 @@
 from typing import Optional, Set
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
 
 
 @dataclass
@@ -18,6 +18,7 @@ class ApplicationConfig:
     new_format: bool = False
     history: Optional[str] = None
     group_by_difficulty: bool = False
+    ij_config: Optional[str] = None
 
     @staticmethod
     def get_default_config() -> 'ApplicationConfig':
diff --git a/hyperstyle/src/python/review/common/parallel_runner.py b/hyperstyle/src/python/review/common/parallel_runner.py
index 7b6a7e45..e0033eb3 100644
--- a/hyperstyle/src/python/review/common/parallel_runner.py
+++ b/hyperstyle/src/python/review/common/parallel_runner.py
@@ -6,8 +6,8 @@
 from typing import Any, Callable, Dict, List
 
 from hyperstyle.src.python.review.application_config import ApplicationConfig
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue
 
 logger = logging.getLogger(__name__)
 
@@ -39,11 +39,11 @@ def inspect_in_parallel(inspector_runner: Callable[[Any, ApplicationConfig, Base
                         data: Any,
                         config: ApplicationConfig,
                         inspectors: List[BaseInspector]) -> List[BaseIssue]:
-    inspectors = filter(lambda i: i.inspector_type not in config.disabled_inspectors, inspectors)
+    inspectors_to_run = filter(lambda i: i.inspector_type not in config.disabled_inspectors, inspectors)
 
     if config.n_cpu == 1:
         issues = []
-        for inspector in inspectors:
+        for inspector in inspectors_to_run:
             inspector_issues = inspector_runner(data, config, inspector)
             issues.extend(inspector_issues)
         return issues
@@ -51,7 +51,7 @@ def inspect_in_parallel(inspector_runner: Callable[[Any, ApplicationConfig, Base
     with multiprocessing.Pool(config.n_cpu) as pool:
         issues = pool.map(
             functools.partial(inspector_runner, data, config),
-            inspectors,
+            inspectors_to_run,
         )
 
     return list(itertools.chain(*issues))
diff --git a/hyperstyle/src/python/review/inspectors/base_inspector.py b/hyperstyle/src/python/review/inspectors/base_inspector.py
deleted file mode 100644
index 401ef6a4..00000000
--- a/hyperstyle/src/python/review/inspectors/base_inspector.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import abc
-from pathlib import Path
-from typing import Any, Dict, List
-
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue
-
-
-class BaseInspector(abc.ABC):
-    """
-    Each external inspector contains a dictionary in which the IssueType corresponds to the original linter classes.
-    The dictionary helps to categorize errors during parsing the linters' output.
-
-    To add a new inspector, you need:
-     - to create a class that inherits from the BaseInspector class,
-     - define the type of inspector (the type filed) by adding a new option in the InspectorType,
-     - implement the <inspect >function.
-
-    Typically, the <inspect> function launches a linter and parses its output (XML or JSON) to get a list of BaseIssue.
-
-    Also, if you need to launch inspectors in memory (without using/creating a file with code, you need to implement
-    <inspect_in_memory> function.
-
-    Some inspectors (internal) do not require creating a dictionary with IssueType.
-    This is connected to the fact that they do not launch an additional analysis tool and work with the code directly,
-    for example, the python AST inspector.
-    """
-
-    # Type of inspection for analyzing, e.g. pylint, detekt and etc
-    @property
-    @abc.abstractmethod
-    def inspector_type(self) -> InspectorType:
-        raise NotImplementedError('inspector_type property not implemented yet')
-
-    @abc.abstractmethod
-    def inspect(self, path: Path, config: Dict[str, Any]) -> List[BaseIssue]:
-        raise NotImplementedError('inspect method not implemented yet')
-
-    @abc.abstractmethod
-    def inspect_in_memory(self, code: str, config: Dict[str, Any]) -> List[BaseIssue]:
-        raise NotImplementedError('inspect in memory method not implemented yet')
diff --git a/hyperstyle/src/python/review/inspectors/checkstyle/checkstyle.py b/hyperstyle/src/python/review/inspectors/checkstyle/checkstyle.py
index e2d726c8..1c83ab45 100644
--- a/hyperstyle/src/python/review/inspectors/checkstyle/checkstyle.py
+++ b/hyperstyle/src/python/review/inspectors/checkstyle/checkstyle.py
@@ -5,13 +5,13 @@
 
 from hyperstyle.src.python.review.common.file_system import check_set_up_env_variable, new_temp_dir
 from hyperstyle.src.python.review.common.subprocess_runner import run_in_subprocess
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
 from hyperstyle.src.python.review.inspectors.checkstyle.issue_configs import ISSUE_CONFIGS
 from hyperstyle.src.python.review.inspectors.checkstyle.issue_types import CHECK_CLASS_NAME_TO_ISSUE_TYPE
 from hyperstyle.src.python.review.inspectors.common.xml_parser import parse_xml_file_result
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 
 logger = logging.getLogger(__name__)
 
diff --git a/hyperstyle/src/python/review/inspectors/checkstyle/files/config.xml b/hyperstyle/src/python/review/inspectors/checkstyle/files/config.xml
index f49b5267..2018949b 100644
--- a/hyperstyle/src/python/review/inspectors/checkstyle/files/config.xml
+++ b/hyperstyle/src/python/review/inspectors/checkstyle/files/config.xml
@@ -6,8 +6,9 @@
 <module name="Checker">
     <property name="fileExtensions" value="java"/>
 
-    <!-- This config was created using the following table: https://bit.ly/3BbxmD5 -->
-
+    <!-- This config was created using the following table: -->
+    <!-- https://docs.google.com/spreadsheets/d/1G3NOpo9PzZcL0IsnSlxIHIWcSZWIX0km -->
+        
     <!-- Size Violations -->
     <module name="LineLength">
         <property name="max" value="120"/>
diff --git a/hyperstyle/src/python/review/inspectors/checkstyle/issue_configs.py b/hyperstyle/src/python/review/inspectors/checkstyle/issue_configs.py
index ddbc1262..d76f4590 100644
--- a/hyperstyle/src/python/review/inspectors/checkstyle/issue_configs.py
+++ b/hyperstyle/src/python/review/inspectors/checkstyle/issue_configs.py
@@ -1,13 +1,13 @@
 import re
 
-from hyperstyle.src.python.review.inspectors.common.tips import (
+from hyperstyle.src.python.review.inspectors.common.issue.tips import (
     get_bool_expr_len_tip,
     get_cyclomatic_complexity_tip,
     get_func_len_tip,
     get_line_len_tip,
     get_magic_number_tip,
 )
-from hyperstyle.src.python.review.inspectors.issue_configs import (
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import (
     IssueConfig,
     IssueDescriptionParser,
     MeasurableIssueConfig,
diff --git a/hyperstyle/src/python/review/inspectors/checkstyle/issue_types.py b/hyperstyle/src/python/review/inspectors/checkstyle/issue_types.py
index 604d8a2d..61941556 100644
--- a/hyperstyle/src/python/review/inspectors/checkstyle/issue_types.py
+++ b/hyperstyle/src/python/review/inspectors/checkstyle/issue_types.py
@@ -1,6 +1,6 @@
 from typing import Dict
 
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 
 CHECK_CLASS_NAME_TO_ISSUE_TYPE: Dict[str, IssueType] = {
     # ---- Annotations ----
diff --git a/hyperstyle/src/python/review/inspectors/common/inspector/__init__.py b/hyperstyle/src/python/review/inspectors/common/inspector/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/hyperstyle/src/python/review/inspectors/common/inspector/base_inspector.py b/hyperstyle/src/python/review/inspectors/common/inspector/base_inspector.py
new file mode 100644
index 00000000..fd669327
--- /dev/null
+++ b/hyperstyle/src/python/review/inspectors/common/inspector/base_inspector.py
@@ -0,0 +1,154 @@
+import logging
+from abc import ABC, abstractmethod
+from pathlib import Path
+from typing import Any, Dict, List
+
+from hyperstyle.src.python.review.common.file_system import get_content_from_file
+from hyperstyle.src.python.review.inspectors.common.inspector.ij_client import IJClient
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.inspector.proto import model_pb2
+from hyperstyle.src.python.review.inspectors.common.issue.base_issue_converter import convert_base_issue
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfig, IssueConfigsHandler
+
+
+logger = logging.getLogger(__name__)
+
+
+class BaseInspector(ABC):
+    """
+    Each external inspector contains a dictionary in which the IssueType corresponds to the original linter classes.
+    The dictionary helps to categorize errors during parsing the linters' output.
+
+    To add a new inspector, you need:
+     - to create a class that inherits from the BaseInspector class,
+     - define the type of inspector (the type filed) by adding a new option in the InspectorType,
+     - implement the <inspect >function.
+
+    Typically, the <inspect> function launches a linter and parses its output (XML or JSON) to get a list of BaseIssue.
+
+    Also, if you need to launch inspectors in memory (without using/creating a file with code, you need to implement
+    <inspect_in_memory> function.
+
+    Some inspectors (internal) do not require creating a dictionary with IssueType.
+    This is connected to the fact that they do not launch an additional analysis tool and work with the code directly,
+    for example, the python AST inspector.
+    """
+
+    # Type of inspection for analyzing, e.g. pylint, detekt and etc
+    @property
+    @abstractmethod
+    def inspector_type(self) -> InspectorType:
+        raise NotImplementedError('inspector_type property not implemented yet')
+
+    @abstractmethod
+    def inspect(self, path: Path, config: Dict[str, Any]) -> List[BaseIssue]:
+        raise NotImplementedError('inspect method not implemented yet')
+
+    @abstractmethod
+    def inspect_in_memory(self, code: str, config: Dict[str, Any]) -> List[BaseIssue]:
+        raise NotImplementedError('inspect in memory method not implemented yet')
+
+
+class BaseIJInspector(BaseInspector):
+    """
+    Base class for every IJ-based inspector.
+
+    It inherits from the `BaseInspector` class, so see its documentation for more information.
+
+    To implement this kind of inspector, you should additionally specify `language_id` from the `model.proto` file,
+    specify issue configs and implement the `choose_issue_type` function.
+
+    Before running the inspector, you should set up connection parameters
+    using the `setup_connection_parameters` function.
+    """
+    host: str
+    port: int
+
+    @property
+    @abstractmethod
+    def language_id(self) -> model_pb2.LanguageId:
+        raise NotImplementedError('language_id property is not implemented yet')
+
+    @property
+    @abstractmethod
+    def issue_configs(self) -> List[IssueConfig]:
+        raise NotImplementedError('issue_configs property is not implemented yet')
+
+    @property
+    @abstractmethod
+    def ij_inspection_to_issue_type(self) -> Dict[str, IssueType]:
+        raise NotImplementedError('ij_inspection_to_issue_type property is not implemented yet')
+
+    @property
+    @abstractmethod
+    def ij_message_to_issue_type(self) -> Dict[str, Dict[str, IssueType]]:
+        raise NotImplementedError('ij_message_to_issue_type property is not implemented yet')
+
+    def setup_connection_parameters(self, host: str, port: int):
+        self.host = host
+        self.port = port
+
+    def inspect(self, path: Path, config: Dict[str, Any]) -> List[BaseIssue]:
+        code = get_content_from_file(path)
+        return self._get_inspection_result(code, path)
+
+    def inspect_in_memory(self, code: str, config: Dict[str, Any]) -> List[BaseIssue]:
+        return self._get_inspection_result(code, Path(""))
+
+    def convert_to_base_issues(self, inspection_result: model_pb2.InspectionResult, file_path: Path) -> List[BaseIssue]:
+        base_issues = []
+        issue_configs_handler = IssueConfigsHandler(*self.issue_configs)
+        for problem in inspection_result.problems:
+            issue_type = self.choose_issue_type(problem)
+            base_issue = BaseIssue(
+                origin_class=problem.inspector,
+                type=issue_type,
+                description=problem.name,
+                file_path=file_path,
+                line_no=problem.lineNumber,
+                column_no=problem.offset,
+                inspector_type=self.inspector_type,
+                difficulty=IssueDifficulty.get_by_issue_type(issue_type),
+            )
+
+            issue = convert_base_issue(base_issue, issue_configs_handler)
+            if issue is None:
+                logger.error(f'{self.inspector_type.value}: an error occurred during converting a base issue.')
+                continue
+
+            base_issues.append(base_issue)
+
+        return base_issues
+
+    def _get_inspection_result(self, code_text: str, file_path: Path) -> List[BaseIssue]:
+        if self.host is None or self.port is None:
+            raise Exception('Connection parameters is not set up.')
+
+        try:
+            client = IJClient(self.host, self.port)
+
+            code = model_pb2.Code()
+            code.languageId = self.language_id
+            code.text = code_text
+
+            inspection_result = client.inspect(code)
+
+            return self.convert_to_base_issues(inspection_result, file_path)
+
+        except Exception as e:
+            # TODO: replace with error when add mock server into tests
+            logger.info('Inspector failed to connect to code server.', e)
+            return []
+
+    def choose_issue_type(self, problem: model_pb2.Problem) -> IssueType:
+        if problem.inspector in self.ij_message_to_issue_type:
+            for key, value in self.ij_message_to_issue_type[problem.inspector].items():
+                if problem.name in key:
+                    return value
+
+        if problem.inspector in self.ij_inspection_to_issue_type:
+            return self.ij_inspection_to_issue_type[problem.inspector]
+
+        # PEP-8 inspection
+        return IssueType.CODE_STYLE
diff --git a/hyperstyle/src/python/review/inspectors/common/inspector/ij_client.py b/hyperstyle/src/python/review/inspectors/common/inspector/ij_client.py
new file mode 100644
index 00000000..ff7a0d20
--- /dev/null
+++ b/hyperstyle/src/python/review/inspectors/common/inspector/ij_client.py
@@ -0,0 +1,28 @@
+import grpc
+
+from hyperstyle.src.python.review.inspectors.common.inspector.proto import model_pb2, model_pb2_grpc
+
+TIMEOUT = 1
+
+
+class IJClient(object):
+    def __init__(self, host: str = 'localhost', port: int = 8080):
+        self.host = host
+        self.port = port
+
+        # instantiate a channel
+        self.channel = grpc.insecure_channel(f'{self.host}:{self.port}')
+
+        # bind the client and the server
+        try:
+            grpc.channel_ready_future(self.channel).result(timeout=TIMEOUT)
+        except grpc.FutureTimeoutError:
+            raise Exception("Failed to connect to ij code server")
+        else:
+            self.stub = model_pb2_grpc.CodeInspectionServiceStub(self.channel)
+
+    def inspect(self, code: model_pb2.Code) -> model_pb2.InspectionResult:
+        return self.stub.inspect(code, timeout=TIMEOUT)
+
+    def init(self, service: model_pb2.Service) -> model_pb2.InitResult:
+        return self.stub.init(service, timeout=TIMEOUT)
diff --git a/hyperstyle/src/python/review/inspectors/inspector_type.py b/hyperstyle/src/python/review/inspectors/common/inspector/inspector_type.py
similarity index 78%
rename from hyperstyle/src/python/review/inspectors/inspector_type.py
rename to hyperstyle/src/python/review/inspectors/common/inspector/inspector_type.py
index 5ce892d6..04c1ceee 100644
--- a/hyperstyle/src/python/review/inspectors/inspector_type.py
+++ b/hyperstyle/src/python/review/inspectors/common/inspector/inspector_type.py
@@ -9,6 +9,7 @@ class InspectorType(Enum):
     PYTHON_AST = 'PYTHON_AST'
     FLAKE8 = 'FLAKE8'
     RADON = 'RADON'
+    IJ_PYTHON = 'IJ_PYTHON'
 
     # Java language
     PMD = 'PMD'
@@ -16,6 +17,7 @@ class InspectorType(Enum):
 
     # Kotlin language
     DETEKT = 'DETEKT'
+    IJ_KOTLIN = 'IJ_KOTLIN'
 
     # JavaScript language
     ESLINT = 'ESLINT'
@@ -25,6 +27,8 @@ class InspectorType(Enum):
 
     UNDEFINED = 'UNDEFINED'
     QODANA = 'QODANA'
+    # TODO: it is used on production for java inspections, remove in the future releases
+    IJ_OLD = 'INTELLIJ'
 
     @classmethod
     def available_values(cls) -> List[str]:
@@ -34,6 +38,7 @@ def available_values(cls) -> List[str]:
             cls.FLAKE8.value,
             cls.PYTHON_AST.value,
             cls.RADON.value,
+            cls.IJ_PYTHON.value,
 
             # Java language
             cls.PMD.value,
@@ -41,10 +46,13 @@ def available_values(cls) -> List[str]:
 
             # Kotlin language
             cls.DETEKT.value,
+            cls.IJ_KOTLIN.value,
 
             # JavaScript language
             cls.ESLINT.value,
 
             # Go language
             cls.GOLANG_LINT.value,
+
+            cls.IJ_OLD.value,
         ]
diff --git a/hyperstyle/src/python/review/inspectors/common/inspector/proto/__init__.py b/hyperstyle/src/python/review/inspectors/common/inspector/proto/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/hyperstyle/src/python/review/inspectors/common/inspector/proto/model.proto b/hyperstyle/src/python/review/inspectors/common/inspector/proto/model.proto
new file mode 100644
index 00000000..4dfb60da
--- /dev/null
+++ b/hyperstyle/src/python/review/inspectors/common/inspector/proto/model.proto
@@ -0,0 +1,41 @@
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "org.jetbrains.research.ij.headless.server";
+option java_outer_classname = "CodeServerProto";
+
+service CodeInspectionService {
+    rpc inspect (Code) returns (InspectionResult) {};
+}
+
+enum LanguageId {
+    Python = 0;
+    kotlin = 1;
+    Java = 2;
+}
+
+message Code {
+    string text = 1;
+    LanguageId languageId = 2;
+}
+
+message Problem {
+    string name = 1;
+    string inspector = 2;
+    uint64 lineNumber = 3;
+    uint64 offset = 4;
+    uint64 length = 5;
+}
+
+message InspectionResult {
+    repeated Problem problems = 1;
+}
+
+message Service {
+    string name = 1;
+    LanguageId languageId = 2;
+}
+
+message InitResult {
+    uint64 status = 1;
+}
diff --git a/hyperstyle/src/python/review/inspectors/common/issue/__init__.py b/hyperstyle/src/python/review/inspectors/common/issue/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/hyperstyle/src/python/review/inspectors/common/base_issue_converter.py b/hyperstyle/src/python/review/inspectors/common/issue/base_issue_converter.py
similarity index 90%
rename from hyperstyle/src/python/review/inspectors/common/base_issue_converter.py
rename to hyperstyle/src/python/review/inspectors/common/issue/base_issue_converter.py
index d2a3b9cf..d8ceaa62 100644
--- a/hyperstyle/src/python/review/inspectors/common/base_issue_converter.py
+++ b/hyperstyle/src/python/review/inspectors/common/issue/base_issue_converter.py
@@ -1,14 +1,14 @@
 import logging
 from typing import Optional
 
-from hyperstyle.src.python.review.inspectors.issue import (
+from hyperstyle.src.python.review.inspectors.common.issue.issue import (
     BaseIssue,
     get_issue_class_by_issue_type,
     get_measure_name_by_measurable_issue_type,
     IssueData,
     Measurable,
 )
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 
 
 logger = logging.getLogger(__name__)
diff --git a/hyperstyle/src/python/review/inspectors/issue.py b/hyperstyle/src/python/review/inspectors/common/issue/issue.py
similarity index 99%
rename from hyperstyle/src/python/review/inspectors/issue.py
rename to hyperstyle/src/python/review/inspectors/common/issue/issue.py
index a8f03bb1..06c509d9 100644
--- a/hyperstyle/src/python/review/inspectors/issue.py
+++ b/hyperstyle/src/python/review/inspectors/common/issue/issue.py
@@ -6,7 +6,7 @@
 from pathlib import Path
 from typing import Any, Dict, List, Type, Union
 
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
 
 logger = logging.getLogger(__name__)
 
diff --git a/hyperstyle/src/python/review/inspectors/issue_configs.py b/hyperstyle/src/python/review/inspectors/common/issue/issue_configs.py
similarity index 100%
rename from hyperstyle/src/python/review/inspectors/issue_configs.py
rename to hyperstyle/src/python/review/inspectors/common/issue/issue_configs.py
diff --git a/hyperstyle/src/python/review/inspectors/common/tips.py b/hyperstyle/src/python/review/inspectors/common/issue/tips.py
similarity index 100%
rename from hyperstyle/src/python/review/inspectors/common/tips.py
rename to hyperstyle/src/python/review/inspectors/common/issue/tips.py
diff --git a/hyperstyle/src/python/review/inspectors/common/utils.py b/hyperstyle/src/python/review/inspectors/common/utils.py
index de633472..981d8e1f 100644
--- a/hyperstyle/src/python/review/inspectors/common/utils.py
+++ b/hyperstyle/src/python/review/inspectors/common/utils.py
@@ -5,7 +5,7 @@
 from typing import List
 
 from hyperstyle.src.python.review.common.file_system import get_content_from_file
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
 
 logger = logging.getLogger(__name__)
 
@@ -37,17 +37,6 @@ def convert_percentage_of_value_to_lack_of_value(percentage_of_value: float) ->
     return floor(100 - percentage_of_value)
 
 
-# TODO: When upgrading to python 3.9+, replace it with removeprefix.
-# See: https://docs.python.org/3.9/library/stdtypes.html#str.removeprefix
-def remove_prefix(text: str, prefix: str) -> str:
-    """
-    Removes the prefix if it is present, otherwise returns the original string.
-    """
-    if text.startswith(prefix):
-        return text[len(prefix):]
-    return text
-
-
 def _get_format_fields(input_string: str) -> List[str]:
     """
     Get all format fields from the input string.
diff --git a/hyperstyle/src/python/review/inspectors/common/xml_parser.py b/hyperstyle/src/python/review/inspectors/common/xml_parser.py
index 7e4dde18..ff4b3fa3 100644
--- a/hyperstyle/src/python/review/inspectors/common/xml_parser.py
+++ b/hyperstyle/src/python/review/inspectors/common/xml_parser.py
@@ -3,11 +3,11 @@
 from typing import Callable, List
 from xml.etree import ElementTree
 
-from hyperstyle.src.python.review.inspectors.common.base_issue_converter import convert_base_issue
+from hyperstyle.src.python.review.inspectors.common.issue.base_issue_converter import convert_base_issue
 from hyperstyle.src.python.review.inspectors.common.utils import is_result_file_correct
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 
 logger = logging.getLogger(__name__)
 
diff --git a/hyperstyle/src/python/review/inspectors/detekt/detekt.py b/hyperstyle/src/python/review/inspectors/detekt/detekt.py
index c0bcf1a9..3bba971c 100644
--- a/hyperstyle/src/python/review/inspectors/detekt/detekt.py
+++ b/hyperstyle/src/python/review/inspectors/detekt/detekt.py
@@ -5,13 +5,13 @@
 
 from hyperstyle.src.python.review.common.file_system import check_set_up_env_variable, new_temp_dir
 from hyperstyle.src.python.review.common.subprocess_runner import run_in_subprocess
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
 from hyperstyle.src.python.review.inspectors.common.xml_parser import parse_xml_file_result
 from hyperstyle.src.python.review.inspectors.detekt.issue_configs import ISSUE_CONFIGS
 from hyperstyle.src.python.review.inspectors.detekt.issue_types import DETEKT_CLASS_NAME_TO_ISSUE_TYPE
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 
 logger = logging.getLogger(__name__)
 
diff --git a/hyperstyle/src/python/review/inspectors/detekt/files/detekt-config.yml b/hyperstyle/src/python/review/inspectors/detekt/files/detekt-config.yml
index 950bc7b4..fe49c429 100644
--- a/hyperstyle/src/python/review/inspectors/detekt/files/detekt-config.yml
+++ b/hyperstyle/src/python/review/inspectors/detekt/files/detekt-config.yml
@@ -1,5 +1,5 @@
-# This config was created based on the default config for Detekt 1.18.0
-# using the following table: https://bit.ly/2V2kCyi
+# This config was created based on the default config for Detekt 1.18.0 using the following table:
+# https://docs.google.com/spreadsheets/d/1N6CMqXKW-FnrlevwwD8iO75M1gTjmLO_
 
 # Some inspections have been commented out due to problems with upgrading to version 1.18.0.
 # The current config contains only those inspections that work on version 1.14.2.
diff --git a/hyperstyle/src/python/review/inspectors/detekt/issue_configs.py b/hyperstyle/src/python/review/inspectors/detekt/issue_configs.py
index 426944ba..813c3a27 100644
--- a/hyperstyle/src/python/review/inspectors/detekt/issue_configs.py
+++ b/hyperstyle/src/python/review/inspectors/detekt/issue_configs.py
@@ -1,12 +1,12 @@
 import re
 
-from hyperstyle.src.python.review.inspectors.common.tips import (
+from hyperstyle.src.python.review.inspectors.common.issue.tips import (
     get_bool_expr_len_tip,
     get_cyclomatic_complexity_tip,
     get_func_len_tip,
     get_magic_number_tip,
 )
-from hyperstyle.src.python.review.inspectors.issue_configs import (
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import (
     IssueConfig,
     IssueDescriptionParser,
     MeasurableIssueConfig,
diff --git a/hyperstyle/src/python/review/inspectors/detekt/issue_types.py b/hyperstyle/src/python/review/inspectors/detekt/issue_types.py
index 19b4da80..35b9d07d 100644
--- a/hyperstyle/src/python/review/inspectors/detekt/issue_types.py
+++ b/hyperstyle/src/python/review/inspectors/detekt/issue_types.py
@@ -1,6 +1,6 @@
 from typing import Dict
 
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 
 DETEKT_CLASS_NAME_TO_ISSUE_TYPE: Dict[str, IssueType] = {
     # Comments
diff --git a/hyperstyle/src/python/review/inspectors/eslint/eslint.py b/hyperstyle/src/python/review/inspectors/eslint/eslint.py
index 20e35092..d0557fb3 100644
--- a/hyperstyle/src/python/review/inspectors/eslint/eslint.py
+++ b/hyperstyle/src/python/review/inspectors/eslint/eslint.py
@@ -3,13 +3,13 @@
 
 from hyperstyle.src.python.review.common.file_system import new_temp_dir
 from hyperstyle.src.python.review.common.subprocess_runner import run_in_subprocess
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
 from hyperstyle.src.python.review.inspectors.common.xml_parser import parse_xml_file_result
 from hyperstyle.src.python.review.inspectors.eslint.issue_configs import ISSUE_CONFIGS
 from hyperstyle.src.python.review.inspectors.eslint.issue_types import ESLINT_CLASS_NAME_TO_ISSUE_TYPE
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 
 PATH_ESLINT_CONFIG = Path(__file__).parent / '.eslintrc'
 
diff --git a/hyperstyle/src/python/review/inspectors/eslint/issue_configs.py b/hyperstyle/src/python/review/inspectors/eslint/issue_configs.py
index fe15f326..7ae584a6 100644
--- a/hyperstyle/src/python/review/inspectors/eslint/issue_configs.py
+++ b/hyperstyle/src/python/review/inspectors/eslint/issue_configs.py
@@ -1,7 +1,10 @@
 import re
 
-from hyperstyle.src.python.review.inspectors.common.tips import get_cyclomatic_complexity_tip
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueDescriptionParser, MeasurableIssueConfig
+from hyperstyle.src.python.review.inspectors.common.issue.tips import get_cyclomatic_complexity_tip
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import (
+    IssueDescriptionParser,
+    MeasurableIssueConfig,
+)
 
 ISSUE_CONFIGS = [
     MeasurableIssueConfig(
diff --git a/hyperstyle/src/python/review/inspectors/eslint/issue_types.py b/hyperstyle/src/python/review/inspectors/eslint/issue_types.py
index db8b082b..1ab776b1 100644
--- a/hyperstyle/src/python/review/inspectors/eslint/issue_types.py
+++ b/hyperstyle/src/python/review/inspectors/eslint/issue_types.py
@@ -1,6 +1,6 @@
 from typing import Dict
 
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 
 ESLINT_CLASS_NAME_TO_ISSUE_TYPE: Dict[str, IssueType] = {
     # Possible errors (according to Eslint doc)
diff --git a/hyperstyle/src/python/review/inspectors/flake8/flake8.py b/hyperstyle/src/python/review/inspectors/flake8/flake8.py
index f63fbe9a..63d5f21d 100644
--- a/hyperstyle/src/python/review/inspectors/flake8/flake8.py
+++ b/hyperstyle/src/python/review/inspectors/flake8/flake8.py
@@ -1,16 +1,17 @@
 import logging
 import re
+import sys
 from pathlib import Path
 from typing import Any, Dict, List
 
 from hyperstyle.src.python.review.common.subprocess_runner import run_in_subprocess
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
-from hyperstyle.src.python.review.inspectors.common.base_issue_converter import convert_base_issue
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.issue.base_issue_converter import convert_base_issue
 from hyperstyle.src.python.review.inspectors.flake8.issue_configs import ISSUE_CONFIGS
 from hyperstyle.src.python.review.inspectors.flake8.issue_types import CODE_PREFIX_TO_ISSUE_TYPE, CODE_TO_ISSUE_TYPE
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 
 logger = logging.getLogger(__name__)
 
@@ -22,6 +23,7 @@
 FORMAT = '%(path)s:%(row)d:%(col)d:%(code)s:%(text)s'
 INSPECTOR_NAME = 'flake8'
 BASE_COMMAND = [
+    sys.executable, '-m',
     'flake8',
     f'--format={FORMAT}',
     f'--config={PATH_FLAKE8_CONFIG}',
diff --git a/hyperstyle/src/python/review/inspectors/flake8/issue_configs.py b/hyperstyle/src/python/review/inspectors/flake8/issue_configs.py
index ee721f76..9117e44c 100644
--- a/hyperstyle/src/python/review/inspectors/flake8/issue_configs.py
+++ b/hyperstyle/src/python/review/inspectors/flake8/issue_configs.py
@@ -1,6 +1,6 @@
 import re
 
-from hyperstyle.src.python.review.inspectors.common.tips import (
+from hyperstyle.src.python.review.inspectors.common.issue.tips import (
     get_augmented_assign_pattern_tip,
     get_cohesion_tip,
     get_cyclomatic_complexity_tip,
@@ -8,7 +8,7 @@
     get_magic_number_tip,
 )
 from hyperstyle.src.python.review.inspectors.common.utils import convert_percentage_of_value_to_lack_of_value
-from hyperstyle.src.python.review.inspectors.issue_configs import (
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import (
     IssueConfig,
     IssueDescriptionParser,
     MeasurableIssueConfig,
diff --git a/hyperstyle/src/python/review/inspectors/flake8/issue_types.py b/hyperstyle/src/python/review/inspectors/flake8/issue_types.py
index d3e549d3..f3cf4a48 100644
--- a/hyperstyle/src/python/review/inspectors/flake8/issue_types.py
+++ b/hyperstyle/src/python/review/inspectors/flake8/issue_types.py
@@ -1,6 +1,6 @@
 from typing import Dict
 
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 
 CODE_TO_ISSUE_TYPE: Dict[str, IssueType] = {
     # Line length
@@ -37,7 +37,8 @@
     # Cohesion
     'H601': IssueType.COHESION,
 
-    # The categorization for WPS was created using the following document: https://bit.ly/3yms06n
+    # The categorization for WPS was created using the following document:
+    # https://docs.google.com/document/d/1Y_DkYuglPvYlYmAJoyvkXEERbNDS3UhS
 
     # WPS: Naming
     'WPS117': IssueType.CODE_STYLE,  # Forbid naming variables self, cls, or mcs.
diff --git a/hyperstyle/src/python/review/inspectors/golang_lint/config.yml b/hyperstyle/src/python/review/inspectors/golang_lint/config.yml
index fe00bdf1..02a1e8e1 100644
--- a/hyperstyle/src/python/review/inspectors/golang_lint/config.yml
+++ b/hyperstyle/src/python/review/inspectors/golang_lint/config.yml
@@ -1,4 +1,5 @@
-# The configuration was made using the following table: https://bit.ly/3Kvz0nK
+# The configuration was made using the following table:
+# https://docs.google.com/spreadsheets/d/1zJrORtjZ3UyoLJV5qaLw9Y_cnK0sNlDW
 
 run:
   tests: false
diff --git a/hyperstyle/src/python/review/inspectors/golang_lint/golang_lint.py b/hyperstyle/src/python/review/inspectors/golang_lint/golang_lint.py
index 087228e4..9f6c6674 100644
--- a/hyperstyle/src/python/review/inspectors/golang_lint/golang_lint.py
+++ b/hyperstyle/src/python/review/inspectors/golang_lint/golang_lint.py
@@ -7,17 +7,17 @@
 
 from hyperstyle.src.python.review.common.file_system import check_set_up_env_variable, new_temp_dir
 from hyperstyle.src.python.review.common.subprocess_runner import run_in_subprocess
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
-from hyperstyle.src.python.review.inspectors.common.base_issue_converter import convert_base_issue
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.issue.base_issue_converter import convert_base_issue
 from hyperstyle.src.python.review.inspectors.common.utils import is_result_file_correct
 from hyperstyle.src.python.review.inspectors.golang_lint.issue_configs import ISSUE_CONFIGS
 from hyperstyle.src.python.review.inspectors.golang_lint.issue_types import (
     CODE_PREFIX_TO_ISSUE_TYPE,
     CODE_TO_ISSUE_TYPE,
 )
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 
 GOLANG_LINT_DIRECTORY_ENV = 'GOLANG_LINT_DIRECTORY'
 GOLANG_LINT_CONFIG_PATH = Path(__file__).parent / 'config.yml'
diff --git a/hyperstyle/src/python/review/inspectors/golang_lint/issue_configs.py b/hyperstyle/src/python/review/inspectors/golang_lint/issue_configs.py
index 18e7233e..a348abbd 100644
--- a/hyperstyle/src/python/review/inspectors/golang_lint/issue_configs.py
+++ b/hyperstyle/src/python/review/inspectors/golang_lint/issue_configs.py
@@ -1,6 +1,6 @@
 import re
 
-from hyperstyle.src.python.review.inspectors.common.tips import (
+from hyperstyle.src.python.review.inspectors.common.issue.tips import (
     get_cyclomatic_complexity_tip,
     get_func_len_tip,
     get_line_len_tip,
@@ -8,7 +8,7 @@
     get_maintainability_index_tip,
 )
 from hyperstyle.src.python.review.inspectors.common.utils import convert_percentage_of_value_to_lack_of_value
-from hyperstyle.src.python.review.inspectors.issue_configs import (
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import (
     IssueConfig,
     IssueDescriptionParser,
     MeasurableIssueConfig,
diff --git a/hyperstyle/src/python/review/inspectors/golang_lint/issue_types.py b/hyperstyle/src/python/review/inspectors/golang_lint/issue_types.py
index 8addba6d..4ee25e90 100644
--- a/hyperstyle/src/python/review/inspectors/golang_lint/issue_types.py
+++ b/hyperstyle/src/python/review/inspectors/golang_lint/issue_types.py
@@ -2,9 +2,10 @@
 
 from typing import Dict
 
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 
-# The configuration was made using the following table: https://bit.ly/3Kvz0nK
+# The configuration was made using the following table:
+# https://docs.google.com/spreadsheets/d/1zJrORtjZ3UyoLJV5qaLw9Y_cnK0sNlDW
 CODE_TO_ISSUE_TYPE: Dict[str, IssueType] = {
     'errcheck': IssueType.ERROR_PRONE,
     'ineffassign': IssueType.ERROR_PRONE,
diff --git a/hyperstyle/src/python/review/inspectors/ij_kotlin/__init__.py b/hyperstyle/src/python/review/inspectors/ij_kotlin/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/hyperstyle/src/python/review/inspectors/ij_kotlin/ij_kotlin.py b/hyperstyle/src/python/review/inspectors/ij_kotlin/ij_kotlin.py
new file mode 100644
index 00000000..6871a1fa
--- /dev/null
+++ b/hyperstyle/src/python/review/inspectors/ij_kotlin/ij_kotlin.py
@@ -0,0 +1,16 @@
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseIJInspector
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.inspector.proto import model_pb2
+from hyperstyle.src.python.review.inspectors.ij_kotlin.issue_configs import ISSUE_CONFIGS
+from hyperstyle.src.python.review.inspectors.ij_kotlin.issue_types import (
+    IJ_INSPECTION_TO_ISSUE_TYPE,
+    IJ_MESSAGE_TO_ISSUE_TYPE,
+)
+
+
+class KotlinIJInspector(BaseIJInspector):
+    inspector_type = InspectorType.IJ_KOTLIN
+    language_id = model_pb2.LanguageId.kotlin
+    issue_configs = ISSUE_CONFIGS
+    ij_inspection_to_issue_type = IJ_INSPECTION_TO_ISSUE_TYPE
+    ij_message_to_issue_type = IJ_MESSAGE_TO_ISSUE_TYPE
diff --git a/hyperstyle/src/python/review/inspectors/ij_kotlin/issue_configs.py b/hyperstyle/src/python/review/inspectors/ij_kotlin/issue_configs.py
new file mode 100644
index 00000000..5b01ae4b
--- /dev/null
+++ b/hyperstyle/src/python/review/inspectors/ij_kotlin/issue_configs.py
@@ -0,0 +1,1 @@
+ISSUE_CONFIGS = []
diff --git a/hyperstyle/src/python/review/inspectors/ij_kotlin/issue_types.py b/hyperstyle/src/python/review/inspectors/ij_kotlin/issue_types.py
new file mode 100644
index 00000000..5f37738f
--- /dev/null
+++ b/hyperstyle/src/python/review/inspectors/ij_kotlin/issue_types.py
@@ -0,0 +1,3 @@
+IJ_INSPECTION_TO_ISSUE_TYPE = {}
+
+IJ_MESSAGE_TO_ISSUE_TYPE = {}
diff --git a/hyperstyle/src/python/review/inspectors/ij_python/__init__.py b/hyperstyle/src/python/review/inspectors/ij_python/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/hyperstyle/src/python/review/inspectors/ij_python/ij_python.py b/hyperstyle/src/python/review/inspectors/ij_python/ij_python.py
new file mode 100644
index 00000000..04f9b40b
--- /dev/null
+++ b/hyperstyle/src/python/review/inspectors/ij_python/ij_python.py
@@ -0,0 +1,16 @@
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseIJInspector
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.inspector.proto import model_pb2
+from hyperstyle.src.python.review.inspectors.ij_python.issue_configs import ISSUE_CONFIGS
+from hyperstyle.src.python.review.inspectors.ij_python.issue_types import (
+    IJ_INSPECTION_TO_ISSUE_TYPE,
+    IJ_MESSAGE_TO_ISSUE_TYPE,
+)
+
+
+class PythonIJInspector(BaseIJInspector):
+    inspector_type = InspectorType.IJ_PYTHON
+    language_id = model_pb2.LanguageId.Python
+    issue_configs = ISSUE_CONFIGS
+    ij_inspection_to_issue_type = IJ_INSPECTION_TO_ISSUE_TYPE
+    ij_message_to_issue_type = IJ_MESSAGE_TO_ISSUE_TYPE
diff --git a/hyperstyle/src/python/review/inspectors/ij_python/issue_configs.py b/hyperstyle/src/python/review/inspectors/ij_python/issue_configs.py
new file mode 100644
index 00000000..827e5f76
--- /dev/null
+++ b/hyperstyle/src/python/review/inspectors/ij_python/issue_configs.py
@@ -0,0 +1,8 @@
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfig
+
+ISSUE_CONFIGS = [
+    IssueConfig(
+        origin_class='E128',
+        new_description='Incorrect indent',
+    ),
+]
diff --git a/hyperstyle/src/python/review/inspectors/ij_python/issue_types.py b/hyperstyle/src/python/review/inspectors/ij_python/issue_types.py
new file mode 100644
index 00000000..6bff3d44
--- /dev/null
+++ b/hyperstyle/src/python/review/inspectors/ij_python/issue_types.py
@@ -0,0 +1,77 @@
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
+
+# Synchronized with https://github.com/JetBrains-Research/code-quality-ij-server/tree/master/docs/inspections/python
+IJ_INSPECTION_TO_ISSUE_TYPE = {
+    # BEST_PRACTICES
+    'PyUnusedLocalInspection': IssueType.BEST_PRACTICES,
+    'PySimplifyBooleanCheckInspection': IssueType.BEST_PRACTICES,
+    'PyArgumentEqualDefaultInspection': IssueType.BEST_PRACTICES,
+    'PyAttributeOutsideInitInspection': IssueType.BEST_PRACTICES,
+    'PyBroadExceptionInspection': IssueType.BEST_PRACTICES,
+    'PyDictCreationInspection': IssueType.BEST_PRACTICES,
+    'PyFromFutureImportInspection': IssueType.BEST_PRACTICES,
+    'PyReturnFromInitInspection': IssueType.BEST_PRACTICES,
+    'PyListCreationInspection': IssueType.BEST_PRACTICES,
+    'PySetFunctionToLiteralInspection': IssueType.BEST_PRACTICES,
+    'PyProtectedMemberInspection': IssueType.BEST_PRACTICES,
+    'PyMethodMayBeStaticInspection': IssueType.BEST_PRACTICES,
+    'PyChainedComparisonsInspection': IssueType.BEST_PRACTICES,
+    'PyGlobalUndefinedInspection': IssueType.BEST_PRACTICES,
+    'PyComparisonWithNoneInspection': IssueType.BEST_PRACTICES,
+    'PyShadowingNamesInspection': IssueType.BEST_PRACTICES,
+
+    # CODE_STYLE
+    'PyRedundantParenthesesInspection': IssueType.CODE_STYLE,
+    'PyAugmentAssignmentInspection': IssueType.CODE_STYLE,
+    'PyMethodParametersInspection': IssueType.CODE_STYLE,
+    'PyUnreachableCodeInspection': IssueType.CODE_STYLE,
+    'PyTrailingSemicolonInspection': IssueType.CODE_STYLE,
+    'PyStatementEffectInspection': IssueType.CODE_STYLE,
+    'PyPep8NamingInspection': IssueType.CODE_STYLE,
+    'PyPep8Inspection': IssueType.CODE_STYLE,
+
+    # ERROR_PRONE
+    'PyDataclassInspection': IssueType.ERROR_PRONE,
+    'PyDefaultArgumentInspection': IssueType.ERROR_PRONE,
+    'PyAssignmentToLoopOrWithParameterInspection': IssueType.ERROR_PRONE,
+    'PyAsyncCallInspection': IssueType.ERROR_PRONE,
+    'PyByteLiteralInspection': IssueType.ERROR_PRONE,
+    'PyCallingNonCallableInspection': IssueType.ERROR_PRONE,
+    'PyDictDuplicateKeysInspection': IssueType.ERROR_PRONE,
+    'PyExceptClausesOrderInspection': IssueType.ERROR_PRONE,
+    'PyFinalInspection': IssueType.ERROR_PRONE,
+    'PyArgumentListInspection': IssueType.ERROR_PRONE,
+    'PyMethodFirstArgAssignmentInspection': IssueType.ERROR_PRONE,
+    'PyStringFormatInspection': IssueType.ERROR_PRONE,
+    'PyMethodOverridingInspection': IssueType.ERROR_PRONE,
+    'PyTupleAssignmentBalanceInspection': IssueType.ERROR_PRONE,
+    'PyExceptionInheritInspection': IssueType.ERROR_PRONE,
+    'PyUnboundLocalVariableInspection': IssueType.ERROR_PRONE,
+    'PySuperArgumentsInspection': IssueType.ERROR_PRONE,
+    'PyTupleItemAssignmentInspection': IssueType.ERROR_PRONE,
+    'PyPropertyAccessInspection': IssueType.ERROR_PRONE,
+    'PyNestedDecoratorsInspection': IssueType.ERROR_PRONE,
+    'PyMissingConstructorInspection': IssueType.ERROR_PRONE,
+    'PyDecoratorInspection': IssueType.ERROR_PRONE,
+    'PyTypeCheckerInspection': IssueType.ERROR_PRONE,
+    'PyNoneFunctionAssignmentInspection': IssueType.ERROR_PRONE,
+    'PyAbstractClassInspection': IssueType.ERROR_PRONE,
+    'PyOverloadsInspection': IssueType.ERROR_PRONE,
+    'PyTypeHintsInspection': IssueType.ERROR_PRONE,
+    'PyTypedDictInspection': IssueType.ERROR_PRONE,
+    'PyShadowingBuiltinsInspection': IssueType.ERROR_PRONE,
+    'PyUnresolvedReferencesInspection': IssueType.ERROR_PRONE,
+}
+
+IJ_MESSAGE_TO_ISSUE_TYPE = {
+    'PyDataclassInspection': {
+        'is useless until ''__post_init__'' is declared': IssueType.BEST_PRACTICES,
+        'should take all init-only variables (incl. inherited) in the same order as they are defined':
+            IssueType.BEST_PRACTICES,
+        'would not be called until \'init\' parameter is set to True \'__attrs_post_init__\' '
+        'should not take any parameters except \'self\'': IssueType.BEST_PRACTICES,
+    },
+    'PyFinalInspection': {
+        'No need to mark method in \'Final\' class as \'@final\'': IssueType.BEST_PRACTICES,
+    },
+}
diff --git a/hyperstyle/src/python/review/inspectors/pmd/files/config.xml b/hyperstyle/src/python/review/inspectors/pmd/files/config.xml
index abf91677..4961705f 100644
--- a/hyperstyle/src/python/review/inspectors/pmd/files/config.xml
+++ b/hyperstyle/src/python/review/inspectors/pmd/files/config.xml
@@ -9,7 +9,8 @@
         The Basic ruleset contains a collection of good practices which should be followed.
     </description>
 
-    <!-- This config was created using the following table: https://bit.ly/2VC6E61 -->
+    <!-- This config was created using the following table: -->
+    <!-- https://docs.google.com/spreadsheets/d/1HS4LrgAV3a59D0zhjfb6OATkgxWkyz2G -->
 
     <!-- Best Practices -->
     <rule ref="category/java/bestpractices.xml/AbstractClassWithoutAbstractMethod"/>
diff --git a/hyperstyle/src/python/review/inspectors/pmd/issue_types.py b/hyperstyle/src/python/review/inspectors/pmd/issue_types.py
index e505acab..856150ae 100644
--- a/hyperstyle/src/python/review/inspectors/pmd/issue_types.py
+++ b/hyperstyle/src/python/review/inspectors/pmd/issue_types.py
@@ -1,6 +1,6 @@
 from typing import Dict
 
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 
 PMD_RULE_TO_ISSUE_TYPE: Dict[str, IssueType] = {
     # ---- Best Practices ----
diff --git a/hyperstyle/src/python/review/inspectors/pmd/pmd.py b/hyperstyle/src/python/review/inspectors/pmd/pmd.py
index 3c1b6d72..104d4aeb 100644
--- a/hyperstyle/src/python/review/inspectors/pmd/pmd.py
+++ b/hyperstyle/src/python/review/inspectors/pmd/pmd.py
@@ -7,12 +7,11 @@
 from hyperstyle.src.python.review.common.file_system import check_set_up_env_variable, new_temp_dir
 from hyperstyle.src.python.review.common.language_version import LanguageVersion
 from hyperstyle.src.python.review.common.subprocess_runner import run_in_subprocess
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
-from hyperstyle.src.python.review.inspectors.common.base_issue_converter import convert_base_issue
-from hyperstyle.src.python.review.inspectors.common.utils import remove_prefix
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.issue.base_issue_converter import convert_base_issue
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 from hyperstyle.src.python.review.inspectors.pmd.issue_configs import ISSUE_CONFIGS
 from hyperstyle.src.python.review.inspectors.pmd.issue_types import PMD_RULE_TO_ISSUE_TYPE
 
@@ -138,4 +137,4 @@ def _get_java_version(language_version: LanguageVersion) -> str:
             )
             java_version = DEFAULT_JAVA_VERSION.value
 
-        return remove_prefix(java_version, "java")
+        return java_version.removeprefix('java')
diff --git a/hyperstyle/src/python/review/inspectors/pyast/python_ast.py b/hyperstyle/src/python/review/inspectors/pyast/python_ast.py
index 5f7d5790..1a473d05 100644
--- a/hyperstyle/src/python/review/inspectors/pyast/python_ast.py
+++ b/hyperstyle/src/python/review/inspectors/pyast/python_ast.py
@@ -6,10 +6,10 @@
 from hyperstyle.src.python.review.common import language
 from hyperstyle.src.python.review.common.file_system import get_all_file_system_items
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
-from hyperstyle.src.python.review.inspectors.common.tips import get_bool_expr_len_tip, get_func_len_tip
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import (
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.issue.tips import get_bool_expr_len_tip, get_func_len_tip
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import (
     BaseIssue, BoolExprLenIssue, FuncLenIssue, IssueDifficulty, IssueType,
 )
 
diff --git a/hyperstyle/src/python/review/inspectors/pylint/issue_configs.py b/hyperstyle/src/python/review/inspectors/pylint/issue_configs.py
index 58604ae6..e95a5e8b 100644
--- a/hyperstyle/src/python/review/inspectors/pylint/issue_configs.py
+++ b/hyperstyle/src/python/review/inspectors/pylint/issue_configs.py
@@ -1,4 +1,4 @@
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfig
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfig
 
 ISSUE_CONFIGS = [
     IssueConfig(
diff --git a/hyperstyle/src/python/review/inspectors/pylint/issue_types.py b/hyperstyle/src/python/review/inspectors/pylint/issue_types.py
index 5375532d..540359e8 100644
--- a/hyperstyle/src/python/review/inspectors/pylint/issue_types.py
+++ b/hyperstyle/src/python/review/inspectors/pylint/issue_types.py
@@ -1,6 +1,6 @@
 from typing import Dict
 
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 
 CODE_TO_ISSUE_TYPE: Dict[str, IssueType] = {
     # Basic checker
diff --git a/hyperstyle/src/python/review/inspectors/pylint/pylint.py b/hyperstyle/src/python/review/inspectors/pylint/pylint.py
index 3c09cdec..476cef1e 100644
--- a/hyperstyle/src/python/review/inspectors/pylint/pylint.py
+++ b/hyperstyle/src/python/review/inspectors/pylint/pylint.py
@@ -1,14 +1,15 @@
 import logging
 import re
+import sys
 from pathlib import Path
 from typing import Any, Dict, List, Optional
 
 from hyperstyle.src.python.review.common.subprocess_runner import run_in_subprocess
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
-from hyperstyle.src.python.review.inspectors.common.base_issue_converter import convert_base_issue
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.issue.base_issue_converter import convert_base_issue
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 from hyperstyle.src.python.review.inspectors.pylint.issue_configs import ISSUE_CONFIGS
 from hyperstyle.src.python.review.inspectors.pylint.issue_types import CATEGORY_TO_ISSUE_TYPE, CODE_TO_ISSUE_TYPE
 
@@ -21,6 +22,7 @@
 INFO_CATEGORY = 'I'
 
 BASE_COMMAND = [
+    sys.executable, '-m',
     'pylint',
     '--load-plugins', 'pylint_django',
     # TODO: ask about django settings via an cli argument?
diff --git a/hyperstyle/src/python/review/inspectors/radon/radon.py b/hyperstyle/src/python/review/inspectors/radon/radon.py
index c7b010c5..aef101ab 100644
--- a/hyperstyle/src/python/review/inspectors/radon/radon.py
+++ b/hyperstyle/src/python/review/inspectors/radon/radon.py
@@ -1,13 +1,14 @@
 import re
+import sys
 from pathlib import Path
 from typing import Any, Dict, List
 
 from hyperstyle.src.python.review.common.subprocess_runner import run_in_subprocess
-from hyperstyle.src.python.review.inspectors.base_inspector import BaseInspector
-from hyperstyle.src.python.review.inspectors.common.tips import get_maintainability_index_tip
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseInspector
+from hyperstyle.src.python.review.inspectors.common.issue.tips import get_maintainability_index_tip
 from hyperstyle.src.python.review.inspectors.common.utils import convert_percentage_of_value_to_lack_of_value
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import (
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import (
     BaseIssue,
     IssueData,
     IssueDifficulty,
@@ -30,6 +31,7 @@ def inspect_in_memory(cls, code: str, config: Dict[str, Any]) -> List[BaseIssue]
     @classmethod
     def inspect(cls, path: Path, config: Dict[str, Any]) -> List[BaseIssue]:
         mi_command = [
+            sys.executable, '-m',
             'radon', 'mi',  # compute the Maintainability Index score
             '--max', 'F',  # set the maximum MI rank to display
             '--show',  # actual MI value is shown in results, alongside the rank
diff --git a/hyperstyle/src/python/review/quality/evaluate_quality.py b/hyperstyle/src/python/review/quality/evaluate_quality.py
index 100a92dc..1b5e609b 100644
--- a/hyperstyle/src/python/review/quality/evaluate_quality.py
+++ b/hyperstyle/src/python/review/quality/evaluate_quality.py
@@ -1,7 +1,7 @@
 from typing import List
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import Quality, Rule
 from hyperstyle.src.python.review.quality.rules.best_practices_scoring import (
     BestPracticesRule,
diff --git a/hyperstyle/src/python/review/quality/model.py b/hyperstyle/src/python/review/quality/model.py
index ca0f40b1..639cbbb4 100644
--- a/hyperstyle/src/python/review/quality/model.py
+++ b/hyperstyle/src/python/review/quality/model.py
@@ -4,7 +4,7 @@
 from functools import total_ordering
 from typing import List
 
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 
 
 @total_ordering
diff --git a/hyperstyle/src/python/review/quality/penalty.py b/hyperstyle/src/python/review/quality/penalty.py
index 2add2917..eea08063 100644
--- a/hyperstyle/src/python/review/quality/penalty.py
+++ b/hyperstyle/src/python/review/quality/penalty.py
@@ -3,7 +3,7 @@
 from typing import Dict, List, Optional, Set
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueType
 from hyperstyle.src.python.review.quality.model import QualityType
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/best_practices_scoring.py b/hyperstyle/src/python/review/quality/rules/best_practices_scoring.py
index 1d3ce256..81bed025 100644
--- a/hyperstyle/src/python/review/quality/rules/best_practices_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/best_practices_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/boolean_length_scoring.py b/hyperstyle/src/python/review/quality/rules/boolean_length_scoring.py
index cf685962..c8d49a55 100644
--- a/hyperstyle/src/python/review/quality/rules/boolean_length_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/boolean_length_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/class_response_scoring.py b/hyperstyle/src/python/review/quality/rules/class_response_scoring.py
index a33d60c2..0956886d 100644
--- a/hyperstyle/src/python/review/quality/rules/class_response_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/class_response_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/code_style_scoring.py b/hyperstyle/src/python/review/quality/rules/code_style_scoring.py
index be450212..20d92b88 100644
--- a/hyperstyle/src/python/review/quality/rules/code_style_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/code_style_scoring.py
@@ -1,7 +1,7 @@
 from dataclasses import dataclass
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/cohesion_scoring.py b/hyperstyle/src/python/review/quality/rules/cohesion_scoring.py
index 541ac226..2e64fa31 100644
--- a/hyperstyle/src/python/review/quality/rules/cohesion_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/cohesion_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/complexity_scoring.py b/hyperstyle/src/python/review/quality/rules/complexity_scoring.py
index a8a6aa6d..95080a7e 100644
--- a/hyperstyle/src/python/review/quality/rules/complexity_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/complexity_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/coupling_scoring.py b/hyperstyle/src/python/review/quality/rules/coupling_scoring.py
index 552dc1c6..77771a0f 100644
--- a/hyperstyle/src/python/review/quality/rules/coupling_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/coupling_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/cyclomatic_complexity_scoring.py b/hyperstyle/src/python/review/quality/rules/cyclomatic_complexity_scoring.py
index b3dc8d75..be939d85 100644
--- a/hyperstyle/src/python/review/quality/rules/cyclomatic_complexity_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/cyclomatic_complexity_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/error_prone_scoring.py b/hyperstyle/src/python/review/quality/rules/error_prone_scoring.py
index d7549633..b7034cd4 100644
--- a/hyperstyle/src/python/review/quality/rules/error_prone_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/error_prone_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/function_length_scoring.py b/hyperstyle/src/python/review/quality/rules/function_length_scoring.py
index e303f7ba..cff285e7 100644
--- a/hyperstyle/src/python/review/quality/rules/function_length_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/function_length_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/inheritance_depth_scoring.py b/hyperstyle/src/python/review/quality/rules/inheritance_depth_scoring.py
index 4c52daf7..3da80633 100644
--- a/hyperstyle/src/python/review/quality/rules/inheritance_depth_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/inheritance_depth_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/line_len_scoring.py b/hyperstyle/src/python/review/quality/rules/line_len_scoring.py
index fee4e186..8aa30362 100644
--- a/hyperstyle/src/python/review/quality/rules/line_len_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/line_len_scoring.py
@@ -1,7 +1,7 @@
 from dataclasses import dataclass
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/maintainability_scoring.py b/hyperstyle/src/python/review/quality/rules/maintainability_scoring.py
index 7ec8e1ef..16fd1cb1 100644
--- a/hyperstyle/src/python/review/quality/rules/maintainability_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/maintainability_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/method_number_scoring.py b/hyperstyle/src/python/review/quality/rules/method_number_scoring.py
index e0da86ec..669f1bb9 100644
--- a/hyperstyle/src/python/review/quality/rules/method_number_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/method_number_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/quality/rules/weighted_methods_scoring.py b/hyperstyle/src/python/review/quality/rules/weighted_methods_scoring.py
index 34ae03d9..1d01d623 100644
--- a/hyperstyle/src/python/review/quality/rules/weighted_methods_scoring.py
+++ b/hyperstyle/src/python/review/quality/rules/weighted_methods_scoring.py
@@ -2,7 +2,7 @@
 from typing import Optional
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.quality.model import QualityType, Rule
 
 
diff --git a/hyperstyle/src/python/review/reviewers/common.py b/hyperstyle/src/python/review/reviewers/common.py
index fbe616fa..a2a28a52 100644
--- a/hyperstyle/src/python/review/reviewers/common.py
+++ b/hyperstyle/src/python/review/reviewers/common.py
@@ -1,3 +1,5 @@
+import json
+import logging
 from collections import defaultdict
 from typing import List, Optional
 
@@ -9,11 +11,14 @@
     run_inspector_in_memory,
 )
 from hyperstyle.src.python.review.inspectors.checkstyle.checkstyle import CheckstyleInspector
+from hyperstyle.src.python.review.inspectors.common.inspector.base_inspector import BaseIJInspector
 from hyperstyle.src.python.review.inspectors.detekt.detekt import DetektInspector
 from hyperstyle.src.python.review.inspectors.eslint.eslint import ESLintInspector
 from hyperstyle.src.python.review.inspectors.flake8.flake8 import Flake8Inspector
 from hyperstyle.src.python.review.inspectors.golang_lint.golang_lint import GolangLintInspector
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue
+from hyperstyle.src.python.review.inspectors.ij_kotlin.ij_kotlin import KotlinIJInspector
+from hyperstyle.src.python.review.inspectors.ij_python.ij_python import PythonIJInspector
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue
 from hyperstyle.src.python.review.inspectors.pmd.pmd import PMDInspector
 from hyperstyle.src.python.review.inspectors.pyast.python_ast import PythonAstInspector
 from hyperstyle.src.python.review.inspectors.pylint.pylint import PylintInspector
@@ -41,6 +46,7 @@
         Flake8Inspector(),
         PythonAstInspector(),
         RadonInspector(),
+        PythonIJInspector(),
     ],
     Language.JAVA: [
         CheckstyleInspector(),
@@ -48,6 +54,7 @@
     ],
     Language.KOTLIN: [
         DetektInspector(),
+        KotlinIJInspector(),
     ],
     Language.JS: [
         ESLintInspector(),
@@ -60,8 +67,31 @@
 
 def _inspect_code(metadata: Metadata, config: ApplicationConfig, language: Language) -> Optional[List[BaseIssue]]:
     inspectors = LANGUAGE_TO_INSPECTORS[language]
+    ij_inspectors = list(
+        filter(
+            lambda inspector: isinstance(inspector, BaseIJInspector)
+            and inspector.inspector_type not in config.disabled_inspectors,
+            inspectors,
+        ),
+    )
+
+    connection_parameters = (
+        None if config.ij_config is None else json.loads(config.ij_config).get(language.value.lower())
+    )
+    if ij_inspectors and connection_parameters is None:
+        logging.warning(
+            f'IJ inspectors for the {language.value} will be disabled '
+            f'as the IJ config for this language was not specified.',
+        )
+
+        config.disabled_inspectors.update(map(lambda inspector: inspector.inspector_type, ij_inspectors))
+    else:
+        for inspector in ij_inspectors:
+            inspector.setup_connection_parameters(connection_parameters['host'], connection_parameters['port'])
+
     if isinstance(metadata, InMemoryMetadata):
         return inspect_in_parallel(run_inspector_in_memory, metadata.code, config, inspectors)
+
     return inspect_in_parallel(run_inspector, metadata.path, config, inspectors)
 
 
@@ -100,9 +130,7 @@ def perform_language_review(metadata: Metadata, config: ApplicationConfig, langu
         difficulty: Punisher(issues, previous_issues) for difficulty, issues in issues_by_difficulty.items()
     }
 
-    general_quality_by_difficulty = {
-        difficulty: Quality([]) for difficulty in issues_by_difficulty.keys()
-    }
+    general_quality_by_difficulty = {difficulty: Quality([]) for difficulty in issues_by_difficulty.keys()}
 
     file_review_results = []
     for file in current_files:
@@ -115,8 +143,10 @@ def perform_language_review(metadata: Metadata, config: ApplicationConfig, langu
         }
 
         for code_statistics in code_statistics_by_difficulty.values():
-            code_statistics.total_lines = min(code_statistics.total_lines,
-                                              get_range_lines(config.start_line, config.end_line))
+            code_statistics.total_lines = min(
+                code_statistics.total_lines,
+                get_range_lines(config.start_line, config.end_line),
+            )
 
         punisher_by_difficulty = {
             difficulty: Punisher(file_issues, previous_issues)
@@ -143,9 +173,11 @@ def perform_language_review(metadata: Metadata, config: ApplicationConfig, langu
     )
 
 
-def filter_out_of_range_issues(issues: List[BaseIssue],
-                               start_line: int = 1,
-                               end_line: Optional[int] = None) -> List[BaseIssue]:
+def filter_out_of_range_issues(
+    issues: List[BaseIssue],
+    start_line: int = 1,
+    end_line: Optional[int] = None,
+) -> List[BaseIssue]:
     if end_line is None:
         end_line = 100_000_000
 
diff --git a/hyperstyle/src/python/review/reviewers/perform_review.py b/hyperstyle/src/python/review/reviewers/perform_review.py
index 025ca409..cb2ccf91 100644
--- a/hyperstyle/src/python/review/reviewers/perform_review.py
+++ b/hyperstyle/src/python/review/reviewers/perform_review.py
@@ -6,7 +6,7 @@
 
 from hyperstyle.src.python.review.application_config import ApplicationConfig
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.reviewers.common import perform_language_review
 from hyperstyle.src.python.review.reviewers.go import perform_go_review
 from hyperstyle.src.python.review.reviewers.python import perform_python_review
diff --git a/hyperstyle/src/python/review/reviewers/review_result.py b/hyperstyle/src/python/review/reviewers/review_result.py
index 8c934008..eac03aaa 100644
--- a/hyperstyle/src/python/review/reviewers/review_result.py
+++ b/hyperstyle/src/python/review/reviewers/review_result.py
@@ -2,7 +2,7 @@
 from pathlib import Path
 from typing import Dict, List
 
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty
 from hyperstyle.src.python.review.quality.model import Quality
 from hyperstyle.src.python.review.quality.penalty import Punisher
 
diff --git a/hyperstyle/src/python/review/reviewers/utils/code_statistics.py b/hyperstyle/src/python/review/reviewers/utils/code_statistics.py
index 7e9c5a8c..074027b5 100644
--- a/hyperstyle/src/python/review/reviewers/utils/code_statistics.py
+++ b/hyperstyle/src/python/review/reviewers/utils/code_statistics.py
@@ -4,7 +4,7 @@
 from typing import Dict, List
 
 from hyperstyle.src.python.review.common.file_system import get_total_code_lines_from_file
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueType
 
 
 @dataclass
diff --git a/hyperstyle/src/python/review/reviewers/utils/issues_filter.py b/hyperstyle/src/python/review/reviewers/utils/issues_filter.py
index 5acd4517..1e235ff8 100644
--- a/hyperstyle/src/python/review/reviewers/utils/issues_filter.py
+++ b/hyperstyle/src/python/review/reviewers/utils/issues_filter.py
@@ -2,7 +2,7 @@
 from typing import Dict, List, Tuple
 
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType, Measurable
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType, Measurable
 from hyperstyle.src.python.review.quality.rules.boolean_length_scoring import LANGUAGE_TO_BOOLEAN_EXPRESSION_RULE_CONFIG
 from hyperstyle.src.python.review.quality.rules.class_response_scoring import LANGUAGE_TO_RESPONSE_RULE_CONFIG
 from hyperstyle.src.python.review.quality.rules.cohesion_scoring import LANGUAGE_TO_COHESION_RULE_CONFIG
diff --git a/hyperstyle/src/python/review/reviewers/utils/print_review.py b/hyperstyle/src/python/review/reviewers/utils/print_review.py
index b0776163..bfffbf8f 100644
--- a/hyperstyle/src/python/review/reviewers/utils/print_review.py
+++ b/hyperstyle/src/python/review/reviewers/utils/print_review.py
@@ -6,8 +6,8 @@
 
 from hyperstyle.src.python.review.application_config import ApplicationConfig
 from hyperstyle.src.python.review.common.file_system import get_file_line
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
 from hyperstyle.src.python.review.quality.model import QualityType
 from hyperstyle.src.python.review.quality.penalty import PenaltyIssue
 from hyperstyle.src.python.review.reviewers.review_result import FileReviewResult, GeneralReviewResult, ReviewResult
diff --git a/hyperstyle/src/python/review/run_tool.py b/hyperstyle/src/python/review/run_tool.py
index 613ed1b0..ce7408e0 100644
--- a/hyperstyle/src/python/review/run_tool.py
+++ b/hyperstyle/src/python/review/run_tool.py
@@ -14,7 +14,7 @@
 from hyperstyle.src.python.review.application_config import ApplicationConfig
 from hyperstyle.src.python.review.common.language import Language
 from hyperstyle.src.python.review.common.language_version import LanguageVersion
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
 from hyperstyle.src.python.review.logging_config import logging_config
 from hyperstyle.src.python.review.reviewers.perform_review import (
     OutputFormat,
@@ -28,10 +28,6 @@
 
 def parse_disabled_inspectors(value: str) -> Set[InspectorType]:
     passed_names = value.upper().split(',')
-    # TODO: delete it after updating the run configuration in production
-    intellij_key_word = 'intellij'.upper()
-    if intellij_key_word in passed_names:
-        passed_names.remove(intellij_key_word)
     allowed_names = {inspector.value for inspector in InspectorType}
     if not all(name in allowed_names for name in passed_names):
         raise argparse.ArgumentError('disable', 'Incorrect inspectors\' names')
@@ -55,7 +51,7 @@ def configure_arguments(parser: argparse.ArgumentParser) -> None:
                         choices=VerbosityLevel.values(),
                         type=int)
 
-    # Usage example: -d Flake8,Intelli
+    # Usage example: -d Flake8,IntelliJ
     parser.add_argument(RunToolArgument.DISABLE.value.short_name,
                         RunToolArgument.DISABLE.value.long_name,
                         help=RunToolArgument.DISABLE.value.description,
@@ -126,6 +122,10 @@ def configure_arguments(parser: argparse.ArgumentParser) -> None:
                         help=RunToolArgument.GROUP_BY_DIFFICULTY.value.description,
                         action='store_true')
 
+    parser.add_argument(RunToolArgument.IJ_CONFIG.value.long_name,
+                        help=RunToolArgument.IJ_CONFIG.value.description,
+                        type=str)
+
 
 def configure_logging(verbosity: VerbosityLevel) -> None:
     if verbosity is VerbosityLevel.ERROR:
@@ -173,6 +173,7 @@ def main() -> int:
             history=args.history,
             with_all_categories=args.with_all_categories,
             group_by_difficulty=args.group_by_difficulty,
+            ij_config=args.ij_config,
         )
 
         n_issues = perform_and_print_review(args.path, OutputFormat(args.format), config)
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 00000000..4f59c863
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,1393 @@
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
+
+[[package]]
+name = "argparse"
+version = "1.4.0"
+description = "Python command-line parsing library"
+optional = false
+python-versions = "*"
+files = [
+    {file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"},
+    {file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"},
+]
+
+[[package]]
+name = "asgiref"
+version = "3.6.0"
+description = "ASGI specs, helper code, and adapters"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"},
+    {file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"},
+]
+
+[package.extras]
+tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
+
+[[package]]
+name = "astor"
+version = "0.8.1"
+description = "Read/rewrite/write Python ASTs"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+files = [
+    {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"},
+    {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"},
+]
+
+[[package]]
+name = "astroid"
+version = "2.11.7"
+description = "An abstract syntax tree for Python with inference support."
+optional = false
+python-versions = ">=3.6.2"
+files = [
+    {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"},
+    {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"},
+]
+
+[package.dependencies]
+lazy-object-proxy = ">=1.4.0"
+setuptools = ">=20.0"
+wrapt = ">=1.11,<2"
+
+[[package]]
+name = "attrs"
+version = "24.2.0"
+description = "Classes Without Boilerplate"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"},
+    {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"},
+]
+
+[package.extras]
+benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
+tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
+
+[[package]]
+name = "certifi"
+version = "2024.8.30"
+description = "Python package for providing Mozilla's CA Bundle."
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
+    {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.3.2"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+optional = false
+python-versions = ">=3.7.0"
+files = [
+    {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
+    {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
+    {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
+    {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
+    {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
+    {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
+    {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
+    {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
+]
+
+[[package]]
+name = "cohesion"
+version = "1.0.0"
+description = "A tool for measuring Python class cohesion."
+optional = false
+python-versions = "*"
+files = [
+    {file = "cohesion-1.0.0-py2.py3-none-any.whl", hash = "sha256:a33a526eb8922c7dbfef19257b3532d16e6246c4ece3ab3b492b290d9d3f2d4b"},
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+    {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+    {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "dataclasses-json"
+version = "0.5.7"
+description = "Easily serialize dataclasses to and from JSON"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "dataclasses-json-0.5.7.tar.gz", hash = "sha256:c2c11bc8214fbf709ffc369d11446ff6945254a7f09128154a7620613d8fda90"},
+    {file = "dataclasses_json-0.5.7-py3-none-any.whl", hash = "sha256:bc285b5f892094c3a53d558858a88553dd6a61a11ab1a8128a0e554385dcc5dd"},
+]
+
+[package.dependencies]
+marshmallow = ">=3.3.0,<4.0.0"
+marshmallow-enum = ">=1.5.1,<2.0.0"
+typing-inspect = ">=0.4.0"
+
+[package.extras]
+dev = ["flake8", "hypothesis", "ipython", "mypy (>=0.710)", "portray", "pytest (>=6.2.3)", "simplejson", "types-dataclasses"]
+
+[[package]]
+name = "dill"
+version = "0.3.8"
+description = "serialize all of Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"},
+    {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"},
+]
+
+[package.extras]
+graph = ["objgraph (>=1.7.2)"]
+profile = ["gprof2dot (>=2022.7.29)"]
+
+[[package]]
+name = "django"
+version = "4.2.5"
+description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "Django-4.2.5-py3-none-any.whl", hash = "sha256:b6b2b5cae821077f137dc4dade696a1c2aa292f892eca28fa8d7bfdf2608ddd4"},
+    {file = "Django-4.2.5.tar.gz", hash = "sha256:5e5c1c9548ffb7796b4a8a4782e9a2e5a3df3615259fc1bfd3ebc73b646146c1"},
+]
+
+[package.dependencies]
+asgiref = ">=3.6.0,<4"
+sqlparse = ">=0.3.1"
+tzdata = {version = "*", markers = "sys_platform == \"win32\""}
+
+[package.extras]
+argon2 = ["argon2-cffi (>=19.1.0)"]
+bcrypt = ["bcrypt"]
+
+[[package]]
+name = "eradicate"
+version = "2.3.0"
+description = "Removes commented-out code."
+optional = false
+python-versions = "*"
+files = [
+    {file = "eradicate-2.3.0-py3-none-any.whl", hash = "sha256:2b29b3dd27171f209e4ddd8204b70c02f0682ae95eecb353f10e8d72b149c63e"},
+    {file = "eradicate-2.3.0.tar.gz", hash = "sha256:06df115be3b87d0fc1c483db22a2ebb12bcf40585722810d809cc770f5031c37"},
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.2.2"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
+    {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "flake8"
+version = "3.9.0"
+description = "the modular source code checker: pep8 pyflakes and co"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+files = [
+    {file = "flake8-3.9.0-py2.py3-none-any.whl", hash = "sha256:12d05ab02614b6aee8df7c36b97d1a3b2372761222b19b58621355e82acddcff"},
+    {file = "flake8-3.9.0.tar.gz", hash = "sha256:78873e372b12b093da7b5e5ed302e8ad9e988b38b063b61ad937f26ca58fc5f0"},
+]
+
+[package.dependencies]
+mccabe = ">=0.6.0,<0.7.0"
+pycodestyle = ">=2.7.0,<2.8.0"
+pyflakes = ">=2.3.0,<2.4.0"
+
+[[package]]
+name = "flake8-broken-line"
+version = "0.3.0"
+description = "Flake8 plugin to forbid backslashes for line breaks"
+optional = false
+python-versions = ">=3.6,<4.0"
+files = [
+    {file = "flake8-broken-line-0.3.0.tar.gz", hash = "sha256:f74e052833324a9e5f0055032f7ccc54b23faabafe5a26241c2f977e70b10b50"},
+    {file = "flake8_broken_line-0.3.0-py3-none-any.whl", hash = "sha256:611f79c7f27118e7e5d3dc098ef7681c40aeadf23783700c5dbee840d2baf3af"},
+]
+
+[package.dependencies]
+flake8 = ">=3.5,<4.0"
+
+[[package]]
+name = "flake8-bugbear"
+version = "21.4.3"
+description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "flake8-bugbear-21.4.3.tar.gz", hash = "sha256:2346c81f889955b39e4a368eb7d508de723d9de05716c287dc860a4073dc57e7"},
+    {file = "flake8_bugbear-21.4.3-py36.py37.py38-none-any.whl", hash = "sha256:4f305dca96be62bf732a218fe6f1825472a621d3452c5b994d8f89dae21dbafa"},
+]
+
+[package.dependencies]
+attrs = ">=19.2.0"
+flake8 = ">=3.0.0"
+
+[package.extras]
+dev = ["black", "coverage", "hypothesis", "hypothesmith"]
+
+[[package]]
+name = "flake8-builtins"
+version = "1.5.3"
+description = "Check for python builtins being used as variables or parameters."
+optional = false
+python-versions = "*"
+files = [
+    {file = "flake8-builtins-1.5.3.tar.gz", hash = "sha256:09998853b2405e98e61d2ff3027c47033adbdc17f9fe44ca58443d876eb00f3b"},
+    {file = "flake8_builtins-1.5.3-py2.py3-none-any.whl", hash = "sha256:7706babee43879320376861897e5d1468e396a40b8918ed7bccf70e5f90b8687"},
+]
+
+[package.dependencies]
+flake8 = "*"
+
+[package.extras]
+test = ["coverage", "coveralls", "mock", "pytest", "pytest-cov"]
+
+[[package]]
+name = "flake8-commas"
+version = "2.0.0"
+description = "Flake8 lint for trailing commas."
+optional = false
+python-versions = "*"
+files = [
+    {file = "flake8-commas-2.0.0.tar.gz", hash = "sha256:d3005899466f51380387df7151fb59afec666a0f4f4a2c6a8995b975de0f44b7"},
+    {file = "flake8_commas-2.0.0-py2.py3-none-any.whl", hash = "sha256:ee2141a3495ef9789a3894ed8802d03eff1eaaf98ce6d8653a7c573ef101935e"},
+]
+
+[package.dependencies]
+flake8 = ">=2,<4.0.0"
+
+[[package]]
+name = "flake8-comprehensions"
+version = "3.4.0"
+description = "A flake8 plugin to help you write better list/set/dict comprehensions."
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "flake8-comprehensions-3.4.0.tar.gz", hash = "sha256:c00039be9f3959a26a98da3024f0fe809859bf1753ccb90e228cc40f3ac31ca7"},
+    {file = "flake8_comprehensions-3.4.0-py3-none-any.whl", hash = "sha256:7258a28e229fb9a8d16370b9c47a7d66396ba0201abb06c9d11df41b18ed64c4"},
+]
+
+[package.dependencies]
+flake8 = ">=3.0,<3.2.0 || >3.2.0,<4"
+
+[[package]]
+name = "flake8-eradicate"
+version = "1.0.0"
+description = "Flake8 plugin to find commented out code"
+optional = false
+python-versions = ">=3.6,<4.0"
+files = [
+    {file = "flake8-eradicate-1.0.0.tar.gz", hash = "sha256:fe7167226676823d50cf540532302a6f576c5a398c5260692571a05ef72c5f5b"},
+    {file = "flake8_eradicate-1.0.0-py3-none-any.whl", hash = "sha256:0fc4ab858a18c7ed630621b5345254c8f55be6060ea5c44a25e384d613618d1f"},
+]
+
+[package.dependencies]
+attrs = "*"
+eradicate = ">=2.0,<3.0"
+flake8 = ">=3.5,<4.0"
+
+[[package]]
+name = "flake8-import-order"
+version = "0.18.1"
+description = "Flake8 and pylama plugin that checks the ordering of import statements."
+optional = false
+python-versions = "*"
+files = [
+    {file = "flake8-import-order-0.18.1.tar.gz", hash = "sha256:a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92"},
+    {file = "flake8_import_order-0.18.1-py2.py3-none-any.whl", hash = "sha256:90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543"},
+]
+
+[package.dependencies]
+pycodestyle = "*"
+setuptools = "*"
+
+[[package]]
+name = "flake8-plugin-utils"
+version = "1.3.2"
+description = "The package provides base classes and utils for flake8 plugin writing"
+optional = false
+python-versions = ">=3.6,<4.0"
+files = [
+    {file = "flake8-plugin-utils-1.3.2.tar.gz", hash = "sha256:20fa2a8ca2decac50116edb42e6af0a1253ef639ad79941249b840531889c65a"},
+    {file = "flake8_plugin_utils-1.3.2-py3-none-any.whl", hash = "sha256:1fe43e3e9acf3a7c0f6b88f5338cad37044d2f156c43cb6b080b5f9da8a76f06"},
+]
+
+[[package]]
+name = "flake8-polyfill"
+version = "1.0.2"
+description = "Polyfill package for Flake8 plugins"
+optional = false
+python-versions = "*"
+files = [
+    {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"},
+    {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"},
+]
+
+[package.dependencies]
+flake8 = "*"
+
+[[package]]
+name = "flake8-return"
+version = "1.1.2"
+description = "Flake8 plugin that checks return values"
+optional = false
+python-versions = ">=3.6,<4.0"
+files = [
+    {file = "flake8-return-1.1.2.tar.gz", hash = "sha256:d646d3b010a9736ddc23c24f98ad3282999f575da45d6eb9cefe4adddb44062d"},
+    {file = "flake8_return-1.1.2-py3-none-any.whl", hash = "sha256:183d0ad2f8553cb2c63c0cf288eb799d967577a74639599525adcd3860f6bb12"},
+]
+
+[package.dependencies]
+flake8-plugin-utils = ">=1.0,<2.0"
+
+[[package]]
+name = "flake8-spellcheck"
+version = "0.24.0"
+description = "Spellcheck variables, comments and docstrings"
+optional = false
+python-versions = "*"
+files = [
+    {file = "flake8-spellcheck-0.24.0.tar.gz", hash = "sha256:833c92222158b5dea74f858ccfb9f980b7c865fbd89cabc18447dadb07ec62e6"},
+    {file = "flake8_spellcheck-0.24.0-py2.py3-none-any.whl", hash = "sha256:33d1727e4edb2b0cda3a78dee467d7bde53ff5a2a90056bc7d1764e0b060f331"},
+]
+
+[package.dependencies]
+flake8 = ">3.0.0"
+
+[[package]]
+name = "flake8-string-format"
+version = "0.3.0"
+description = "string format checker, plugin for flake8"
+optional = false
+python-versions = "*"
+files = [
+    {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"},
+    {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"},
+]
+
+[package.dependencies]
+flake8 = "*"
+
+[[package]]
+name = "future"
+version = "1.0.0"
+description = "Clean single-source support for Python 3 and 2"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+files = [
+    {file = "future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216"},
+    {file = "future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05"},
+]
+
+[[package]]
+name = "grpcio"
+version = "1.66.1"
+description = "HTTP/2-based RPC framework"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"},
+    {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"},
+    {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"},
+    {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"},
+    {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"},
+    {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"},
+    {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"},
+    {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"},
+    {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"},
+    {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"},
+    {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"},
+    {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"},
+    {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"},
+    {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"},
+    {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"},
+    {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"},
+    {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"},
+    {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"},
+    {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"},
+    {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"},
+    {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"},
+    {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"},
+    {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"},
+    {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"},
+    {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"},
+    {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"},
+    {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"},
+    {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"},
+    {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"},
+    {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"},
+    {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"},
+    {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"},
+    {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"},
+    {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"},
+    {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"},
+    {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"},
+    {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"},
+    {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"},
+    {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"},
+    {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"},
+    {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"},
+    {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"},
+    {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"},
+    {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"},
+    {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"},
+    {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"},
+]
+
+[package.extras]
+protobuf = ["grpcio-tools (>=1.66.1)"]
+
+[[package]]
+name = "grpcio-tools"
+version = "1.66.1"
+description = "Protobuf code generator for gRPC"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "grpcio_tools-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:e0c71405399ef59782600b1f0bdebc69ba12d7c9527cd268162a86273971d294"},
+    {file = "grpcio_tools-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:df1a174a6f9d3b4c380f005f33352d2e95464f33f021fb08084735a2eb6e23b1"},
+    {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7d789bfe53fce9e87aa80c3694a366258ce4c41b706258e9228ed4994832b780"},
+    {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95c44a265ff01fd05166edae9350bc2e7d1d9a95e8f53b8cd04d2ae0a588c583"},
+    {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b962a8767c3c0f9afe92e0dd6bb0b2305d35195a1053f84d4d31f585b87557ed"},
+    {file = "grpcio_tools-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d8616773126ec3cdf747b06a12e957b43ac15c34e4728def91fa67249a7c689a"},
+    {file = "grpcio_tools-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0067e79b6001560ac6acc78cca11fd3504fa27f8af46e3cdbac2f4998505e597"},
+    {file = "grpcio_tools-1.66.1-cp310-cp310-win32.whl", hash = "sha256:fa4f95a79a34afc3b5464895d091cd1911227fc3ab0441b9a37cd1817cf7db86"},
+    {file = "grpcio_tools-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:3acce426f5e643de63019311171f4d31131da8149de518716a95c29a2c12dd38"},
+    {file = "grpcio_tools-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9a07e24feb7472419cf70ebbb38dd4299aea696f91f191b62a99b3ee9ff03f89"},
+    {file = "grpcio_tools-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:097a069e7c640043921ecaf3e88d7af78ccd40c25dbddc91db2a4a2adbd0393d"},
+    {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:016fa273dc696c9d8045091ac50e000bce766183a6b150801f51c2946e33dbe3"},
+    {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ec9f4f964f8e8ed5e9cc13deb678c83d5597074c256805373220627833bc5ad"},
+    {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3198815814cdd12bdb69b7580d7770a4ad4c8b2093e0bd6b987bc817618e3eec"},
+    {file = "grpcio_tools-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:796620fc41d3fbb566d9614ef22bc55df67fac1f1e19c1e0fb6ec48bc9b6a44b"},
+    {file = "grpcio_tools-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:222d8dc218560698e1abf652fb47e4015994ec7a265ef46e012fd9c9e77a4d6b"},
+    {file = "grpcio_tools-1.66.1-cp311-cp311-win32.whl", hash = "sha256:56e17a11f34df252b4c6fb8aa8cd7b44d162dba9f3333be87ddf7c8bf496622a"},
+    {file = "grpcio_tools-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:edd52d667f2aa3c73233be0a821596937f24536647c12d96bfc54aa4cb04747d"},
+    {file = "grpcio_tools-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:869b6960d5daffda0dac1a474b44144f0dace0d4336394e499c4f400c5e2f8d9"},
+    {file = "grpcio_tools-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:68d9390bf9ba863ac147fc722d6548caa587235e887cac1bc2438212e89d1de7"},
+    {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:b8660401beca7e3af28722439e07b0bcdca80b4a68f5a5a1138ae7b7780a6abf"},
+    {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb67b9aa9cd69468bceb933e8e0f89fd13695746c018c4d2e6b3b84e73f3ad97"},
+    {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5daceb9716e31edc0e1ba0f93303785211438c43502edddad7a919fc4cb3d664"},
+    {file = "grpcio_tools-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0a86398a4cd0665bc7f09fa90b89bac592c959d2c895bf3cf5d47a98c0f2d24c"},
+    {file = "grpcio_tools-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1b4acb53338072ab3023e418a5c7059cb15686abd1607516fa1453406dd5f69d"},
+    {file = "grpcio_tools-1.66.1-cp312-cp312-win32.whl", hash = "sha256:88e04b7546101bc79c868c941777efd5088063a9e4f03b4d7263dde796fbabf7"},
+    {file = "grpcio_tools-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:5b4fc56abeafae74140f5da29af1093e88ce64811d77f1a81c3146e9e996fb6a"},
+    {file = "grpcio_tools-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:d4dd2ff982c1aa328ef47ce34f07af82f1f13599912fb1618ebc5fe1e14dddb8"},
+    {file = "grpcio_tools-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:066648543f786cb74b1fef5652359952455dbba37e832642026fd9fd8a219b5f"},
+    {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d19d47744c30e6bafa76b3113740e71f382d75ebb2918c1efd62ebe6ba7e20f9"},
+    {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739c53571130b359b738ac7d6d0a1f772e15779b66df7e6764bee4071cd38689"},
+    {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2226ff8d3ecba83b7622946df19d6e8e15cb52f761b8d9e2f807b228db5f1b1e"},
+    {file = "grpcio_tools-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2f4b1498cb8b422fbae32a491c9154e8d47650caf5852fbe6b3b34253e824343"},
+    {file = "grpcio_tools-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:93d2d9e14e81affdc63d67c42eb16a8da1b6fecc16442a703ca60eb0e7591691"},
+    {file = "grpcio_tools-1.66.1-cp38-cp38-win32.whl", hash = "sha256:d761dfd97a10e4aae73628b5120c64e56f0cded88651d0003d2d80e678c3e7c9"},
+    {file = "grpcio_tools-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:e1c2ac0955f5fb87b8444316e475242d194c3f3cd0b7b6e54b889a7b6f05156f"},
+    {file = "grpcio_tools-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:5f1f04578b72c281e39274348a61d240c48d5321ba8d7a8838e194099ecbc322"},
+    {file = "grpcio_tools-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:da9b0c08dbbf07535ee1b75a22d0acc5675a808a3a3df9f9b21e0e73ddfbb3a9"},
+    {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e302b4e1fa856d74ff65c65888b3a37153287ce6ad5bad80b2fdf95130accec2"},
+    {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc3f62494f238774755ff90f0e66a93ac7972ea1eb7180c45acf4fd53b25cca"},
+    {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23cad65ff22459aa387f543d293f54834c9aac8f76fb7416a7046556df75b567"},
+    {file = "grpcio_tools-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3d17a27c567a5e4d18f487368215cb51b43e2499059fd6113b92f7ae1fee48be"},
+    {file = "grpcio_tools-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4df167e67b083f96bc277032a526f6186e98662aaa49baea1dfb8ecfe26ce117"},
+    {file = "grpcio_tools-1.66.1-cp39-cp39-win32.whl", hash = "sha256:f94d5193b2f2a9595795b83e7978b2bee1c0399da66f2f24d179c388f81fb99c"},
+    {file = "grpcio_tools-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:66f527a1e3f063065e29cf6f3e55892434d13a5a51e3b22402e09da9521e98a3"},
+    {file = "grpcio_tools-1.66.1.tar.gz", hash = "sha256:5055ffe840ea8f505c30378be02afb4dbecb33480e554debe10b63d6b2f641c3"},
+]
+
+[package.dependencies]
+grpcio = ">=1.66.1"
+protobuf = ">=5.26.1,<6.0dev"
+setuptools = "*"
+
+[[package]]
+name = "idna"
+version = "3.10"
+description = "Internationalized Domain Names in Applications (IDNA)"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
+    {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
+]
+
+[package.extras]
+all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+    {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+]
+
+[[package]]
+name = "isort"
+version = "5.13.2"
+description = "A Python utility / library to sort Python imports."
+optional = false
+python-versions = ">=3.8.0"
+files = [
+    {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
+    {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
+]
+
+[package.extras]
+colors = ["colorama (>=0.4.6)"]
+
+[[package]]
+name = "jsonschema"
+version = "4.23.0"
+description = "An implementation of JSON Schema validation for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"},
+    {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"},
+]
+
+[package.dependencies]
+attrs = ">=22.2.0"
+jsonschema-specifications = ">=2023.03.6"
+referencing = ">=0.28.4"
+rpds-py = ">=0.7.1"
+
+[package.extras]
+format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
+format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"]
+
+[[package]]
+name = "jsonschema-specifications"
+version = "2023.12.1"
+description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"},
+    {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"},
+]
+
+[package.dependencies]
+referencing = ">=0.31.0"
+
+[[package]]
+name = "lazy-object-proxy"
+version = "1.10.0"
+description = "A fast and thorough lazy object proxy."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"},
+    {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"},
+]
+
+[[package]]
+name = "mando"
+version = "0.6.4"
+description = "Create Python CLI apps with little to no effort at all!"
+optional = false
+python-versions = "*"
+files = [
+    {file = "mando-0.6.4-py2.py3-none-any.whl", hash = "sha256:4ce09faec7e5192ffc3c57830e26acba0fd6cd11e1ee81af0d4df0657463bd1c"},
+    {file = "mando-0.6.4.tar.gz", hash = "sha256:79feb19dc0f097daa64a1243db578e7674909b75f88ac2220f1c065c10a0d960"},
+]
+
+[package.dependencies]
+six = "*"
+
+[package.extras]
+restructuredtext = ["rst2ansi"]
+
+[[package]]
+name = "marshmallow"
+version = "3.22.0"
+description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"},
+    {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"},
+]
+
+[package.dependencies]
+packaging = ">=17.0"
+
+[package.extras]
+dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
+docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"]
+tests = ["pytest", "pytz", "simplejson"]
+
+[[package]]
+name = "marshmallow-enum"
+version = "1.5.1"
+description = "Enum field for Marshmallow"
+optional = false
+python-versions = "*"
+files = [
+    {file = "marshmallow-enum-1.5.1.tar.gz", hash = "sha256:38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58"},
+    {file = "marshmallow_enum-1.5.1-py2.py3-none-any.whl", hash = "sha256:57161ab3dbfde4f57adeb12090f39592e992b9c86d206d02f6bd03ebec60f072"},
+]
+
+[package.dependencies]
+marshmallow = ">=2.0.0"
+
+[[package]]
+name = "mccabe"
+version = "0.6.1"
+description = "McCabe checker, plugin for flake8"
+optional = false
+python-versions = "*"
+files = [
+    {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
+    {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.0.0"
+description = "Type system extensions for programs checked with the mypy type checker."
+optional = false
+python-versions = ">=3.5"
+files = [
+    {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
+    {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
+]
+
+[[package]]
+name = "packaging"
+version = "24.1"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+    {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
+]
+
+[[package]]
+name = "pep8-naming"
+version = "0.11.1"
+description = "Check PEP-8 naming conventions, plugin for flake8"
+optional = false
+python-versions = "*"
+files = [
+    {file = "pep8-naming-0.11.1.tar.gz", hash = "sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724"},
+    {file = "pep8_naming-0.11.1-py2.py3-none-any.whl", hash = "sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738"},
+]
+
+[package.dependencies]
+flake8-polyfill = ">=1.0.2,<2"
+
+[[package]]
+name = "platformdirs"
+version = "4.3.6"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
+    {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
+]
+
+[package.extras]
+docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
+type = ["mypy (>=1.11.2)"]
+
+[[package]]
+name = "pluggy"
+version = "1.5.0"
+description = "plugin and hook calling mechanisms for python"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+    {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "protobuf"
+version = "5.27.2"
+description = ""
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "protobuf-5.27.2-cp310-abi3-win32.whl", hash = "sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38"},
+    {file = "protobuf-5.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505"},
+    {file = "protobuf-5.27.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5"},
+    {file = "protobuf-5.27.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b"},
+    {file = "protobuf-5.27.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e"},
+    {file = "protobuf-5.27.2-cp38-cp38-win32.whl", hash = "sha256:4fadd8d83e1992eed0248bc50a4a6361dc31bcccc84388c54c86e530b7f58863"},
+    {file = "protobuf-5.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:610e700f02469c4a997e58e328cac6f305f649826853813177e6290416e846c6"},
+    {file = "protobuf-5.27.2-cp39-cp39-win32.whl", hash = "sha256:9e8f199bf7f97bd7ecebffcae45ebf9527603549b2b562df0fbc6d4d688f14ca"},
+    {file = "protobuf-5.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:7fc3add9e6003e026da5fc9e59b131b8f22b428b991ccd53e2af8071687b4fce"},
+    {file = "protobuf-5.27.2-py3-none-any.whl", hash = "sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470"},
+    {file = "protobuf-5.27.2.tar.gz", hash = "sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714"},
+]
+
+[[package]]
+name = "pycodestyle"
+version = "2.7.0"
+description = "Python style guide checker"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+    {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
+    {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
+]
+
+[[package]]
+name = "pyflakes"
+version = "2.3.1"
+description = "passive checker of Python programs"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+    {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
+    {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
+]
+
+[[package]]
+name = "pygments"
+version = "2.18.0"
+description = "Pygments is a syntax highlighting package written in Python."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
+    {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
+]
+
+[package.extras]
+windows-terminal = ["colorama (>=0.4.6)"]
+
+[[package]]
+name = "pylint"
+version = "2.13.9"
+description = "python code static checker"
+optional = false
+python-versions = ">=3.6.2"
+files = [
+    {file = "pylint-2.13.9-py3-none-any.whl", hash = "sha256:705c620d388035bdd9ff8b44c5bcdd235bfb49d276d488dd2c8ff1736aa42526"},
+    {file = "pylint-2.13.9.tar.gz", hash = "sha256:095567c96e19e6f57b5b907e67d265ff535e588fe26b12b5ebe1fc5645b2c731"},
+]
+
+[package.dependencies]
+astroid = ">=2.11.5,<=2.12.0-dev0"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+dill = ">=0.2"
+isort = ">=4.2.5,<6"
+mccabe = ">=0.6,<0.8"
+platformdirs = ">=2.2.0"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+
+[package.extras]
+testutil = ["gitpython (>3)"]
+
+[[package]]
+name = "pylint-django"
+version = "2.5.3"
+description = "A Pylint plugin to help Pylint understand the Django web framework"
+optional = false
+python-versions = "*"
+files = [
+    {file = "pylint-django-2.5.3.tar.gz", hash = "sha256:0ac090d106c62fe33782a1d01bda1610b761bb1c9bf5035ced9d5f23a13d8591"},
+    {file = "pylint_django-2.5.3-py3-none-any.whl", hash = "sha256:56b12b6adf56d548412445bd35483034394a1a94901c3f8571980a13882299d5"},
+]
+
+[package.dependencies]
+pylint = ">=2.0,<3"
+pylint-plugin-utils = ">=0.7"
+
+[package.extras]
+for-tests = ["coverage", "django-tables2", "django-tastypie", "factory-boy", "pylint (>=2.13)", "pytest", "wheel"]
+with-django = ["Django"]
+
+[[package]]
+name = "pylint-plugin-utils"
+version = "0.8.2"
+description = "Utilities and helpers for writing Pylint plugins"
+optional = false
+python-versions = ">=3.7,<4.0"
+files = [
+    {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"},
+    {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"},
+]
+
+[package.dependencies]
+pylint = ">=1.7"
+
+[[package]]
+name = "pytest"
+version = "8.3.3"
+description = "pytest: simple powerful testing with Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"},
+    {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=1.5,<2"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+
+[[package]]
+name = "pytest-runner"
+version = "6.0.1"
+description = "Invoke py.test as distutils command with dependency resolution"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "pytest-runner-6.0.1.tar.gz", hash = "sha256:70d4739585a7008f37bf4933c013fdb327b8878a5a69fcbb3316c88882f0f49b"},
+    {file = "pytest_runner-6.0.1-py3-none-any.whl", hash = "sha256:ea326ed6f6613992746062362efab70212089a4209c08d67177b3df1c52cd9f2"},
+]
+
+[package.extras]
+docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"]
+testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-virtualenv", "types-setuptools"]
+
+[[package]]
+name = "pytest-subtests"
+version = "0.13.1"
+description = "unittest subTest() support and subtests fixture"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "pytest_subtests-0.13.1-py3-none-any.whl", hash = "sha256:ab616a22f64cd17c1aee65f18af94dbc30c444f8683de2b30895c3778265e3bd"},
+    {file = "pytest_subtests-0.13.1.tar.gz", hash = "sha256:989e38f0f1c01bc7c6b2e04db7d9fd859db35d77c2c1a430c831a70cbf3fde2d"},
+]
+
+[package.dependencies]
+attrs = ">=19.2.0"
+pytest = ">=7.0"
+
+[[package]]
+name = "radon"
+version = "4.5.0"
+description = "Code Metrics in Python"
+optional = false
+python-versions = "*"
+files = [
+    {file = "radon-4.5.0-py2.py3-none-any.whl", hash = "sha256:ad4c1b7a228cb4d33f7500e89326fd98d287938b96820e748ccdbd1a9bf91f0b"},
+    {file = "radon-4.5.0.tar.gz", hash = "sha256:7afa65db14d759616ab68033e0e1caf1f624c97308dd256afa47518ecebddf6e"},
+]
+
+[package.dependencies]
+colorama = {version = ">=0.4.1", markers = "python_version > \"3.4\""}
+future = "*"
+mando = ">=0.6,<0.7"
+
+[package.extras]
+flake8 = ["flake8-polyfill"]
+
+[[package]]
+name = "referencing"
+version = "0.35.1"
+description = "JSON Referencing + Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"},
+    {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"},
+]
+
+[package.dependencies]
+attrs = ">=22.2.0"
+rpds-py = ">=0.7.0"
+
+[[package]]
+name = "requests"
+version = "2.32.3"
+description = "Python HTTP for Humans."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
+    {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
+]
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = ">=2,<4"
+idna = ">=2.5,<4"
+urllib3 = ">=1.21.1,<3"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)"]
+use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+
+[[package]]
+name = "rpds-py"
+version = "0.20.0"
+description = "Python bindings to Rust's persistent data structures (rpds)"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"},
+    {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"},
+    {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"},
+    {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"},
+    {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"},
+    {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"},
+    {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"},
+    {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"},
+    {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"},
+    {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"},
+    {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"},
+    {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"},
+    {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"},
+    {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"},
+    {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"},
+    {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"},
+    {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"},
+    {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"},
+    {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"},
+    {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"},
+    {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"},
+    {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"},
+    {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"},
+    {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"},
+    {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"},
+    {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"},
+    {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"},
+    {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"},
+    {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"},
+    {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"},
+    {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"},
+    {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"},
+    {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"},
+    {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"},
+    {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"},
+    {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"},
+    {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"},
+    {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"},
+    {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"},
+    {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"},
+    {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"},
+    {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"},
+    {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"},
+    {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"},
+    {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"},
+    {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"},
+    {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"},
+    {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"},
+    {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"},
+    {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"},
+    {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"},
+    {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"},
+    {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"},
+    {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"},
+    {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"},
+    {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"},
+    {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"},
+    {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"},
+    {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"},
+    {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"},
+    {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"},
+    {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"},
+    {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"},
+    {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"},
+    {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"},
+    {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"},
+    {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"},
+    {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"},
+    {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"},
+    {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"},
+    {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"},
+    {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"},
+    {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"},
+    {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"},
+    {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"},
+    {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"},
+    {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"},
+    {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"},
+    {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"},
+    {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"},
+    {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"},
+]
+
+[[package]]
+name = "setuptools"
+version = "56.0.0"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "setuptools-56.0.0-py3-none-any.whl", hash = "sha256:7430499900e443375ba9449a9cc5d78506b801e929fef4a186496012f93683b5"},
+    {file = "setuptools-56.0.0.tar.gz", hash = "sha256:08a1c0f99455307c48690f00d5c2ac2c1ccfab04df00454fef854ec145b81302"},
+]
+
+[package.extras]
+certs = ["certifi (==2016.9.26)"]
+docs = ["jaraco.packaging (>=8.2)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-inline-tabs"]
+ssl = ["wincertstore (==0.2)"]
+testing = ["flake8-2020", "jaraco.envs", "jaraco.path (>=3.2.0)", "mock", "paver", "pip (>=19.1)", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy", "pytest-virtualenv (>=1.2.7)", "pytest-xdist", "sphinx", "virtualenv (>=13.0.0)", "wheel"]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+files = [
+    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+
+[[package]]
+name = "sqlparse"
+version = "0.5.1"
+description = "A non-validating SQL parser."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4"},
+    {file = "sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"},
+]
+
+[package.extras]
+dev = ["build", "hatch"]
+doc = ["sphinx"]
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+    {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+]
+
+[[package]]
+name = "typing-extensions"
+version = "3.10.0.2"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+optional = false
+python-versions = "*"
+files = [
+    {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
+    {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
+    {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
+]
+
+[[package]]
+name = "typing-inspect"
+version = "0.9.0"
+description = "Runtime inspection utilities for typing module."
+optional = false
+python-versions = "*"
+files = [
+    {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"},
+    {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"},
+]
+
+[package.dependencies]
+mypy-extensions = ">=0.3.0"
+typing-extensions = ">=3.7.4"
+
+[[package]]
+name = "tzdata"
+version = "2024.1"
+description = "Provider of IANA time zone data"
+optional = false
+python-versions = ">=2"
+files = [
+    {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
+    {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
+]
+
+[[package]]
+name = "urllib3"
+version = "2.2.3"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"},
+    {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"},
+]
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+h2 = ["h2 (>=4,<5)"]
+socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
+zstd = ["zstandard (>=0.18.0)"]
+
+[[package]]
+name = "wps-light"
+version = "0.15.3"
+description = "The strictest and most opinionated python linter ever (lighter fork)."
+optional = false
+python-versions = ">=3.6,<4.0"
+files = [
+    {file = "wps-light-0.15.3.tar.gz", hash = "sha256:e3040611340796427c346006298954c859ab12471cf31ec60d6254af95d70c37"},
+    {file = "wps_light-0.15.3-py3-none-any.whl", hash = "sha256:1193bad23fd2bfd74c0cb2558f3dd66eb0619cfaee895a1c113252d1965d555e"},
+]
+
+[package.dependencies]
+astor = ">=0.8,<0.9"
+attrs = "*"
+flake8 = ">=3.7,<4.0"
+flake8-polyfill = ">=1.0.2,<2.0.0"
+pygments = ">=2.4,<3.0"
+typing_extensions = ">=3.6,<4.0"
+
+[[package]]
+name = "wrapt"
+version = "1.16.0"
+description = "Module for decorators, wrappers and monkey patching."
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
+    {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
+    {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
+    {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
+    {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
+    {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
+    {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
+    {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
+    {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
+    {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
+    {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
+    {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
+    {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
+    {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
+    {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
+    {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
+    {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
+    {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
+    {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
+    {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
+    {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
+    {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
+    {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
+    {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
+    {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
+    {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
+    {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
+    {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
+    {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
+    {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
+    {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
+    {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
+    {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
+    {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
+    {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
+    {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
+    {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
+    {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
+    {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
+    {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
+    {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
+    {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
+    {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
+    {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
+    {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
+    {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
+    {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
+    {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
+    {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
+    {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
+    {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
+    {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
+    {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
+    {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
+    {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
+    {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
+    {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
+    {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
+    {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
+    {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
+    {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
+    {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
+    {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
+    {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
+    {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
+    {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
+    {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
+    {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
+    {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
+    {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
+]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "~3.10"
+content-hash = "313305578f918bbeb47a11a96f72df48fbabca013ad8ad2222f5ceb9518bd357"
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..55fabe23
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,70 @@
+[tool.poetry]
+name = "hyperstyle"
+version = "1.5.0"
+description = "A tool for running a set of pre-configured linters and evaluating code quality."
+authors = ["Hyperskill Team"]
+readme = "README.md"
+keywords = ["code review"]
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "Environment :: Console",
+    "Intended Audience :: Developers",
+    "Intended Audience :: Education",
+    "Intended Audience :: Science/Research",
+    "License :: OSI Approved :: Apache Software License",
+    "Operating System :: OS Independent",
+    "Programming Language :: Python :: 3",
+    "Topic :: Education",
+]
+
+[project.urls]
+Homepage = "https://support.hyperskill.org/hc/en-us/articles/360049582712-Code-style-Code-quality"
+Repository = "https://github.com/hyperskill/hyperstyle"
+
+[project.scripts]
+review = "hyperstyle.src.python.review.run_tool:main"
+
+[tool.poetry.dependencies]
+python = "~3.10"
+setuptools = "56.0.0"
+# python code analysis tools
+pylint = "2.13.9"
+pylint-django = "2.5.3"
+flake8 = "3.9.0"
+dataclasses-json="0.5.7"
+# flake8 plugins
+flake8-plugin-utils = "1.3.2"
+flake8-bugbear = "21.4.3"
+flake8-builtins = "1.5.3"
+flake8-comprehensions = "3.4.0"
+flake8-eradicate = "1.0.0"
+flake8-import-order = "0.18.1"
+flake8-polyfill = "1.0.2"
+flake8-return = "1.1.2"
+flake8-spellcheck = "0.24.0"
+mccabe = "0.6.1"
+pep8-naming = "0.11.1"
+wps-light = "0.15.3"
+flake8-broken-line = "0.3.0"
+flake8-string-format = "0.3.0"
+flake8-commas = "2.0.0"
+cohesion = "1.0.0"
+radon = "4.5.0"
+# extra libraries and frameworks
+argparse = "1.4.0"
+django = "4.2.5"
+requests = "2.32.3"
+# grpc
+grpcio-tools = "1.66.1"
+protobuf = "5.27.2"
+
+
+[tool.poetry.group.dev.dependencies]
+pytest = "8.3.3"
+pytest-runner = "6.0.1"
+pytest-subtests = "0.13.1"
+jsonschema = "4.23.0"
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index fd49f0a6..00000000
--- a/requirements.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-setuptools==56.0.0
-
-# python code analysis tools
-pylint==2.13.0
-pylint-django==2.5.3
-flake8==3.9.0
-
-# flake8 plugins
-flake8-plugin-utils==1.3.2
-flake8-bugbear==21.4.3
-flake8-builtins==1.5.3
-flake8-comprehensions==3.4.0
-flake8-eradicate==1.0.0
-flake8-import-order==0.18.1
-flake8-polyfill==1.0.2
-flake8-return==1.1.2
-flake8-spellcheck==0.24.0
-mccabe==0.6.1
-pep8-naming==0.11.1
-wps-light==0.15.2
-flake8-broken-line==0.3.0
-flake8-string-format==0.3.0
-flake8-commas==2.0.0
-cohesion==1.0.0
-radon==4.5.0
-
-# extra libraries and frameworks
-django==4.2.5
-requests==2.25.1
-argparse==1.4.0
\ No newline at end of file
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 33ebde4e..00000000
--- a/setup.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import os
-from pathlib import Path
-from typing import List
-
-from setuptools import find_packages, setup
-
-current_dir = Path(__file__).parent.absolute()
-
-
-def get_long_description() -> str:
-    with open(current_dir / 'README.md', encoding='utf-8') as f:
-        return f.read()
-
-
-def get_version() -> str:
-    with open(current_dir / 'VERSION.md') as version_file:
-        return version_file.read().replace('\n', '')
-
-
-def get_inspectors_additional_files() -> List[str]:
-    inspectors_path = current_dir / 'hyperstyle' / 'src' / 'python' / 'review' / 'inspectors'
-    configs = ['xml', 'yml', 'eslintrc', 'flake8', 'txt', 'pylintrc']
-    result = []
-    for root, _, files in os.walk(inspectors_path):
-        for file in files:
-            if not file.endswith('.py') and file.split('.')[-1] in configs:
-                result.append(str(Path(root) / file))
-    return result
-
-
-def get_requires() -> List[str]:
-    with open(current_dir / 'requirements.txt') as requirements_file:
-        return requirements_file.read().split('\n')
-
-
-setup(
-    name='hyperstyle',
-    version=get_version(),
-    description='A tool for running a set of pre-configured linters and evaluating code quality.',
-    long_description=get_long_description(),
-    long_description_content_type='text/markdown',
-    url='https://github.com/hyperskill/hyperstyle',
-    author='Stepik.org',
-    author_email='ivan.magda@stepik.org',
-    classifiers=[
-        'Development Status :: 3 - Alpha',
-        'Intended Audience :: Developers',
-        'Topic :: Software Development :: Build Tools',
-        'License :: OSI Approved :: MIT License',
-        'Programming Language :: Python :: 3',
-        'Operating System :: OS Independent',
-    ],
-    keywords='code review',
-    python_requires='>=3.8, <4',
-    install_requires=get_requires(),
-    include_package_data=True,
-    packages=find_packages(exclude=[
-        '*.unit_tests',
-        '*.unit_tests.*',
-        'unit_tests.*',
-        'unit_tests',
-        '*.functional_tests',
-        '*.functional_tests.*',
-        'functional_tests.*',
-        'functional_tests',
-    ]),
-    zip_safe=False,
-    package_data={
-        '': get_inspectors_additional_files(),
-    },
-    entry_points={
-        'console_scripts': [
-            'review=hyperstyle.src.python.review.run_tool:main',
-        ],
-    },
-)
diff --git a/setup_environment.sh b/setup_environment.sh
index cf2bbe85..a782d594 100755
--- a/setup_environment.sh
+++ b/setup_environment.sh
@@ -73,24 +73,13 @@ echo "The variables are defined."
 
 echo
 
-read -p "Do you want to install Python requirements for the Hyperstyle project? (Y/n): " -r
+read -p "Do you want to install Python development requirements for the Hyperstyle project? (Y/n): " -r
 if [[ $REPLY =~ ^[Yy]$ ]]; then
-  echo "Installing Python requirements..."
-  pip install --no-cache-dir -r requirements.txt
-  check_return_code $? "Python requirements installed." "Python requirements installation failed."
+  echo "Installing Python development requirements..."
+  pip install --no-cache-dir -r requirements-dev.txt
+  check_return_code $? "Python development requirements installed." "Python development requirements installation failed."
 else
-  echo "Python requirements installation skipped."
-fi
-
-echo
-
-read -p "Do you want to install Python test requirements for the Hyperstyle project? (Y/n): " -r
-if [[ $REPLY =~ ^[Yy]$ ]]; then
-  echo "Installing Python test requirements..."
-  pip install --no-cache-dir -r requirements-test.txt
-  check_return_code $? "Python test requirements installed." "Python test requirements installation failed."
-else
-  echo "Python test requirements installation skipped."
+  echo "Python development requirements installation skipped."
 fi
 
 echo
@@ -147,3 +136,7 @@ if need_to_install_linter "golangci-lint" "${GOLANG_LINT_DIRECTORY}"; then
 else
   echo "Golangci-lint ${GOLANG_LINT_VERSION} installation skipped."
 fi
+
+echo "Generating proto files  ..."
+   export PROTO_PATH="hyperstyle/src/python/review/inspectors/common/inspector/proto"
+   python3 -m grpc_tools.protoc --proto_path=. --python_out=. --pyi_out=. --grpc_python_out=. ${PROTO_PATH}/model.proto
diff --git a/whitelist.txt b/whitelist.txt
index 50298483..ad4d1ecc 100644
--- a/whitelist.txt
+++ b/whitelist.txt
@@ -9,6 +9,7 @@ ECMA
 EXPR
 G10
 IGNORECASE
+IJ
 INTELLIJ
 Intelli
 JS
@@ -33,6 +34,7 @@ astype
 atclause
 barmode
 bce
+bdist
 bgcolor
 binarizer
 capsys
@@ -42,6 +44,7 @@ cbo
 changelog
 checkstyle
 cloneable
+cmdclass
 concat
 config
 configs
@@ -91,6 +94,7 @@ exprs
 f1
 file
 filemode
+filepath
 fillna
 formatter
 fs
@@ -106,12 +110,14 @@ getuid
 gradle
 groupby
 groupdict
+grpc
 hashtable
 hline
 hyperstyle
 idiv
 idx
 ignorecase
+ij
 iloc
 inerop
 initializer
@@ -182,6 +188,7 @@ parametrize
 params
 parsers
 pathlib
+pb2
 pickler
 plotly
 pmd
@@ -192,6 +199,8 @@ preprocess
 preprocessing
 pretrained
 println
+proto
+protoc
 punisher
 puppycrawl
 pyast
@@ -207,6 +216,7 @@ removeprefix
 rfind
 rmdir
 runtime
+sdist
 setdefault
 setslice
 showline


Test patch:
diff --git a/requirements-test.txt b/requirements-test.txt
deleted file mode 100644
index 824641d9..00000000
--- a/requirements-test.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-pytest==6.2.3
-pytest-runner==5.2
-pytest-subtests==0.4.0
-jsonschema==3.2.0
diff --git a/test/python/functional_tests/conftest.py b/test/python/functional_tests/conftest.py
index 89fac5e8..abeb3445 100644
--- a/test/python/functional_tests/conftest.py
+++ b/test/python/functional_tests/conftest.py
@@ -1,5 +1,8 @@
+import sys
 from dataclasses import dataclass, field
 from pathlib import Path
+
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
 from test.python import TEST_DATA_FOLDER
 from typing import List, Optional
 
@@ -25,15 +28,20 @@ class LocalCommandBuilder:
     new_format: bool = False
     group_by_difficulty: bool = False
     history: Optional[str] = None
+    ij_config: Optional[str] = None
 
     def build(self) -> List[str]:
         assert self.path is not None
 
         command = [
-            'python3', (MAIN_FOLDER.parent / 'review/run_tool.py'),
+            sys.executable, (MAIN_FOLDER.parent / 'review/run_tool.py'),
             RunToolArgument.VERBOSITY.value.long_name, str(self.verbosity),
         ]
 
+        # TODO: remove after adding a test server
+        self.disable.append(InspectorType.IJ_PYTHON.value)
+        self.disable.append(InspectorType.IJ_KOTLIN.value)
+
         if self.disable:
             command.extend([RunToolArgument.DISABLE.value.long_name, ','.join(self.disable)])
 
@@ -49,6 +57,9 @@ def build(self) -> List[str]:
         if self.history is not None:
             command.extend([RunToolArgument.HISTORY.value.long_name, self.history])
 
+        if self.ij_config is not None:
+            command.extend([RunToolArgument.IJ_CONFIG.value.long_name, self.ij_config])
+
         if self.group_by_difficulty:
             command.append(RunToolArgument.GROUP_BY_DIFFICULTY.value.long_name)
 
diff --git a/test/python/functional_tests/test_difficulty_levels.py b/test/python/functional_tests/test_difficulty_levels.py
index fd138e32..b38e3b3c 100644
--- a/test/python/functional_tests/test_difficulty_levels.py
+++ b/test/python/functional_tests/test_difficulty_levels.py
@@ -4,7 +4,7 @@
 from typing import Dict, Optional
 
 import pytest
-from hyperstyle.src.python.review.inspectors.common.tips import get_cohesion_tip
+from hyperstyle.src.python.review.inspectors.common.issue.tips import get_cohesion_tip
 
 
 def _get_output_json(local_command: LocalCommandBuilder, file_path: str, history: Optional[str] = None) -> Dict:
diff --git a/test/python/inspectors/conftest.py b/test/python/inspectors/conftest.py
index d7e9524c..c807bf2e 100644
--- a/test/python/inspectors/conftest.py
+++ b/test/python/inspectors/conftest.py
@@ -6,7 +6,7 @@
 
 import pytest
 from hyperstyle.src.python.review.common.file_system import new_temp_dir
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueType
 from hyperstyle.src.python.review.reviewers.utils.metadata_exploration import explore_file, FileMetadata
 
 
diff --git a/test/python/inspectors/test_checkstyle_inspector.py b/test/python/inspectors/test_checkstyle_inspector.py
index b0823e46..f60d1aec 100644
--- a/test/python/inspectors/test_checkstyle_inspector.py
+++ b/test/python/inspectors/test_checkstyle_inspector.py
@@ -7,7 +7,7 @@
 from hyperstyle.src.python.review.common.language import Language
 from hyperstyle.src.python.review.inspectors.checkstyle.checkstyle import CheckstyleInspector
 from hyperstyle.src.python.review.inspectors.checkstyle.issue_configs import ISSUE_CONFIGS
-from hyperstyle.src.python.review.inspectors.common.tips import (
+from hyperstyle.src.python.review.inspectors.common.issue.tips import (
     get_bool_expr_len_tip,
     get_cyclomatic_complexity_tip,
     get_func_len_tip,
@@ -15,8 +15,8 @@
     get_magic_number_tip,
 )
 from hyperstyle.src.python.review.inspectors.common.xml_parser import parse_xml_file_result
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import (
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import (
     BoolExprLenIssue,
     CodeIssue,
     CyclomaticComplexityIssue,
@@ -25,7 +25,7 @@
     IssueType,
     LineLenIssue,
 )
-from hyperstyle.src.python.review.inspectors.issue_configs import IssueConfigsHandler
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import IssueConfigsHandler
 from hyperstyle.src.python.review.reviewers.utils.issues_filter import filter_low_measure_issues
 
 FILE_NAME_AND_ISSUES = [
diff --git a/test/python/inspectors/test_detekt_inspector.py b/test/python/inspectors/test_detekt_inspector.py
index 40e8ceef..29c8291c 100644
--- a/test/python/inspectors/test_detekt_inspector.py
+++ b/test/python/inspectors/test_detekt_inspector.py
@@ -3,7 +3,7 @@
 
 import pytest
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.common.tips import (
+from hyperstyle.src.python.review.inspectors.common.issue.tips import (
     get_bool_expr_len_tip,
     get_cyclomatic_complexity_tip,
     get_func_len_tip,
diff --git a/test/python/inspectors/test_eslint_inspector.py b/test/python/inspectors/test_eslint_inspector.py
index 81b77ffb..29229297 100644
--- a/test/python/inspectors/test_eslint_inspector.py
+++ b/test/python/inspectors/test_eslint_inspector.py
@@ -3,7 +3,7 @@
 
 import pytest
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.common.tips import get_cyclomatic_complexity_tip
+from hyperstyle.src.python.review.inspectors.common.issue.tips import get_cyclomatic_complexity_tip
 from hyperstyle.src.python.review.inspectors.eslint.eslint import ESLintInspector
 from hyperstyle.src.python.review.reviewers.utils.issues_filter import filter_low_measure_issues
 
diff --git a/test/python/inspectors/test_filter_duplicate_issues.py b/test/python/inspectors/test_filter_duplicate_issues.py
index 92d17b4e..324c0754 100644
--- a/test/python/inspectors/test_filter_duplicate_issues.py
+++ b/test/python/inspectors/test_filter_duplicate_issues.py
@@ -1,7 +1,7 @@
 from pathlib import Path
 
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import CodeIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import CodeIssue, IssueDifficulty, IssueType
 from hyperstyle.src.python.review.reviewers.utils.issues_filter import filter_duplicate_issues
 
 
diff --git a/test/python/inspectors/test_flake8_inspector.py b/test/python/inspectors/test_flake8_inspector.py
index 7435cb17..df290731 100644
--- a/test/python/inspectors/test_flake8_inspector.py
+++ b/test/python/inspectors/test_flake8_inspector.py
@@ -4,7 +4,7 @@
 
 import pytest
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.common.tips import (
+from hyperstyle.src.python.review.inspectors.common.issue.tips import (
     get_augmented_assign_pattern_tip,
     get_cohesion_tip,
     get_cyclomatic_complexity_tip,
@@ -12,7 +12,7 @@
     get_magic_number_tip,
 )
 from hyperstyle.src.python.review.inspectors.flake8.flake8 import Flake8Inspector
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.reviewers.utils.issues_filter import filter_low_measure_issues
 
 FILE_NAMES_AND_N_ISSUES = [
diff --git a/test/python/inspectors/test_golang_lint_inspector.py b/test/python/inspectors/test_golang_lint_inspector.py
index c66a1fca..cc68e5a9 100644
--- a/test/python/inspectors/test_golang_lint_inspector.py
+++ b/test/python/inspectors/test_golang_lint_inspector.py
@@ -5,7 +5,7 @@
 
 import pytest
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.common.tips import (
+from hyperstyle.src.python.review.inspectors.common.issue.tips import (
     get_cyclomatic_complexity_tip,
     get_func_len_tip,
     get_line_len_tip,
@@ -13,8 +13,8 @@
     get_maintainability_index_tip,
 )
 from hyperstyle.src.python.review.inspectors.golang_lint.golang_lint import GolangLintInspector
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import (
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import (
     CodeIssue,
     CyclomaticComplexityIssue,
     FuncLenIssue,
diff --git a/test/python/inspectors/test_issue_configs.py b/test/python/inspectors/test_issue_configs.py
index 63be075d..fba85966 100644
--- a/test/python/inspectors/test_issue_configs.py
+++ b/test/python/inspectors/test_issue_configs.py
@@ -2,7 +2,7 @@
 from typing import Callable, Dict, List, Optional, Pattern, Tuple, Type, Union
 
 import pytest
-from hyperstyle.src.python.review.inspectors.issue_configs import (
+from hyperstyle.src.python.review.inspectors.common.issue.issue_configs import (
     IssueConfig,
     IssueConfigsHandler,
     IssueDescriptionParser,
diff --git a/test/python/inspectors/test_local_review.py b/test/python/inspectors/test_local_review.py
index 6fc09fa6..1004b776 100644
--- a/test/python/inspectors/test_local_review.py
+++ b/test/python/inspectors/test_local_review.py
@@ -1,5 +1,7 @@
 import json
 from collections import namedtuple
+
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
 from test.python.inspectors import PYTHON_DATA_FOLDER
 
 import pytest
@@ -19,7 +21,7 @@
 @pytest.fixture
 def config() -> ApplicationConfig:
     return ApplicationConfig(
-        disabled_inspectors=set(),
+        disabled_inspectors={InspectorType.IJ_PYTHON},
         allow_duplicates=False,
         n_cpu=1,
         inspectors_config={"n_cpu": 1},
diff --git a/test/python/inspectors/test_out_of_range_issues.py b/test/python/inspectors/test_out_of_range_issues.py
index 0dc60493..89e6d4d5 100644
--- a/test/python/inspectors/test_out_of_range_issues.py
+++ b/test/python/inspectors/test_out_of_range_issues.py
@@ -1,7 +1,7 @@
 from pathlib import Path
 
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, CodeIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, CodeIssue, IssueDifficulty, IssueType
 from hyperstyle.src.python.review.reviewers.common import filter_out_of_range_issues
 
 DEFAULT_PATH = Path('test_out_of_range_issues.py')
diff --git a/test/python/inspectors/test_pmd_inspector.py b/test/python/inspectors/test_pmd_inspector.py
index 94ea7d5f..7cc9e559 100644
--- a/test/python/inspectors/test_pmd_inspector.py
+++ b/test/python/inspectors/test_pmd_inspector.py
@@ -4,8 +4,8 @@
 
 import pytest
 from hyperstyle.src.python.review.common.language_version import LanguageVersion
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import CodeIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import CodeIssue, IssueDifficulty, IssueType
 from hyperstyle.src.python.review.inspectors.pmd.pmd import DEFAULT_JAVA_VERSION, PMDInspector
 
 from .conftest import use_file_metadata
diff --git a/test/python/inspectors/test_pylint_inspector.py b/test/python/inspectors/test_pylint_inspector.py
index a25a4339..69c89d11 100644
--- a/test/python/inspectors/test_pylint_inspector.py
+++ b/test/python/inspectors/test_pylint_inspector.py
@@ -2,7 +2,7 @@
 from test.python.inspectors import PYLINT_DATA_FOLDER, PYTHON_DATA_FOLDER
 
 import pytest
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.inspectors.pylint.pylint import PylintInspector
 
 from .conftest import use_file_metadata
diff --git a/test/python/inspectors/test_python_ast.py b/test/python/inspectors/test_python_ast.py
index 0d1e4692..1439af02 100644
--- a/test/python/inspectors/test_python_ast.py
+++ b/test/python/inspectors/test_python_ast.py
@@ -3,8 +3,8 @@
 from test.python.inspectors.conftest import use_file_metadata
 
 import pytest
-from hyperstyle.src.python.review.inspectors.common.tips import get_bool_expr_len_tip, get_func_len_tip
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.tips import get_bool_expr_len_tip, get_func_len_tip
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
 from hyperstyle.src.python.review.inspectors.pyast.python_ast import (
     BoolExpressionLensGatherer,
     FunctionLensGatherer,
diff --git a/test/python/inspectors/test_radon_inspector.py b/test/python/inspectors/test_radon_inspector.py
index 420c768a..0d7e9404 100644
--- a/test/python/inspectors/test_radon_inspector.py
+++ b/test/python/inspectors/test_radon_inspector.py
@@ -4,8 +4,8 @@
 
 import pytest
 from hyperstyle.src.python.review.common.language import Language
-from hyperstyle.src.python.review.inspectors.common.tips import get_maintainability_index_tip
-from hyperstyle.src.python.review.inspectors.issue import IssueType
+from hyperstyle.src.python.review.inspectors.common.issue.tips import get_maintainability_index_tip
+from hyperstyle.src.python.review.inspectors.common.issue.issue import IssueType
 from hyperstyle.src.python.review.inspectors.radon.radon import RadonInspector
 from hyperstyle.src.python.review.reviewers.utils.issues_filter import filter_low_measure_issues
 
diff --git a/test/python/quality/test_penalty.py b/test/python/quality/test_penalty.py
index 0947c46d..fcb941c7 100644
--- a/test/python/quality/test_penalty.py
+++ b/test/python/quality/test_penalty.py
@@ -2,8 +2,8 @@
 from typing import List, Set
 
 import pytest
-from hyperstyle.src.python.review.inspectors.inspector_type import InspectorType
-from hyperstyle.src.python.review.inspectors.issue import BaseIssue, IssueDifficulty, IssueType
+from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
+from hyperstyle.src.python.review.inspectors.common.issue.issue import BaseIssue, IssueDifficulty, IssueType
 from hyperstyle.src.python.review.quality.penalty import categorize, PreviousIssue, Punisher
 
 punisher = Punisher([], [])
diff --git a/test/resources/inspectors/python/case39_no_issues.py b/test/resources/inspectors/python/case39_no_issues.py
index 1385fe3e..f7cf60e1 100644
--- a/test/resources/inspectors/python/case39_no_issues.py
+++ b/test/resources/inspectors/python/case39_no_issues.py
@@ -1,1 +1,1 @@
-print("Hello, world!")
\ No newline at end of file
+print("Hello, world!")