Basedmypy is a Python type checker that is built on top of the work done by the mypy project. It adds based functionality and breaks compatibility with the cringe parts of pep 484.
Based features include:
- Typesafe by default (optional and dynamic typing still supported)
- Baseline functionality
- Support for
Intersection
types - Default return type of
None
instead ofAny
- Generic
TypeVar
bounds - Infer parameter type from default value
- Infer overload types
- Bare literals
- Tuple literal types
See the features for a more information.
Basedmypy can be installed using pip from PyPI or from this GitHub repo:
python -m pip install -U basedmypy
Basedmypy is installed as an alternative to, and in place of, the mypy
installation:
mypy test.py
python -m mypy test.py
Have you ever tried to use Pythons type system and thought to yourself "This doesn't seem based"?
Well fret no longer as basedmypy has got you covered!
Basedmypy has baseline, baseline is based! It allows you to adopt new strictness or features without the burden of refactoring and fixing every new error, just save all current errors to the baseline file and resolve them at what ever pace you want. Only new code will report new errors.
Read more and see examples in the docs
Using the &
operator or basedtyping.Intersection
you can denote intersection types:
class Growable(ABC, Generic[T]):
@abstractmethod
def add(self, item: T): ...
class Resettable(ABC):
@abstractmethod
def reset(self): ...
def f(x: Resettable & Growable[str]):
x.reset()
x.add("first")
Mypy joins types to their common base type:
a: int
b: str
reveal_type(a if bool() else b) # Revealed type is "builtins.object"
Basedmypy joins types into unions instead:
a: int
b: str
reveal_type(a if bool() else b) # Revealed type is "int | str"
Literal
is so cumbersome! just use a bare literal instead.
class Color(Enum):
RED = auto()
a: 1 | 2
b: True | Color.RED
The default return type of functions is None
instead of Any
:
(configurable with the default_return
option.)
def f(name: str):
print(f"Hello, {name}!")
reveal_type(f) # (str) -> None
Basedmpy allows the bounds of TypeVar
s to be generic.
So you are able to have functions with polymorphic generic parameters:
E = TypeVar("E")
I = TypeVar("I", bound=Iterable[E])
def foo(i: I, e: E) -> I:
assert e not in i
return i
reveal_type(foo(["based"], "mypy")) # N: Revealed type is "list[str]"
reveal_type(foo({1, 2}, 3)) # N: Revealed type is "set[int]"
The types in overload implementations (including properties) can be inferred:
@overload
def f(a: int) -> str: ...
@overload
def f(a: str) -> int: ...
def f(a):
reveal_type(a) # int | str
return None # error: expected str | int
class A:
@property
def foo(self) -> int: ...
@foo.setter
def foo(self, value): ... # no need for annotations
Infer the type of a function parameter from its default value:
def f(a=1, b=True):
reveal_type((a, b)) # (int, bool)
Basedmypy allows denotation of tuple types with tuple literals:
a: (int, str) = (1, "a")
Basedmypy makes significant changes to error and info messages, consider:
T = TypeVar("T", bound=int)
def f(a: T, b: list[str | 1 | 2]) -> Never:
reveal_type((a, b))
reveal_type(f)
Mypy shows:
Revealed type is "Tuple[T`-1, Union[builtins.str, Literal[1], Literal[2]]]"
Revealed type is "def [T <: builtins.int] (a: T`-1, b: Union[builtins.str, Literal[1], Literal[2]]) -> <nothing>"
Basedmypy shows:
Revealed type is "(T@f, str | 1 | 2)"
Revealed type is "def [T: int] (a: T, b: str | 1 | 2) -> Never"
Feel free to start a discussion or raise an issue, we're happy to respond:
- basedmypy tracker for basedmypy issues
- basedtypeshed tracker for issues with specific modules
- basedtyping tracker for issues with the 'basedtyping' package (runtime functionality).