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

Protocol Type Incompatibility: str not assignable to str | None despite being less strict #9595

Closed
Filimoa opened this issue Dec 17, 2024 · 1 comment
Labels
as designed Not a bug, working as intended bug Something isn't working

Comments

@Filimoa
Copy link

Filimoa commented Dec 17, 2024

Describe the bug
In the following example, you will see that although the protocol is less strict than what's being passed (str), it raises an error that the type is not exactly str | None

Code or Screenshots

from dataclasses import dataclass
from typing import Protocol

class SampleProtocol(Protocol):
    name: str | None

@dataclass
class SampleObj:
    name: str # more strict than the protocol

def print_age(obj: SampleProtocol):
    if obj.name:
        print(obj.name)
    else:
        print("No name")


var = SampleObj(name="John")
print_age(var) # Type "str" is not assignable to type "str | None"

print_age will have the following error:

Argument of type "SampleObj" cannot be assigned to parameter "obj" of type "SampleProtocol" in function "print_age"
  "SampleObj" is incompatible with protocol "SampleProtocol"
    "name" is invariant because it is mutable
    "name" is an incompatible type
      Type "str" is not assignable to type "str | None"
        "str" is not assignable to "None"  (reportArgumentType)

I thought maybe this is because the dataclass is mutable, but making it a frozen dataclass does not change anything.

VS Code extension or command-line
Tried this in vscode and the online playground

@Filimoa Filimoa added the bug Something isn't working label Dec 17, 2024
@erictraut
Copy link
Collaborator

Pyright is working correctly here and in accordance with the typing spec, so this isn't a bug.

Your protocol indicates that the type must accept a value of type str | None for the name field. That means someone could attempt to write a None value to this field and still comply with the protocol. However, this would violate the SampleObj type. For example, consider if print_age included the statement obj.name = None.

Mutable attributes are necessarily treated as invariant by a type checker. It sounds like you expect it to be treated as covariant, which would allow for the "less strict" behavior that you're looking for.

There is a draft PEP 767 that allows an attribute for attributes to be marked ReadOnly. Read-only attributes can be safely treated as covariant. This PEP is still in the development phase and hasn't yet been implemented in pyright, but it is something I plan to add soon.

@erictraut erictraut added the as designed Not a bug, working as intended label Dec 17, 2024
@erictraut erictraut closed this as not planned Won't fix, can't repro, duplicate, stale Dec 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
as designed Not a bug, working as intended bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants