Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JsonValue is contravariant, should be covariant #9445

Open
1 task done
tamird opened this issue May 16, 2024 · 5 comments
Open
1 task done

JsonValue is contravariant, should be covariant #9445

tamird opened this issue May 16, 2024 · 5 comments
Labels
bug V2 Bug related to Pydantic V2 pending Awaiting a response / confirmation

Comments

@tamird
Copy link

tamird commented May 16, 2024

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

See the code sample. It produces the following error in mypy:

foo.py:10: error: Incompatible types in assignment (expression has type "list[str]", target has type "JsonValue")  [assignment]

and in pyright:

Argument of type "list[str]" cannot be assigned to parameter "value" of type "JsonValue" in function "__setitem__"
  Type "list[str]" is incompatible with type "JsonValue"
    "list[str]" is incompatible with "List[JsonValue]"
      Type parameter "_T@list" is invariant, but "str" is not the same as "JsonValue"
      Consider switching from "list" to "Sequence" which is covariant
    "list[str]" is incompatible with "Dict[str, JsonValue]"
    "list[str]" is incompatible with "str"
    "list[str]" is incompatible with "bool"
    "list[str]" is incompatible with "int"

This works with an identical type where types.Dict is replaced with collections.abc.Mapping and types.List is replaced with collections.abc.Sequence.

Example Code

from pydantic import JsonValue

def bar() -> list[str]:
  raise NotImplementedError

foo: dict[str, JsonValue] = {
  "a": {"b": "hello"},
  "b": "c",
}
foo["l"] = bar()

Python, Pydantic & OS Version

poetry run python -c "import pydantic.version; print(pydantic.version.version_info())"
             pydantic version: 2.7.1
        pydantic-core version: 2.18.2
          pydantic-core build: profile=release pgo=true
                 install path: /Users/tamird/Library/Caches/pypoetry/virtualenvs/common-hf-Ms37h-py3.11/lib/python3.11/site-packages/pydantic
               python version: 3.11.9 (main, Apr 30 2024, 08:22:59) [Clang 15.0.0 (clang-1500.3.9.4)]
                     platform: macOS-14.4.1-arm64-arm-64bit
             related packages: mypy-1.10.0 typing_extensions-4.11.0 pydantic-extra-types-2.7.0 fastapi-0.110.3
                       commit: unknown

@tamird tamird added bug V2 Bug related to Pydantic V2 pending Awaiting a response / confirmation labels May 16, 2024
@sydney-runkle
Copy link
Member

@tamird,

Thanks for reporting this - definitely looks like a typing bug. The concern I have here is that we can't use Sequence as the type hint, as not all sequence types are JSON serializable (consider sets, for example).

Here's one possible alternative:

if TYPE_CHECKING:
    # This seems to only be necessary for mypy
    J = TypeVar('J', bound='JsonValue')
    JsonValue: TypeAlias = Union[
        List[J],
        Dict[str, J],
        str,
        bool,
        int,
        float,
        None,
    ]

Let me know your thoughts on that. I can open a PR to fix this issue if you approve of said approach as well :).

@sydney-runkle
Copy link
Member

One drawback is that this requires homogeneity in terms of the subtype of JsonValue used within a list or dict, which we might not want...

@tamird
Copy link
Author

tamird commented May 16, 2024

I'm pretty sure sets are not Sequences: https://mypy-play.net/?mypy=latest&python=3.12&gist=f71b466422216f85473dbb9880dd5f5c&flags=warn-unreachable.

What would be the problem with

    JsonValue: TypeAlias = Union[
        Sequence['JsonValue'],
        Mapping[str, 'JsonValue'],
        str,
        bool,
        int,
        float,
        None,
    ]

? Other than this discriminator having to be made more general.

@sydney-runkle
Copy link
Member

sydney-runkle commented May 16, 2024

@tamird,

Good point about sets. What about deque types? Deques are sequences, but aren't json serializable...

@tamird
Copy link
Author

tamird commented May 16, 2024

Can you give a runnable example? I don't understand why it isn't possible to serialize any sequence as a JSON array and to deserialize any JSON array as a list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug V2 Bug related to Pydantic V2 pending Awaiting a response / confirmation
Projects
None yet
Development

No branches or pull requests

2 participants