typing icon indicating copy to clipboard operation
typing copied to clipboard

`TypedMapping` for extended compatibility?

Open ego-thales opened this issue 9 months ago • 4 comments

Hello,

Consider the following, where lock aims at making provided argument immutable.

@dataclass
class Works:
    foo: Mapping

    def lock(self) -> None:
        self.foo = MappingProxyType(self.foo)

It works great with mypy, no issue. Now imagine I want to enforce a bit more to foo by requesting it be compatible with a Movie. I would try the following, without success.

class Movie(TypedDict):
    author: str
    year: int

@dataclass
class Fails:
    foo: Movie

    def lock(self) -> None:
        self.foo = MappingProxyType(self.foo)  # error: Incompatible types in assignment (expression has type "MappingProxyType[str, object]", variable has type "Movie")  [assignment]

Is there an workaround? If not, is if a good idea to discuss the possibility of a TypedMapping, that would simply work as follow?

class Movie(TypedMapping):  # New feature ?
    author: str
    year: int

@dataclass
class Fixed:
    foo: Movie

    def lock(self) -> None:
        self.foo = MappingProxyType(self.foo)  # Would work

Thanks for your time.

All the best! Élie.

ego-thales avatar Mar 26 '25 13:03 ego-thales

I found a few references mentioning this, most notably PEP 705 and a few related posts on the python forum:

  • https://discuss.python.org/t/pep-705-typedmapping/24827
  • https://discuss.python.org/t/introduce-a-typedmapping-analog-to-typeddict-but-frozen/51905

One of the mentioned issue was the lack of "strong use-case", which is a bit hard to discuss in complete objectivity and time-consistency. I personally find the use case I described in opening quite standard.

A point I'd like to make is that many discuss TypedMapping as a "read-only TypedDict", which seems incorrect to me, as it misses the distinction between Mapping and MappingProxyType. I think describing TypedMapping as a base of TypedDict would be more appropriate.

ego-thales avatar Mar 27 '25 12:03 ego-thales

Yeah that was how I thought about it originally. I would have liked it to type data frames and other mapping like things.

Gobot1234 avatar Mar 27 '25 21:03 Gobot1234

With enum.Enum you can do something like

class DoubleThink(int, Enum):
    FIVE = 2 + 2

So how about using this same trick to avoid having to introduce a new typing member, and instead extend TypedDict so that you can do e.g.

class Movie(Mapping[str, str | int], TypedDict):
    author: str
    year: int

I'm not sure if this has been proposed before, but I thought I might as well throw it out there.

jorenham avatar Apr 11 '25 14:04 jorenham

I'm not entirely sure, but I think your approach forces inheritance from TypedDict which seems counterproductive (TypedMapping should be looser, am I right?)

ego-thales avatar Apr 11 '25 15:04 ego-thales