Comprehensive Guide to Typing Extensions Unlocking New Possibilities in Python Typing

Welcome to the Comprehensive Guide to typing-extensions

typing-extensions is a package designed to provide forward-compatible typing features that are not yet available in the standard Python typing module. It is essential for developers who want to use new type hinting features in older versions of Python or stay ahead with enhanced flexibility while dealing with type annotations.

Key APIs and How to Use Them

Below are some of the popular features and APIs provided by typing-extensions, including usage examples:

1. Literal: Restricting Values

Literal allows defining a variable that can only hold specific predefined values:

  from typing_extensions import Literal

  def get_status(status: Literal["active", "inactive", "pending"]) -> str:
      return f"Status is {status}"

  print(get_status("active"))  # Valid
  # print(get_status("error"))  # Invalid: Raises a type checker error

2. TypedDict: Dictionary with Typed Keys and Values

Create dictionaries where keys and values have predefined types:

  from typing_extensions import TypedDict

  class User(TypedDict):
      id: int
      username: str
      is_active: bool

  user: User = {"id": 1, "username": "john_doe", "is_active": True}

3. Protocol: Define Structural Subtyping

Protocols enable structural subtyping (duck typing) to ensure that a type implements specific methods:

  from typing_extensions import Protocol

  class Greeter(Protocol):
      def greet(self) -> str:
          pass

  class EnglishGreeter:
      def greet(self) -> str:
          return "Hello!"

  def greet_all(greeter: Greeter):
      print(greeter.greet())

  english = EnglishGreeter()
  greet_all(english)

4. Final: Prevent Overriding Variables or Methods

Make certain fields or methods immutable:

  from typing_extensions import Final

  MAX_CONNECTIONS: Final = 10

  # MAX_CONNECTIONS = 15  # This will cause a MyPy error

5. Annotated: Adding Extra Metadata to Types

Annotated allows attaching additional metadata to a type:

  from typing_extensions import Annotated

  def process_data(data: Annotated[int, "Data must be positive"]) -> int:
      if data < 0:
          raise ValueError("Invalid data")
      return data

Building a Simple Application Using typing-extensions

Let’s create a simple user management system with the help of typing-extensions:

  from typing_extensions import Literal, TypedDict, Protocol

  # Define user schema using TypedDict
  class User(TypedDict):
      id: int
      name: str
      status: Literal["active", "inactive", "pending"]

  # Define output protocol
  class UserManagerProtocol(Protocol):
      def list_users(self) -> list[User]:
          pass

  # Implement the UserManagerProtocol
  class UserManager:
      def __init__(self):
          self.users = [
              {"id": 1, "name": "Alice", "status": "active"},
              {"id": 2, "name": "Bob", "status": "inactive"},
          ]

      def list_users(self) -> list[User]:
          return self.users

  # Application Logic
  def main():
      user_manager: UserManagerProtocol = UserManager()
      users = user_manager.list_users()
      for user in users:
          print(f"User {user['name']} is {user['status']}")

  if __name__ == "__main__":
      main()

Why typing-extensions is a Must-Have

If you’re working on robust Python code with strong typing, typing-extensions becomes indispensable. It bridges the gap between different Python versions and provides cutting-edge features to make your code more intelligible and less error-prone.

Leave a Reply

Your email address will not be published. Required fields are marked *