Skip to content

TypedDict with generic function #9827

@achimnol

Description

@achimnol

Bug Report

Using a TypedDict with a generic function gives a strange type error with the same "expected" and "actual" types.
I also wonder that if we could use TypeVar('TD', bound=TypedDict) as well.
(NOTE: This is a follow-up to #9820)

To Reproduce

Given

import sys
from typing import Any, Mapping, Type, TypeVar, cast

import typeguard

TD = TypeVar('TD')

def check_typed_dict(value: Mapping[Any, Any], expected_type: Type[TD]) -> TD:
    """
    Validates the given dict against the given TypedDict class, and wraps the value as the given TypedDict type.

    This is a shortcut to :func:`typeguard.check_typed_dict()` function to fill extra information
    """
    assert issubclass(expected_type, dict) and hasattr(expected_type, '__annotations__'), \
           f"expected_type ({type(expected_type)}) must be a TypedDict class"
    frame = sys._getframe(1)
    _globals = frame.f_globals
    _locals = frame.f_locals
    memo = typeguard._TypeCheckMemo(_globals, _locals)
    typeguard.check_typed_dict('value', value, expected_type, memo)
    # Here we passed the check, so return it after casting.
    return cast(TD, value)

and

from typing import Dict, Literal, Optional, TypedDict

class HardwareMetadata(TypedDict):
    status: Literal["healthy", "degraded", "offline", "unavailable"]
    status_info: Optional[str]
    metadata: Dict[str, str]

,
running mypy against the following code snippet

    async def gather_agent_hwinfo(self, instance_id: AgentId) -> Mapping[str, HardwareMetadata]:
        agent = await self.get_instance(instance_id, agents.c.addr)
        async with RPCContext(agent['addr'], None) as rpc:
            result = await rpc.call.gather_hwinfo()
            return {
                k: check_typed_dict(v, HardwareMetadata)  # L259
                for k, v in result.items()
            }

    async def gather_storage_hwinfo(self, vfolder_host: str) -> HardwareMetadata:
        proxy_name, volume_name = self.storage_manager.split_host(vfolder_host)
        async with self.storage_manager.request(
            proxy_name, 'GET', 'volumes/hwinfo',
            query={'volume': volume_name},
            raise_for_status=True,
        ) as (_, storage_resp):
            return check_typed_dict(await storage_resp.json(), HardwareMetadata)  # L270

reports

src/ai/backend/manager/registry.py:259: error: Value expression in dictionary comprehension has incompatible type "HardwareMetadata"; expected type "HardwareMetadata"
src/ai/backend/manager/registry.py:270: error: Incompatible return value type (got "HardwareMetadata", expected "HardwareMetadata")

Expected Behavior

It should pass the type check, or give a more understandable error message.

Actual Behavior

It's reporting a type error with the same "expected" and "actual" types.

Your Environment

  • Mypy version used: 0.790
  • Mypy command-line flags: python -m mypy src/ai/backend tests
  • Mypy configuration options from mypy.ini (and other config files):
[mypy]
ignore_missing_imports = true
namespace_packages = true
  • Python version used: 3.8.5
  • Operating system and version: Ubuntu 20.04

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions