Skip to content

Instantly share code, notes, and snippets.

@Hultner
Created March 5, 2021 12:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hultner/993f5d73de2457b579eb5c4e62c416ff to your computer and use it in GitHub Desktop.
Save Hultner/993f5d73de2457b579eb5c4e62c416ff to your computer and use it in GitHub Desktop.
Use assignment with bound TypeVar in conditional.
from typing import Type, TypeVar
class Base:
common: str = "Data"
class ImportModel(Base):
pass
class RunModel(Base):
pass
ModelType = TypeVar("ModelType", bound=Base)
def process(model: Type[ModelType]) -> ModelType:
return model()
def use_process(is_run: bool, is_imported: bool) -> str:
if is_run and is_imported:
raise ValueError("Ambigious")
# This is required in this case as mypy can't infer the type of x properly
# in this case and will infer it to RunModel unless explicitly defined
# before.
#
# To do this will result in the following error
# bound_base_conditional.py:37: error: Incompatible types in assignment (expression has type "ImportModel", variable has type "RunModel")
x: Base
if is_run:
x = process(RunModel)
elif is_imported:
x = process(ImportModel)
else:
raise ValueError("Need to be either imported or run")
return x.common
print(__name__)
print(
use_process(
is_run=(__name__ == "__main__"),
is_imported=(__name__ == "bound_base_conditional"),
)
)
@Hultner
Copy link
Author

Hultner commented Mar 5, 2021

I got caught with this today where the inferred type worked in some cases but not when the conditional was based on external/run-time arguments. In those cases the variable needs to be declared with the base-class type before assignment or mypy will assume it to the first assignment type.

Easy fix when one sees it but a bit tricky semantics when it suddenly stops working. I was first thinking I had done something weird with my TypeVar.

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