-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
require Generator for contextmanager
#7430
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
just to gauge impact here, while there's a lot of repositories with this mistake, the fix is pretty easy. here's black for instance: $ git diff | cat
diff --git a/src/black/__init__.py b/src/black/__init__.py
index c4ec99b..e6f4e80 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -19,7 +19,6 @@ from typing import (
Any,
Dict,
Generator,
- Iterator,
List,
MutableMapping,
Optional,
@@ -1405,7 +1404,7 @@ def assert_stable(src: str, dst: str, mode: Mode) -> None:
@contextmanager
-def nullcontext() -> Iterator[None]:
+def nullcontext() -> Generator[None, None, None]:
"""Return an empty context manager.
To be used like `nullcontext` in Python 3.7.
diff --git a/src/blib2to3/pgen2/driver.py b/src/blib2to3/pgen2/driver.py
index 8fe8206..5f8a905 100644
--- a/src/blib2to3/pgen2/driver.py
+++ b/src/blib2to3/pgen2/driver.py
@@ -24,12 +24,12 @@ import sys
from typing import (
Any,
cast,
+ Generator,
IO,
Iterable,
List,
Optional,
Text,
- Iterator,
Tuple,
TypeVar,
Generic,
@@ -66,7 +66,7 @@ class TokenProxy:
self._release_ranges: List[ReleaseRange] = []
@contextmanager
- def release(self) -> Iterator["TokenProxy"]:
+ def release(self) -> Generator["TokenProxy", None, None]:
release_range = ReleaseRange(self._counter)
self._release_ranges.append(release_range)
try:
diff --git a/src/blib2to3/pgen2/parse.py b/src/blib2to3/pgen2/parse.py
index a9dc11f..f7c31bf 100644
--- a/src/blib2to3/pgen2/parse.py
+++ b/src/blib2to3/pgen2/parse.py
@@ -17,13 +17,13 @@ from . import grammar, token, tokenize
from typing import (
cast,
Any,
+ Generator,
Optional,
Text,
Union,
Tuple,
Dict,
List,
- Iterator,
Callable,
Set,
TYPE_CHECKING,
@@ -72,7 +72,7 @@ class Recorder:
return self._dead_ilabels.symmetric_difference(self._ilabels)
@contextmanager
- def switch_to(self, ilabel: int) -> Iterator[None]:
+ def switch_to(self, ilabel: int) -> Generator[None, None, None]:
with self.backtrack():
self.parser.stack = self._points[ilabel]
try:
@@ -83,7 +83,7 @@ class Recorder:
self.parser.stack = self._start_point
@contextmanager
- def backtrack(self) -> Iterator[None]:
+ def backtrack(self) -> Generator[None, None, None]:
"""
Use the node-level invariant ones for basic parsing operations (push/pop/shift).
These still will operate on the stack; but they won't create any new nodes, or |
Arguably the mypy docs would also have to be updated:
There's also similar guidance in the CPython docs for |
I don't think either of those need updates -- this is specifically for context managers (the problem here is a |
I know this is quite controversial, but I would like to see this go in rather sooner than later. The longer we wait the worse it gets, and our stubs are arguably wrong. |
I also agree we should do this, but it's definitely going to be quite disruptive. mypy-primer is only going to show a fraction of the real-world code that needs to be changed. I agree with @AlexWaygood that we should call this out explicitly in the mypy and typing docs; @asottile do you think it's possible to provide a codemod that fixes these annotations? |
^I started typing out a response, but (as is so often the case) @JelleZijlstra beat me to it, and I agree with everything he said :) |
here's the start of one, based on the approach outlined here (same as from __future__ import annotations
import argparse
import ast
import sys
from typing import Sequence
import tokenize_rt
def is_ctxmanager(node: ast.AST, name: str) -> bool:
return (
isinstance(node, ast.Name) and node.id == name
) or (
isinstance(node, ast.Attribute) and
node.attr == name and
# simple name, might be an aliased import :shrugs:
isinstance(node.value, ast.Name)
)
def is_iterator(node: ast.AST, name: str) -> bool:
return (
isinstance(node, ast.Subscript) and (
(
isinstance(node.value, ast.Name) and
node.value.id == name
) or (
isinstance(node.value, ast.Attribute) and
node.value.attr == name and
# simple name, might be an aliased import :shrugs:
isinstance(node.value.value, ast.Name)
)
)
)
class Visitor(ast.NodeVisitor):
def __init__(self) -> None:
self.iter_to_generator: set[tokenize_rt.Offset] = set()
self.aiter_to_agenerator: set[tokenize_rt.Offset] = set()
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
if (
len(node.decorator_list) == 1 and
is_ctxmanager(node.decorator_list[0], 'contextmanager') and
node.returns is not None and
is_iterator(node.returns, 'Iterator')
):
offset = tokenize_rt.Offset(
node.returns.lineno,
node.returns.col_offset,
)
self.iter_to_generator.add(offset)
self.generic_visit(node)
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
if (
len(node.decorator_list) == 1 and
is_ctxmanager(node.decorator_list[0], 'asynccontextmanager') and
node.returns is not None and
is_iterator(node.returns, 'AsyncIterator')
):
offset = tokenize_rt.Offset(
node.returns.lineno,
node.returns.col_offset,
)
self.aiter_to_agenerator.add(offset)
self.generic_visit(node)
def _fix(
i: int,
tokens: list[tokenize_rt.Token],
n: int,
orig: str,
dest: str,
) -> None:
j = i
try:
while tokens[i].src != orig:
i += 1
except Exception:
breakpoint()
raise
tokens[i] = tokens[i]._replace(src=dest)
while tokens[i].src != '[':
i += 1
i += 1
depth = 1
while depth:
if tokens[i].src in '([{':
depth += 1
elif tokens[i].src in ')]}':
depth -= 1
i += 1
tokens[i - 1] = tokens[i - 1]._replace(src=f'{", None" * n}]')
def _fix_file(filename: str) -> int:
with open(filename, 'rb') as fb:
contents_bytes = fb.read()
try:
contents_text_orig = contents_text = contents_bytes.decode()
except UnicodeDecodeError:
print(f'{filename} is non-utf-8 (not supported)')
return 1
tree = ast.parse(contents_text, filename=filename)
visitor = Visitor()
visitor.visit(tree)
if not visitor.iter_to_generator and not visitor.aiter_to_agenerator:
return 0
tokens = tokenize_rt.src_to_tokens(contents_text)
for i, token in tokenize_rt.reversed_enumerate(tokens):
if token.offset in visitor.iter_to_generator:
_fix(i, tokens, n=2, orig='Iterator', dest='Generator')
elif token.offset in visitor.aiter_to_agenerator:
_fix(i, tokens, n=1, orig='AsyncIterator', dest='AsyncGenerator')
contents_text = tokenize_rt.tokens_to_src(tokens)
if contents_text == contents_text_orig:
return 0
print(f'Rewriting {filename}')
with open(filename, 'w', encoding='UTF-8', newline='') as f:
f.write(contents_text)
return 1
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv)
if sys.version_info < (3, 10):
raise SystemExit(
'this script makes some assumptions about ast.Subscript and '
'needs python 3.10+',
)
ret = 0
for filename in args.filenames:
ret |= _fix_file(filename)
return ret
if __name__ == '__main__':
raise SystemExit(main()) it doesn't fix the imports, but that's tricky to do accurately and with respecting the user's style import contextlib
from contextlib import contextmanager
from contextlib import asynccontextmanager
from typing import AsyncIterator, Iterator
import typing
import typing as t
def fn_unrelated() -> Iterator[None]:
yield
@contextlib.contextmanager
def ctx1() -> Iterator[None]:
yield
@contextmanager
def ctx2() -> Iterator[None]:
yield
@contextlib.asynccontextmanager
async def ctx3() -> AsyncIterator[None]:
yield
@asynccontextmanager
async def ctx4() -> AsyncIterator[None]:
yield
@contextlib.contextmanager
def ctx5() -> typing.Iterator[None]:
yield
@contextlib.contextmanager
def ctx6() -> t.Iterator[None]:
yield $ diff -u t2.py.bak t2.py
--- t2.py.bak 2022-03-03 14:24:22.591697064 -0500
+++ t2.py 2022-03-03 14:42:18.439722185 -0500
@@ -10,25 +10,25 @@
yield
@contextlib.contextmanager
-def ctx1() -> Iterator[None]:
+def ctx1() -> Generator[None, None, None]:
yield
@contextmanager
-def ctx2() -> Iterator[None]:
+def ctx2() -> Generator[None, None, None]:
yield
@contextlib.asynccontextmanager
-async def ctx3() -> AsyncIterator[None]:
+async def ctx3() -> AsyncGenerator[None, None]:
yield
@asynccontextmanager
-async def ctx4() -> AsyncIterator[None]:
+async def ctx4() -> AsyncGenerator[None, None]:
yield
@contextlib.contextmanager
-def ctx5() -> typing.Iterator[None]:
+def ctx5() -> typing.Generator[None, None, None]:
yield
@contextlib.contextmanager
-def ctx6() -> t.Iterator[None]:
+def ctx6() -> t.Generator[None, None, None]:
yield |
Seems like I'm in the minority, but I'm -1 on this. Breaking almost every use case of contextmanager for something pretty niche is too much for me. Other than your reports, I haven't encountered instances of people running into this. There are zero emojis on the issue report. There are two out links, neither of which seems to actually have the bug in question. Maybe a failure of my imagination, but I'm also struggling to think of the real world code that motivates someone to return an iterator here. On the other hand, it's very common for users with correct code to get confused when adding types for Iterable, Iterator, Generator. By steering users to use the scary and not ergonomic Generator, this is a real loss of usability in the 99.99% use case going forward as well. There's a reason there's so much code out there that this would break. |
It's a shame we don't have something like the following construct in _T_co = TypeVar("_T_co", covariant=True)
SimpleGenerator = Generator[_T_co, None, None] Returning |
it would be a bit nicer if we had TypeVar defaults so we could just write |
Also note that a linter could warn for contextmanager decorated functions that don't yield. Could be a good check to add to pylint or flake8-bugbear. Since this wouldn't look at annotations, it'd side step the issue of forcing users to change their annotations. |
the case I had would not have been detected by that -- it returned an iterable from another function call. I think this really needs type checkers to do the proper thing |
@asottile I'm not sure I understand. If you have a function like this: @contextmanager
def func():
return other_generator_function() Then a flake8 plugin could easily require that you do this instead, ensuring type safety: @contextmanager
def func():
yield from other_generator_function() It doesn't need to do any fancy guesswork about whether We can agree that this would definitely be an imperfect solution, but it would be far less disruptive. |
I prefer Another possible solution would be mypy automatically inferring the correct return type for functions that |
But it's also confusing. A generator function return a generator after all (which happens to be an iterator as well). |
You really shouldn't have to care. The type annotation in this case should IMO be something simple that doesn't get in the way, not something full of |
I'm still in favor of allowing |
@asottile could you share more thoughts on why your case couldn't be caught by a simple and much less disruptive new linter check? It's unclear to at least Alex and I, see #7430 (comment) |
@hauntsaninja I addressed that here: #7430 (comment) |
additionally, the recommendation also does not work for async where there's no |
This comment has been minimized.
This comment has been minimized.
This comment was marked as outdated.
This comment was marked as outdated.
When changes are really disruptive, like python#7430, we truncate output. Mention this, so that it's known that the change is even more disruptive than it might appear.
FWIW, I'm strongly against this change, mostly because of the confusing
In my opinion, this would a requirement to (potentially) make this a net improvement. If we could write
To be honest, I think that we should be more principled than this in typeshed, since the number of impacted users can be very large. If 1000 projects that use type checking each need to spend 15 minutes to fix their codebase, that's 250 hours lost. |
fwiw, one outage easily costs more than that |
Yeah, it can cost more than than, but not every outage will be this expensive? It seems hard to estimate the likelihood of a change like this preventing an outage, though -- or is there an actual outage you're familiar with? The likelihood of many users having to update code is very high, since I have an alternative proposal that would mostly solve the original issue and wouldn't require a lot of work from end users: special case the Type checkers would allow these: @contextmanager
def f() -> Iterator[None]:
yield None
@contextmanager
def f() -> Generator[None, None, None]:
return <some generator> But this wouldn't pass, since the decorated function isn't a generator function: @contextmanager
def f() -> Iterator[None]:
return iter(...) |
this particular typing wart has already cost 3x more than your estimates above just from my experiences, and I'm sure I'm not alone in this. I also believe you're over-estimating the fix cost here, it is a simple find and replace to resolve the issue for the easiest case (and points out real bugs in the not-easiest case!). the typeshed stubs-no-longer-included migration easily dwarfs this in toil to mypy users. this is an exceptionally easy mistake to make even above while arguing against the change!. and again, your suggestion won't help anything except for the most basic situation (which could already be detected by a linter) my situation looks roughly like: async def other_funtion_which_is_async_generator() -> AsyncIterator[...]:
if some_precondition:
return [...] # bug was here
elif ...:
yield ...
return ...
yield ...
return ...
@asynccontextmanger
async def ctx() -> AsyncIterator[...]:
if some_complicated_condition:
return other_function_which_is_async_generator()
elif other_condition:
async with ..."
yield ...
return
yield ...
return unless your suggestion is to transparently change all "returns iterator but is a generator" functions from: def f() -> Iterator[T]:
yield ... to def f() -> Generator[T, None, None]:
yield ... then I don't think it will help (and even then I'm not sure it helps my case given it's not yielding on all branches but returning another function) |
@asottile Thanks for the additional context! This change now makes more sense to me.
Generally imports need to be adjusted as well to update code, which is somewhat hard to to do using find/replace tools. I agree that the fix is quite easy, but figuring out what needs to be done, and investigating whether this is a false positive or a mypy issue + the required context switch, creating a PR/change request, possibly getting it reviewed, waiting for CI, etc. is included in my 15 minute estimate (which is still pretty arbitrary). To reduce the toil, we could add a dedicated error message about the non-Generator return type with clear instructions about what needs to be done. Providing a tool to automatically upgrade simple cases in affected code would further help, if it could also update imports.
Yes, we knew that to have a big cost. We decided to move forward with it since not doing it risked making every future mypy upgrade painful for larger projects (mainly due to requiring all third-party stubs to be upgraded in lockstep with mypy). We try to avoid similar big breakages whenever there is a good alternative. That's why I'm trying to figure out if there is something less disruptive we could do here. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
Thanks for considering this change. I came here after learning that I have to wrap async generators in an Unfortunately,
|
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
Diff from mypy_primer, showing the effect of this PR on open source code: vision (https://github.com/pytorch/vision)
+ torchvision/datasets/imagenet.py:127: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[str]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ torchvision/datasets/imagenet.py:142: error: Need type annotation for "tmp_dir" [var-annotated]
pegen (https://github.com/we-like-parsers/pegen)
+ src/pegen/parser_generator.py:64: error: Argument 1 to "contextmanager" has incompatible type "Callable[[ParserGenerator], Iterator[None]]"; expected "Callable[[ParserGenerator], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/pegen/parser_generator.py:78: error: Argument 1 to "contextmanager" has incompatible type "Callable[[ParserGenerator], Iterator[None]]"; expected "Callable[[ParserGenerator], Generator[<nothing>, Any, Any]]" [arg-type]
porcupine (https://github.com/Akuli/porcupine)
+ porcupine/textutils.py:445: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Text], Iterator[None]]"; expected "Callable[[Text], Generator[<nothing>, Any, Any]]" [arg-type]
streamlit (https://github.com/streamlit/streamlit)
+ lib/streamlit/runtime/caching/cache_utils.py: note: In class "CacheWarningCallStack":
+ lib/streamlit/runtime/caching/cache_utils.py:285:6: error: Argument 1 to "contextmanager" has incompatible type "Callable[[CacheWarningCallStack, FunctionType], Iterator[None]]"; expected "Callable[[CacheWarningCallStack, FunctionType], Generator[<nothing>, Any, Any]]" [arg-type]
+ lib/streamlit/runtime/caching/cache_utils.py:293:6: error: Argument 1 to "contextmanager" has incompatible type "Callable[[CacheWarningCallStack], Iterator[None]]"; expected "Callable[[CacheWarningCallStack], Generator[<nothing>, Any, Any]]" [arg-type]
+ lib/streamlit/runtime/caching/cache_utils.py: note: In class "CacheMessagesCallStack":
+ lib/streamlit/runtime/caching/cache_utils.py:389:6: error: Argument 1 to "contextmanager" has incompatible type "Callable[[CacheMessagesCallStack], Iterator[None]]"; expected "Callable[[CacheMessagesCallStack], Generator[<nothing>, Any, Any]]" [arg-type]
+ lib/streamlit/runtime/legacy_caching/caching.py: note: At top level:
+ lib/streamlit/runtime/legacy_caching/caching.py:165:2: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Callable[..., Any]], Iterator[None]]"; expected "Callable[[Callable[..., Any]], Generator[<nothing>, Any, Any]]" [arg-type]
+ lib/streamlit/runtime/legacy_caching/caching.py:174:2: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ lib/streamlit/runtime/caching/__init__.py:72:2: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ lib/streamlit/__init__.py:393:2: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str], Iterator[None]]"; expected "Callable[[str], Generator[<nothing>, Any, Any]]" [arg-type]
jax (https://github.com/google/jax)
+ jax/_src/config.py:902: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ jax/_src/config.py:913: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ jax/_src/config.py:1006: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str], Iterator[None]]"; expected "Callable[[str], Generator[<nothing>, Any, Any]]" [arg-type]
+ jax/_src/source_info_util.py:198: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str], Iterator[NameStack]]"; expected "Callable[[str], Generator[<nothing>, Any, Any]]" [arg-type]
+ jax/_src/source_info_util.py:209: error: Argument 1 to "contextmanager" has incompatible type "Callable[[NameStack], Iterator[None]]"; expected "Callable[[NameStack], Generator[<nothing>, Any, Any]]" [arg-type]
+ jax/_src/source_info_util.py:219: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ jax/_src/source_info_util.py:224: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str], Iterator[NameStack]]"; expected "Callable[[str], Generator[<nothing>, Any, Any]]" [arg-type]
mypy-protobuf (https://github.com/dropbox/mypy-protobuf)
+ mypy_protobuf/main.py:229: error: Argument 1 to "contextmanager" has incompatible type "Callable[[PkgWriter], Iterator[None]]"; expected "Callable[[PkgWriter], Generator[<nothing>, Any, Any]]" [arg-type]
+ mypy_protobuf/main.py:973: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[Tuple[CodeGeneratorRequest, CodeGeneratorResponse]]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ mypy_protobuf/main.py:1005: error: <nothing> object is not iterable [misc]
+ mypy_protobuf/main.py:1007: error: Cannot determine type of "request" [has-type]
+ mypy_protobuf/main.py:1008: error: Cannot determine type of "response" [has-type]
+ mypy_protobuf/main.py:1009: error: Cannot determine type of "request" [has-type]
+ mypy_protobuf/main.py:1010: error: Cannot determine type of "request" [has-type]
+ mypy_protobuf/main.py:1011: error: Cannot determine type of "request" [has-type]
+ mypy_protobuf/main.py:1017: error: <nothing> object is not iterable [misc]
+ mypy_protobuf/main.py:1019: error: Cannot determine type of "request" [has-type]
+ mypy_protobuf/main.py:1020: error: Cannot determine type of "response" [has-type]
+ mypy_protobuf/main.py:1021: error: Cannot determine type of "request" [has-type]
+ mypy_protobuf/main.py:1022: error: Cannot determine type of "request" [has-type]
+ mypy_protobuf/main.py:1023: error: Cannot determine type of "request" [has-type]
black (https://github.com/psf/black)
+ src/blib2to3/pgen2/parse.py:74:6: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Recorder, int], Iterator[None]]"; expected "Callable[[Recorder, int], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/blib2to3/pgen2/parse.py:85:6: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Recorder], Iterator[None]]"; expected "Callable[[Recorder], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/blib2to3/pgen2/driver.py:68:6: error: Argument 1 to "contextmanager" has incompatible type "Callable[[TokenProxy], Iterator[TokenProxy]]"; expected "Callable[[TokenProxy], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/black/__init__.py:1334:2: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
kopf (https://github.com/nolar/kopf)
+ kopf/_core/actions/invocation.py:73: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Iterable[Tuple[ContextVar[Any], Any]]], Iterator[None]]"; expected "Callable[[Iterable[Tuple[ContextVar[Any], Any]]], Generator[<nothing>, Any, Any]]"
+ kopf/_core/actions/execution.py:193: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[], AsyncIterator[None]]"; expected "Callable[[], AsyncGenerator[<nothing>, Any]]"
+ kopf/_cogs/clients/watching.py:71: error: Need type annotation for "operator_pause_waiter"
+ kopf/_cogs/clients/watching.py:89: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[NamedArg(Resource, 'resource'), NamedArg(Optional[NamespaceName], 'namespace'), DefaultNamedArg(Optional[ToggleSet], 'operator_paused')], AsyncIterator[Future[Any]]]"; expected "Callable[[NamedArg(Resource, 'resource'), NamedArg(Optional[NamespaceName], 'namespace'), DefaultNamedArg(Optional[ToggleSet], 'operator_paused')], AsyncGenerator[<nothing>, Any]]"
+ kopf/_core/reactor/subhandling.py:17: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[], AsyncIterator[None]]"; expected "Callable[[], AsyncGenerator[<nothing>, Any]]"
isort (https://github.com/pycqa/isort)
+ isort/io.py:55: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Union[str, Path]], Iterator[File]]"; expected "Callable[[Union[str, Path]], Generator[<nothing>, Any, Any]]"
+ isort/deprecated/finders.py:40: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str], Iterator[None]]"; expected "Callable[[str], Generator[<nothing>, Any, Any]]"
+ isort/api.py:337: error: Need type annotation for "source_file"
+ isort/api.py:353: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[TextIO]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]"
+ isort/api.py:358: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Union[str, Path], File], Iterator[TextIO]]"; expected "Callable[[Union[str, Path], File], Generator[<nothing>, Any, Any]]"
+ isort/api.py:405: error: Need type annotation for "source_file"
+ isort/api.py:423: error: Need type annotation for "output_stream_context"
+ isort/api.py:591: error: Need type annotation for "source_file"
+ isort/pylama_isort.py:13: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]"
pandas (https://github.com/pandas-dev/pandas)
+ pandas/util/_exceptions.py:10: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str, str], Iterator[None]]"; expected "Callable[[str, str], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/_config/config.py:745: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Any], Iterator[None]]"; expected "Callable[[Any], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/_config/localization.py:20: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Union[str, Tuple[str, str]], int], Iterator[Union[str, Tuple[str, str]]]]"; expected "Callable[[Union[str, Tuple[str, str]], int], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/core/common.py:536: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Any, str, Any], Iterator[None]]"; expected "Callable[[Any, str, Any], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/plotting/_misc.py:596: error: Argument 1 to "contextmanager" has incompatible type "Callable[[_Options, Any, Any], Iterator[_Options]]"; expected "Callable[[_Options, Any, Any], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/io/formats/format.py:1208: error: Need type annotation for "f" [var-annotated]
+ pandas/io/formats/format.py:1212: error: Unused "type: ignore" comment
+ pandas/io/formats/format.py:1216: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Union[Union[str, PathLike[str]], WriteBuffer[str], None], Optional[str]], Union[Iterator[WriteBuffer[str]], Iterator[StringIO]]]"; expected "Callable[[Union[Union[str, PathLike[str]], WriteBuffer[str], None], Optional[str]], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/core/groupby/groupby.py:1108: error: Argument 1 to "contextmanager" has incompatible type "Callable[[GroupBy[NDFrameT]], Iterator[GroupBy[Any]]]"; expected "Callable[[GroupBy[NDFrameT]], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/compat/pickle_compat.py:296: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/_testing/contexts.py:22: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Any, Any], Iterator[IO[bytes]]]"; expected "Callable[[Any, Any], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/_testing/contexts.py:43: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str], Iterator[None]]"; expected "Callable[[str], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/_testing/contexts.py:129: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[str]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/_testing/contexts.py:148: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/_testing/contexts.py:164: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Any, KwArg(Any)], Iterator[None]]"; expected "Callable[[Any, KwArg(Any)], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/_testing/contexts.py:198: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Any, Any], Iterator[None]]"; expected "Callable[[Any, Any], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/util/_test_decorators.py:259: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ pandas/plotting/_matplotlib/converter.py:101: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
pybind11 (https://github.com/pybind/pybind11)
+ setup.py:114: error: Argument 1 to "contextmanager" has incompatible type "Callable[[VarArg(str)], Iterator[None]]"; expected "Callable[[VarArg(str)], Generator[<nothing>, Any, Any]]" [arg-type]
+ pybind11/setup_helpers.py:215: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[str]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/utils.py:1657: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[None]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]"
+ paasta_tools/utils.py:1759: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Union[IOBase, IO[Any]]], Iterator[None]]"; expected "Callable[[Union[IOBase, IO[Any]]], Generator[<nothing>, Any, Any]]"
+ paasta_tools/utils.py:1768: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Union[IOBase, IO[Any]], int], Iterator[None]]"; expected "Callable[[Union[IOBase, IO[Any]], int], Generator[<nothing>, Any, Any]]"
+ paasta_tools/utils.py:1774: error: Need type annotation for "flock_context"
+ paasta_tools/utils.py:2828: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str], Iterator[IO[Any]]]"; expected "Callable[[str], Generator[<nothing>, Any, Any]]"
+ paasta_tools/bounce_lib.py:132: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str, Optional[SystemPaastaConfig]], Iterator[Any]]"; expected "Callable[[str, Optional[SystemPaastaConfig]], Generator[<nothing>, Any, Any]]"
+ paasta_tools/generate_deployments_for_service.py:236: error: Need type annotation for "newf"
+ paasta_tools/autoscaling/autoscaling_service_lib.py:984: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str, str], Iterator[None]]"; expected "Callable[[str, str], Generator[<nothing>, Any, Any]]"
core (https://github.com/home-assistant/core)
+ homeassistant/components/fjaraskupan/__init__.py:105: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[Coordinator], AsyncIterator[Any]]"; expected "Callable[[Coordinator], AsyncGenerator[<nothing>, Any]]" [arg-type]
+ homeassistant/components/file_upload/__init__.py:31: error: Argument 1 to "contextmanager" has incompatible type "Callable[[HomeAssistant, str], Iterator[Path]]"; expected "Callable[[HomeAssistant, str], Generator[<nothing>, Any, Any]]" [arg-type]
+ homeassistant/components/zha/config_flow.py:408: error: Need type annotation for "file_path" [var-annotated]
+ homeassistant/components/script/trace.py:21: error: Argument 1 to "contextmanager" has incompatible type "Callable[[HomeAssistant, str, Dict[str, Any], Dict[str, Any], Context, Dict[str, Any]], Iterator[ScriptTrace]]"; expected "Callable[[HomeAssistant, str, Dict[str, Any], Dict[str, Any], Context, Dict[str, Any]], Generator[<nothing>, Any, Any]]" [arg-type]
+ homeassistant/components/fjaraskupan/number.py:61: error: Need type annotation for "device" [var-annotated]
+ homeassistant/components/fjaraskupan/light.py:50: error: Need type annotation for "device" [var-annotated]
+ homeassistant/components/fjaraskupan/light.py:60: error: Need type annotation for "device" [var-annotated]
+ homeassistant/components/fjaraskupan/fan.py:88: error: Need type annotation for "device" [var-annotated]
+ homeassistant/components/fjaraskupan/fan.py:109: error: Need type annotation for "device" [var-annotated]
+ homeassistant/components/fjaraskupan/fan.py:126: error: Need type annotation for "device" [var-annotated]
+ homeassistant/components/fjaraskupan/fan.py:133: error: Need type annotation for "device" [var-annotated]
+ homeassistant/components/amcrest/__init__.py:208: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[AmcrestChecker, VarArg(Any), KwArg(Any)], AsyncIterator[Any]]"; expected "Callable[[AmcrestChecker, VarArg(Any), KwArg(Any)], AsyncGenerator[<nothing>, Any]]" [arg-type]
+ homeassistant/components/amcrest/__init__.py:217: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[AmcrestChecker], AsyncIterator[None]]"; expected "Callable[[AmcrestChecker], AsyncGenerator[<nothing>, Any]]" [arg-type]
spark (https://github.com/apache/spark)
+ python/pyspark/pandas/utils.py:489: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Dict[str, Any], DefaultNamedArg(Optional[SparkSession], 'spark')], Iterator[None]]"; expected "Callable[[Dict[str, Any], DefaultNamedArg(Optional[SparkSession], 'spark')], Generator[<nothing>, Any, Any]]" [arg-type]
+ python/pyspark/pandas/config.py:375: error: Argument 1 to "contextmanager" has incompatible type "Callable[[VarArg(Any)], Iterator[None]]"; expected "Callable[[VarArg(Any)], Generator[<nothing>, Any, Any]]" [arg-type]
pyinstrument (https://github.com/joerick/pyinstrument)
- pyinstrument/vendor/decorator.py:295: error: Incompatible types in assignment (expression has type "Callable[[Any, Any, VarArg(Any), KwArg(Any)], Any]", variable has type "Callable[[_GeneratorContextManager[_T_co], Callable[..., Iterator[_T_co]], Tuple[Any, ...], Dict[str, Any]], None]")
+ pyinstrument/vendor/decorator.py:295: error: Incompatible types in assignment (expression has type "Callable[[Any, Any, VarArg(Any), KwArg(Any)], Any]", variable has type "Callable[[_GeneratorContextManager[_T_co], Callable[..., Generator[_T_co, Any, Any]], Tuple[Any, ...], Dict[str, Any]], None]")
- pyinstrument/vendor/decorator.py:301: error: Incompatible types in assignment (expression has type "Callable[[Any, Any, VarArg(Any), KwArg(Any)], Any]", variable has type "Callable[[_GeneratorContextManager[_T_co], Callable[..., Iterator[_T_co]], Tuple[Any, ...], Dict[str, Any]], None]")
+ pyinstrument/vendor/decorator.py:301: error: Incompatible types in assignment (expression has type "Callable[[Any, Any, VarArg(Any), KwArg(Any)], Any]", variable has type "Callable[[_GeneratorContextManager[_T_co], Callable[..., Generator[_T_co, Any, Any]], Tuple[Any, ...], Dict[str, Any]], None]")
poetry (https://github.com/python-poetry/poetry)
+ src/poetry/config/file_config_source.py:34: error: Need type annotation for "toml" [var-annotated]
+ src/poetry/config/file_config_source.py:49: error: Need type annotation for "toml" [var-annotated]
+ src/poetry/config/file_config_source.py:65: error: Argument 1 to "contextmanager" has incompatible type "Callable[[FileConfigSource], Iterator[Any]]"; expected "Callable[[FileConfigSource], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/utils/helpers.py:30: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Path], Iterator[Path]]"; expected "Callable[[Path], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/utils/helpers.py:89: error: Need type annotation for "update_context" [var-annotated]
+ src/poetry/utils/env.py:1751: error: Argument 1 to "contextmanager" has incompatible type "Callable[[VirtualEnv], Iterator[None]]"; expected "Callable[[VirtualEnv], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/utils/env.py:1885: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Union[str, Path, None], Optional[Dict[str, bool]]], Iterator[VirtualEnv]]"; expected "Callable[[Union[str, Path, None], Optional[Dict[str, bool]]], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/utils/env.py:1901: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Any, Optional[Env], Optional[Any]], Iterator[Env]]"; expected "Callable[[Any, Optional[Env], Optional[Any]], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/utils/env.py:1913: error: Need type annotation for "venv" [var-annotated]
+ src/poetry/inspection/info.py:578: error: Need type annotation for "venv" [var-annotated]
+ src/poetry/puzzle/provider.py:66: error: Argument 1 to "contextmanager" has incompatible type "Callable[[], Iterator[Callable[[Optional[str]], None]]]"; expected "Callable[[], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/puzzle/provider.py:178: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Provider, Path], Iterator[Provider]]"; expected "Callable[[Provider, Path], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/puzzle/provider.py:188: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Provider, Env], Iterator[Provider]]"; expected "Callable[[Provider, Env], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/puzzle/provider.py:201: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Provider, Collection[Any]], Iterator[Provider]]"; expected "Callable[[Provider, Collection[Any]], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/puzzle/solver.py:62: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Solver, Env], Iterator[None]]"; expected "Callable[[Solver, Env], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/puzzle/solver.py:104: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Solver], Iterator[None]]"; expected "Callable[[Solver], Generator[<nothing>, Any, Any]]" [arg-type]
+ src/poetry/masonry/builders/editable.py:86: error: Need type annotation for "env" [var-annotated]
+ src/poetry/console/commands/build.py:26: error: Need type annotation for "env" [var-annotated]
pylint (https://github.com/pycqa/pylint)
+ pylint/lint/utils.py:88: error: Argument 1 to "contextmanager" has incompatible type "Callable[[Sequence[str]], Iterator[None]]"; expected "Callable[[Sequence[str]], Generator[<nothing>, Any, Any]]" [arg-type]
+ pylint/lint/pylinter.py:670: error: Need type annotation for "check_astroid_module" [var-annotated]
+ pylint/lint/pylinter.py:719: error: Need type annotation for "check_astroid_module" [var-annotated]
+ pylint/lint/pylinter.py:916: error: Argument 1 to "contextmanager" has incompatible type "Callable[[PyLinter], Iterator[Callable[[Any], Optional[bool]]]]"; expected "Callable[[PyLinter], Generator[<nothing>, Any, Any]]" [arg-type]
ibis (https://github.com/ibis-project/ibis)
+ ibis/config.py:141: error: Argument 1 to "contextmanager" has incompatible type "Callable[[str, Any], Iterator[None]]"; expected "Callable[[str, Any], Generator[<nothing>, Any, Any]]"
jinja (https://github.com/pallets/jinja)
+ src/jinja2/compiler.py:1753: error: Argument 1 to "contextmanager" has incompatible type "Callable[[CodeGenerator, Union[Filter, Test], Frame, bool], Iterator[None]]"; expected "Callable[[CodeGenerator, Union[Filter, Test], Frame, bool], Generator[<nothing>, Any, Any]]" [arg-type]
rotki (https://github.com/rotki/rotki)
+ rotkehlchen/db/upgrades/v34_v35.py:100: error: Need type annotation for "write_cursor"
+ rotkehlchen/db/upgrades/v33_v34.py:13: error: Need type annotation for "write_cursor"
+ rotkehlchen/globaldb/handler.py:1131: error: Need type annotation for "user_db_cursor"
+ rotkehlchen/assets/utils.py:100: error: Need type annotation for "cursor"
+ rotkehlchen/db/upgrades/v32_v33.py:485: error: Need type annotation for "cursor"
+ rotkehlchen/db/ledger_actions.py:138: error: Need type annotation for "cursor"
+ rotkehlchen/db/history_events.py:86: error: Need type annotation for "cursor"
+ rotkehlchen/db/history_events.py:120: error: Need type annotation for "cursor"
+ rotkehlchen/db/eth2.py:120: error: Need type annotation for "cursor"
+ rotkehlchen/db/eth2.py:227: error: Need type annotation for "cursor"
+ rotkehlchen/db/eth2.py:252: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/yearn/vaults.py:772: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/yearn/vaults.py:823: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/yearn/vaults.py:879: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/l2/loopring.py:540: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/balancer/balancer.py:319: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/balancer/balancer.py:1101: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/balancer/balancer.py:1121: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/adex/adex.py:516: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/adex/adex.py:756: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/adex/adex.py:896: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/adex/adex.py:972: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/aave/graph.py:808: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/interfaces/ammswap/ammswap.py:425: error: Need type annotation for "cursor"
+ rotkehlchen/chain/bitcoin/xpub.py:212: error: Need type annotation for "cursor"
+ rotkehlchen/chain/bitcoin/xpub.py:274: error: Need type annotation for "cursor"
+ rotkehlchen/balances/manual.py:87: error: Need type annotation for "cursor"
+ rotkehlchen/balances/manual.py:107: error: Need type annotation for "cursor"
+ rotkehlchen/balances/manual.py:124: error: Need type annotation for "cursor"
+ rotkehlchen/exchanges/exchange.py:288: error: Need type annotation for "cursor"
+ rotkehlchen/exchanges/exchange.py:339: error: Need type annotation for "cursor"
+ rotkehlchen/exchanges/exchange.py:401: error: Need type annotation for "cursor"
+ rotkehlchen/exchanges/exchange.py:458: error: Need type annotation for "cursor"
+ rotkehlchen/db/upgrade_manager.py:115: error: Need type annotation for "cursor"
+ rotkehlchen/db/upgrade_manager.py:170: error: Need type annotation for "cursor"
+ rotkehlchen/db/ethtx.py:224: error: Need type annotation for "cursor"
+ rotkehlchen/db/ethtx.py:466: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/nfts.py:201: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/nfts.py:271: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/nfts.py:287: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/sushiswap/sushiswap.py:223: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/eth2/eth2.py:91: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/eth2/eth2.py:148: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/eth2/eth2.py:266: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/eth2/eth2.py:477: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/aave/aave.py:204: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/aave/aave.py:311: error: Need type annotation for "cursor"
+ rotkehlchen/exchanges/kraken.py:1172: error: Need type annotation for "write_cursor"
+ rotkehlchen/chain/ethereum/modules/yearn/vaultsv2.py:347: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/yearn/vaultsv2.py:370: error: Need type annotation for "cursor"
+ rotkehlchen/chain/ethereum/modules/uniswap/uniswap.py:343: error: Need type annotation for "cursor"
+ rotkehlchen/exchanges/manager.py:120: error: Need type annotation for "cursor"
+ rotkehlchen/db/reports.py:87: error: Need type annotation for "cursor"
+ rotkehlchen/db/reports.py:131: error: Need type annotation for "cursor"
+ rotkehlchen/db/reports.py:242: error: Need type annotation for "cursor"
+ rotkehlchen/db/reports.py:267: error: Need type annotation for "cursor"
+ rotkehlchen/db/dbhandler.py:268: error: Need type annotation for "cursor"
+ rotkehlchen/db/dbhandler.py:536: error: Argument 1 to "contextmanager" has incompatible type "Callable[[DBHandler], Iterator[DBCursor]]"; expected "Callable[[DBHandler], Generator[<nothing>, Any, Any]]"
+ rotkehlchen/db/dbhandler.py:558: error: Argument 1 to "contextmanager" has incompatible type "Callable[[DBHandler], Iterator[DBCursor]]"; expected "Callable[[DBHandler], Generator[<nothing>, Any, Any]]"
+ rotkehlchen/db/dbhandler.py:607: error: Need type annotation for "cursor"
+ rotkehlchen/db/dbhandler.py:677: error: Need type annotation for "write_cursor"
+ rotkehlchen/db/dbhandler.py:1025: error: Need type annotation for "cursor"
+ rotkehlchen/db/dbhandler.py:1660: error: Need type annotation for "cursor"
+ rotkehlchen/db/dbhandler.py:2359: error: Need type annotation for "cursor"
+ rotkehlchen/db/dbhandler.py:2606: error: Need type annotation for "write_cursor"
+ rotkehlchen/db/dbhandler.py:3114: error: Need type annotation for "write_cursor"
+ rotkehlchen/db/dbhandler.py:3278: error: Need type annotation for "cursor"
+ rotkehlchen/db/dbhandler.py:3299: error: Need type
... (truncated 521 lines) ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Question for other maintainers: What is needed for this to go in?
I think this is too disruptive until we have PEP-696 (TypeVar defaults). |
I also think this is just too disruptive, I'm afraid :( I'd much prefer it if type checkers could add some special-casing for this, as @JukkaL suggested. This is one of those occasions where it really should be solved in the stubs... but I just don't think the backwards-compatibility break is worth the cost (and I don't want to have to tell people they need to make their type hints much uglier every time they use |
The mypy-primer output looks disruptive but these cases are all easy to fix (for example in pandas pandas-dev/pandas#48383). Importantly, it is only necessary to fix the few(er) |
I'm against merging python#7430 because: - Generator is not an ergonomic type - It's very widely breaking - Safety could easily be enforced by a linter that ensures use of `yield from` instead of `return` in an @contextmanager To expand on that a little: - I care about the typing system being accessible. Generator with its three type vars is not a friendly type. I see enough confusion about Iterable, Iterator, Generator as it is. - Maintaining a high signal to noise / effort ratio is an important part of making typing accessible. Breaking changes that seem arbitrary to a casual user reinforce existing negative impressions of typing that hurt the ecosystem as a whole. - In all the years of typing, this has come up basically never (reported twice by asottile, none of the outlinks from the issue contain the bug, issue itself is not popular), so I think this is 99.99% noise. Between typeshed and mypy, I've seen a lot of typing issue reports. But I'm willing to admit that the considerations are slightly different for asynccontextmanager: - async is less widely used than sync, so maybe this is less widely breaking (let's at least see primer) - async is typically used by more experienced Python users, so there's already an accessibility floor here - There is no equivalent to `yield from` in async that a linter could enforce, so this genuinely can only be solved in type checkers - The issue that led to python#7430 was with asynccontextmanager - We've gotten away with some pedantic narrowings of async types before This is just an experiment, I'm not sure whether I'm actually in favour of this change, but I'm a lot more willing to consider it. Note that I'm not open to slippery slope consistency arguments here; if a constraint is that we do both async and sync or neither, then I'm firmly in the neither camp.
There are currently three maintainers against and one maintainer for, so I'm closing this PR. We can think about revisiting if PEP 696 is accepted and implemented. I do think the pros and cons are slightly different for contextmanager vs asynccontextmanager, so I'm willing to consider the asynccontextmanager change on its own. This is #8698 If you stumble upon this, I'm also interested in hearing about non-hypothetical cases where you actually have first-hand run into issues preventable by this; please comment on issue #2772 if so. Hearing about such cases helps us make informed decisions about the value of changes like this. As is mentioned in this PR, if you're looking for safety in the sync case, a linter can enforce use of @contextmanager
def func():
return other_generator_function() A linter could ask you to change to: @contextmanager
def func():
yield from other_generator_function() You may need an empty return if you have something that does branching and you need to short circuit. |
re-try of #2772 / #2773