python / python/mypy

`--extra-checks` is too strict when using `Concatenate` within a `Protocol`

Open
#17,122 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug topic-paramspec
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Bug Report

This is admittedly a fairly niche use-case, but this came up when adding more precise type hints to itsdangerous and trying to write a Protocol that accepts any object that has a loads and dumps method where dumps can take arbitrary extra keyword arguments.

The docs for --extra-checks state that uses of Concatenate will force positional-only for equality, but this seems incorrect to me in the specific case of a property on a Protocol, since we're performing a structural match against methods there. *args: Any, **kwargs: Any doesn't really work, because it will only be recognized as a gradual extension of the method by some type checkers and not consistently, it's also not part of the typing spec, unless I'm mistaken. So Concatenate with ... is the only way to spell this.

To Reproduce
mypy Playground

import typing as t
import typing_extensions as te

class _PDataSerializer(t.Protocol[t.AnyStr]):
    def loads(self, payload: t.AnyStr, /) -> t.Any: ...
    @property
    def dumps(self) -> t.Callable[te.Concatenate[t.Any, ...], t.AnyStr]: ...
    
class MySerializer:
    def loads(self, payload: bytes) -> t.Any: ...
    def dumps(self, obj: object, *, some_required_option: bool) -> bytes:
        return NotImplemented
    
foo: _PDataSerializer[bytes] = MySerializer()

Expected Behavior

I would expect MySerializer to match the Protocol in my example. As soon as I change the payload argument to positional-only it does. It also does if I disable --extra-checks.

Actual Behavior

It is rejected based on the first argument not being positional-only.

Your Environment

  • Mypy version used: 1.9.0
  • Mypy command-line flags: --strict
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.8-3.12

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the linked mypy Playground reproduction with --strict and compare behavior with --extra-checks disabled. Investigate the handling of Concatenate and structural Protocol method matching; done means the shown MySerializer assignment is accepted without requiring the first argument to be positional-only.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.