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

Type narrowing when one of two fields may be None #5350

Closed
Peter9192 opened this issue Jan 12, 2024 · 5 comments
Closed

Type narrowing when one of two fields may be None #5350

Peter9192 opened this issue Jan 12, 2024 · 5 comments
Assignees

Comments

@Peter9192
Copy link

The following example:

def a_or_b(a: int | None = None, b: int | None = None):
    assert a or b
    if not b:
        return a + 5

complains about: Operator "+" not supported for "None" Pylance (reportOptionalOperand). However, from the context it is clear that a cannot be None. Is there a way to get successful type narrowing in this case?

FYI, my real use case is a bit more complex, using pydantic validators to ensure the input is valid. Something like this, as discussed here.

from pydantic import BaseModel, model_validator

class AorB(BaseModel):
    a: int | None
    b: int | None

    @model_validator(mode='after')
    def check_a_or_b(self):
        if not self.a and not self.b:
            raise ValueError('Either a or b (or both) is required')
        return self
    
    def make_pylance_unhappy(self):
        if self.a is None:
            return self.b + 5
@github-actions github-actions bot added the needs repro Issue has not been reproduced yet label Jan 12, 2024
@erictraut
Copy link
Contributor

The behavior of pyright (the type checker upon which pylance is built) is as designed here, so this isn't a bug.

Type narrowing is applied to individual symbols (in this case, a and b). Types are not tracked conditionally between symbols (e.g. "the type of b is int if a is None but int | None if a is not None"). The assert statement in this case is not able to narrow the type of either a or b in this case. You'll see the same error if you run other static type checkers (e.g. mypy) on this code.

If you want to learn more about type narrowing, refer to this documentation.

You can change your simple example as follows:

def a_or_b(a: int | None = None, b: int | None = None):
    if not b:
        assert a
        return a + 5

In your "real" example, you can fix it by doing the following:

    def make_pylance_unhappy(self):
        if self.a is None:
            assert self.b is not None
            return self.b + 5

@debonte debonte added by design and removed needs repro Issue has not been reproduced yet labels Jan 12, 2024
@debonte debonte closed this as completed Jan 12, 2024
@Peter9192
Copy link
Author

Thanks for the swift response, I suspected as much. Just noting that I clicked "question" when creating this issue, so I never intended to report it as a bug.

@debonte
Copy link
Contributor

debonte commented Jan 12, 2024

Just noting that I clicked "question" when creating this issue, so I never intended to report it as a bug.

@rchiodo, this makes me wonder if we should have a "Question" option when creating new issues or perhaps just steer questions towards the discussions area. Or maybe we need a "question" label.

@rchiodo
Copy link
Contributor

rchiodo commented Jan 12, 2024

@rchiodo, this makes me wonder if we should have a "Question" option when creating new issues or perhaps just steer questions towards the discussions area. Or maybe we need a "question" label.

There's actually a question option already:

image

I wonder if we can just redirect that to the discussions tab?

@debonte
Copy link
Contributor

debonte commented Jan 12, 2024

Sorry, I didn't phrase that well -- what I meant was, maybe we should remove it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants