Skip to content

Review/reconsider PC140 precommit + mypy #726

Open

Description

@Carreau

I'm in the process of removing/forbiding the run of mypy via pre-commit/hatch on jupyter_client and likely other places as running via pre-commit/hach can (often?) swallow error.

try to clone jupyter client main, currently 0bce36fb216d228d755f6c1ca3473cfb4e0f29a6, and run:

$ hatch run typing:test # noerror
$ pre-commit run --all-files --hook-stage manual mypy  #no error
$ mypy  # this works and detect **real** issues.

My guess is pre-commit/hatch create envs just to run those tools, and if something goes wrong and mypy run in those envs, it may find the wrong version of your package.

Activity

  1. Carreau commented on Dec 11, 2025

    @Carreau
    MemberAuthor
  2. henryiii commented on Dec 11, 2025

    @henryiii
    Collaborator

    You should always run mypy in a typing environment. Typing dependencies are not runtime dependencies. Sometimes they are, but sometimes they are not. That's not the problem.

    Your hatch task literally just runs pre-commit. So it's not anything to do with hatch, you are just running pre-commit inside hatch.

    When you are running mypy without pre-commit, you probably forgot to install the dependencies. If I do that, I get lots of errors, but they all come from missing traitlets ipykernel jupyter_core, which are installed by the pre-commit hook, or types-pexpect types-paramiko types-netifaces types-psutil types-python-dateutil, which are installed by --install-types in the pre-commit hook (though you really should list them explicitly and not use that flag, it will cache better). These are not runtime dependencies, by the way.

    If I do all that, it passes, just like the pre-commit hook. So then enforces that you should always run it in pre-commit.

    $ gh repo clone jupyter/jupyter_client
    $ cd jupyter_client
    $ uv venv
    $ . .venv/bin/activate.fish
    $ uv pip install mypy==1.18.2
    $ uv pip install traitlets ipykernel jupyter_core
    $ uv pip install types-pexpect types-paramiko types-netifaces types-psutil types-python-dateutil
    $ mypy
    Success: no issues found in 38 source files
  3. Carreau commented on Dec 11, 2025

    @Carreau
    MemberAuthor

    I do have those dependencies installed; and I tried to running it directly i hatch as well (with local modifs), I am aware that that hatch calls pre-commit I find it horrendous to nest that many env but anyway, I was not the one who made those choices). Running via pre-commit did miss a typo in a PR (options not option), and I do not really understand why/when pre-commit swallows errors but it sometime (often) does.

    I agree that we should run mypy, but enforcing it via pre-commit seem weird.

  4. Carreau commented on Dec 11, 2025

    @Carreau
    MemberAuthor

    Here is the specific commit PR where mypy was not failing because of pre-commit:

    jupyter_client[8e73d8af]  $ pre-commit run --all-files --hook-stage manual mypy
    mypy.....................................................................Passed
    jupyter_client[8e73d8af]  $ mypy
    jupyter_client/session.py:133: error: Incompatible types in assignment (expression has type "None", variable has type Module)  [assignment]
            orjson = None
                     ^~~~
    jupyter_client/session.py: note: In function "orjson_packer":
    jupyter_client/session.py:142: error: Unexpected keyword argument "options" for "dumps"; did you mean "option"?  [call-arg]
                    return orjson.dumps(obj, default=json_default, options=options)
                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    jupyter_client/session.py: note: At top level:
    jupyter_client/session.py:157: error: Skipping analyzing "msgpack": module is installed, but missing library stubs or py.typed marker
    [import-untyped]
            import msgpack  # type:ignore[import-not-found]
        ^
    jupyter_client/session.py:157: note: Error code "import-untyped" not covered by "type: ignore" comment
    jupyter_client/session.py:157: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
    
  5. henryiii commented on Dec 11, 2025

    @henryiii
    Collaborator

    We don't "enforce it" via pre-commit, we just have a check for that which you can skip (with a description these days, too!). If we could find it reliable in tox/nox/hatch, then we could also check for that, but I don't know of a good way to do that reliably.

    You are missing msgpack locally, does installing that change things? Missing dependencies cause other failures that look unrelated. Is there a way I can find that commit, is there a link to the PR? I don't see orjson in the code. By the way, IIRC mypy runs faster if orson is installed.

  6. henryiii commented on Dec 11, 2025

    @henryiii
    Collaborator

    I found the PR at jupyter/jupyter_client#1064, pre-commit is working perfectly.

    jupyter_client/session.py:131:    import orjson  # type:ignore[import-not-found]
    

    That import tells mypy to treat orjson as Any if it's not installed. It wasn't added to the pre-commit additional_dependencies, so it is Any, and can't tell that there's a mistake. Same happens locally if you don't install orjson. Don't ever do that - add it to pyproject.toml if you have to, that is, it doesn't support typing and you don't want to write stubs.

  7. henryiii commented on Dec 11, 2025

    @henryiii
    Collaborator

    I'd love improvements to the experience here, there are some quirks:

    • You need to specify args (even if empty), because the mypy hook has the ignore imports flag (terrible). MyPy could offer their own hook to fix this, I think.
    • You can't pull dependencies from pyproject.toml; I understand that pre-commit wants to stay general (and the author hates pyproject.toml, and toml in general) - I think prek might fix this eventually though!
    • You can't lock anything other than mypy and get auto-updates; another thing I think prek might fix eventually

    But you do want a controlled environment for mypy, it's not something you should run in a normal environment.

    That said, I use mypy in pre-commit in almost every project I work on, including packaging, pybind11, scikit-build-core, cibuildwheel, pipx, check-sdist, nox, boost-histogram, hist, flake8-errmsg, plumbum, and others, and it works perfectly and reliably, never missing anything that a manual run would not.

  8. Carreau commented on Dec 12, 2025

    @Carreau
    MemberAuthor

    The commit is part of jupyter/jupyter_client#1064
    If you think there are really no issues, feel free to close. It's just not the first time for me that I end up with issue with mypy + precommit (and yes it seems to works great like 99% of the time)

  9. mfisher87 commented on Feb 28, 2026

    @mfisher87
    Contributor

    You can't pull dependencies from pyproject.toml; I understand that pre-commit wants to stay general (and the author hates pyproject.toml, and toml in general) - I think prek might fix this eventually though!

    This is my biggest reason I never run mypy with pre-commit. Duplicating dependencies between pyproject.toml and .pre-commit.config.yaml is something I'll inevitably screw up. I never never never want to manually keep things in sync :) I also have high hopes for prek solving this problem, but until then my ideal workflow is typechecking in GitHub actions and manually with uv run --only-dev mypy. I would like to not have to think about what dependency groups I need for running a task, and I'm hoping uv provides pixi-like task running functionality to solve this as well.

    My other reason to avoid pre-commit for mypy is that it isn't fast enough for something that happens at commit time my taste, but that's subjective. I'm hoping ty solves this one for me ;)

  10. henryiii commented on Feb 28, 2026

    @henryiii
    Collaborator

    You don't necessarily have these dependencies in pyproject.toml, though. Typing dependencies are not the same as runtime dependencies, and putting them in dev will require uv to solve them together (IIRC there is a way to tell uv to not solve all dependency groups together, but it's something you need to configure. For example, types-PyYAML is only a typing dependency of this project (sp-repo-review), not a runtime dependency. So it is only in .pre-commit-config.yaml.

    I find mypy fast enough for pre-commit after it runs the first time. Once ty is stable enough to have a pre-commit hook, I'd be happy to have it checked here too. :)

  11. henryiii commented on Feb 28, 2026

    @henryiii
    Collaborator

    I'd be interested in allowing alternatives if there's some way to tell they are there. Something like detecting a nox/tox job for typing isn't reliable. I think currently the best way is for a user to have this in their pyproject.toml:

    [tool.repo-review.ignore]
    PC140 = "Using `uv run --only-dev mypy` to run mypy"

    That indicates that it's supported, just via a different mechanism.

  12. mfisher87 commented on Mar 1, 2026

    @mfisher87
    Contributor

    You don't necessarily have these dependencies in pyproject.toml, though. Typing dependencies are not the same as runtime dependencies, and putting them in dev will require uv to solve them together (IIRC there is a way to tell uv to not solve all dependency groups together, but it's something you need to configure. For example, types-PyYAML is only a typing dependency of this project (sp-repo-review), not a runtime dependency. So it is only in .pre-commit-config.yaml.

    Yeah, understood. I didn't meant to communicate my typing dependencies would always be in my dev group, just that I would prefer to manage my dependencies using groups and not have to sync anything to .pre-commit-config.yaml. I would replace --only-dev with --no-dev --group typing or something to solve that problem!

  13. mfisher87 commented on Mar 3, 2026

    @mfisher87
    Contributor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions