tessl install tessl/pypi-typeshed-client@2.8.4A library for accessing stubs in typeshed.
The search context provides configuration for customizing stub finding and parsing behavior. It controls which typeshed directory to use, where to search for stubs, what Python version to target, and how to handle parsing errors.
| Function/Method | Return Type | Can Return None? |
|---|---|---|
get_search_context(...) | SearchContext | No |
SearchContext.is_python2() | bool | No |
Raises: ValueError if both python_executable and search_path are provided
Creates a configured SearchContext for stub operations.
def get_search_context(
*,
typeshed: Optional[Path] = None,
search_path: Optional[Sequence[Path]] = None,
python_executable: Optional[str] = None,
version: Optional[PythonVersion] = None,
platform: str = sys.platform,
raise_on_warnings: bool = False,
allow_py_files: bool = False
) -> SearchContext:
"""
Return a context for finding stubs.
This context can be passed to other functions in typeshed_client to customize
stub finding behavior.
Args:
typeshed (Path, optional): Path to typeshed directory. If not provided,
uses the bundled version of typeshed.
search_path (Sequence[Path], optional): List of directories to search for stubs.
If not provided, sys.path is used.
python_executable (str, optional): Path to Python executable for determining
search_path. Default is sys.executable. Must be a valid path to an existing
Python interpreter. Raises ValueError if both python_executable and search_path
are provided.
version (PythonVersion, optional): Python version as (major, minor) tuple,
e.g. (3, 11). Used for interpreting sys.version_info checks in stubs.
Default is current Python version.
platform (str, optional): Platform string for interpreting sys.platform checks.
Default is sys.platform of current process.
raise_on_warnings (bool, optional): If True, raise InvalidStub for any warnings
encountered by the parser. Default is False (warnings are logged).
allow_py_files (bool, optional): If True, search for .py files in addition to
.pyi stub files. Useful for typed packages with inline stubs. Default is False.
Returns:
SearchContext: Configured context for stub operations
Raises:
ValueError: If both python_executable and search_path are provided
Example:
# Use default context
ctx = get_search_context()
# Configure for specific Python version
ctx = get_search_context(version=(3, 9))
# Use custom typeshed location
ctx = get_search_context(typeshed=Path('/custom/typeshed'))
# Allow searching .py files
ctx = get_search_context(allow_py_files=True)
"""Return Value Details:
SearchContext object (never None)Error Cases:
ValueError if both python_executable and search_path providedFileNotFoundError if python_executable doesn't existtypeshed path will cause stub lookup failures (not immediate error)The SearchContext is a NamedTuple containing all configuration options.
class SearchContext(NamedTuple):
"""
Configuration context for stub finding operations.
Attributes:
typeshed (Path): Path to typeshed directory
search_path (Sequence[Path]): Directories to search for stubs
version (PythonVersion): Python version as (major, minor) tuple
platform (str): Platform string for sys.platform checks
raise_on_warnings (bool): Whether to raise exceptions on parser warnings
allow_py_files (bool): Whether to search .py files in addition to .pyi files
"""
typeshed: Path
search_path: Sequence[Path]
version: PythonVersion
platform: str
raise_on_warnings: bool = False
allow_py_files: bool = False
def is_python2(self) -> bool:
"""
Check if the configured version is Python 2.
Returns:
bool: True if version[0] == 2, False otherwise
Example:
ctx = get_search_context(version=(2, 7))
print(ctx.is_python2()) # True
ctx = get_search_context(version=(3, 11))
print(ctx.is_python2()) # False
"""Attributes:
typeshed (Path): Path to typeshed directory
stdlib/ and stubs/ subdirectoriessearch_path (Sequence[Path]): List of directories to search
sys.pathversion (PythonVersion): Python version tuple
(major, minor) e.g., (3, 11)platform (str): Platform string
sys.platform'linux', 'win32', 'darwin'raise_on_warnings (bool): Error handling mode
True: Raise InvalidStub on parser warningsFalse: Log warnings, continue parsingFalseallow_py_files (bool): Python file support
True: Search .py files if no .pyi foundFalse: Only search .pyi stub filesFalseImmutability: SearchContext is immutable (NamedTuple); create new instance for different configuration
Thread Safety: Safe to share across threads
Controls which typeshed directory to use for standard library stubs.
Default: Bundled typeshed included with typeshed_client
Custom usage:
from pathlib import Path
from typeshed_client import get_search_context, get_stub_file
# Use a custom typeshed checkout
custom_typeshed = Path('/home/user/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 custom location
if str(custom_typeshed) in str(path):
print("✓ Correct typeshed in use")
else:
print("Custom typeshed not found")Requirements:
stdlib/ subdirectory for standard library stubsstubs/ subdirectory for third-party stubsList of directories to search for stubs, including stub packages and inline stubs.
Default: sys.path of current Python interpreter
Custom usage:
from pathlib import Path
from typeshed_client import get_search_context
# Search specific directories
ctx = get_search_context(search_path=[
Path('/project/stubs'),
Path('/project/venv/lib/python3.11/site-packages'),
])Resolution Order:
*-stubs) found in these directories.pyi files) found in these directoriesPerformance: More directories = slower stub lookup; keep list minimal
Specifies which Python executable to use for determining the search path.
Default: sys.executable (current Python interpreter)
Usage:
from typeshed_client import get_search_context
# Use a different Python interpreter's path
ctx = get_search_context(python_executable='/usr/bin/python3.9')Important Notes:
ValueError if both python_executable and search_path are provided (cannot use both simultaneously)sys.pathError Handling:
from typeshed_client import get_search_context
try:
ctx = get_search_context(python_executable='/nonexistent/python')
except (ValueError, FileNotFoundError) as e:
print(f"Error: {e}")
# Fall back to default
ctx = get_search_context()Target Python version for version-specific stub handling.
Default: Current Python version (sys.version_info[:2])
Usage:
from typeshed_client import get_search_context, get_stub_names
# Parse stubs as if running Python 3.9
ctx = get_search_context(version=(3, 9))
names = get_stub_names('typing', search_context=ctx)
# Stubs with "if sys.version_info >= (3, 10):" will exclude those definitions
if names:
has_uniontype = 'UnionType' in names # False for Python 3.9
print(f"UnionType available: {has_uniontype}")Affects:
sys.version_infoExamples:
# Check feature availability across versions
versions = [(3, 8), (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:
available = [name for name in ['Literal', 'TypeGuard', 'TypedDict']
if name in names]
print(f"Python {version[0]}.{version[1]}: {available}")Target platform for platform-specific stub handling.
Default: sys.platform of current system
Usage:
from typeshed_client import get_search_context, get_stub_names
# Parse stubs as if on Linux
ctx = get_search_context(platform='linux')
names = get_stub_names('os', search_context=ctx)
# Stubs with "if sys.platform == 'win32':" will be excluded
if names:
has_startfile = 'startfile' in names # False on Linux
print(f"os.startfile available: {has_startfile}")Common platform values:
'linux' - Linux systems'win32' - Windows (both 32-bit and 64-bit)'darwin' - macOS'cygwin' - Cygwin on Windows'freebsd' - FreeBSD'openbsd' - OpenBSDAffects:
sys.platformExamples:
# Compare platform-specific APIs
platforms = ['linux', 'win32', 'darwin']
module = 'os'
for platform in platforms:
ctx = get_search_context(platform=platform)
names = get_stub_names(module, search_context=ctx)
if names:
print(f"\n{platform}:")
# Check for platform-specific functions
specific_funcs = []
if platform == 'win32' and 'startfile' in names:
specific_funcs.append('startfile')
if platform in ('linux', 'darwin') and 'fork' in names:
specific_funcs.append('fork')
print(f" Platform-specific: {specific_funcs}")Controls whether parser warnings are raised as exceptions.
Default: False (warnings are logged)
Usage:
from typeshed_client import get_search_context, get_stub_names
from typeshed_client.parser import InvalidStub
# Enable strict parsing
ctx = get_search_context(raise_on_warnings=True)
try:
names = get_stub_names('some_module', search_context=ctx)
if names:
print(f"✓ Parsed {len(names)} names")
else:
print("✗ Module not found")
except InvalidStub as e:
print(f"✗ Parser warning: {e}")
if hasattr(e, 'file_path') and e.file_path:
print(f" File: {e.file_path}")When to Use:
True: When validating stub quality, testing, strict modeFalse: When robustness is needed, production use, lenient parsingPerformance: No significant performance impact
Examples:
# Validate multiple modules with strict parsing
from typeshed_client.parser import InvalidStub
ctx_strict = get_search_context(raise_on_warnings=True)
modules = ['typing', 'collections', 'asyncio', 'dataclasses']
results = {'valid': [], 'invalid': []}
for module in modules:
try:
names = get_stub_names(module, search_context=ctx_strict)
if names:
results['valid'].append(module)
except InvalidStub as e:
results['invalid'].append((module, str(e)))
print(f"Valid: {len(results['valid'])}")
print(f"Invalid: {len(results['invalid'])}")
for module, error in results['invalid']:
print(f" {module}: {error}")Controls whether to search .py files in addition to .pyi stub files.
Default: False (only search .pyi files)
Usage:
from typeshed_client import get_search_context, get_stub_file
# Search both .pyi and .py files
ctx = get_search_context(allow_py_files=True)
path = get_stub_file('mypackage', search_context=ctx)
if path:
print(f"Found: {path}")
if path.suffix == '.py':
print("✓ Using Python source file (not stub)")
elif path.suffix == '.pyi':
print("✓ Using stub file")Use Cases:
Resolution Order (when allow_py_files=True):
.pyi stub files (preferred).py files (if no stub found)Performance: Minimal impact (only checked if stub not found)
Examples:
# Compare stub vs source file
from pathlib import Path
ctx_stubs_only = get_search_context(allow_py_files=False)
ctx_with_py = get_search_context(allow_py_files=True)
modules = ['typing', 'mypy', 'custom_package']
for module in modules:
path_stub = get_stub_file(module, search_context=ctx_stubs_only)
path_any = get_stub_file(module, search_context=ctx_with_py)
print(f"\n{module}:")
if path_stub:
print(f" Stub: {path_stub.name}")
else:
print(f" Stub: Not found")
if path_any:
file_type = path_any.suffix
print(f" With .py: {path_any.name} ({file_type})")
else:
print(f" With .py: Not found")from typeshed_client import get_search_context
# Create default context
ctx = get_search_context()
print(f"Version: {ctx.version}")
print(f"Platform: {ctx.platform}")
print(f"Typeshed: {ctx.typeshed}")
print(f"Search path: {len(ctx.search_path)} directories")
print(f"Raise on warnings: {ctx.raise_on_warnings}")
print(f"Allow .py files: {ctx.allow_py_files}")
# Check if Python 2
if ctx.is_python2():
print("Python 2 context")
else:
print("Python 3 context")from typeshed_client import get_search_context, get_stub_names
versions = [(3, 8), (3, 9), (3, 10), (3, 11), (3, 12)]
results = {}
print("Analyzing '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] = {
'total': len(names),
'exported': sum(1 for info in names.values() if info.is_exported),
'names': set(names.keys())
}
print(f" Python {version[0]}.{version[1]}: {results[version]['total']} names")
# Find names added in newer versions
if (3, 8) in results and (3, 12) in results:
new_names = results[(3, 12)]['names'] - results[(3, 8)]['names']
print(f"\nNames added after Python 3.8: {sorted(new_names)[:10]}")from typeshed_client import get_search_context, get_stub_names
platforms = ['linux', 'win32', 'darwin']
module = 'os'
print(f"Analyzing '{module}' module across platforms:")
platform_names = {}
for platform in platforms:
ctx = get_search_context(platform=platform)
names = get_stub_names(module, search_context=ctx)
if names:
platform_names[platform] = set(names.keys())
print(f" {platform}: {len(names)} names")
# Find platform-specific names
if len(platform_names) > 1:
all_platforms = set.intersection(*platform_names.values())
print(f"\nCommon to all platforms: {len(all_platforms)} names")
for platform, names in platform_names.items():
unique = names - all_platforms
if unique:
print(f"{platform}-specific: {sorted(unique)[:5]}")from pathlib import Path
from typeshed_client import get_search_context, get_stub_file
# Use development version of typeshed
dev_typeshed = Path('/home/user/git/typeshed')
if dev_typeshed.exists():
ctx = get_search_context(typeshed=dev_typeshed)
# Find stubs from custom typeshed
path = get_stub_file('typing', search_context=ctx)
if path:
print(f"✓ Using stub from custom typeshed: {path}")
# Verify it's from our custom location
if str(dev_typeshed) in str(path):
print("✓ Confirmed custom typeshed in use")
else:
print("✗ Stub not found in custom typeshed")
else:
print(f"✗ Custom typeshed not found at {dev_typeshed}")from pathlib import Path
from typeshed_client import get_search_context, get_stub_file
# Search project-specific stubs
project_stubs = Path('./stubs')
venv_packages = Path('./venv/lib/python3.11/site-packages')
search_dirs = [d for d in [project_stubs, venv_packages] if d.exists()]
if search_dirs:
ctx = get_search_context(search_path=search_dirs)
# Find stubs in custom locations
modules_to_find = ['myproject', 'requests', 'numpy']
for module in modules_to_find:
path = get_stub_file(module, search_context=ctx)
if path:
# Determine which search directory it came from
for search_dir in search_dirs:
if str(search_dir) in str(path):
print(f"✓ {module}: found in {search_dir.name}")
break
else:
print(f"✗ {module}: not found")
else:
print("✗ No custom search directories exist")from typeshed_client import get_search_context, get_stub_names
from typeshed_client.parser import InvalidStub
# Enable strict parsing
ctx = get_search_context(raise_on_warnings=True)
modules = ['typing', 'collections', 'asyncio', 'dataclasses']
results = {'success': [], 'failed': []}
print("Validating modules with strict parsing:")
for module in modules:
try:
names = get_stub_names(module, search_context=ctx)
if names:
results['success'].append(module)
print(f" ✓ {module}: {len(names)} names")
else:
results['failed'].append((module, "Not found"))
print(f" ✗ {module}: Not found")
except InvalidStub as e:
results['failed'].append((module, str(e)))
print(f" ✗ {module}: {e}")
print(f"\nSummary: {len(results['success'])} passed, {len(results['failed'])} failed")from pathlib import Path
from typeshed_client import get_search_context, get_stub_file, get_stub_names
# Allow searching .py files
ctx = get_search_context(
allow_py_files=True,
search_path=[Path('./myproject')]
)
modules = ['mymodule', 'utils', 'config']
print("Searching for modules (including .py files):")
for module in modules:
path = get_stub_file(module, search_context=ctx)
if path:
file_type = "stub" if path.suffix == '.pyi' else "source"
print(f" {module}: {path.name} ({file_type})")
# Parse it
names = get_stub_names(module, search_context=ctx)
if names:
exported = sum(1 for info in names.values() if info.is_exported)
print(f" Names: {len(names)} ({exported} exported)")
else:
print(f" {module}: Not found")from pathlib import Path
from typeshed_client import get_search_context, Resolver
# Create a fully customized context
ctx = get_search_context(
typeshed=Path('/custom/typeshed'),
search_path=[
Path('./stubs'),
Path('./venv/lib/python3.11/site-packages'),
],
version=(3, 11),
platform='linux',
raise_on_warnings=False, # Lenient for production
allow_py_files=True
)
print("Custom context configuration:")
print(f" Typeshed: {ctx.typeshed}")
print(f" Search paths: {len(ctx.search_path)}")
print(f" Version: Python {ctx.version[0]}.{ctx.version[1]}")
print(f" Platform: {ctx.platform}")
print(f" Raise on warnings: {ctx.raise_on_warnings}")
print(f" Allow .py files: {ctx.allow_py_files}")
# Use with resolver
resolver = Resolver(search_context=ctx)
result = resolver.get_fully_qualified_name('typing.List')
if result:
print(f"\n✓ Resolved typing.List: {type(result).__name__}")
else:
print(f"\n✗ Could not resolve typing.List")from typeshed_client import get_search_context, get_stub_names
def analyze_versions(module_name: str, versions: list[tuple[int, int]]):
"""Analyze a module across multiple Python versions."""
results = {}
print(f"Analyzing '{module_name}' across versions:")
for version in versions:
ctx = get_search_context(version=version)
names = get_stub_names(module_name, search_context=ctx)
if names:
results[version] = set(names.keys())
exported = sum(1 for info in names.values() if info.is_exported)
print(f" {version[0]}.{version[1]}: {len(names)} total, {exported} exported")
else:
results[version] = set()
print(f" {version[0]}.{version[1]}: Not found")
return results
# Compare typing module across versions
versions = [(3, 8), (3, 9), (3, 10), (3, 11), (3, 12)]
results = analyze_versions('typing', versions)
# Analyze progression
print("\nFeature additions:")
prev_names = set()
for version in sorted(results.keys()):
current_names = results[version]
if prev_names:
added = current_names - prev_names
if added:
print(f" Python {version[0]}.{version[1]}: {sorted(added)[:5]}")
prev_names = current_namesfrom typeshed_client import get_search_context, get_stub_file
# Create Python 2 context
ctx_py2 = get_search_context(version=(2, 7))
ctx_py3 = get_search_context(version=(3, 11))
print("Python 2 vs Python 3:")
print(f" Python 2 context: {ctx_py2.is_python2()}")
print(f" Python 3 context: {ctx_py3.is_python2()}")
# Check module availability
modules = ['typing', 'asyncio', '__builtin__', 'builtins']
for module in modules:
path_py2 = get_stub_file(module, search_context=ctx_py2)
path_py3 = get_stub_file(module, search_context=ctx_py3)
py2_status = "✓" if path_py2 else "✗"
py3_status = "✓" if path_py3 else "✗"
print(f" {module}: Py2 {py2_status}, Py3 {py3_status}")from pathlib import Path
from typeshed_client import get_search_context, SearchContext
class ContextFactory:
"""Factory for creating SearchContext instances with common configurations."""
def __init__(self, base_search_path: list[Path]):
self.base_search_path = base_search_path
def for_version(self, version: tuple[int, int]) -> SearchContext:
"""Create context for specific Python version."""
return get_search_context(
version=version,
search_path=self.base_search_path
)
def for_platform(self, platform: str) -> SearchContext:
"""Create context for specific platform."""
return get_search_context(
platform=platform,
search_path=self.base_search_path
)
def strict(self) -> SearchContext:
"""Create context with strict parsing."""
return get_search_context(
raise_on_warnings=True,
search_path=self.base_search_path
)
def lenient(self) -> SearchContext:
"""Create context with lenient parsing and .py file support."""
return get_search_context(
raise_on_warnings=False,
allow_py_files=True,
search_path=self.base_search_path
)
# Use the factory
factory = ContextFactory([Path('./stubs'), Path('./venv/lib/python3.11/site-packages')])
ctx_39 = factory.for_version((3, 9))
ctx_311 = factory.for_version((3, 11))
ctx_windows = factory.for_platform('win32')
ctx_strict = factory.strict()
ctx_lenient = factory.lenient()
print("Created 5 contexts with common base configuration")from typeshed_client import get_search_context, get_stub_names
def test_compatibility(module_name: str, min_version: tuple[int, int], max_version: tuple[int, int]):
"""Test if a module is available across a version range."""
def next_version(v):
major, minor = v
if minor >= 12: # Arbitrary upper bound for minor version
return (major + 1, 0)
return (major, minor + 1)
current = min_version
available_versions = []
unavailable_versions = []
while current <= max_version:
ctx = get_search_context(version=current)
names = get_stub_names(module_name, search_context=ctx)
if names:
available_versions.append(current)
else:
unavailable_versions.append(current)
current = next_version(current)
if current > max_version:
break
return {
'available': available_versions,
'unavailable': unavailable_versions
}
# Test asyncio availability
result = test_compatibility('asyncio', (3, 5), (3, 12))
print(f"asyncio available in: {result['available']}")
if result['unavailable']:
print(f"asyncio unavailable in: {result['unavailable']}")from typeshed_client import get_search_context, get_stub_names
def get_platform_specific_names(module_name: str, platforms: list[str] = None):
"""Get names that are platform-specific."""
if platforms is None:
platforms = ['linux', 'win32', 'darwin']
all_names = {}
for platform in platforms:
ctx = get_search_context(platform=platform)
names = get_stub_names(module_name, search_context=ctx)
if names:
all_names[platform] = set(names.keys())
else:
all_names[platform] = set()
# Find platform-specific names
if len(all_names) > 1:
# Common to all platforms
common = set.intersection(*[names for names in all_names.values() if names])
# Platform-specific
platform_specific = {
platform: names - common
for platform, names in all_names.items()
if names
}
return {
'common': common,
'platform_specific': platform_specific,
'total_per_platform': {p: len(n) for p, n in all_names.items()}
}
return {'common': set(), 'platform_specific': {}, 'total_per_platform': {}}
# Find platform-specific names in os module
result = get_platform_specific_names('os')
print(f"os module analysis:")
print(f" Common to all platforms: {len(result['common'])} names")
for platform, names in result['platform_specific'].items():
if names:
print(f" {platform}-specific: {sorted(names)[:5]}")from pathlib import Path
from typeshed_client import get_search_context
import os
def create_context_from_env():
"""Create SearchContext from environment variables."""
# Get custom typeshed path from env
typeshed_path = os.getenv('TYPESHED_PATH')
typeshed = Path(typeshed_path) if typeshed_path else None
# Get search path from env
search_path_env = os.getenv('STUB_SEARCH_PATH')
search_path = None
if search_path_env:
search_path = [Path(p) for p in search_path_env.split(os.pathsep)]
# Get version from env
version_env = os.getenv('PYTHON_VERSION')
version = None
if version_env:
try:
major, minor = version_env.split('.')
version = (int(major), int(minor))
except ValueError:
print(f"Invalid PYTHON_VERSION: {version_env}")
# Get platform from env
platform = os.getenv('TARGET_PLATFORM', None)
# Build context
kwargs = {}
if typeshed:
kwargs['typeshed'] = typeshed
if search_path:
kwargs['search_path'] = search_path
if version:
kwargs['version'] = version
if platform:
kwargs['platform'] = platform
return get_search_context(**kwargs)
# Use environment-configured context
print("Creating context from environment variables...")
print(f" TYPESHED_PATH: {os.getenv('TYPESHED_PATH', 'not set')}")
print(f" STUB_SEARCH_PATH: {os.getenv('STUB_SEARCH_PATH', 'not set')}")
print(f" PYTHON_VERSION: {os.getenv('PYTHON_VERSION', 'not set')}")
print(f" TARGET_PLATFORM: {os.getenv('TARGET_PLATFORM', 'not set')}")
ctx = create_context_from_env()
print(f"\nCreated context:")
print(f" Version: {ctx.version}")
print(f" Platform: {ctx.platform}")from typeshed_client import get_search_context, get_stub_file, get_stub_names
from typeshed_client.parser import InvalidStub
from pathlib import Path
def validate_stubs(stub_dir: Path, strict: bool = True):
"""Validate all stubs in a directory."""
ctx = get_search_context(
search_path=[stub_dir],
raise_on_warnings=strict
)
results = {
'valid': [],
'invalid': [],
'missing': [],
'total': 0,
}
# Find all .pyi files
if not stub_dir.exists():
print(f"Directory does not exist: {stub_dir}")
return results
pyi_files = list(stub_dir.rglob('*.pyi'))
results['total'] = len(pyi_files)
print(f"Validating {len(pyi_files)} stub files...")
for pyi_file in pyi_files:
# Convert file path to module name
relative = pyi_file.relative_to(stub_dir)
module_parts = list(relative.parts[:-1]) # Exclude filename
if relative.stem != '__init__':
module_parts.append(relative.stem)
module_name = '.'.join(module_parts) if module_parts else relative.stem
try:
names = get_stub_names(module_name, search_context=ctx)
if names is not None:
results['valid'].append(module_name)
else:
results['missing'].append(module_name)
except InvalidStub as e:
results['invalid'].append((module_name, str(e)))
except Exception as e:
results['invalid'].append((module_name, f"Unexpected error: {e}"))
return results
# Example usage (commented out - requires actual stub directory)
# stub_directory = Path('./my_stubs')
# validation = validate_stubs(stub_directory, strict=True)
# print(f"\nValidation results:")
# print(f" Total files: {validation['total']}")
# print(f" Valid: {len(validation['valid'])}")
# print(f" Invalid: {len(validation['invalid'])}")
# print(f" Missing: {len(validation['missing'])}")SearchContext objects are immutable and can be reused safely:
from typeshed_client import get_search_context, get_stub_file, get_stub_names, Resolver
# Create context once
ctx = get_search_context(version=(3, 11), platform='linux')
# Reuse across multiple operations
path1 = get_stub_file('typing', search_context=ctx)
path2 = get_stub_file('collections', search_context=ctx)
names1 = get_stub_names('typing', search_context=ctx)
names2 = get_stub_names('collections', search_context=ctx)
resolver = Resolver(search_context=ctx)
info1 = resolver.get_fully_qualified_name('typing.List')
info2 = resolver.get_fully_qualified_name('collections.OrderedDict')
print("✓ All operations use the same context configuration")
print(f" Version: {ctx.version}")
print(f" Platform: {ctx.platform}")Benefits of Reuse:
When no search context is provided, functions use a default context with:
sys.path.pyi files searchedfrom typeshed_client import get_stub_file, get_search_context
# Uses default context
path = get_stub_file('typing')
# Equivalent to:
ctx = get_search_context()
path = get_stub_file('typing', search_context=ctx)
print("Default context settings:")
print(f" Version: {ctx.version}")
print(f" Platform: {ctx.platform}")
print(f" Raise on warnings: {ctx.raise_on_warnings}")
print(f" Allow .py files: {ctx.allow_py_files}")from typeshed_client import get_search_context
from pathlib import Path
try:
# This will raise ValueError
ctx = get_search_context(
python_executable='/usr/bin/python3',
search_path=[Path('./stubs')] # Cannot specify both
)
except ValueError as e:
print(f"✗ Error: {e}")
print(" Use either python_executable OR search_path, not both")
# Use one or the other
ctx1 = get_search_context(search_path=[Path('./stubs')])
print(f"✓ Created context with search_path")
ctx2 = get_search_context(python_executable='/usr/bin/python3')
print(f"✓ Created context with python_executable")from typeshed_client import get_search_context
try:
ctx = get_search_context(python_executable='/nonexistent/python')
except (ValueError, FileNotFoundError) as e:
print(f"✗ Error: {e}")
print(" Python executable not found")
# Fall back to default
ctx = get_search_context()
print(f"✓ Using default context")from pathlib import Path
from typeshed_client import get_search_context, get_stub_file
# Invalid typeshed path doesn't raise immediately
bad_typeshed = Path('/nonexistent/typeshed')
ctx = get_search_context(typeshed=bad_typeshed)
print(f"✓ Created context with invalid typeshed (no immediate error)")
# Error occurs when trying to use it
path = get_stub_file('typing', search_context=ctx)
if path is None:
print(f"✗ Could not find stub (invalid typeshed path)")
else:
print(f"✓ Found stub: {path}")SearchContext once and reuse it across multiple operations for consistency# CORRECT - efficient and consistent
ctx = get_search_context(version=(3, 11))
for module in ['typing', 'collections', 'asyncio']:
names = get_stub_names(module, search_context=ctx)
# Process names
# INCORRECT - creates new context each time
for module in ['typing', 'collections', 'asyncio']:
ctx = get_search_context(version=(3, 11)) # Wasteful
names = get_stub_names(module, search_context=ctx)# CORRECT - separate contexts for different versions
ctx_39 = get_search_context(version=(3, 9))
ctx_311 = get_search_context(version=(3, 11))
names_39 = get_stub_names('typing', search_context=ctx_39)
names_311 = get_stub_names('typing', search_context=ctx_311)# CORRECT - explicit platform configuration
ctx_linux = get_search_context(platform='linux')
ctx_windows = get_search_context(platform='win32')
# Analyze platform-specific APIs
names_linux = get_stub_names('os', search_context=ctx_linux)
names_windows = get_stub_names('os', search_context=ctx_windows)raise_on_warnings=True when validating stubs# CORRECT - strict mode for validation
ctx_strict = get_search_context(raise_on_warnings=True)
from typeshed_client.parser import InvalidStub
try:
names = get_stub_names('module', search_context=ctx_strict)
print("✓ Stub is valid")
except InvalidStub as e:
print(f"✗ Stub has issues: {e}")search_path# CORRECT - include package directories
from pathlib import Path
venv_packages = Path('./venv/lib/python3.11/site-packages')
ctx = get_search_context(search_path=[venv_packages])allow_py_files=True when you specifically need to analyze Python source files# CORRECT - explicit when needed
ctx_with_py = get_search_context(allow_py_files=True)
path = get_stub_file('typed_package', search_context=ctx_with_py)
# CORRECT - default for normal stub analysis
ctx_stubs = get_search_context(allow_py_files=False) # Or just omit parameter# CORRECT - documented configuration
# Configure for Python 3.9 with strict parsing for validation
ctx = get_search_context(
version=(3, 9),
raise_on_warnings=True,
allow_py_files=False
)
print(f"Using Python {ctx.version} stubs with strict parsing")# CORRECT - comprehensive version testing
def test_stub_compatibility(module_name: str):
versions = [(3, 8), (3, 9), (3, 10), (3, 11), (3, 12)]
for version in versions:
ctx = get_search_context(version=version)
names = get_stub_names(module_name, search_context=ctx)
print(f"Python {version}: {len(names) if names else 0} names")# CORRECT - environment-aware configuration
import os
version_str = os.getenv('PYTHON_VERSION', '3.11')
major, minor = map(int, version_str.split('.'))
ctx = get_search_context(
version=(major, minor),
platform=os.getenv('TARGET_PLATFORM', 'linux')
)# CORRECT - factory for common configurations
class StubContextFactory:
def __init__(self, base_path: Path):
self.base_path = base_path
def for_version(self, version):
return get_search_context(
version=version,
search_path=[self.base_path]
)
factory = StubContextFactory(Path('./stubs'))
ctx_39 = factory.for_version((3, 9))
ctx_311 = factory.for_version((3, 11))Context Creation: Fast; no I/O operations Context Reuse: No performance penalty; contexts are lightweight Search Path Length: Longer paths increase stub lookup time Typeshed Size: Larger typeshed directories may affect startup time Version/Platform: No performance impact on context creation
Optimization Tips: