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

Add support for functools.partial #16939

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

hauntsaninja
Copy link
Collaborator

@hauntsaninja hauntsaninja commented Feb 23, 2024

Fixes #1484

Turns out that this is currently the second most popular mypy issue (and first most popular is a type system feature request that would need a PEP). I'm sure there's stuff missing, but this should handle most cases.

This comment has been minimized.

Fixes python#1484

This is currently the most popular mypy issue that does not need a PEP.
I'm sure there's stuff missing, but this should handle most cases.

This comment has been minimized.

Copy link
Member

@sobolevn sobolevn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks awesome! Thanks for working on this.

I suggest to test that first / second runs work correctly as well.

This comment has been minimized.

@hauntsaninja
Copy link
Collaborator Author

hauntsaninja commented Feb 23, 2024

Looking randomly through primer, looks pretty good:

  • aiohttp is a true positive (Middleware needs to be typed as callback protocol if you want to pass a kwarg to it)
  • ibis has a true positive (tests testing that code crashes in test_generic, couldn't really tell if data_types hits were true positive or not)
  • pip is true positive
  • CPython is probably a true positive? I think it'd be better off just using dict[str, Any] for those kwargs
  • tornado is true positive
  • graphql, extend_schema is true positive (mypy prevents passing TypedDict to dict[str, Any], hopefully PEP 705 will help), tests/validation is true positive (dict is not a GraphQLError)

This comment has been minimized.

Copy link
Collaborator

@JukkaL JukkaL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent! This is a really old feature request and many users will be happy about this. Didn't do a full review yet, but left some ideas about test cases.

p1 = functools.partial(foo)
p1(1, "a", 3) # OK
p1(1, "a", c=3) # OK
p1(1, b="a", c=3) # OK
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using reveal_type(p1) etc. in tests?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added one of these, it's not very useful because it'll just show you partial[return_type]... would need a custom mypy type / some very involved generics logic / non-standard features to make it show up in the reveal

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah makes sense. The current approach seems fine then.

def foo(fn: typing.Callable[[typing.Unpack[Ts]], None], /, *arg: typing.Unpack[Ts], kwarg: str) -> None: ...
# just test that this doesn't crash
functools.partial(foo, kwarg="asdf")
[builtins fixtures/dict.pyi]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideas for test cases (it's not important to support all of these cases precisely, but they at least shouldn't crash or behave erratically):

  • Test with a value of type[x] as an argument to partial.
  • Test with a class reference as an argument to partial (e.g. partial(Foo)). This is different from the above.
  • Test calling partial with *args and/or **kwargs.
  • Test passing Callable[..., Foo] to partial (with explicit ...).
  • Test passing a type guard to partial.
  • Test passing an instance that has a __call__ method to partial.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review! Added the various tests and some small fixes. The TypeGuard one surfaces a pre-existing bug, I can make a separate PR edit: already fixed by TypeIs PR. There's also some work to be done with overloads, but similarly gets into some pre-existing behaviour

This comment has been minimized.

@hauntsaninja
Copy link
Collaborator Author

Looks like callback protocol support hurts overloads more than previously. Probably need to improve extract_callable_type

This comment has been minimized.

This comment has been minimized.

Copy link
Contributor

github-actions bot commented Mar 1, 2024

Diff from mypy_primer, showing the effect of this PR on open source code:

anyio (https://github.com/agronholm/anyio)
+ src/anyio/_backends/_asyncio.py:2259: error: Argument 1 to "_forcibly_shutdown_process_pool_on_exit" has incompatible type "set[anyio.abc._subprocesses.Process]"; expected "set[anyio._backends._asyncio.Process]"  [arg-type]

pip (https://github.com/pypa/pip)
+ src/pip/_internal/utils/misc.py:136: error: "Callable[[FunctionType, Path, BaseException], Any] | Callable[[FunctionType, Path, tuple[type[BaseException], BaseException, TracebackType]], Any]" not callable  [misc]
+ src/pip/_internal/cli/progress_bars.py:66: error: Argument 2 to "_rich_progress_bar" has incompatible type "int | None"; expected "int"  [arg-type]

CPython (Argument Clinic) (https://github.com/python/cpython)
+ Tools/clinic/clinic.py:2799: error: "CConverterClassT" not callable  [misc]

porcupine (https://github.com/Akuli/porcupine)
+ porcupine/plugins/git_status.py:139: error: Argument 1 to "run_git_status" has incompatible type "Path | None"; expected "Path"  [arg-type]

pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/window/rolling.py:1499: error: Argument 3 to "roll_apply" has incompatible type "bool | bool_"; expected "bool"  [arg-type]
+ pandas/core/tools/datetimes.py:997: error: Argument 6 to "_convert_listlike_datetimes" has incompatible type "bool | Literal[_NoDefault.no_default]"; expected "bool"  [arg-type]
+ pandas/core/groupby/groupby.py:3843: error: Argument "mask" to "group_fillna_indexer" has incompatible type "ndarray[Any, dtype[bool_]] | Any"; expected "ndarray[Any, dtype[unsignedinteger[_8Bit]]]"  [arg-type]
+ pandas/core/groupby/groupby.py:4368: error: Argument 3 to "group_quantile" has incompatible type "str"; expected "Literal['linear', 'lower', 'higher', 'nearest', 'midpoint']"  [arg-type]
+ pandas/core/groupby/groupby.py:4400: error: Argument "mask" to "group_quantile" has incompatible type "ndarray[Any, dtype[bool_]]"; expected "ndarray[Any, dtype[unsignedinteger[_8Bit]]]"  [arg-type]
+ pandas/tests/frame/test_ufunc.py:69: error: "_UFunc_Nin2_Nout1[Literal['add'], Literal[22], Literal[0]]" not callable  [misc]
+ pandas/tests/frame/test_ufunc.py:76: error: "_UFunc_Nin1_Nout1[Literal['negative'], Literal[19], None]" not callable  [misc]
+ pandas/tests/indexes/test_base.py:135: error: Unexpected keyword argument "data" for "RangeIndex"  [call-arg]
+ pandas/core/indexes/range.py:135: note: "RangeIndex" defined here

websockets (https://github.com/aaugustin/websockets)
+ src/websockets/legacy/server.py:1048: error: "type[WebSocketServerProtocol] | Callable[..., WebSocketServerProtocol]" not callable  [misc]
+ src/websockets/legacy/client.py:496: error: "type[WebSocketClientProtocol] | Callable[..., WebSocketClientProtocol]" not callable  [misc]
+ src/websockets/legacy/client.py:649: error: Redundant cast to "WebSocketClientProtocol"  [redundant-cast]
+ src/websockets/legacy/auth.py:180: error: "type[BasicAuthWebSocketServerProtocol] | Callable[..., BasicAuthWebSocketServerProtocol]" not callable  [misc]

prefect (https://github.com/PrefectHQ/prefect)
+ src/prefect/engine.py:1611: error: Argument 3 to "create_task_run_then_submit" has incompatible type "int"; expected "str"  [arg-type]
+ src/prefect/cli/worker.py:206: error: Argument 2 to "start_healthcheck_server" has incompatible type "float"; expected "int"  [arg-type]

koda-validate (https://github.com/keithasaurus/koda-validate)
+ koda_validate/signature.py:173: error: Argument 1 to "_get_validator" has incompatible type "tuple[Literal['return_key']]"; expected "str"  [arg-type]
- koda_validate/signature.py:384: error: Returning Any from function declared to return "_DecoratedFunc"  [no-any-return]
- koda_validate/signature.py:388: error: Returning Any from function declared to return "_DecoratedFunc | Callable[[_DecoratedFunc], _DecoratedFunc]"  [no-any-return]

isort (https://github.com/pycqa/isort)
+ isort/exceptions.py:13: error: "Type[ISortError]" not callable  [misc]

tornado (https://github.com/tornadoweb/tornado)
+ tornado/tcpclient.py:274: error: Argument 1 to "_create_stream" of "TCPClient" has incompatible type "Optional[int]"; expected "int"  [arg-type]
+ tornado/curl_httpclient.py:362: error: Argument 2 to "_curl_header_callback" of "CurlAsyncHTTPClient" has incompatible type "Optional[Callable[[str], None]]"; expected "Callable[[str], None]"  [arg-type]

vision (https://github.com/pytorch/vision)
+ torchvision/models/video/swin_transformer.py:518: error: Argument 2 to "VideoClassification" has incompatible type "tuple[int]"; expected "tuple[int, int]"  [arg-type]
+ torchvision/models/video/swin_transformer.py:549: error: Argument 2 to "VideoClassification" has incompatible type "tuple[int]"; expected "tuple[int, int]"  [arg-type]
+ torchvision/models/video/swin_transformer.py:580: error: Argument 2 to "VideoClassification" has incompatible type "tuple[int]"; expected "tuple[int, int]"  [arg-type]
+ torchvision/models/video/swin_transformer.py:607: error: Argument 2 to "VideoClassification" has incompatible type "tuple[int]"; expected "tuple[int, int]"  [arg-type]
+ torchvision/models/video/mvit.py:607: error: Argument 2 to "VideoClassification" has incompatible type "tuple[int]"; expected "tuple[int, int]"  [arg-type]
+ torchvision/models/video/mvit.py:640: error: Argument 2 to "VideoClassification" has incompatible type "tuple[int]"; expected "tuple[int, int]"  [arg-type]

kopf (https://github.com/nolar/kopf)
+ kopf/_core/actions/invocation.py:127: error: Incompatible types in assignment (expression has type "partial[partial[object | Coroutine[None, None, object]]]", variable has type "partial[object | Coroutine[None, None, object | None] | None]")  [assignment]
+ kopf/_core/actions/invocation.py:127: error: Argument 1 to "run" of "Context" has incompatible type "partial[object | Coroutine[None, None, object | None] | None]"; expected "Callable[[VarArg(Any), KwArg(Any)], partial[object | Coroutine[None, None, object]]]"  [arg-type]
+ kopf/_core/actions/invocation.py:127: note: "partial[object | Coroutine[None, None, object | None] | None].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], object | Coroutine[None, None, object | None] | None]"
+ kopf/_core/reactor/orchestration.py:42: error: Unexpected keyword argument "resource" for "__call__" of "WatchStreamProcessor"  [call-arg]
+ kopf/_core/reactor/queueing.py:42: note: "__call__" of "WatchStreamProcessor" defined here

graphql-core (https://github.com/graphql-python/graphql-core)
+ src/graphql/utilities/extend_schema.py:360: error: Argument 1 to "extend_input_object_type_fields" of "ExtendSchemaImpl" has incompatible type "GraphQLInputObjectTypeKwargs"; expected "dict[str, Any]"  [arg-type]
+ src/graphql/utilities/extend_schema.py:427: error: Argument 1 to "extend_object_type_interfaces" of "ExtendSchemaImpl" has incompatible type "GraphQLObjectTypeKwargs"; expected "dict[str, Any]"  [arg-type]
+ src/graphql/utilities/extend_schema.py:429: error: Argument 1 to "extend_object_type_fields" of "ExtendSchemaImpl" has incompatible type "GraphQLObjectTypeKwargs"; expected "dict[str, Any]"  [arg-type]
+ src/graphql/utilities/extend_schema.py:467: error: Argument 1 to "extend_interface_type_interfaces" of "ExtendSchemaImpl" has incompatible type "GraphQLInterfaceTypeKwargs"; expected "dict[str, Any]"  [arg-type]
+ src/graphql/utilities/extend_schema.py:469: error: Argument 1 to "extend_interface_type_fields" of "ExtendSchemaImpl" has incompatible type "GraphQLInterfaceTypeKwargs"; expected "dict[str, Any]"  [arg-type]
+ src/graphql/utilities/extend_schema.py:491: error: Argument 1 to "extend_union_type_types" of "ExtendSchemaImpl" has incompatible type "GraphQLUnionTypeKwargs"; expected "dict[str, Any]"  [arg-type]
+ tests/validation/test_variables_in_allowed_position.py:174: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_in_allowed_position.py:196: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_in_allowed_position.py:222: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_in_allowed_position.py:240: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_in_allowed_position.py:258: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_in_allowed_position.py:274: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_in_allowed_position.py:290: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_in_allowed_position.py:309: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_in_allowed_position.py:328: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_are_input_types.py:39: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_are_input_types.py:43: error: List item 1 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_variables_are_input_types.py:48: error: List item 2 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:176: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:193: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:210: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:227: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:245: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:262: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:280: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:297: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:314: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:332: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:349: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:366: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:384: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:401: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:418: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:436: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:454: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:472: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:490: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:509: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:527: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:545: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:564: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:582: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:600: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:664: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:681: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:810: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:814: error: List item 1 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:831: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:848: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:945: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:966: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:986: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:1006: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:1035: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]
+ tests/validation/test_values_of_correct_type.py:1067: error: List item 0 has incompatible type "dict[str, Sequence[Sequence[object]]]"; expected "GraphQLError"  [list-item]

... (truncated 428 lines) ...

jax (https://github.com/google/jax)
+ jax/_src/linear_util.py:339: error: Missing positional argument "tree" in call to "tree_map"  [call-arg]
+ jax/_src/interpreters/partial_eval.py:2652: error: Argument 2 to "_eval_jaxpr_padded" has incompatible type "Sequence[Any]"; expected "list[Any]"  [arg-type]
+ jax/_src/custom_derivatives.py:245: error: Missing positional argument "tree" in call to "tree_map"  [call-arg]
+ jax/_src/nn/functions.py:483: error: Argument 1 to "custom_jvp" has incompatible type "Callable[[Array | Any | Any | Any | bool | int | float | complex, int | tuple[int, ...] | None, Array | Any | Any | Any | bool | int | float | complex | None, Array | Any | Any | Any | bool | int | float | complex | None], Array]"; expected "Callable[..., Never]"  [arg-type]
+ jax/_src/scipy/special.py:579: error: Argument 1 to "custom_jvp" has incompatible type "Callable[[Array | Any | Any | Any | bool | int | float | complex, int], Array]"; expected "Callable[..., Never]"  [arg-type]
+ jax/_src/scipy/special.py:1601: error: Argument 1 to "_expn3" has incompatible type "Array"; expected "int"  [arg-type]
+ jax/_src/scipy/special.py:1602: error: Argument 1 to "_expn2" has incompatible type "Array"; expected "int"  [arg-type]
+ jax/_src/scipy/special.py:1603: error: Argument 1 to "_expn1" has incompatible type "Array"; expected "int"  [arg-type]
+ jax/_src/scipy/signal.py:430: error: Argument 1 to "detrend" has incompatible type "Literal[True] | str"; expected "str"  [arg-type]
+ jax/experimental/rnn.py:245: error: Argument 1 to "custom_vjp" has incompatible type "Callable[[Array, Array, Array, Array, Array, int, int, int, float, bool, str | Precision | tuple[str, str] | tuple[Precision, Precision] | None], tuple[Array, Array, Array]]"; expected "Callable[..., Never]"  [arg-type]
+ jax/_src/pallas/pallas_call.py:421: error: Argument 1 to "_batch_block_mapping" has incompatible type "tuple[int | None, ...]"; expected "tuple[int, ...]"  [arg-type]
+ jax/_src/cudnn/fused_attention_stablehlo.py:747: error: Need type annotation for "output"  [var-annotated]
+ jax/experimental/pallas/ops/rms_norm.py:225: error: Argument 1 to "custom_vjp" has incompatible type "list[int]"; expected "tuple[int, ...]"  [arg-type]
+ jax/experimental/pallas/ops/layer_norm.py:244: error: Argument 1 to "custom_vjp" has incompatible type "list[int]"; expected "tuple[int, ...]"  [arg-type]
+ jax/experimental/pallas/ops/attention.py:149: error: Argument 1 to "custom_vjp" has incompatible type "list[int]"; expected "tuple[int, ...]"  [arg-type]
+ jax/experimental/pallas/ops/tpu/flash_attention.py:203: error: Argument 1 to "custom_vjp" has incompatible type "range"; expected "tuple[int, ...]"  [arg-type]
+ jax/experimental/pallas/ops/tpu/flash_attention.py:1088: error: Argument 1 to "_flash_attention_dkv_kernel" has incompatible type "int | None"; expected "int"  [arg-type]
+ jax/experimental/pallas/ops/tpu/flash_attention.py:1089: error: Argument 2 to "_flash_attention_dkv_kernel" has incompatible type "int | None"; expected "int"  [arg-type]
+ jax/experimental/pallas/ops/tpu/flash_attention.py:1432: error: Argument 4 to "_flash_attention_dq_kernel" has incompatible type "int | None"; expected "int"  [arg-type]
+ jax/experimental/pallas/ops/tpu/flash_attention.py:1568: error: Need type annotation for "res"  [var-annotated]

cwltool (https://github.com/common-workflow-language/cwltool)
+ cwltool/workflow_job.py: note: In member "try_make_job" of class "WorkflowJob":
+ cwltool/workflow_job.py:616:57: error: Argument 3 to "receive_output" of "WorkflowJob" has incompatible type "OutputCallbackType | None"; expected "OutputCallbackType"  [arg-type]
+ cwltool/command_line_tool.py: note: In member "job" of class "CommandLineTool":
+ cwltool/command_line_tool.py:960:52: error: Argument 1 to "update_status_output_callback" has incompatible type "OutputCallbackType | None"; expected "OutputCallbackType"  [arg-type]
+ cwltool/workflow.py: note: In member "job" of class "WorkflowStep":
+ cwltool/workflow.py:463:56: error: Argument 1 to "receive_output" of "WorkflowStep" has incompatible type "OutputCallbackType | None"; expected "OutputCallbackType"  [arg-type]

aiohttp (https://github.com/aio-libs/aiohttp)
+ aiohttp/web_app.py:56:37: error: Unexpected keyword argument "handler"  [call-arg]
+ aiohttp/web_app.py:56:37: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-call-arg for more info

ibis (https://github.com/ibis-project/ibis)
+ ibis/expr/operations/tests/test_generic.py:147: error: Too many arguments for "Literal"  [call-arg]
+ ibis/expr/operations/tests/test_generic.py:147: error: Unexpected keyword argument "name" for "Literal"  [call-arg]
+ ibis/expr/operations/generic.py:147: note: "Literal" defined here
+ ibis/expr/operations/tests/test_generic.py:147: error: "Literal" gets multiple values for keyword argument "dtype"  [misc]
+ ibis/backends/sql/datatypes.py:31: error: Argument 1 to "Map" has incompatible type "String"; expected "bool"  [arg-type]
+ ibis/backends/sql/datatypes.py:48: error: Argument 1 to "Map" has incompatible type "String"; expected "bool"  [arg-type]
+ ibis/backends/sql/datatypes.py:66: error: Argument 1 to "Array" has incompatible type "String"; expected "bool"  [arg-type]

bokeh (https://github.com/bokeh/bokeh)
+ src/bokeh/driving.py: note: In function "bounce":
+ src/bokeh/driving.py:97:36: error: Argument 1 to "force" has incompatible type "Iterable[int]"; expected "Iterator[Any]"  [arg-type]
+ src/bokeh/driving.py:97:36: note: "Iterable" is missing following "Iterator" protocol member:
+ src/bokeh/driving.py:97:36: note:     __next__
+ src/bokeh/driving.py: note: In function "cosine":
+ src/bokeh/driving.py:116:36: error: Argument 1 to "force" has incompatible type "Iterable[float]"; expected "Iterator[Any]"  [arg-type]
+ src/bokeh/driving.py:116:36: note: "Iterable" is missing following "Iterator" protocol member:
+ src/bokeh/driving.py:116:36: note:     __next__
+ src/bokeh/driving.py: note: In function "count":
+ src/bokeh/driving.py:122:36: error: Argument 1 to "force" has incompatible type "Iterable[int]"; expected "Iterator[Any]"  [arg-type]
+ src/bokeh/driving.py:122:36: note: "Iterable" is missing following "Iterator" protocol member:
+ src/bokeh/driving.py:122:36: note:     __next__
+ src/bokeh/driving.py: note: In function "linear":
+ src/bokeh/driving.py:154:36: error: Argument 1 to "force" has incompatible type "Iterable[float]"; expected "Iterator[Any]"  [arg-type]
+ src/bokeh/driving.py:154:36: note: "Iterable" is missing following "Iterator" protocol member:
+ src/bokeh/driving.py:154:36: note:     __next__
+ src/bokeh/driving.py: note: In function "repeat":
+ src/bokeh/driving.py:172:36: error: Argument 1 to "force" has incompatible type "Iterable[int]"; expected "Iterator[Any]"  [arg-type]
+ src/bokeh/driving.py:172:36: note: "Iterable" is missing following "Iterator" protocol member:
+ src/bokeh/driving.py:172:36: note:     __next__
+ src/bokeh/driving.py: note: In function "sine":
+ src/bokeh/driving.py:191:36: error: Argument 1 to "force" has incompatible type "Iterable[float]"; expected "Iterator[Any]"  [arg-type]
+ src/bokeh/driving.py:191:36: note: "Iterable" is missing following "Iterator" protocol member:
+ src/bokeh/driving.py:191:36: note:     __next__

hydra-zen (https://github.com/mit-ll-responsible-ai/hydra-zen)
+ src/hydra_zen/wrapper/_implementations.py:58: error: Too few arguments for "run" of "Context"  [call-arg]

Copy link
Collaborator

@JukkaL JukkaL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found some potential issues, but overall looks good. At least the incremental mode bug seems important to fix, since this will likely cause issues.

)

ret = ctx.api.named_generic_type("functools.partial", [ret_type])
ret = ret.copy_with_extra_attr("__mypy_partial", partially_applied)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra_attrs is currently a bit poorly thought out, so this will cause some problems. First, it isn't serialized, so storing the additional information there breaks in incremental mode. For example, consider a module like this:

...
p = partial(f, 1)

Now if we import p from another module, the second run which uses serialized data will produce a different type for p compared to the first run.

I'm not sure what is the best way to fix this. Probably the simplest option would be to serialize extra_attrs. It seems that everything we put there can be serialized -- it just hasn't been implemented.

Also it would be good to have an incremental mode test case.

It looks like mypy daemon doesn't keep track of extra_attrs. mypy.server.astdiff should look into extra_attrs to detect changes in extra_attrs, as these may need to be propagated.

if len(bound.arg_types) == len(fn_type.arg_types):
arg_type = bound.arg_types[i]
if isinstance(get_proper_type(arg_type), UninhabitedType):
arg_type = fn_type.arg_types[i] # bit of a hack
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this could refer to a type variable type, so I wonder if this can leak type variables, in the target function is a generic one, and one of the provided arguments can be used to bind the type variable.

else:
# TODO: I assume that bound and fn_type have the same arguments. It appears this isn't
# true when PEP 646 things are happening. See testFunctoolsPartialTypeVarTuple
arg_type = fn_type.arg_types[i]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above.


ret_type = bound.ret_type
if isinstance(get_proper_type(ret_type), UninhabitedType):
ret_type = fn_type.ret_type # same kind of hack as above
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above -- it seems that this might leak type variables. If that is the case, it would probably be better to fall back to the default return type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this doesn't leak type variables, because partially_applied has the same variables as fn_type (from copy_modified). partially_applied just remains generic. See the test cases in testFunctoolsPartialGeneric. I can add some more comments to the code

But let me know if I'm off the mark!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are right, and this is good already.

p1 = functools.partial(foo)
p1(1, "a", 3) # OK
p1(1, "a", c=3) # OK
p1(1, b="a", c=3) # OK
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah makes sense. The current approach seems fine then.

@kevxwan-zv
Copy link

Hey folks, this looks amazing, but when are you planning to release the change to mypy?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

functools.partial support
6 participants