pip icon indicating copy to clipboard operation
pip copied to clipboard

Mypy and the `--no-implicit-optional` option

Open DiddiLeija opened this issue 4 years ago • 23 comments

Overview

This is the second part of the discussion from #10065 (review).

Description

I have been working on converting the type hint commentaries into proper annotations (#10004, #10018, #10065, #10074). But I had a simple mistake on #10065 by annotating this:

        download_dir: str = None

when it must be annotated like this:

        download_dir: Optional[str] = None

We expected that Mypy was going to fail, but it just passed by. Then, we saw that Mypy accepts implicit optional annotations, until we use --no-implicit-optional.

What we want

We want Mypy to use --no-implicit-optional on its checks, to make a better diagnosis. View the PR review where the original discussion is for more information.

Code of Conduct

DiddiLeija avatar Jun 17 '21 15:06 DiddiLeija

I opened the issue here because it is not exactly a Mypy problem. It is related to the way we use it on pypa/pip checks.

DiddiLeija avatar Jun 17 '21 15:06 DiddiLeija

Yea, we have to enable a bunch of mypy's strictness flags (this is probably the highest impact among those).

The ideal goal would be to get to a point where we can run mypy --strict . and have everything pass.

pradyunsg avatar Jun 18 '21 10:06 pradyunsg

The ideal goal would be to get to a point where we can run mypy --strict . and have everything pass.

How can we make that for the CI checks?

DiddiLeija avatar Jun 20 '21 17:06 DiddiLeija

How can we make that for the CI checks?

Sorry, I found it. We are already using options for Mypy checks (--pretty). I will submit a PR to add a --strict option to Mypy.

DiddiLeija avatar Jun 21 '21 13:06 DiddiLeija

Can I self-assign this, as I created the related pull request?

DiddiLeija avatar Jul 24 '21 22:07 DiddiLeija

Assigned

uranusjr avatar Jul 25 '21 02:07 uranusjr

Hi, do we still wanna get this done? You know, we couldn't apply #10091, but maybe we can try again :)

Thoughts @pypa/pip-committers?

DiddiLeija avatar Jul 08 '22 16:07 DiddiLeija

I think so -- ideally, we'd be passing --strict to mypy.

pradyunsg avatar Jul 08 '22 16:07 pradyunsg

As of c260ecc3e3e836b4d0dcfd56e1c365e40421ac6e, running pre-commit run --all-files mypy (with --strict added to mypy's arguments in pre-commit-config.yaml results in:

Found 185 errors in 73 files (checked 288 source files)

I'd like to fix all of those, TBH, so that we're getting the benefits of the complete checks/protections that mypy can provide.

pradyunsg avatar Jul 08 '22 16:07 pradyunsg

Sounds good to me. We once tried to add --strict, but it crashed with vendored code. Anyway, if we can either fix or ignore that, then I'm a +1 to adding the --strict flag.

DiddiLeija avatar Jul 08 '22 16:07 DiddiLeija

There are lots of errors cs mypy cant use annotations of 3rd party libraries (like requests) as they are imported from _vendor and its annoying

93578237 avatar Aug 04 '22 14:08 93578237

FYI. You can configure mypy (in pyproject.toml/setup.cfg etc. ) to ignore certain errors in certain packages. https://mypy.readthedocs.io/en/stable/config_file.html?highlight=pyproject%20toml#examples

This what we do in airflow for one (and I believe vendored-in code should be minimally modified if ever so probably it's better to ignore those in vendored packages, rather than fix them).

# Let's assume all google.cloud packages have no implicit optional
# Most of them don't but even if they do, it does not matter
[mypy-google.cloud.*]
no_implicit_optional = False

My 3 cents.

potiuk avatar Aug 04 '22 14:08 potiuk

I try to adapt strict=True. I also need types for requests, so additional dependency types-requests. Maybe vendor types-requests or rewrite imports in the pip codebase.

93578237 avatar Aug 04 '22 15:08 93578237

It does not recognize requests.Response as it was imported from _vendor.requests. So the solution would be adding #type: ignore comments to the function signatures and so on. Somewhere you can use TYPE_CHECKING to import from requests.

93578237 avatar Aug 04 '22 15:08 93578237

I'm not sure I follow what you're seeing. Could you point to a commit that shows the behaviour that you're seeing?

pradyunsg avatar Aug 04 '22 18:08 pradyunsg

it's better to ignore those in vendored packages

That defeats the whole point of having stubs in place for vendored modules -- we want to have the type from these modules and want the type checking benefits that come from that, while ignoring errors in only the vendored source files (ie. in pip._vendor). We've got the appropriate mypy configuration to achieve that.

We also have redirects for the vendored modules, via stub files generated for them. If that isn't working for some reason, I'd like to get clearer details on what that looks like that's more detailed than "there are lots of errors". :)

pradyunsg avatar Aug 04 '22 18:08 pradyunsg

it's better to ignore those in vendored packages

That defeats the whole point of having stubs in place for vendored modules -- we want to have the type from these modules and want the type checking benefits that come from that, while ignoring errors in only the vendored source files (ie. in pip._vendor). We've got the appropriate mypy configuration to achieve that.

You probably misunderstood what I was explaining. I was only referring to "no_implicit_option" case. (and this is precisely what we are ignoring in the example).

This particular setting is quite problematic and disabling it has benefits in this case. Because when the 3rd-party library has it set to False and your code has it set to true (like it is in this case and is in cae of google packages I mentioned) it's the easiest way to reap all the benefits of the vendored-in code without changing it. In the case I described, Google libraries were built with "no_implicit_optional=False" and our code with = True, setting this (and only this) setting for Google packages allowed us to still get all the Static Typing benefitsm while ignoring problem that Google code had parameter: String = None rather than parameter: Optional[String]=None in hundreds of methods.

MyPy in this case is still able to correclty infer the parameter type, but simply does not complain.

potiuk avatar Aug 04 '22 18:08 potiuk

That option is a subset of strict (which is what the rest of the discussion is about) and, as I said, we have mypy configured to not report errors about vendored code but to pick up annotations appropriately.

pradyunsg avatar Aug 04 '22 19:08 pradyunsg

@pradyunsg for example reveal_type for Response in network/utils.py. it would be pip._vendor.requests.models.Response so on line https://github.com/pypa/pip/blob/90665842980ba25ecbec68f48037e1dd03661378/src/pip/_internal/network/utils.py#L43 it would raise Unsupported operand types for <= ("int" and "None") because mypy does not know about types-requests stubs, but this works

if TYPE_CHECKING:
    from requests.models import CONTENT_CHUNK_SIZE, Response

93578237 avatar Aug 05 '22 11:08 93578237

How are you running mypy? I reckon this is because we don’t have any types-requests in https://github.com/pypa/pip/blob/main/.pre-commit-config.yaml#L47

pradyunsg avatar Aug 05 '22 12:08 pradyunsg

Via pre-commit. I've added types-requests=2.28.1 in additional-dependencies

93578237 avatar Aug 05 '22 12:08 93578237

src/pip/_internal/network/utils.py:45: note: Revealed type is "pip._vendor.requests.models.Response"

Hmm... I think there's something more happening here.

pradyunsg avatar Aug 05 '22 12:08 pradyunsg

It happens because of imports. Vendor pyi or rewrite imports

93578237 avatar Aug 05 '22 12:08 93578237

Renamed this thread, since we're now discussing to add --strict :upside_down_face:

DiddiLeija avatar Dec 20 '22 20:12 DiddiLeija

Here's a checklist ordered by benefit:

  • [ ] Enable strict optional checking everywhere because we're in the 2020s (grep for # mypy: strict-optional)
    • https://github.com/pypa/pip/pull/11379
    • https://github.com/pypa/pip/pull/11382
    • https://github.com/pypa/pip/pull/12091
    • https://github.com/pypa/pip/pull/12295
    • src/pip/_internal/locations/_distutils.py
    • src/pip/_internal/operations/prepare.py
    • src/pip/_internal/cli/cmdoptions.py
  • [x] Opt in to most strictness checks https://github.com/pypa/pip/pull/12209
  • [ ] Upgrade mypy https://github.com/pypa/pip/pull/12293
  • [x] Avoid follow_imports=skip for vendored code https://github.com/pypa/pip/pull/12294
  • [ ] Get pre-commit to be strict about missing imports
  • [x] Use --no-implicit-optional https://github.com/pypa/pip/pull/11374

I think once these are done, pip will be in a great place, with much lower marginal benefit to any additional type checker configuration changes

hauntsaninja avatar Sep 24 '23 00:09 hauntsaninja