|
It's possible to introspect type hints at runtime, in the same way you can inspect e.g. a function's name or parameters. However, there is no built in way of using these type hints as a runtime-enforced check. There are a handful of libraries that can do some introspection and runtime checking, but they typically come with a significant performance cost. That said, using these sorts of tools (or trying to enable runtime type checks in general) is usually a misunderstanding of how the type checking works. Think of type annotations less as a way to enforce types, and more as a way to explicitly declare intention. If you annotate a function as taking a string and returning an int, then you're saying that, if the caller obeys their side of the deal and passes you a string, then you'll obey your side of the deal and return an integer. Moreover, a type checker can demonstrate that this is the case: that for every path through your function, assuming the input is always a string, then you will return an integer. This frees you from runtime checks entirely, because the checks can happen entirely at compile time: assuming the input types are correct, your code will return values of the correct type in return. If your whole program is type checked, then we can be confident that the whole program behaves correctly in terms of types. There are two big exceptions to this. Firstly, runtime data cannot be checked in this way, so needs to be checked at runtime. Usually this happens at very explicit boundaries: you load a TOML file, and use Pydantic to validate the file and parse it into a Python structure. If the Pydantic check succeeded, then you know the data had the correct types, and therefore that your function will behave correctly as outlined above. If it failed, you get a runtime error at the exact point that the invalid data enters your system. The danger is doing something like json.loads and then not validating the result in any way. The second exception is when you manually choose to ignore the type system and tell it what's going on. This includes type assertions where you override Mypy's expectations for what a type should be, or the use of the `Any` type. In these situations, you deliberately can the compact you've made with the type checker, but you also get a chance to handle cases of extreme dynamism in Python that the type checker can't deal with. These sections of your code will naturally be unsafe, but are usually more explicit and so can be checked more carefully in code review. The result is that, with a type system that handles this stuff well, you do not need to enforce type hints at runtime (and trying to enforce type hints at runtime is usually a sign of not leaning sufficiently into the type system in the first place). That said, "a type system that handles this stuff well" is the key phrase here - Typescript is a good example of a tool that works well as a static-only type checker, in that it gives you a lot of power to define types that match the underlying runtime. In my experience, there isn't yet a tool powerful enough to handle that in Python, which can make developing with type hints quite frustrating at times. But I don't think runtime type checking would change that at all. |