Search code examples
pythonmypypython-typing

Dealing with lack of non-null assertion operator in Python


I would like to allow Mypy' strict_optional flag. However, consider this:

emails = [get_user(uuid).email for uuid in user_uuids]

where get_user could return None in theory, but in this use case, I know it can't (and am fine with getting an exception if it did). This would have to become:

emails = []
for uuid in user_uuids:
    user = get_user(uuid)
    assert user is not None
    emails.append(user.email)

In TypeScript, there's a non-null assertion operator which would allows you to just add a ! (as in getUser(uuid)!.email).

Is there any better or more elegant way to handle this problem?


Solution

  • I found two ways that I think get close to a non-null assertion operator and therefore cast type Optional[User] to User:

    1) Use typing.cast

    from typing import cast
    
    emails = [cast(User, get_user(uuid)).email for uuid in user_uuids]
    

    2) Imitate non-null assertion with function

    For Python 3.12+:

    def not_none[T](obj: T | None) -> T:
        assert obj is not None
        return obj
    
    emails = [not_none(get_user(uuid)).email for uuid in user_uuids]
    

    or older Python versions:

    from typing import Optional, TypeVar
    
    T = TypeVar('T')
    
    def not_none(obj: Optional[T]) -> T:
        assert obj is not None
        return obj
    
    emails = [not_none(get_user(uuid)).email for uuid in user_uuids]