Ctrl + k

or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/typeshed-client@2.8.x

docs

index.md
tile.json

tessl/pypi-typeshed-client

tessl install tessl/pypi-typeshed-client@2.8.4

A library for accessing stubs in typeshed.

finding-stubs.mddocs/reference/

Finding Stub Files

Functions for locating stub files for Python modules. The finder module implements PEP 561 import resolution ordering, supporting typeshed stubs, stub packages (packages ending in -stubs), and inline stubs within regular packages.

Quick Reference

FunctionReturn TypeCan Return None?
get_stub_file(module_name)Optional[Path]Yes
get_stub_ast(module_name)Optional[ast.Module]Yes
get_all_stub_files()Iterable[tuple[str, Path]]No (but may be empty)
find_typeshed()PathNo
parse_stub_file(path)ast.ModuleNo (raises on error)

Capabilities

Get Stub File

Returns the path to a module's stub file.

def get_stub_file(module_name: str, *, search_context: Optional[SearchContext] = None) -> Optional[Path]:
    """
    Return the path to the stub file for this module, if any.

    Args:
        module_name (str): Module name as a dotted string (e.g., 'typing', 'collections.abc')
        search_context (SearchContext, optional): Context for finding stubs. Uses default if None.

    Returns:
        Path: Path to the stub file, or None if no stub is found

    Example:
        path = get_stub_file('typing')
        # Returns: Path('/path/to/typeshed/stdlib/typing.pyi')
    """

Return Value Details:

  • Returns Path object pointing to .pyi stub file
  • Returns Path to .py file if allow_py_files=True in SearchContext and no .pyi exists
  • Returns None if module not found in any search location
  • Path is absolute, not relative

Performance: O(n) where n is number of directories in search path

Get Stub AST

Returns the parsed AST for a module's stub file.

def get_stub_ast(module_name: str, *, search_context: Optional[SearchContext] = None) -> Optional[ast.Module]:
    """
    Return the AST for the stub for the given module name.

    Args:
        module_name (str): Module name as a dotted string
        search_context (SearchContext, optional): Context for finding stubs. Uses default if None.

    Returns:
        ast.Module: Parsed AST of the stub file, or None if no stub is found

    Example:
        ast_tree = get_stub_ast('collections')
        # Returns: ast.Module object with the parsed stub
    """

Return Value Details:

  • Returns ast.Module object from Python's ast module
  • Returns None if module not found
  • Raises SyntaxError if stub file has invalid Python syntax
  • AST represents the structure of the entire stub file

Performance: File I/O + parsing cost on first call; consider caching result if needed

Get All Stub Files

Returns paths to all available stub files for the configured Python version.

def get_all_stub_files(search_context: Optional[SearchContext] = None) -> Iterable[tuple[str, Path]]:
    """
    Return paths to all stub files for a given Python version.

    Args:
        search_context (SearchContext, optional): Context for finding stubs. Uses default if None.

    Returns:
        Iterable[tuple[str, Path]]: Pairs of (module name, module path)

    Example:
        for module_name, path in get_all_stub_files():
            print(f"{module_name}: {path}")
        # Prints: typing: /path/to/typeshed/stdlib/typing.pyi
        #         collections: /path/to/typeshed/stdlib/collections/__init__.pyi
        #         ...
    """

Return Value Details:

  • Iterator of tuples: (module_name: str, stub_path: Path)
  • Module names are dotted strings (e.g., 'collections.abc')
  • Never returns None, but iterator may be empty
  • Includes all modules from typeshed, stub packages, and inline stubs
  • Results depend on Python version in SearchContext

Performance: Scans all directories in search path; may be slow for large environments

Usage Note: Results are not cached; convert to list if multiple iterations needed

Deprecated Functions

Get Search Path (Deprecated)

This function is deprecated and may be removed in a future version. The function is marked with the @deprecated decorator for type checkers and IDEs, but does not emit runtime warnings when called.

@deprecated("This function is not useful with the current layout of typeshed. It may be removed from a future version of typeshed-client.")
def get_search_path(typeshed_dir: Path, pyversion: tuple[int, int]) -> tuple[Path, ...]:
    """
    Returns search paths for given typeshed and Python version.

    This function is not useful with the current layout of typeshed.
    Marked as deprecated for type checkers but does not emit runtime warnings.

    Args:
        typeshed_dir (Path): Path to typeshed directory
        pyversion (tuple[int, int]): Python version as (major, minor)

    Returns:
        tuple[Path, ...]: Tuple of search paths
    """

Agent Guidance: Avoid using this function; use get_search_context() instead for configuring search paths.

Stub Resolution Order

The finder follows PEP 561 import resolution ordering:

  1. Typeshed: Searches the bundled or custom typeshed directory first
    • Standard library stubs in stdlib/
    • Third-party package stubs in stubs/
  2. Stub Packages: Searches for packages ending in -stubs on the search path
    • Example: requests-stubs for requests module
  3. Inline Stubs: Searches for .pyi files in regular packages
    • Example: package/module.pyi or package/py.typed marker
  4. Python Files (optional): Searches for .py files if allow_py_files is True
    • Only when no .pyi stub exists

Version Handling: Version-specific stubs are handled using the VERSIONS file in typeshed, which maps modules to minimum and maximum Python versions.

Platform Handling: Platform-specific stubs are determined by directory structure and conditional imports.

Usage Examples

Basic Stub Finding

from typeshed_client import get_stub_file

# Find typing module stub
path = get_stub_file('typing')
if path:
    print(f"Found: {path}")  # /path/to/typeshed/stdlib/typing.pyi
else:
    print("Stub not found")

# Find collections.abc module stub
path = get_stub_file('collections.abc')
if path:
    print(f"Found: {path}")  # /path/to/typeshed/stdlib/collections/abc.pyi

# Try to find a non-existent module
path = get_stub_file('nonexistent_module')
print(path)  # None

Key Points:

  • Always check for None before using the path
  • Path is absolute, can be used directly with open() or Path methods
  • Dotted module names are supported (e.g., 'collections.abc')

Getting Stub AST

from typeshed_client import get_stub_ast
import ast

# Get the AST for the typing module
tree = get_stub_ast('typing')

if tree:
    # Walk the AST to find all class definitions
    classes = []
    for node in ast.walk(tree):
        if isinstance(node, ast.ClassDef):
            classes.append(node.name)
    print(f"Found classes: {classes}")
    
    # Count function definitions
    func_count = sum(1 for node in ast.walk(tree) if isinstance(node, ast.FunctionDef))
    print(f"Found {func_count} function definitions")
else:
    print("Stub not found")

Key Points:

  • Returns full ast.Module object
  • Use ast.walk() to traverse all nodes
  • Check for specific node types with isinstance()
  • Always handle None case

Iterating All Stubs

from typeshed_client import get_all_stub_files

# Get all available stubs
all_stubs = list(get_all_stub_files())
print(f"Total stubs available: {len(all_stubs)}")

# Filter to specific modules
collections_stubs = []
for module_name, path in get_all_stub_files():
    if module_name.startswith('collections'):
        collections_stubs.append((module_name, path))
        print(f"{module_name}: {path}")

print(f"\nFound {len(collections_stubs)} collections-related stubs")

Key Points:

  • Returns iterator; convert to list if needed multiple times
  • Module names include submodules (e.g., 'collections.abc')
  • Can be filtered by prefix or pattern matching

Custom Typeshed Location

from pathlib import Path
from typeshed_client import get_search_context, get_stub_file

# Use a custom typeshed location
custom_typeshed = Path('/custom/typeshed')
if custom_typeshed.exists():
    ctx = get_search_context(typeshed=custom_typeshed)
    path = get_stub_file('typing', search_context=ctx)
    if path:
        print(f"Using custom typeshed: {path}")
        # Verify it's from our custom location
        if str(custom_typeshed) in str(path):
            print("✓ Using correct typeshed")
else:
    print("Custom typeshed not found")

Key Points:

  • Custom typeshed must have same structure as official typeshed
  • Useful for testing unreleased typeshed changes
  • Verify path to ensure correct typeshed is being used

Version-Specific Stub Finding

from typeshed_client import get_search_context, get_stub_file

# Find stubs for Python 3.9
ctx_39 = get_search_context(version=(3, 9))
path_39 = get_stub_file('asyncio', search_context=ctx_39)

# Find stubs for Python 3.11
ctx_311 = get_search_context(version=(3, 11))
path_311 = get_stub_file('asyncio', search_context=ctx_311)

# The paths may be the same, but version checks inside stubs will differ
if path_39 and path_311:
    print(f"Python 3.9: {path_39}")
    print(f"Python 3.11: {path_311}")
    print(f"Same file: {path_39 == path_311}")
    
# Check for version-specific modules
tomllib_39 = get_stub_file('tomllib', search_context=ctx_39)
tomllib_311 = get_stub_file('tomllib', search_context=ctx_311)

print(f"tomllib in 3.9: {tomllib_39 is not None}")  # False (added in 3.11)
print(f"tomllib in 3.11: {tomllib_311 is not None}")  # True

Key Points:

  • Same stub file may be used for different versions
  • Version affects which conditional definitions are included
  • Some modules only exist in certain Python versions

Finding Third-Party Stubs

from pathlib import Path
from typeshed_client import get_search_context, get_stub_file

# Search for stubs in a virtual environment
venv_site_packages = Path('./venv/lib/python3.11/site-packages')

if venv_site_packages.exists():
    ctx = get_search_context(search_path=[venv_site_packages])
    
    # Try to find a third-party package stub
    path = get_stub_file('requests', search_context=ctx)
    if path:
        print(f"Found requests stub: {path}")
        # Check if it's a stub package
        if '-stubs' in str(path):
            print("✓ Using stub package")
    else:
        print("No stub found for requests")
        
    # Find inline stub
    path = get_stub_file('mypy', search_context=ctx)
    if path:
        print(f"Found mypy stub: {path}")
        if path.suffix == '.pyi':
            print("✓ Using inline stub (.pyi)")
else:
    print("Virtual environment not found")

Key Points:

  • Stub packages are named {package}-stubs
  • Inline stubs are .pyi files alongside .py files
  • Search path should include site-packages directory

Checking Stub File Existence

from typeshed_client import get_stub_file

modules_to_check = ['typing', 'collections', 'asyncio', 'nonexistent']

results = {}
for module_name in modules_to_check:
    path = get_stub_file(module_name)
    results[module_name] = path is not None
    status = "✓ Found" if path else "✗ Not found"
    print(f"{module_name}: {status}")

# Summary
found_count = sum(1 for exists in results.values() if exists)
print(f"\nFound {found_count}/{len(modules_to_check)} modules")

Key Points:

  • Simple boolean check: path is not None
  • Useful for validating module availability
  • Can be used to detect missing stub files

Error Handling

from typeshed_client import get_stub_file, get_stub_ast
from pathlib import Path

module_name = 'some_module'

# Step 1: Check if stub file exists
stub_path = get_stub_file(module_name)

if stub_path is None:
    print(f"Warning: No stub found for {module_name}")
else:
    # Step 2: Verify the file actually exists
    from typeshed_client.finder import safe_exists
    if not safe_exists(stub_path):
        print(f"Error: Stub path {stub_path} does not exist")
    else:
        # Step 3: Try to parse it
        try:
            from typeshed_client.finder import parse_stub_file
            tree = parse_stub_file(stub_path)
            print(f"✓ Successfully parsed {module_name} stub")
            
            # Verify it's a Module node
            import ast
            if isinstance(tree, ast.Module):
                print(f"✓ Valid AST Module with {len(tree.body)} top-level statements")
        except SyntaxError as e:
            print(f"✗ Syntax error in stub: {e}")
            print(f"  Line {e.lineno}: {e.text}")
        except Exception as e:
            print(f"✗ Error parsing stub: {e}")

Key Points:

  • Check for None first
  • Verify file existence with safe_exists()
  • Catch SyntaxError for malformed stubs
  • Log detailed error information for debugging

Utility Functions

Find Typeshed

Returns the path to the bundled typeshed directory.

def find_typeshed() -> Path:
    """
    Return the path to the bundled typeshed directory.

    Returns:
        Path: Path to the bundled typeshed directory included with typeshed_client

    Example:
        from typeshed_client.finder import find_typeshed

        typeshed_path = find_typeshed()
        print(typeshed_path)  # Path to bundled typeshed
    """

Return Value Details:

  • Always returns a Path object (never None)
  • Points to the typeshed directory bundled with typeshed_client
  • Path is absolute
  • Directory structure: typeshed/stdlib/ and typeshed/stubs/

Usage Example:

from typeshed_client.finder import find_typeshed

typeshed_path = find_typeshed()
print(f"Bundled typeshed: {typeshed_path}")

# Explore structure
stdlib_dir = typeshed_path / 'stdlib'
stubs_dir = typeshed_path / 'stubs'

print(f"Standard library stubs: {stdlib_dir.exists()}")
print(f"Third-party stubs: {stubs_dir.exists()}")

Parse Stub File

Parses a stub file at the given path into an AST.

def parse_stub_file(path: Path) -> ast.Module:
    """
    Parse a stub file into an AST.

    Args:
        path (Path): Path to the stub file to parse

    Returns:
        ast.Module: Parsed AST of the stub file

    Raises:
        SyntaxError: If the stub file contains invalid Python syntax
        FileNotFoundError: If the file does not exist
        IOError: If the file cannot be read

    Example:
        from pathlib import Path
        from typeshed_client.finder import parse_stub_file

        stub_path = Path('/path/to/stub.pyi')
        tree = parse_stub_file(stub_path)
        # Returns: ast.Module object
    """

Return Value Details:

  • Returns ast.Module object (never None)
  • Raises exceptions on errors (doesn't return None)
  • File is read with UTF-8 encoding
  • Parsed with ast.parse() in stub mode

Error Handling:

from pathlib import Path
from typeshed_client.finder import parse_stub_file

stub_path = Path('/path/to/stub.pyi')

try:
    tree = parse_stub_file(stub_path)
    print(f"✓ Parsed successfully")
except FileNotFoundError:
    print(f"✗ File not found: {stub_path}")
except SyntaxError as e:
    print(f"✗ Syntax error: {e}")
except Exception as e:
    print(f"✗ Unexpected error: {e}")

Safe File System Operations

These utility functions provide safe file system operations that handle errors gracefully.

def safe_exists(path: Path) -> bool:
    """
    Check if a path exists, returning False on OSError.

    Args:
        path (Path): Path to check

    Returns:
        bool: True if path exists, False otherwise or on error
    """

Behavior:

  • Returns True if path exists
  • Returns False if path doesn't exist
  • Returns False on permission errors or other OSErrors
  • Never raises exceptions
def safe_is_dir(path: Union[Path, os.DirEntry]) -> bool:
    """
    Check if a path is a directory, returning False on OSError.

    Args:
        path (Path | os.DirEntry): Path to check

    Returns:
        bool: True if path is a directory, False otherwise or on error
    """

Behavior:

  • Returns True if path is a directory
  • Returns False if path is not a directory
  • Returns False on errors
  • Accepts both Path and os.DirEntry objects
def safe_is_file(path: Union[Path, os.DirEntry]) -> bool:
    """
    Check if a path is a file, returning False on OSError.

    Args:
        path (Path | os.DirEntry): Path to check

    Returns:
        bool: True if path is a file, False otherwise or on error
    """

Behavior:

  • Returns True if path is a file
  • Returns False if path is not a file
  • Returns False on errors
  • Accepts both Path and os.DirEntry objects
def safe_scandir(path: os.PathLike[str]) -> Iterable[os.DirEntry]:
    """
    Iterate over directory entries, ignoring OSError.

    Args:
        path (PathLike[str]): Directory path to scan

    Returns:
        Iterable[os.DirEntry]: Iterator over directory entries

    Example:
        from typeshed_client.finder import safe_scandir

        for entry in safe_scandir('/some/directory'):
            print(entry.name)
    """

Behavior:

  • Returns iterator of os.DirEntry objects
  • Silently skips entries that cause OSErrors
  • Returns empty iterator if directory doesn't exist
  • Never raises exceptions

Usage Example:

from pathlib import Path
from typeshed_client.finder import safe_exists, safe_is_file, safe_is_dir, find_typeshed

# Check if a stub file exists safely
stub_path = Path('/path/to/typing.pyi')
if safe_exists(stub_path):
    if safe_is_file(stub_path):
        print("✓ Stub file exists and is a file")
    elif safe_is_dir(stub_path):
        print("✗ Path is a directory, not a file")
else:
    print("✗ Stub file not found")

# Get bundled typeshed location
typeshed_dir = find_typeshed()
stdlib_dir = typeshed_dir / 'stdlib'
if safe_exists(stdlib_dir) and safe_is_dir(stdlib_dir):
    print(f"✓ Standard library stubs at: {stdlib_dir}")
else:
    print("✗ Standard library stubs not found")

Scanning Directories Safely

from typeshed_client.finder import find_typeshed, safe_scandir, safe_is_dir, safe_is_file

# List all directories in typeshed stdlib
typeshed = find_typeshed()
stdlib = typeshed / 'stdlib'

print("Directories in stdlib:")
dir_count = 0
file_count = 0
for entry in safe_scandir(stdlib):
    if safe_is_dir(entry):
        print(f"  📁 {entry.name}")
        dir_count += 1
    elif safe_is_file(entry):
        file_count += 1

print(f"\nTotal: {dir_count} directories, {file_count} files")

Key Points:

  • safe_scandir() returns os.DirEntry objects, not Path
  • os.DirEntry objects can be used with safe_is_dir() and safe_is_file()
  • Access name with entry.name, full path with entry.path
  • Very efficient for large directories (doesn't stat all entries)

Advanced Patterns

Pattern: Batch Stub Finding

from typeshed_client import get_stub_file, get_search_context

# Find stubs for multiple modules efficiently
ctx = get_search_context()  # Create context once
modules = ['typing', 'collections', 'asyncio', 'dataclasses']

stub_paths = {}
for module in modules:
    path = get_stub_file(module, search_context=ctx)
    if path:
        stub_paths[module] = path

print(f"Found {len(stub_paths)} out of {len(modules)} stubs")
for module, path in stub_paths.items():
    print(f"  {module}: {path.name}")

Pattern: Analyze Stub Coverage

from typeshed_client import get_all_stub_files, get_search_context
from collections import defaultdict

# Check which standard library modules have stubs
ctx = get_search_context()
all_stubs = list(get_all_stub_files(search_context=ctx))

print(f"Total stub files: {len(all_stubs)}")

# Group by top-level module
top_level = defaultdict(int)

for module_name, _ in all_stubs:
    top = module_name.split('.')[0]
    top_level[top] += 1

print("\nTop-level modules with stubs:")
for module, count in sorted(top_level.items()):
    print(f"  {module}: {count} file(s)")

# Find largest module families
top_5 = sorted(top_level.items(), key=lambda x: x[1], reverse=True)[:5]
print("\nTop 5 module families:")
for module, count in top_5:
    print(f"  {module}: {count} files")

Pattern: Compare ASTs Across Versions

from typeshed_client import get_search_context, get_stub_ast
import ast

def count_definitions(tree):
    """Count classes and functions in AST."""
    classes = sum(1 for n in ast.walk(tree) if isinstance(n, ast.ClassDef))
    functions = sum(1 for n in ast.walk(tree) if isinstance(n, ast.FunctionDef))
    return classes, functions

# Get ASTs for different Python versions
versions = [(3, 9), (3, 12)]
results = {}

for version in versions:
    ctx = get_search_context(version=version)
    tree = get_stub_ast('typing', search_context=ctx)
    if tree:
        classes, functions = count_definitions(tree)
        results[version] = {'classes': classes, 'functions': functions}
        print(f"Python {version[0]}.{version[1]}: {classes} classes, {functions} functions")

# Compare
if len(results) == 2:
    v1, v2 = versions
    class_diff = results[v2]['classes'] - results[v1]['classes']
    func_diff = results[v2]['functions'] - results[v1]['functions']
    print(f"\nChanges from {v1} to {v2}:")
    print(f"  Classes: {class_diff:+d}")
    print(f"  Functions: {func_diff:+d}")

Pattern: Validate Module Exists

from typeshed_client import get_stub_file

def has_stub_support(module_name: str) -> bool:
    """Check if a module has type stub support."""
    return get_stub_file(module_name) is not None

# Use in validation logic
required_modules = ['typing', 'dataclasses', 'pydantic']
all_available = True

for module in required_modules:
    if has_stub_support(module):
        print(f"✓ {module} has stub support")
    else:
        print(f"✗ {module} lacks stub support")
        all_available = False

if all_available:
    print("\n✓ All required modules have stubs")
else:
    print("\n✗ Some required modules lack stubs")

Pattern: Extract Module Docstrings

from typeshed_client import get_stub_ast
import ast

def get_module_docstring(module_name: str) -> str | None:
    """Extract the docstring from a module's stub."""
    tree = get_stub_ast(module_name)
    if tree:
        return ast.get_docstring(tree)
    return None

# Get docstrings from standard library modules
modules = ['typing', 'collections', 'asyncio', 'dataclasses']
for module in modules:
    doc = get_module_docstring(module)
    if doc:
        first_line = doc.split('\n')[0]
        print(f"{module}: {first_line}")
    else:
        print(f"{module}: No docstring")

Pattern: Find Package Structure

from typeshed_client import get_all_stub_files, get_search_context

def get_package_structure(package_name: str) -> dict:
    """Get all submodules of a package."""
    ctx = get_search_context()
    structure = {
        'submodules': [],
        'total': 0
    }
    
    prefix = f"{package_name}."
    for module_name, path in get_all_stub_files(search_context=ctx):
        if module_name == package_name or module_name.startswith(prefix):
            structure['submodules'].append(module_name)
            structure['total'] += 1
    
    return structure

# Analyze collections package
collections_structure = get_package_structure('collections')
print(f"collections package structure:")
print(f"  Total modules: {collections_structure['total']}")
print(f"  Submodules:")
for submodule in sorted(collections_structure['submodules']):
    print(f"    - {submodule}")

Pattern: Detect Stub Type

from typeshed_client import get_stub_file, get_search_context
from pathlib import Path

def detect_stub_type(module_name: str) -> str:
    """Determine what kind of stub a module has."""
    path = get_stub_file(module_name)
    
    if path is None:
        return "no_stub"
    
    path_str = str(path)
    
    # Check if from typeshed
    from typeshed_client.finder import find_typeshed
    typeshed_dir = find_typeshed()
    if str(typeshed_dir) in path_str:
        if 'stdlib' in path_str:
            return "typeshed_stdlib"
        elif 'stubs' in path_str:
            return "typeshed_thirdparty"
    
    # Check if stub package
    if '-stubs' in path_str:
        return "stub_package"
    
    # Check if inline stub
    if path.suffix == '.pyi':
        return "inline_pyi"
    
    # Must be .py file (if allow_py_files=True)
    if path.suffix == '.py':
        return "python_source"
    
    return "unknown"

# Test with various modules
modules = ['typing', 'collections', 'requests', 'mypy']
for module in modules:
    stub_type = detect_stub_type(module)
    print(f"{module}: {stub_type}")

Best Practices for Agents

Core Principles

  1. Always check for None: get_stub_file() and get_stub_ast() return None when a module is not found
# CORRECT
path = get_stub_file('module')
if path is not None:
    # Use path
    pass

# INCORRECT
path = get_stub_file('module')
with open(path) as f:  # May raise TypeError if path is None
    content = f.read()
  1. Reuse SearchContext: Create a SearchContext once and reuse it across multiple calls for consistency
# CORRECT - efficient
ctx = get_search_context(version=(3, 11))
path1 = get_stub_file('typing', search_context=ctx)
path2 = get_stub_file('collections', search_context=ctx)

# LESS EFFICIENT
path1 = get_stub_file('typing', search_context=get_search_context(version=(3, 11)))
path2 = get_stub_file('collections', search_context=get_search_context(version=(3, 11)))
  1. Use safe file operations: Prefer safe_exists(), safe_is_file(), etc. over direct path operations to avoid exceptions
# CORRECT - handles errors gracefully
from typeshed_client.finder import safe_exists, safe_is_file

if safe_exists(path) and safe_is_file(path):
    # Use path
    pass

# MAY RAISE - OSError on permission issues
if path.exists() and path.is_file():
    # Use path
    pass

Version Handling

  1. Handle version differences: Be aware that stub availability may differ across Python versions
# CORRECT - check each version
versions = [(3, 9), (3, 10), (3, 11)]
availability = {}

for version in versions:
    ctx = get_search_context(version=version)
    path = get_stub_file('tomllib', search_context=ctx)
    availability[version] = path is not None

print(f"tomllib added in: {[v for v, avail in availability.items() if avail]}")
  1. Check file existence: Even when you get a path from get_stub_file(), verify it exists with safe_exists()
# CORRECT - defensive check
from typeshed_client.finder import safe_exists

path = get_stub_file('module')
if path is not None and safe_exists(path):
    # Now safe to use
    pass

Resolution Order

  1. Consider PEP 561 order: Remember that stub packages take precedence over inline stubs
# Understanding resolution:
# 1. Typeshed (stdlib or stubs/)
# 2. Stub packages (package-stubs)
# 3. Inline stubs (.pyi files)
# 4. Python files (.py if allow_py_files=True)

# To find what type of stub was found:
path = get_stub_file('requests')
if path:
    if '-stubs' in str(path):
        print("Using stub package")
    elif path.suffix == '.pyi':
        print("Using inline stub")
    elif path.suffix == '.py':
        print("Using Python source")

Performance

  1. Batch operations: When checking multiple modules, create a SearchContext once and reuse it
# CORRECT - batch efficiently
ctx = get_search_context()
modules = ['typing', 'collections', 'asyncio', 'dataclasses']
results = {}

for module in modules:
    results[module] = get_stub_file(module, search_context=ctx)
  1. Parse safely: Wrap parse_stub_file() calls in try-except to handle syntax errors gracefully
# CORRECT - handle errors
from typeshed_client.finder import parse_stub_file

try:
    tree = parse_stub_file(path)
    # Use tree
except SyntaxError as e:
    print(f"Invalid stub syntax: {e}")
except Exception as e:
    print(f"Error parsing: {e}")

Iterator Handling

  1. Convert iterators when needed: get_all_stub_files() returns an iterator; convert to list for multiple passes
# CORRECT - convert once
all_stubs = list(get_all_stub_files())
print(f"Total: {len(all_stubs)}")
for module_name, path in all_stubs:
    # Process
    pass

# INCORRECT - iterator exhausted after first loop
all_stubs = get_all_stub_files()  # Iterator
for module_name, path in all_stubs:
    pass
for module_name, path in all_stubs:  # Empty, iterator exhausted
    pass

Edge Cases

  1. Handle package vs module: Some modules are packages with __init__.pyi
# Module can be:
# - Single file: typing.pyi
# - Package: collections/__init__.pyi
# - Submodule: collections/abc.pyi

path = get_stub_file('collections')
if path:
    if path.name == '__init__.pyi':
        print(f"Package: {path.parent.name}")
    else:
        print(f"Module: {path.stem}")