1

Problem

Mypy as a local hook reports the same error multiple times.

related issues

Difference between running Mypy directly VS with pre-commit run --all-files

.pre-commit-config.yaml

  - repo: local
    hooks:
      - id: mypy
        name: mypy
        # or `entry: poetry run mypy`, but perhaps both are the same
        entry: mypy
        # From the docs it is not clear the difference between `language: python` VS `language: system`
        language: system
        types: [ python ]

sample file

Let's make some silly mistake only to trigger Mypy warnings, e.g.:

# sample.py 

foo: int = "bar"

Note: there are many other files in the project.

pre-commit run --all-files output

The same mistake reported 3 times:

pre-commit run mypy --all-files
mypy.....................................................................Failed
- hook id: mypy
- exit code: 1

src/sample.py:12:12: error: Incompatible types in assignment (expression
has type "str", variable has type "int")  [assignment]
    foo: int = "bar"
               ^
Found 1 error in 1 file (checked 23 source files)
src/sample.py:12:12: error: Incompatible types in assignment (expression
has type "str", variable has type "int")  [assignment]
    foo: int = "bar"
               ^
Found 1 error in 1 file (checked 23 source files)
src/sample.py:12:12: error: Incompatible types in assignment (expression
has type "str", variable has type "int")  [assignment]
    foo: int = "bar"
               ^
Found 1 error in 1 file (checked 23 source files)
src/sample.py:12:12: error: Incompatible types in assignment (expression
has type "str", variable has type "int")  [assignment]
    foo: int = "bar"
               ^
Found 1 error in 1 file (checked 22 source files)

mypy src/ output

The mistake is reported only once, which is a correct behavior:

src/cache/cache.py:12:12: error: Incompatible types in assignment (expression has type "str", variable has type "int")  [assignment]
    foo: int = "bar"
               ^
Found 1 error in 1 file (checked 91 source files)

git ls-files --exclude='*.py' --ignored -c -z | xargs -0r mypy output (try to reproduce "pre-commit" behaviour):

  • the same as above (the warning reported only once)

Question

How to avoid duplicate warnings and still use Mypy via "pre-commit"?

PS:

Do not propose me to use https://github.com/pre-commit/mirrors-mypy. It is overkill for my huge project to copy/paste manually dozens of my dependencies to the additional_dependencies to create one more virtual environment. These dependencies will be unpinned, and I will need to update them manually every time I add/remove something. It is only a redundant duplicate work. Anyway, I used to use "mirrors-mypy" before, and it stopped to give consistent results with the plain Mypy. This is why I am trying to use Mypy as a local hook. Currently, I simply run plain Mypy in my CI/CD as one job, and the "pre-commit" with all other hooks as a separate job.

Nairum
  • 1,217
  • 1
  • 15
  • 36

2 Answers2

3

It looks like you can use

require_serial: true

in your .pre-commit-config.yaml file for the hooks: section, like this:

- repo: local
  hooks:
    - id: mypy
      name: mypy
      # or `entry: poetry run mypy`, but perhaps both are the same
      entry: mypy
      require_serial: true
      # From the docs it is not clear the difference between `language: python` VS `language: system`
      language: system
      types: [ python ]

That will pass all the necessary files to the mypy in one piece, without splitting the file list into several processes.

Yes, this can cause a longer check, but the output will be clean.

Pycz
  • 366
  • 3
  • 12
0

Do not propose me to use https://github.com/pre-commit/mirrors-mypy.

your entire problem is because you didn't follow this. it configures everything correctly for you and does not have the problems you're seeing. as it is right now you've created a fork bomb and you're linting every file N times (where N is the number of cores you've got) because mypy will follow imports

pre-commit passes a list of positional arguments when executing and by default will split those up to the number of cores on your machine. with a tool like mypy it will follow imports (dynamic analysis) and re-lint everything it follows

I'd really recommend you utilize mirrors-mypy -- or look at the differences between your incorrect configuration and the correct one


disclaimer: I wrote pre-commit

anthony sottile
  • 61,815
  • 15
  • 148
  • 207
  • Using - or encouraging the use of - the `mirrors-mypy` hook for type checking strikes me as a bad idea. Here's why: In essence, `mypy` is an example of the exact same case as `pylint`, in that it is a static analysis tool that takes the context (env) of a project into account. Your example for why a repository local hook can be useful (https://pre-commit.com/index.html#repository-local-hooks) is `pylint`, but it could just as well have been `mypy`. – Harald Husum Aug 29 '23 at 10:05
  • @HaraldHusum the difference is mypy can create a useful result with a subset of the dependencies whereas pylint cannot – anthony sottile Aug 29 '23 at 15:10
  • That might be a difference, yes, but it also feels like a fairly unimportant difference. In my experience, the usefulness of `mypy` is severely limited when not having access to 3rd party types. Using the `mirrors-mypy` hook is surely better than not doing type checking, but If you're going to be using `mypy`, you might as well use it to its full potential, as opposed to the handicapped version that is `mirrors-mypy`. Currently that involves either running `mypy` entirely separately from pre-commit, or, you make a local hook, like OP. There's a great case to be made for the latter. – Harald Husum Aug 30 '23 at 11:23
  • 1
    I'm being slightly unfair to `mirrors-mypy` in claiming it's handicapped. It does have the `additional_dependencies` config option, enabling you to replicate your environment for `mypy`. The problem with this approach is that it forces you to express your dependencies in two distinct places, making it hard to maintain. If it was possible to somehow outsource the specification of `additional_dependencies` to, say, `poetry`, the story around `mirrors-mypy` would be so much more compelling. – Harald Husum Aug 30 '23 at 11:36