12

I'm using pre-commit for most of my Python projects, and in many of them, I need to use pylint as a local repo. When I want to commit, I always have to activate python venv and then commit; otherwise, I'll get the following error:

black....................................................................Passed
pylint...................................................................Failed
- hook id: pylint
- exit code: 1

Executable `pylint` not found

When I use vscode version control to commit, I get the same error; I searched about the problem and didn't find any solution to avoid the error in VSCode.

This is my typical .pre-commit-config.yaml:

repos:
-   repo: https://github.com/ambv/black
    rev: 21.9b0
    hooks:
    - id: black
      language_version: python3.8
      exclude: admin_web/urls\.py
-   repo: local
    hooks:
    -   id: pylint
        name: pylint
        entry: pylint
        language: python
        types: [python]
        args: 
         - --rcfile=.pylintrc

3 Answers 3

10

you have ~essentially two options here -- neither are great (language: system is kinda the unsupported escape hatch so it's on you to make those things available on PATH)

you could use a specific path to the virtualenv entry: venv/bin/pylint -- though that will reduce the portability.

or you could start vscode with your virtualenv activated (usually code .) -- this doesn't always work if vscode is already running


disclaimer: I created pre-commit

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you @anthony-sottile, for your great framework and your answer. Do you think it is possible to fix this problem by creating an extension to extend vscode source control and run commands inside virtual environments? And is the problem exists in Pycharm too? If there is a way to add (kind of) native support to vscode, I will be happy to work on it.
Hey Anthony, I found another solution that makes your suggestion portable! I would like to have your opinion about that solution.
poetry is not portable -- I definitely don't have it globally available on my computer and neither will all of your contributors
Can't imagine why entry does't work with pylint even if it parsed. But yeah, entry: venv/bin/pylint works.
4

I found a great solution. Use poetry or hatch!

When you use either of these, you can easily access your environment with, e.g., poetry run command. That's the trick to accessing local pylint.

Here is an example using poetry; everything is the same except language and entry:

repos:
    - repo: https://github.com/ambv/black
      rev: 21.9b0
      hooks:
          - id: black
            language_version: python3.8
            exclude: admin_web/urls\.py
    - repo: local
      hooks:
          - id: pylint
            name: pylint
            entry: poetry run pylint # was: pylint
            language: system         # was: python
            types: [python]
            args:
                - --rcfile=.pylintrc

And works like a charm in VS Code!

1 Comment

I also decided to go for this solution; only difference is I'm using hatch hatch.pypa.io/latest instead of poetry
2

Another approach is to activate virtualenv from the shell during git hook execution. Because sometimes you can have custom command living in pre-commit like:

- repo: local
  hooks:
  - id: mypy
    name: Run mypy
    entry: python -m mypy --install-types --non-interactive --cache-dir=.mypy_cache/ myapp/
    language: system
    types: [python]
    pass_filenames: false

And installing python packages globally is definitely what I don't want to do. Especially when this hook is a part of CI that you can't change for a good reason.

Solution is to invoke virtualenv directly from shell used by Git:

in ~/.bash_profile

function activate_venv_in_cwd() {
    # Activate virtualenv when running within VS Code subprocess
    # fixes pre-commit hooks running from VS Code Source Control thingy
    VENV_PATH=.venv
    # don't invoke outside of VS Code
    if [ -z "$VSCODE_PID" ]; then
        return
    fi
    if [ -d "./$VENV_PATH" ]; then
        if [ -f "./$VENV_PATH/bin/activate" ]; then
            source "./$VENV_PATH/bin/activate"
        fi
    fi
}

function cd() {
    builtin cd "$@" && activate_venv_in_cwd
}

activate_venv_in_cwd

and add -l option to default pre-commit bash shebang:

sed -i '' '1s|#!/usr/bin/env bash|#!/usr/bin/env bash -l|' .git/hooks/pre-commit

Now, virtualenv will be activated for all pre-commit hooks without hassle with system wide packages.

1 Comment

This worked for me! However, I had to tweak it slightly to have #!/bin/bash -l instead of #!/usr/bin/env bash -l.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.