tessl install tessl/pypi-typeshed-client@2.8.4A library for accessing stubs in typeshed.
Functions for parsing stub files into structured dictionaries that map names to their AST nodes. The parser handles exports, imports, overloads, and conditional definitions based on Python version and platform checks.
| Function | Return Type | Can Return None? |
|---|---|---|
get_stub_names(module_name) | Optional[NameDict] | Yes |
parse_ast(ast, ctx, module_name) | NameDict | No |
get_import_star_names(module_name, *, search_context) | Optional[list[str]] | Yes |
get_dunder_all_from_info(info) | Optional[list[str]] | Yes |
Returns a dictionary of all names defined in a module's stub.
def get_stub_names(module_name: str, *, search_context: Optional[SearchContext] = None) -> Optional[NameDict]:
"""
Given a module name, return a dictionary of names defined in that module.
Args:
module_name (str): Module name as a dotted string (e.g., 'typing', 'collections.abc')
search_context (SearchContext, optional): Context for finding and parsing stubs. Uses default if None.
Returns:
NameDict: Dictionary mapping name strings to NameInfo objects, or None if module not found
Example:
names = get_stub_names('collections')
if names:
for name, info in names.items():
if info.is_exported:
print(f"{name}: exported={info.is_exported}")
"""Return Value Details:
NameDict (dict[str, NameInfo]) mapping names to their informationNone if module not found'OrderedDict', not 'collections.OrderedDict')Performance: Parses stub file; results should be cached if used multiple times
Parses an AST into a dictionary of names, handling conditional definitions and imports.
def parse_ast(
ast: ast.AST,
search_context: SearchContext,
module_name: ModulePath,
*,
is_init: bool = False,
file_path: Optional[Path] = None,
is_py_file: bool = False
) -> NameDict:
"""
Parse an AST into a dictionary of names.
Args:
ast (ast.AST): AST to parse
search_context (SearchContext): Context for parsing (version, platform)
module_name (ModulePath): Module path as tuple of strings
is_init (bool, optional): Whether this is an __init__ file. Default: False
file_path (Path, optional): Path to source file for error reporting
is_py_file (bool, optional): Whether this is a .py file (vs .pyi). Default: False
Returns:
NameDict: Dictionary mapping names to NameInfo objects
Example:
from typeshed_client import get_stub_ast, get_search_context, ModulePath
from typeshed_client.parser import parse_ast
ast_tree = get_stub_ast('typing')
ctx = get_search_context()
names = parse_ast(ast_tree, ctx, ModulePath(('typing',)))
"""Return Value Details:
NameDict (never None)search_context.version and search_context.platform@overload decorators by creating OverloadedName objectsImportedName objectsRaises: InvalidStub if raise_on_warnings=True and parser encounters issues
A NameDict is a dictionary mapping name strings to NameInfo objects:
from typeshed_client import get_stub_names
names = get_stub_names('typing')
# Structure:
# names: NameDict = {
# 'function_name': NameInfo(
# name='function_name',
# is_exported=True,
# ast=<ast.FunctionDef>,
# child_nodes=None
# ),
# 'ClassName': NameInfo(
# name='ClassName',
# is_exported=True,
# ast=<ast.ClassDef>,
# child_nodes={
# 'method_name': NameInfo(...),
# '__init__': NameInfo(...),
# }
# ),
# '_private_func': NameInfo(
# name='_private_func',
# is_exported=False,
# ast=<ast.FunctionDef>,
# child_nodes=None
# ),
# }Key Points:
NameInfo.name fieldNameInfo objectsNone for parse_ast())child_nodes is recursive NameDict for class membersNames are considered exported (part of the public interface) if:
__all__Special Cases:
_name) are private unless in __all____name__) follow same rulesfrom module import * respect source module's __all__Examples:
# In stub file:
# public_name: int # is_exported=True (no underscore)
# _private_name: int # is_exported=False (starts with _)
# __all__ = ['public_name', '_exported_private']
# _exported_private: int # is_exported=True (in __all__)
names = get_stub_names('module')
if names:
print(f"public_name exported: {names['public_name'].is_exported}") # True
print(f"_private_name exported: {names['_private_name'].is_exported}") # False
print(f"_exported_private exported: {names['_exported_private'].is_exported}") # TrueWhen a stub contains multiple definitions of the same name (typically from @overload decorator), the parser creates an OverloadedName:
from typeshed_client import get_stub_names, OverloadedName
import ast
# Example stub content:
# @overload
# def func(x: int) -> int: ...
# @overload
# def func(x: str) -> str: ...
# def func(x): ...
names = get_stub_names('module_with_overloads')
if names and 'func' in names:
func_info = names['func']
if isinstance(func_info.ast, OverloadedName):
print(f"func has {len(func_info.ast.definitions)} definitions")
for i, defn in enumerate(func_info.ast.definitions):
if isinstance(defn, ast.FunctionDef):
print(f" Definition {i}: {defn.name}")
# Get arguments
args = [arg.arg for arg in defn.args.args]
print(f" Args: {args}")
# Get return type
if defn.returns:
ret = ast.unparse(defn.returns)
print(f" Returns: {ret}")Key Points:
OverloadedName.definitions is a list of AST nodes@overload)ast.FunctionDef nodeast.FunctionDef and ImportedName if overload is re-exportedWhen a name is imported from another module, the parser creates an ImportedName:
from typeshed_client import get_stub_names, ImportedName
# Example stub: from collections import OrderedDict
names = get_stub_names('example_module')
if names and 'OrderedDict' in names:
info = names['OrderedDict']
if isinstance(info.ast, ImportedName):
print(f"Imported from: {'.'.join(info.ast.module_name)}")
print(f"Name: {info.ast.name}") # 'OrderedDict'
print(f"Exported: {info.is_exported}")For module imports:
from typeshed_client import get_stub_names, ImportedName
# Example stub: import collections
names = get_stub_names('example_module')
if names and 'collections' in names:
info = names['collections']
if isinstance(info.ast, ImportedName):
print(f"Module import: {'.'.join(info.ast.module_name)}")
print(f"Name: {info.ast.name}") # None for module importsKey Points:
ImportedName.module_name is a ModulePath (tuple of strings)ImportedName.name is None for module imports (import foo)ImportedName.name is the imported name for specific imports (from foo import bar)Resolver to follow imports to their sourceThe parser evaluates conditions based on sys.version_info and sys.platform:
from typeshed_client import get_search_context, get_stub_names
# Example stub:
# if sys.version_info >= (3, 10):
# def new_function() -> None: ...
# if sys.platform == 'win32':
# def windows_only() -> None: ...
# Parse with Python 3.10
ctx_310 = get_search_context(version=(3, 10))
names_310 = get_stub_names('example_module', search_context=ctx_310)
if names_310:
print(f"Has new_function: {'new_function' in names_310}") # True
# Parse with Python 3.9
ctx_39 = get_search_context(version=(3, 9))
names_39 = get_stub_names('example_module', search_context=ctx_39)
if names_39:
print(f"Has new_function: {'new_function' in names_39}") # False
# Parse with Windows platform
ctx_win = get_search_context(platform='win32')
names_win = get_stub_names('example_module', search_context=ctx_win)
if names_win:
print(f"Has windows_only: {'windows_only' in names_win}") # TrueSupported conditional expressions:
sys.version_info comparisons: >=, >, <, <=, ==, !=sys.platform comparisons: ==, !=TYPE_CHECKING: Always evaluated as True in stubsMYPY: Always evaluated as Falseand, or, notsys.version_info >= (3, 10)Examples:
# In stub file:
# if sys.version_info >= (3, 10):
# new_feature: int
# if sys.version_info < (3, 11):
# deprecated_feature: int
# if sys.platform == "win32":
# windows_feature: int
# if sys.platform in ("linux", "darwin"):
# unix_feature: int
# if TYPE_CHECKING:
# forward_ref: SomeClass # Always includedfrom typeshed_client import get_stub_names
names = get_stub_names('typing')
if names:
# Print all exported names
exported = [name for name, info in names.items() if info.is_exported]
print(f"Exported names: {len(exported)}")
print(f"First 5: {exported[:5]}")
# Print all private names
private = [name for name, info in names.items() if not info.is_exported]
print(f"Private names: {len(private)}")
# Count by type
functions = sum(1 for info in names.values()
if isinstance(info.ast, ast.FunctionDef))
classes = sum(1 for info in names.values()
if isinstance(info.ast, ast.ClassDef))
print(f"Functions: {functions}, Classes: {classes}")
else:
print("Module not found")from typeshed_client import get_stub_names
import ast
names = get_stub_names('typing')
if names:
# Find function definitions
print("Functions in typing module:")
for name, info in names.items():
if isinstance(info.ast, ast.FunctionDef):
# Get arguments
args = [arg.arg for arg in info.ast.args.args]
arg_str = ', '.join(args)
# Get return type annotation
ret_str = "Any"
if info.ast.returns:
ret_str = ast.unparse(info.ast.returns)
exported = "๐" if info.is_exported else "๐"
print(f"{exported} {name}({arg_str}) -> {ret_str}")from typeshed_client import get_stub_names
import ast
names = get_stub_names('typing')
if names and 'TypedDict' in names:
class_info = names['TypedDict']
if isinstance(class_info.ast, ast.ClassDef):
print(f"Class: {class_info.name}")
print(f"Exported: {class_info.is_exported}")
# Get base classes
if class_info.ast.bases:
bases = [ast.unparse(base) for base in class_info.ast.bases]
print(f"Bases: {', '.join(bases)}")
# Examine methods
if class_info.child_nodes:
print(f"\nMethods ({len(class_info.child_nodes)}):")
for method_name, method_info in class_info.child_nodes.items():
exported = "public" if method_info.is_exported else "private"
if isinstance(method_info.ast, ast.FunctionDef):
print(f" {method_name}: function ({exported})")
elif isinstance(method_info.ast, (ast.Assign, ast.AnnAssign)):
print(f" {method_name}: attribute ({exported})")
else:
print("No child nodes available")from typeshed_client import get_stub_names, OverloadedName
import ast
names = get_stub_names('typing')
if names:
# Find overloaded functions
print("Overloaded functions:")
for name, info in names.items():
if isinstance(info.ast, OverloadedName):
print(f"\n{name} ({len(info.ast.definitions)} overloads):")
for i, defn in enumerate(info.ast.definitions, 1):
if isinstance(defn, ast.FunctionDef):
# Get signature
args = [arg.arg for arg in defn.args.args]
arg_str = ', '.join(args) or 'no args'
# Get return type
ret_str = "Any"
if defn.returns:
ret_str = ast.unparse(defn.returns)
# Check for @overload decorator
is_overload = any(
isinstance(dec, ast.Name) and dec.id == 'overload'
for dec in defn.decorator_list
)
marker = "@overload" if is_overload else "implementation"
print(f" {i}. [{marker}] ({arg_str}) -> {ret_str}")from typeshed_client import get_stub_names, ImportedName
names = get_stub_names('collections')
if names:
# Find imported names
imports = {}
local_definitions = {}
for name, info in names.items():
if isinstance(info.ast, ImportedName):
import_info = info.ast
if import_info.name:
source = f"{'.'.join(import_info.module_name)}.{import_info.name}"
else:
source = f"{'.'.join(import_info.module_name)} (module)"
imports[name] = source
else:
local_definitions[name] = type(info.ast).__name__
print(f"Imported names ({len(imports)}):")
for name, source in sorted(imports.items())[:10]:
print(f" {name} <- {source}")
print(f"\nLocal definitions ({len(local_definitions)}):")
for name, ast_type in sorted(local_definitions.items())[:10]:
print(f" {name}: {ast_type}")from typeshed_client import get_search_context, get_stub_names
versions = [(3, 9), (3, 10), (3, 11), (3, 12)]
results = {}
print("Parsing typing module across Python versions:")
for version in versions:
ctx = get_search_context(version=version)
names = get_stub_names('typing', search_context=ctx)
if names:
results[version] = set(names.keys())
exported = sum(1 for info in names.values() if info.is_exported)
print(f"Python {version[0]}.{version[1]}: {len(names)} names ({exported} exported)")
# Compare which names are available
if len(results) >= 2:
versions_sorted = sorted(results.keys())
v_oldest = versions_sorted[0]
v_newest = versions_sorted[-1]
names_only_in_newest = results[v_newest] - results[v_oldest]
if names_only_in_newest:
print(f"\nNames added after Python {v_oldest[0]}.{v_oldest[1]}:")
for name in sorted(names_only_in_newest):
print(f" + {name}")
names_removed = results[v_oldest] - results[v_newest]
if names_removed:
print(f"\nNames removed:")
for name in sorted(names_removed):
print(f" - {name}")from typeshed_client import get_search_context, ModulePath
from typeshed_client.parser import parse_ast
import ast
# Create a custom AST
code = """
def foo(x: int) -> str:
'''A function that does something.'''
...
class Bar:
'''A simple class.'''
def method(self) -> None: ...
attribute: int = 42
_private_var: int = 42
__all__ = ['foo', 'Bar']
"""
tree = ast.parse(code)
# Parse it with typeshed_client
ctx = get_search_context()
names = parse_ast(tree, ctx, ModulePath(('mymodule',)))
print(f"Names found: {list(names.keys())}") # ['foo', 'Bar', '_private_var', '__all__']
# Check export status
print("\nExport status:")
for name, info in names.items():
status = "exported" if info.is_exported else "private"
ast_type = type(info.ast).__name__
print(f" {name}: {status} ({ast_type})")
# Examine class structure
if 'Bar' in names:
bar_info = names['Bar']
if bar_info.child_nodes:
print(f"\nBar class members:")
for member_name, member_info in bar_info.child_nodes.items():
print(f" {member_name}: {type(member_info.ast).__name__}")The parser raises InvalidStub exception for malformed stubs.
class InvalidStub(Exception):
"""Raised when the parser encounters invalid stub syntax."""
def __init__(self, message: str, file_path: Optional[Path] = None) -> None: ...When Raised:
raise_on_warnings=True in SearchContext and parser encounters warnings__all__ definitionsAttributes:
message: Description of the error (from str(exception))file_path: Path to problematic stub file (if provided, may be None)Usage:
from typeshed_client import get_search_context, ModulePath
from typeshed_client.parser import parse_ast, InvalidStub
import ast
try:
# Parse potentially invalid AST
tree = ast.parse("x + y") # Expression, not a module definition
ctx = get_search_context()
names = parse_ast(tree, ctx, ModulePath(('test',)))
except InvalidStub as e:
print(f"Invalid stub: {e}")
if hasattr(e, 'args') and len(e.args) > 1:
print(f"File: {e.args[1]}")By default, warnings are logged rather than raised. To raise on warnings:
from typeshed_client import get_search_context, get_stub_names
from typeshed_client.parser import InvalidStub
ctx = get_search_context(raise_on_warnings=True)
modules = ['typing', 'collections', 'some_malformed_module']
for module in modules:
try:
names = get_stub_names(module, search_context=ctx)
if names:
print(f"โ {module}: {len(names)} names")
else:
print(f"โ {module}: Not found")
except InvalidStub as e:
print(f"โ {module}: Parsing failed: {e}")Returns the list of names that would be imported by from module import *.
def get_import_star_names(
module_name: str,
*,
search_context: SearchContext,
file_path: Optional[Path] = None
) -> Optional[list[str]]:
"""
Return list of names that would be imported by 'from module import *'.
Args:
module_name (str): Module name as a dotted string
search_context (SearchContext): Context for finding and parsing stubs
file_path (Path, optional): Path to source file for error reporting
Returns:
list[str]: List of names that would be imported, or None if module not found
Example:
from typeshed_client.parser import get_import_star_names
from typeshed_client import get_search_context
ctx = get_search_context()
names = get_import_star_names('typing', search_context=ctx)
print(names) # ['Any', 'Union', 'Optional', ...]
"""Behavior:
__all__ is defined, returns its contentsNone if module not foundUsage Example:
from typeshed_client.parser import get_import_star_names
from typeshed_client import get_search_context
ctx = get_search_context()
# Get names from typing module
typing_names = get_import_star_names('typing', search_context=ctx)
if typing_names:
print(f"'from typing import *' would import {len(typing_names)} names")
print(f"First 10: {typing_names[:10]}")
else:
print("Module not found")Extracts the __all__ list from a NameInfo object.
def get_dunder_all_from_info(
info: NameInfo,
file_path: Optional[Path] = None
) -> Optional[list[str]]:
"""
Extract the __all__ list from a NameInfo object.
Args:
info (NameInfo): NameInfo object for __all__
file_path (Path, optional): Path to source file for error reporting
Returns:
list[str]: List of names in __all__, or None if cannot be extracted
Example:
from typeshed_client import get_stub_names
from typeshed_client.parser import get_dunder_all_from_info
names = get_stub_names('typing')
if names and '__all__' in names:
all_list = get_dunder_all_from_info(names['__all__'])
print(all_list) # ['Any', 'Union', 'Optional', ...]
"""Behavior:
__all__ definitionNone if __all__ is not a simple list of stringsAssign and AnnAssign nodesNone if extraction failsUsage Example:
from typeshed_client import get_stub_names, get_search_context
from typeshed_client.parser import get_import_star_names, get_dunder_all_from_info
ctx = get_search_context()
# Method 1: Get names that would be imported with 'from typing import *'
star_names = get_import_star_names('typing', search_context=ctx)
if star_names:
print(f"Star import would get {len(star_names)} names")
# Method 2: Get __all__ directly from parsed names
names = get_stub_names('typing')
if names and '__all__' in names:
all_list = get_dunder_all_from_info(names['__all__'])
if all_list:
print(f"__all__ contains {len(all_list)} names")
# Should be equal
if star_names and set(star_names) == set(all_list):
print("โ Both methods return same names")
else:
print("Could not extract __all__")from typeshed_client import get_stub_names
import ast
def filter_by_node_type(module_name: str, node_type):
"""Get all names of a specific AST node type."""
names = get_stub_names(module_name)
if not names:
return []
result = []
for name, info in names.items():
if isinstance(info.ast, node_type):
result.append(name)
return result
# Find all classes in typing module
classes = filter_by_node_type('typing', ast.ClassDef)
print(f"Classes in typing ({len(classes)}): {classes[:5]}...")
# Find all functions
functions = filter_by_node_type('typing', ast.FunctionDef)
print(f"Functions in typing ({len(functions)}): {functions[:5]}...")
# Find all type aliases (assignments)
aliases = filter_by_node_type('typing', (ast.Assign, ast.AnnAssign))
print(f"Type aliases in typing ({len(aliases)}): {aliases[:5]}...")from typeshed_client import get_stub_names
import ast
def analyze_annotations(module_name: str):
"""Analyze type annotations in a module."""
names = get_stub_names(module_name)
if not names:
return {}
annotations = {}
for name, info in names.items():
if isinstance(info.ast, ast.AnnAssign):
# Variable with type annotation
if info.ast.annotation:
ann_str = ast.unparse(info.ast.annotation)
annotations[name] = ann_str
elif isinstance(info.ast, ast.FunctionDef):
# Function with return annotation
if info.ast.returns:
ret_str = ast.unparse(info.ast.returns)
annotations[name] = f"-> {ret_str}"
return annotations
# Get annotated items
typed_items = analyze_annotations('typing')
if typed_items:
print(f"Found {len(typed_items)} annotated items")
for item, type_str in list(typed_items.items())[:10]:
print(f" {item}: {type_str}")from typeshed_client import get_stub_names
import ast
def get_class_hierarchy(module_name: str):
"""Extract class inheritance information."""
names = get_stub_names(module_name)
if not names:
return {}
hierarchy = {}
for name, info in names.items():
if isinstance(info.ast, ast.ClassDef):
bases = []
for base in info.ast.bases:
bases.append(ast.unparse(base))
hierarchy[name] = {
'bases': bases,
'exported': info.is_exported,
'methods': list(info.child_nodes.keys()) if info.child_nodes else []
}
return hierarchy
# Get inheritance info
classes = get_class_hierarchy('collections')
if classes:
print(f"Found {len(classes)} classes")
for cls, details in list(classes.items())[:5]:
print(f"\n{cls}:")
if details['bases']:
print(f" Inherits from: {', '.join(details['bases'])}")
print(f" Exported: {details['exported']}")
print(f" Methods: {len(details['methods'])}")from typeshed_client import get_stub_names
import ast
def find_deprecated(module_name: str):
"""Find deprecated functions/classes by looking for deprecation decorators."""
names = get_stub_names(module_name)
if not names:
return []
deprecated = []
for name, info in names.items():
if isinstance(info.ast, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
for decorator in info.ast.decorator_list:
# Check for @deprecated or @deprecate decorator
is_deprecated = False
if isinstance(decorator, ast.Name):
if 'deprecat' in decorator.id.lower():
is_deprecated = True
elif isinstance(decorator, ast.Call):
if isinstance(decorator.func, ast.Name):
if 'deprecat' in decorator.func.id.lower():
is_deprecated = True
if is_deprecated:
deprecated.append(name)
break
return deprecated
# Find deprecated items in multiple modules
for module in ['typing', 'collections', 'asyncio']:
deprecated_items = find_deprecated(module)
if deprecated_items:
print(f"{module}: {deprecated_items}")from typeshed_client import get_stub_names
def compare_modules(module1: str, module2: str):
"""Compare the public APIs of two modules."""
names1 = get_stub_names(module1)
names2 = get_stub_names(module2)
if not names1 or not names2:
return None
exported1 = {name for name, info in names1.items() if info.is_exported}
exported2 = {name for name, info in names2.items() if info.is_exported}
return {
'common': exported1 & exported2,
'only_in_first': exported1 - exported2,
'only_in_second': exported2 - exported1,
'total_first': len(exported1),
'total_second': len(exported2),
}
# Compare collections and collections.abc
result = compare_modules('collections', 'collections.abc')
if result:
print(f"collections: {result['total_first']} exported names")
print(f"collections.abc: {result['total_second']} exported names")
print(f"Common: {len(result['common'])} names")
print(f"Only in collections: {len(result['only_in_first'])}")
print(f"Only in collections.abc: {len(result['only_in_second'])}")
if result['common']:
print(f"\nSome common names: {list(result['common'])[:5]}")from typeshed_client import get_stub_names
import ast
def extract_generic_params(module_name: str):
"""Extract generic type parameters from classes."""
names = get_stub_names(module_name)
if not names:
return {}
generics = {}
for name, info in names.items():
if isinstance(info.ast, ast.ClassDef):
# Look for Generic base class
for base in info.ast.bases:
base_str = ast.unparse(base)
if 'Generic[' in base_str or base_str == 'Generic':
generics[name] = base_str
break
return generics
# Find generic classes
generic_classes = extract_generic_params('typing')
if generic_classes:
print(f"Found {len(generic_classes)} generic classes:")
for cls, base in list(generic_classes.items())[:10]:
print(f" {cls}: {base}")get_stub_names() returns None when a module is not found# CORRECT
names = get_stub_names('module')
if names is None:
print("Module not found")
elif len(names) == 0:
print("Module found but empty")
else:
print(f"Found {len(names)} names")
# INCORRECT
names = get_stub_names('module')
for name in names: # TypeError if names is None
print(name)isinstance() to determine what kind of AST node a name represents# CORRECT
from typeshed_client import ImportedName, OverloadedName
import ast
info = names['somename']
if isinstance(info.ast, ImportedName):
print("It's an import")
elif isinstance(info.ast, OverloadedName):
print("It's overloaded")
elif isinstance(info.ast, ast.FunctionDef):
print("It's a function")
elif isinstance(info.ast, ast.ClassDef):
print("It's a class")ImportedName and OverloadedName are not regular AST nodes# CORRECT - check for special types first
from typeshed_client import ImportedName, OverloadedName
import ast
if isinstance(info.ast, (ImportedName, OverloadedName)):
# Handle special types
pass
elif isinstance(info.ast, ast.AST):
# Handle regular AST nodes
passis_exported to distinguish public from private APIs# CORRECT - filter by export status
public_names = {name: info for name, info in names.items()
if info.is_exported}
private_names = {name: info for name, info in names.items()
if not info.is_exported}
print(f"Public: {len(public_names)}, Private: {len(private_names)}")child_nodes to access methods and attributes# CORRECT
info = names['SomeClass']
if isinstance(info.ast, ast.ClassDef) and info.child_nodes:
for method_name, method_info in info.child_nodes.items():
print(f"Method: {method_name}")
else:
print("No child nodes available")
# INCORRECT
info = names['SomeClass']
for method in info.child_nodes: # AttributeError if child_nodes is None
print(method)SearchContext for version-specific behavior# CORRECT - explicit version handling
versions = [(3, 9), (3, 10), (3, 11)]
for version in versions:
ctx = get_search_context(version=version)
names = get_stub_names('typing', search_context=ctx)
if names:
print(f"Python {version}: {len(names)} names")raise_on_warnings=True# CORRECT
from typeshed_client.parser import InvalidStub
ctx = get_search_context(raise_on_warnings=True)
try:
names = get_stub_names('module', search_context=ctx)
if names:
print(f"Success: {len(names)} names")
except InvalidStub as e:
print(f"Parsing failed: {e}")SearchContext once and reuse for multiple parsing operations# CORRECT - efficient
ctx = get_search_context(version=(3, 11))
modules = ['typing', 'collections', 'asyncio']
for module in modules:
names = get_stub_names(module, search_context=ctx)
# Process names# CORRECT
if isinstance(info.ast, ast.FunctionDef):
# Check for return annotation
if info.ast.returns:
ret_type = ast.unparse(info.ast.returns)
print(f"Returns: {ret_type}")
else:
print("No return annotation")
# Check for decorators
if info.ast.decorator_list:
for dec in info.ast.decorator_list:
print(f"Decorator: {ast.unparse(dec)}")
# INCORRECT
if isinstance(info.ast, ast.FunctionDef):
ret_type = ast.unparse(info.ast.returns) # May be NoneOverloadedName, iterate through all definitions# CORRECT
from typeshed_client import OverloadedName
import ast
if isinstance(info.ast, OverloadedName):
for i, defn in enumerate(info.ast.definitions):
if isinstance(defn, ast.FunctionDef):
print(f"Overload {i}: {ast.unparse(defn.args)}")
elif isinstance(defn, ImportedName):
print(f"Overload {i}: imported")