Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore typing errors in individual blocks #6948

Open
exhuma opened this issue Jun 7, 2019 · 16 comments
Open

Ignore typing errors in individual blocks #6948

exhuma opened this issue Jun 7, 2019 · 16 comments
Labels
feature topic-type-ignore # type: ignore comments

Comments

@exhuma
Copy link

exhuma commented Jun 7, 2019

It is already possible to silence errors using # type: ignore on individual lines, and - since #626) also for a whole file.

Additionally it would be nice to ignore errors on individual blocks. Especially on a class/function level when abstracting away an untyped library. For example:

class MyAbstraction:

    def __init__(self, url: str) -> None:
        self.hostname = url.host  # <-- I want this to be caught by the type-checker

    def some_call(self) -> int:
        # type: ignore
        result = api.untyped_call()
        return result

    def another_large_call(self) -> int:
        # type: ignore
        result_1 = api.untyped_call_1()
        result_2 = api.untyped_call_2()
        result = result_1 + result_2
        return result

When running mypy in "strict" mode the code will be littered with messages like Call to an untyped function ... in a typed context. Currently the only options are:

  • Add # type: ignore on each line causing errors by the external library
    • Disadvantage: Cumbersome (many type-comments needed)
    • Advantage: Only the problematic calls are silenced
  • Add # type: ignore to the file
    • Disadvantage: Typing errors unrelated to the external library become invisible
    • Advantage: Only one line to add

The second option is best used if a module contains only calls to the external library. For new projects this is doable, but older/larger code-bases often need some refactoring for this. And this can be error-prone and seems risky to do only for type-hinting.

Having the option to disable errors on either a class-level or block-level would add a lot of flexibility.

@brandtbucher
Copy link
Member

brandtbucher commented Jun 7, 2019

See #6830 (the PR that closed #626). It’s not currently possible to get the line number of the colon that immediately precedes an indented block from the AST alone... at least not without resorting to error-prone guessing. We can't reliably tell the difference between this:

def f() -> (
    None
):  # type: ignore
    ...

And this:

def f() -> (
    None):
    # type: ignore
    ...

So I think block-scoped ignores using a # type: ignore comment on the first line are not very likely. Perhaps there is another way, like @no_type_check, but with preserved annotations in the function signature.

I wanted this also, but I don't think it's worth changing mypy's source parsing 🙁.

@JelleZijlstra
Copy link
Member

You can use the @typing.no_type_check decorator for this.

@rwarren
Copy link

rwarren commented May 7, 2020

@typing.no_type_check works perfectly (thanks @JelleZijlstra).

Why is this issue still open?

@JelleZijlstra
Copy link
Member

The original issue asked for something slightly different (block-scoped type-ignore), which is a bit different from @no_type_check, which is always function-scoped.

I'm not sure block-scoped type-ignores are all that necessary, but it's a reasonable request.

@leonardoramirezr
Copy link

Try with:

from typing import Any, cast

import api as untype_api # It is import to use a different name to untyped module
api = cast(Any, untype_api)

class MyAbstraction:
     def some_call(self) -> int:
        result = api.untyped_call() # this shouldn't show type warnings.
        return result

@jentyk
Copy link

jentyk commented Mar 3, 2021

It would be nice if @typing.no_type_check would be also a class decorator

@kamilturek
Copy link
Contributor

kamilturek commented Mar 3, 2021

It would be nice if @typing.no_type_check would be also a class decorator

@jentyk According to the docs it should work as well as a class decorator, but it isn't apparently true.
https://docs.python.org/3/library/typing.html#typing.no_type_check

Mentioned here #607.

@analog-cbarber
Copy link
Contributor

pylint supports this type of pragma, so it is clearly theoretically possible.

@analog-cbarber
Copy link
Contributor

Also no_type_check only takes a single argument for the decorated object. There is no way to limit the ignore to one particular class of error. For example. there is no way to apply

# type: ignore[misc]

to a function. You have to ignore everything or write a separate comment for each instance. And if you add a new decorator for this purpose, then using it would mean adding a runtime dependency to mypy just for that.

@AlexWaygood AlexWaygood added feature topic-type-ignore # type: ignore comments labels Apr 4, 2022
@filips123
Copy link

What about introducing two comments to disable and enable specific rule(s) between them? For example:

class MyAbstraction:
    def __init__(self, url: str) -> None:
        self.hostname = url.host

    def large_call(self) -> int:
        # type: off[no-untyped-call]
        result_1 = api.untyped_call_1()
        result_2 = api.untyped_call_2()
        result_3 = api.untyped_call_2()
        result_4 = api.untyped_call_2()
        result = result_1 + result_2 + result_3 + result_4
        return result
        # type: on[no-untyped-call]

This is also similar to formatters such as Black which uses # fmt: off and # fmt: on comments. It's still not disabling specific rule for a whole block with one comment, but two comments (at the start and the end) for a function are still better than having to put a comment on every line with typing error, especially on larger functions.

@etandel
Copy link

etandel commented Sep 1, 2022

What about having something like typing.no_type_check() be usable as a context manager?
For instance:

import typing


class MyAbstraction:
    ...

    def another_large_call(self) -> int:
		x : int = f()  # This is checked

        with typing.no_type_check: # no lines inside this context are checked
	        result_1 = api.untyped_call_1()
	        result_2 = api.untyped_call_2()
	        result = result_1 + result_2

		y: str = g() # this is also checked
        return result

@exhuma
Copy link
Author

exhuma commented Sep 15, 2022

I don't like the context-manager idea. Type-hints are just hints with no behaviour during runtime. They are PEP-3107 "annotations". The with block is a runtime construct. And mixing this feels wrong. The annotations can be extracted by static analysis tools but should have no impact on the runtime. When we add a "with" block they will trigger runtime behaviour. Even if typing.no_type_check is part of the standard library it could still cause unexpected side-effects during runtime.

I initially thought about a with block as well, as it just "looks right" when looking at the code. But I dismissed it because it mixes runtime with static-analysis.

This is a "gut-feeling" and may be subjective as I have no concrete proof that this can cause problems. If there are any, I'd be happy to learn.

@anentropic
Copy link

anentropic commented May 19, 2023

Another case...

I have code like this:

class TurnLogAdmin(ModelView, model=TurnLog):
    column_list = [
        TurnLog.id,
        TurnLog.turn_id,
        TurnLog.timestamp,
        TurnLog.key,
    ]  # type: ignore

Due to issues between SQLModel (which mashes SQL Alchemy and Pydantic models together, resulting in difficult typing situation) and sqladmin (whose typings expect SQL Alchemy model fields here)... mypy generates a type error for each element of the list:

src/server/app.py:60: error: List item 0 has incompatible type "Optional[int]"; expected "Union[str, InstrumentedAttribute]"  [list-item]
src/server/app.py:61: error: List item 1 has incompatible type "int"; expected "Union[str, InstrumentedAttribute]"  [list-item]
src/server/app.py:62: error: List item 2 has incompatible type "datetime"; expected "Union[str, InstrumentedAttribute]"  [list-item]

I was hoping the # type: ignore on the last line would cover the whole list literal, but it has no effect. Since there is no block-ignore syntax it seems like I need an ignore comment for each line individually.

FWIW #6648 and #1032 seem to intend that the ignore comment would cover the whole expression

but perhaps multi-line list literal is a different construct? (I'm not sure exactly what does and doesn't count as an 'expression' ... https://docs.python.org/3/reference/expressions.html seems to include list literals though)

@ilevkivskyi
Copy link
Member

For those who wants this for the --disallow-untyped-calls problems: #15845 added a new flag to not show this error for calls to functions/methods from specific packages/modules/classes (will be available in next mypy release).

@MattSom
Copy link

MattSom commented Jan 17, 2024

A solution for blocks or class annotation would come in handy. Mypy can't handle things going on inside django ORM constraint definitions.

@GhostOps77
Copy link

GhostOps77 commented Apr 14, 2024

It would be great if there's start and stop comments to pause mypy's type check, like

# mypy: off
...
# mypy: on

cuz i do need to exclude only a block of code (i.e., an if block)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature topic-type-ignore # type: ignore comments
Projects
None yet
Development

No branches or pull requests