`NameError` from `inspect.signature()` or `obj.__annotations__` for types present only in `TYPE_CHECKING` block

Summary

The issue arises when using inspect.signature() or obj.__annotations__ to get a function signature as a string, resulting in a NameError. This error occurs because the type TracebackType is only defined within a TYPE_CHECKING block, which is not evaluated at runtime.

Root Cause

The root cause of this issue is that the TYPE_CHECKING block is only evaluated by static type checkers, not at runtime. As a result, the TracebackType is not defined when inspect.signature() or obj.__annotations__ is called, leading to a NameError. The causes of this issue include:

  • TYPE_CHECKING block not being evaluated at runtime
  • TracebackType not being defined outside the TYPE_CHECKING block
  • inspect.signature() and obj.__annotations__ requiring the type to be defined at runtime

Why This Happens in Real Systems

This issue can occur in real systems when using inspect.signature() or obj.__annotations__ to generate documentation or perform other tasks that require access to a function’s signature. The impacts of this issue include:

  • NameError exceptions being raised
  • Inability to generate accurate documentation
  • Potential issues with other tools or libraries that rely on inspect.signature() or obj.__annotations__

Real-World Impact

The real-world impact of this issue is that it can prevent developers from generating accurate documentation or performing other tasks that rely on inspect.signature() or obj.__annotations__. This can lead to:

  • Inaccurate or incomplete documentation
  • Issues with other tools or libraries that rely on inspect.signature() or obj.__annotations__
  • Increased development time and effort to work around the issue

Example or Code

from typing import TYPE_CHECKING
from inspect import signature

if TYPE_CHECKING:
    from types import TracebackType

def some_function(tb_param: TracebackType) -> str:
    return 'this is actually not important'

print(signature(some_function))

How Senior Engineers Fix It

Senior engineers can fix this issue by using the annotation_format=Format.STRING parameter when calling inspect.signature(), like this:

from inspect import signature, Format

print(signature(some_function, annotation_format=Format.STRING))

Alternatively, they can use string literals for types used only for annotations, or use from __future__ import annotations to enable postponed evaluation of annotations.

Why Juniors Miss It

Junior engineers may miss this issue because they:

  • Are not familiar with the TYPE_CHECKING block and its implications
  • Do not understand how inspect.signature() and obj.__annotations__ work
  • Do not recognize the importance of defining types at runtime
  • Are not aware of the annotation_format=Format.STRING parameter or its use case
  • Do not consider the potential impacts of using inspect.signature() or obj.__annotations__ on their code.

Leave a Comment