Skip to content

Incorrect default for constrained type variables #358

@decorator-factory

Description

@decorator-factory

From the docs:

https://adaptix.readthedocs.io/en/latest/loading-and-dumping/extended-usage.html

TypeVar Derived implicit parameter
T = TypeVar('T') Any
B = TypeVar('B', bound=Book) Book
C = TypeVar('C', str, bytes) Union[str, bytes]

The behaviour for the third case is wrong: Union[str, bytes] is not a valid value for C.

A constrained type variable can only be resolved to one of the options, so Thing[int] is valid, Thing[str] is valid, but Thing[str | int], Thing[Literal[0]], Thing[str | Literal[0]], and Thing[bool] are all invalid. (see the specification on TypeVars)

In other words, TypeVar("S", str, int) is not the same as TypeVar("S", bound=str | int).


Example code:

from dataclasses import dataclass
from typing import Generic, TypeVar

import adaptix

S = TypeVar("S", str, int)

@dataclass
class Thing(Generic[S]):
    first: S
    second: S

    def should_always_be_true(self) -> bool:
        return (
            isinstance(self.first, str) and isinstance(self.second, str)
            or
            isinstance(self.first, int) and isinstance(self.second, int)
        )


thing = adaptix.load({"first": "hmm", "second": 42}, Thing)
print(thing)  # Thing(first='hmm', second=42)
print(thing.should_always_be_true())  # False

Possible solutions:

  • ban unqualified constrained typevars (i.e. Thing[str] is legal as the second argument to load, Thing is not)
  • change Thing with no parameter to mean Thing[str] | Thing[int]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions