Earlier quoted context omitted.
If you have a TypeError it's already too late. Decorating everything with beartype means I can catch things way in advance. It also forces me to specify types to all my functions which really helps LLM. See for example in wdoc, my advanced personal RAG library: https://github.com/thiswillbeyourgithub/wdoc/blob/main/wdoc/...
In advance but at runtime? I don't get the point still...
So you can have for example:
``` import numpy as np
def func(inputA: np.ndarray, inputB: np.ndarray) -> np.ndarray: return np.concat(inputA, inputB) ```
But then you want to modify the code for some reason way later and do this:
``` def func(inputA: np.ndarray, inputB: np.ndarray) -> np.ndarray: if len(inputB.shape) == 1: return np.concat(inputA, inputB) else: return inputB ```
Now imagine that inputB is directly received from some library you imported. So pyright might not be able to check its type and inputB is actually a List. Then you will never get a crash in the version 1. But its types are wrong as inputB is a List.
The version 2 on the other hand will crash as List don't have a shape attribute. Notice also how func returns inputB, propagating the wrong type.
Sure that means the code still works until you modify version 1, but any developpers or LLM that reads func would get the wrong idea about how to modify such code. Also this example is trivial but it can become much much more complicated of course.
This would not be caught in pyright but beartype would. I'm basically using beartype absolutely everywhere I can and it really made me way more sure of my code.
If you're not convinced I'm super curious about your reasoning!
PS: also try/except in python are extremely slow so figuring out types in advance is always AFAIK a good idea performance wise.