python / python/mypy

dict literals inferred like immutable Mapping

Open
#11,090 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug topic-join-v-union
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Bug Report

dict literal behaves as though it is inferred to be (immutable) Mapping

To Reproduce

class OrmBase:
    pass

class Orm1(OrmBase):
    pass

class Orm1Prime(OrmBase):
    pass

class Orm2(OrmBase):
    pass

class Orm2Prime(OrmBase):
    pass

def func(orm: Union[Orm1, Orm2]):
    orm_mapper = {
        Orm1: Orm1Prime, 
        Orm2: Orm2Prime,
    }
    mapped: Union[Type[Orm1Prime], Type[Orm2Prime]] = orm_mapper[orm.__class__]
    # Incompatible types in assignment (expression has type "Type[OrmBase]",
    # variable has type "Union[Type[Orm1Prime], Type[Orm2Prime]]") [E]
    return mapped

Expected Behavior

dict is a MutableMapping, which is invariant with respect to value types. The Union on mapped should be acceptable here. (Pyright has no issue with this.)

Actual Behavior

The Union is rejected, apparently because the parent class is expected (see the error message above).

Adding a plain dict annotation resolves the error:

orm_mapper: dict = { ...

I believe this resolves the error by telling Mypy that this is a MutableMapping with invariant VT and not a Mapping with covariant VT_co. typing's definitions of both

Your Environment

  • Mypy version used: 0.910
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files):
[mypy]
plugins = sqlalchemy.ext.mypy.plugin
ignore_missing_imports = True

[mypy-sqlalchemy.*]
  • Python version used: 3.9.6
  • Operating system and version: Fedora 34 (Workstation)

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 supplied reproduction with mypy 0.910 and compare the inferred type of the unannotated dict literal with the explicitly annotated version. Trace the dict-literal inference path and verify that the assignment accepts the expected union without weakening type safety; the issue is resolved when the reproduction passes.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.