Skip to content

mypy rejects subclassing Any typed *args entries within a function #21998

Open

Description

@willy-b

(No AI or LLMs were used in finding or writing up this issue.)

In Python and passing other type checkers (e.g. pyright 1.1.414 and pyrefly 1.3.1) it is ok to subclass a type passed within the *args variadic argument which is typed as Any, but mypy 2.3.1 and recent master seems to reject this.

E.g. the following Python program runs fine and typechecks via pyright 1.1.414 and pyrefly 1.3.1
(btw if you have an objection to this on the basis of the intermediate x variable, please also see the example pair at the end of the issue showing mypy will allow a keyword arg for a type to subclass but not an element of *args without an intermediate variable)

https://mypy-play.net/?gist=caa19ef606afd2b352e2e5e3fa608bca

from typing import Any

def fun_varargs_element_to_type_used_for_inherit(
   *args: *tuple[int, Any],
) -> Any:
  # not allowed by mypy but allowed by pyright and others
  x = args[1]
  class Sub(x): pass
  return Sub()

s = fun_varargs_element_to_type_used_for_inherit(1, str)
print('isinstance(s, str): ', isinstance(s, str))

Per pyright 1.1.414 and pyrefly 1.3.1 it typechecks and in python 3.13.2 (and 3.12 see gist above) runs and produces valid output:

(venv) [liveuser@localhost-live mypy]$ pyright ~/examplecomplex2.py
0 errors, 0 warnings, 0 informations
(venv) [liveuser@localhost-live mypy]$ python ~/examplecomplex2.py
isinstance(s, str):  True

But mypy 2.3.1 rejects it (see mypy playground link and associated gist https://mypy-play.net/?gist=caa19ef606afd2b352e2e5e3fa608bca ) .

Here are some unit test cases showing this behavior as well:

[case testVarargsArgumentTypeCheckingUsingArgumentTypeFromVarArgsForInheritanceExplicitCastOk]
# flags: --python-version 3.12
from typing import Any

def fun_explicit_cast_varargs_element_to_type_used_for_inherit(
   *args: *tuple[int, Any],
):
  # allowed
  x : type = args[1]
  class Sub(x): pass

fun_explicit_cast_varargs_element_to_type_used_for_inherit(1, str)

[builtins fixtures/tuple.pyi]

[case testVarargsArgumentTypeCheckingUsingArgumentTypeFromVarArgsForInheritance-xfail]
# flags: --python-version 3.12
from typing import Any

def fun_varargs_element_to_type_used_for_inherit(
   *args: *tuple[int, Any],
):
  # not allowed by mypy but allowed by pyright and others
  x = args[1]
  class Sub(x): pass

fun_varargs_element_to_type_used_for_inherit(1, str)

[builtins fixtures/tuple.pyi]

The first case passes, the second xfails (I marked it expected fail for now per current behavior) when it should pass it seems.

Note that passing a type via an Any typed keyword parameter is OK-ed by mypy:
https://mypy-play.net/?gist=9ff16e8bc2f873f3fe5d0dcb4ff8baeb

[liveuser@localhost-live mypy]$ cat ~/examplecomplex3.py
from typing import Any

def fun_keyword_parameter_to_type_used_for_inherit(
   *args: *tuple[int, Any],
   keywordarg: Any
) -> Any:
  class Sub(keywordarg): pass
  return Sub()

s = fun_keyword_parameter_to_type_used_for_inherit(1, 1, keywordarg=str)
print('isinstance(s, str): ', isinstance(s, str))

[liveuser@localhost-live mypy]$ python ~/examplecomplex3.py
isinstance(s, str):  True

[liveuser@localhost-live mypy]$ mypy ~/examplecomplex3.py
Success: no issues found in 1 source file

But using args[1] (an element of the *args tuple directly) when Any typed to pass a type for subclassing is not allowed by mypy, while it is allowed by pyright 1.1.414 and pyrefly 1.3.1 and python:
https://mypy-play.net/?gist=d12a1ef7f71677f150d09200fadf1958

[liveuser@localhost-live mypy]$ cat ~/examplecomplex4.py
from typing import Any

def fun_varargs_element_to_type_used_for_inherit(
   *args: *tuple[int, Any],
) -> Any:
  # not allowed by mypy but allowed by pyright and others
  class Sub(args[1]): pass
  return Sub()

s = fun_varargs_element_to_type_used_for_inherit(1, str)
print('isinstance(s, str): ', isinstance(s, str))

[liveuser@localhost-live mypy]$ python ~/examplecomplex4.py
isinstance(s, str):  True

[liveuser@localhost-live mypy]$ pyright ~/examplecomplex4.py
0 errors, 0 warnings, 0 informations

[liveuser@localhost-live mypy]$ mypy ~/examplecomplex4.py
/home/liveuser/examplecomplex4.py:7: error: Variable "args" is not valid as a type  [valid-type]
/home/liveuser/examplecomplex4.py:7: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
/home/liveuser/examplecomplex4.py:7: error: Invalid base class "args"  [misc]
/home/liveuser/examplecomplex4.py:7: error: Invalid type: try using Literal[1] instead?  [valid-type]
Found 3 errors in 1 file (checked 1 source file)

(Note the above typechecks fine in pyright 1.1.414 and pyrefly 1.3.1 )

  • Mypy version used: 2.3.1 (and recent master)
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): defaults
  • Python version used: 3.12 and 3.13.2

Thanks so much for building this tool!

Activity

  1. willy-b commented on Sep 16, 2026

    @willy-b
    ContributorAuthor

    See as related #5865 .

  2. willy-b commented on Sep 21, 2026

    @willy-b
    ContributorAuthor

    (This came up when writing unit tests around a fix for an outright crash of MyPy reported at #21907 3 weeks ago.)

  3. changed the title [-]Possible bug - mypy rejects subclassing `Any` typed `*args` entries within a function (OK in python; pyright and pyrefly say OK) though allowing the same from keyword parameters[/-] [+]mypy rejects subclassing `Any` typed `*args` entries within a function[/+] on Sep 22, 2026
  4. added 2 commits that reference this issue on Sep 24, 2026
  5. willy-b commented on Sep 27, 2026

    @willy-b
    ContributorAuthor

    anyone working on this is advised to please review #22002 to see items that came up within the review there.

    @dibrinsofor , @Dextheking1 not my call on who takes this but I have separated this from #21907 (working on that revealed this to me) and will put out a fix for that first.

    I would be happy to review PRs for this though as the issue reporter if someone sends them.

  6. willy-b commented on Oct 5, 2026

    @willy-b
    ContributorAuthor

    I think @ilevkivskyi is working on this area fwiw and will likely get to it. He just landed a PR with something very close to what I was testing in my fork to fix the crash I reported earlier at #21907 .

  7. willy-b commented on Oct 26, 2026

    @willy-b
    ContributorAuthor

    BTW, in Pyrefly with preset basic disabled (preset basic is the default for the CLI tool), pyrefly also complains about subclassing explicit Any from within *args but not for normal parameters, e.g.:

    Without preset basic, with https://pyrefly.org/en/docs/error-kinds/#invalid-inheritance enabled,
    subclassing variables whose reveal_type is Any is allowed by pyrefly (an explicitly Any typed parameter, as they could contain a type):
    https://pyrefly.org/sandbox/?project=v2.pZRBCsMgEEWvMpCFoZS6zykK3QbENpJ0oyFqobfvd7TFUAgt2SVDxuj85_tDmYcKUKXtU816QWDQh4reDAqxYBMTtBlaHpPuWLO9ZQzwyNOrdNVqBEWUr-UlXvHelVg--KUyp57O_8OfE8S9zSiIinF4ivkmcazRL2W0fOfZ0DkXqIwGJgMIY4QvEfCptw1UaWgKYfadlO92t4zSWDm4m5erDkkJXf3Qd7bwejlyzJ_HsntAEpkjsYWRSBThi00yXg

    from typing import *
    
    def fun_any_parameter_used_for_inherit(
       a: Any,
    ) -> Any:
      reveal_type(a)
      class Sub(a): pass
      return Sub()
    
    s = fun_any_parameter_used_for_inherit(str)
    print('isinstance(s, str): ', isinstance(s, str))
    

    where the Pyrefly output (WITHOUT preset basic) is:

    INFO sandbox.py:6:14-17: revealed type: Any [[reveal-type](https://pyrefly.org/en/docs/error-kinds/#reveal-type)]
    

    with no errors.

    However, WITHOUT preset basic (with preset basic this is still OK), subclassing an explicitly Any typed *args entry is marked with "Invalid base class: tuple[int, Any] [invalid-inheritance]" (runs fine in Python), even though it also shows as Any by reveal_type and was explicitly typed as Any by the user:
    https://pyrefly.org/sandbox/?project=v2.pZRBCoMwEEWvMtBFRKTBracodKkSbA1aaJNiEs_fn8RKpCBIdzJmQph5_x1QZp4AKuZuArJGyKd8SQU16SAg4YzsBRaE54wQqM3CwHJ_tqLcOgSvxpYKL-C2aFQgBN9hsInJMt9Rly02SRRze3W3tVot21sp9T8DHH5Mhx5YRvAbFfFhSS7gtpAJYkUal6WMll8GTnSJBVrGCfsBnsHBsYDi3KgT9CpptPZtKs6_7XoauFS813fDNx2cPO7d3D2CubfXkQ7MGlz7D3wsssf20GOePJzYpekD

    from typing import *
    
    def fun_varargs_element_to_type_used_for_inherit(
       *args: *tuple[int, Any],
    ) -> Any:
      reveal_type(args[1])
      class Sub(args[1]): pass
      return Sub()
    
    s = fun_varargs_element_to_type_used_for_inherit(1, str)
    print('isinstance(s, str): ', isinstance(s, str))
    

    above runs fine in Python and outputs

    runtime type is 'type'
    isinstance(s, str):  True
    

    but pyrefly analysis (without preset basic), gives:

    ERROR sandbox.py:7:13-20: Invalid base class: `tuple[int, Any]` [[invalid-inheritance](https://pyrefly.org/en/docs/error-kinds/#invalid-inheritance)]
    INFO sandbox.py:6:14-23: revealed type: Any [[reveal-type](https://pyrefly.org/en/docs/error-kinds/#reveal-type)]
    

    I will ask the Pyrefly team about that as well since these are two cases where reveal_type shows the same type (Any) and in both cases the Any was typed explicitly, but the checker is saying one is valid to inherit from and not the other.

  8. willy-b commented on Oct 27, 2026

    @willy-b
    ContributorAuthor

    For the record, here is a Pyright playground demonstration of Pyright allowing subclassing an Any typed *args entry (by index) without any typechecking errors (I showed above on commandline but did not produce a reproducible playground link):

    Code sample in https://pyright-play.net/?code=GYJw9gtgBALgngBwJYDsDmUkQWEMoCCKcAUCQCYCmwUwArigPoBuAhiO2gM6OUA2lCJRQxGMMGMSVGdLpXKNguRqgAWlEEhgAKElH0AqTlwBcUAzDoIBAbVQwANIWIBdByQCUUALQA%2BZ3AmelAglMyUrHySCJTaxjYAjC4ewQDGfKxcXFAAynQARnEg3InJZgiZXMGhliAouQXaKSTZALy0DCzsxrwCQiJiEvAxMnIKSiAqKOqaOglOXDAgKQiaItoA5EhcqIusKKmxXAtLHmYbTtu7MPuH2sdQi8seQA

    from typing import Any
    
    def fun_varargs_element_to_type_used_for_inherit(
       *args: *tuple[int, Any],
    ) -> Any:
      reveal_type(args[1])
      class Sub(args[1]): pass
      return Sub()
    
    s = fun_varargs_element_to_type_used_for_inherit(1, str)
    print('isinstance(s, str): ', isinstance(s, str))
    Image

    As to pyrefly, they may disallow it when the basic preset is disabled (I think also unintentionally as they allow subclassing explicit Anys in other contexts),
    why I re-confirmed above in the pyright playground that it is checking for valid inheritance AND specifically allowing this (behavior I think MyPy also should have (purpose of this ticket) since MyPy allows explicitly Any typed variables to be subclassed, just not when indexed into *args, which seems to be a bug).

    Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-callsFunction calls, *args, **kwargs, defaults

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions