Search code examples
pythonpython-typingmypy

Unexpected Optional behavior with Python3 typing and mypy


I'm new to using typing and mypy.

With the below block of code, mypy complains that ret cannot be assigned None because Incompatible types in assignment (expression has type "None", variable has type "Tuple[Connection, Cursor]") (python-mypy).

    def __connect__(self) -> Tuple[Optional[Tuple[Conn, Cursor]], Status]:
        """Establish DB connection."""
        if self.db_type is DB_Type.SQLITE:
            conn = sqlite3.connect(self.db_name)
            cur = conn.cursor()
            ret, status = (conn, cur), Status(Code.OK)
        else:
            ret, status = None, self.INVALID_STATUS    # mypy error 
        return ret, status

But I'm defining the return type signature as Optional[Tuple[Connection, Cursor], instead of Tuple[Connection, Cursor]. So either I'm overlooking something, or there is a limitation in mypy static analysis, for which there is probably some workaround... pointers much appreciated.


Solution

  • mypy deals with untyped variables by using the type of the first assignment as the type of the variable. So having two assignments to the same untyped variable with different types is considered a type mismatch. For example:

    $ cat test.py 
    foo, bar = 1, 2
    foo, bar = None, 2
    $ mypy test.py 
    test.py:2: error: Incompatible types in assignment (expression has type "None", variable has type "int")  [assignment]
    Found 1 error in 1 file (checked 1 source file)
    

    The same issue is happening in your code, although it's less obvious: ret is assigned the type Tuple[Connection, Cursor], but another branch is assigning None to ret. The return type is irrelevant.

    One workaround is to set an explicity type ret: Optional[Tuple[Connection, Cursor]].