Skip to content

Instantly share code, notes, and snippets.

@reillysiemens
Created December 19, 2018 23:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save reillysiemens/4f60233f321d84b5db911f00b17757a0 to your computer and use it in GitHub Desktop.
Save reillysiemens/4f60233f321d84b5db911f00b17757a0 to your computer and use it in GitHub Desktop.
Python Typing Optional String Callback

The Python here works just fine, but mypy doesn't like it...

example.py:14: error: Argument 1 to "callback" has incompatible type "Optional[str]"; expected "str"

The if url is None: conditional should prevent the url in the else block from ever being None, but maybe it doesn't work like that? Or I'm just wrong...

from typing import Callable, Optional
def callback(url: str, value: int) -> None:
print(f"Did something with {url} and {value}")
def build_callback(url: Optional[str]) -> Callable[[int], None]:
if url is None:
def cb(value: int) -> None:
pass
else:
def cb(value: int) -> None:
callback(url, value)
return cb
build_callback(None)(42) # noop
build_callback('https://lol.wut')(42)
@reillysiemens
Copy link
Author

reillysiemens commented Dec 19, 2018

The answer here is kind of ugly... The mypy type checker doesn't appear to be aware of the context for url across the function boundary of cb, so it thinks url still has type Optional[str] even though that's made impossible by the conditional. This can be "resolved" by defining the callback for the str case as

def cb(value: int) -> None:
    # This assert forces the type checker to be aware of the true type of `url`.
    assert url is not None
    callback(url, value)

Gross, but it works and the type checker is satisfied. Thanks again to @RadicalZephyr for the assist.

@Raefey
Copy link

Raefey commented Nov 22, 2019

I'm not so keen on the assert in production code here.

I prefer to have an if starement
if url is None:
then raise some blank error
raise ValueError

as you said, its impossible for this error to actually get raised, but it should satisfy mypy, and I think it looks a bit nicer than the assertion :)

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