How to Work with Generated Code in Pylance

April 8, 2026 · View on GitHub

Projects often contain generated Python files — protobuf/gRPC stubs, SQLAlchemy models, Django migrations, Pydantic datamodel-code-generator output, and similar. These files can cause noisy diagnostics or missing type information. This guide covers strategies for making Pylance work well with generated code.


Table of Contents


Strategy 1: Add Generated Output to extraPaths

If your build step generates Python files in an output directory, add that directory to extraPaths so Pylance can resolve imports from it:

project/
├── proto/
│   └── service.proto
├── generated/          # protoc output: service_pb2.py, service_pb2_grpc.py
├── src/api/
│   └── client.py       # from generated.service_pb2 import ServiceRequest
{
    "python.analysis.extraPaths": ["./generated"]
}

When to use: The generated files have useful type information and you want Pylance to resolve imports from them.


Strategy 2: Use stubPath for Hand-Written Stubs

If generated code is complex or has missing type info, create .pyi stub files that provide accurate type annotations:

{
    "python.analysis.stubPath": "./typestubs"
}
project/
├── typestubs/
│   └── generated/
│       ├── __init__.pyi
│       └── service_pb2.pyi   # hand-written or mypy-protobuf generated

When to use: The generated code exists but lacks type information, or you want cleaner type annotations than what the generator produces (e.g., mypy-protobuf stubs for protobuf).


Strategy 3: Suppress Diagnostics with ignore

To keep Pylance from reporting errors in generated files you don't want to fix:

{
    "python.analysis.ignore": ["generated/**"]
}

This still allows imports from generated code to resolve — it only suppresses diagnostics in those files.

When to use: The generated files are importable and work fine, but contain patterns that trigger Pylance diagnostics (missing annotations, dynamic attributes, etc.) that you don't want to see.


Strategy 4: Use useNearestConfiguration with a Relaxed Config

Drop a pyrightconfig.json in the generated directory with relaxed rules:

// generated/pyrightconfig.json
{
    "typeCheckingMode": "off"
}

This requires python.analysis.useNearestConfiguration to be true so Pylance picks up the per-directory config.

When to use: You want different type-checking strictness for generated code vs. hand-written code, and you're already using useNearestConfiguration.


Choosing a Strategy

ScenarioRecommended Strategy
Generated files have good types, just need import resolutionStrategy 1 (extraPaths)
Generated files lack types, need manual .pyi stubsStrategy 2 (stubPath)
Generated files work but produce noisy diagnosticsStrategy 3 (ignore)
Different strictness levels for generated vs. hand-written codeStrategy 4 (useNearestConfiguration)
Import resolution + suppress diagnostics in generated filesCombine Strategy 1 + Strategy 3

Framework-Specific Guidance

Django

Django uses metaclasses and dynamic model fields that Pylance cannot analyze from source alone. Install django-stubs to provide type information:

pip install django-stubs

Add to pyproject.toml:

[tool.django-stubs]
django_settings_module = "myproject.settings"

For django-stubs to work properly with Pylance, you may also need django-stubs-ext and a manage.py or settings module reachable from the workspace root.

For dynamic model fields that stubs don't cover, create custom stubs under stubPath.

Protobuf / gRPC

Use mypy-protobuf to generate .pyi stubs alongside the Python files:

pip install mypy-protobuf
protoc --python_out=generated --mypy_out=generated service.proto

Then add the generated directory to extraPaths so Pylance resolves the imports. The .pyi stubs colocated with .py files are picked up automatically.

FastAPI and Pydantic

Pydantic v2 ships type annotations natively (py.typed), so Pylance recognizes BaseModel fields, model_validator, and field_validator out of the box. If Pylance doesn't recognize Pydantic features:

  1. Ensure you have Pydantic v2+ installed (pip install pydantic>=2.0)
  2. Restart Pylance after installing (Ctrl+Shift+P → Pylance: Restart Language Server)

FastAPI builds on Pydantic and uses Depends() extensively. Pylance understands FastAPI's Depends typing when the dependency function has return type annotations:

from fastapi import Depends, FastAPI

def get_db() -> Database:  # Return type annotation required
    ...

@app.get("/items")
async def read_items(db: Database = Depends(get_db)):  # Pylance infers db type
    ...

Common issues:

  • response_model not validated: response_model=MyModel on route decorators is not statically checked by Pylance — use return type annotations instead: async def read_items(...) -> MyModel
  • Missing attributes on request body: Ensure the Pydantic model is imported and the field types are annotated
  • Pydantic v1 compatibility: If using pydantic.v1 compat layer, Pylance may not recognize v1-style validators — migrate to v2 @field_validator / @model_validator

SQLAlchemy 2.0

SQLAlchemy 2.0 uses Mapped[] type annotations and mapped_column() which Pylance recognizes natively. For best results:

from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column()

If Pylance doesn't recognize Mapped attributes, ensure SQLAlchemy 2.0+ is installed in your selected interpreter. For SQLAlchemy 1.4 projects that haven't migrated, install sqlalchemy2-stubs (pip install sqlalchemy2-stubs). Note: sqlalchemy2-stubs is only compatible with SQLAlchemy 1.4 and must be uninstalled before upgrading to 2.0.

Dynamic Attribute Access (__getattr__ / __getitem__)

If your code or a library uses __getattr__ for dynamic attribute access, Pylance cannot infer what attributes are available. Provide explicit type information:

Option 1: Type the dunder method in a stub

# typings/my_dynamic_lib/__init__.pyi
from typing import Any

class DynamicConfig:
    # Allow any attribute access:
    def __getattr__(self, name: str) -> Any: ...

    # Or be specific with overloads:
    from typing import overload
    @overload
    def __getitem__(self, key: str) -> str: ...
    @overload
    def __getitem__(self, key: int) -> bytes: ...

Option 2: Annotate in your code

from typing import Any

class Config:
    def __getattr__(self, name: str) -> Any:
        return self._data[name]

When __getattr__ returns Any, Pylance suppresses reportAttributeAccessIssue for that class. When it returns a more specific type, Pylance validates attribute usage against that type.


Dataclass, attrs, and Decorator-Synthesized Members

Pylance has built-in support for @dataclasses.dataclass, @attrs.define, and several other common decorators that synthesize methods (__init__, __eq__, __repr__, etc.). If Pylance does not recognize fields or methods on a decorated class, check these common causes:

Missing stubs for attrs

For attrs, install the stubs package:

pip install attrs  # attrs >= 22.1 ships py.typed; older versions need types-attrs

from __future__ import annotations interaction

When from __future__ import annotations is active, all annotations are strings at runtime. Pylance handles this correctly for @dataclass and @attrs.define, but some less common decorators may not be recognized. If fields appear as Unknown, try removing from __future__ import annotations as a test to confirm this is the cause.

Custom decorators that synthesize attributes

Pylance only recognizes a fixed set of well-known decorators (@dataclass, @attrs.define, @attrs.s, @pydantic.BaseModel, etc.). For custom decorators that add attributes:

  1. Write a stub listing the synthesized attributes explicitly:
# typings/my_framework/__init__.pyi
class MyModel:
    id: int
    name: str
    def save(self) -> None: ...
  1. Use __getattr__ as a fallback for truly dynamic attributes (see Dynamic Attribute Access above).

reportAttributeAccessIssue on known fields

If reportAttributeAccessIssue fires on fields that should exist (e.g., @dataclass fields), check:

  • The decorator import is correct (from dataclasses import dataclass, not a custom wrapper)
  • Field types are annotated (name: str, not just name = "default")
  • The class is not inheriting from a dynamic base that shadows the decorator behavior

FAQ

Q: Can I combine multiple strategies?

Yes. A common pattern is to add the generated output to extraPaths (so imports resolve) and also add it to ignore (so diagnostics in those files are suppressed):

{
    "python.analysis.extraPaths": ["./generated"],
    "python.analysis.ignore": ["generated/**"]
}

Q: My protobuf/gRPC stubs have no type information. What should I do?

Use mypy-protobuf to generate .pyi stubs alongside the Python files:

protoc --python_out=generated --mypy_out=generated service.proto

Then either put the .pyi files alongside the generated .py files (Strategy 1 handles both) or move them to stubPath (Strategy 2).



For more information on Pylance settings and customization, refer to the Pylance Settings and Customization documentation.


This document was generated with the assistance of AI and has been reviewed by humans for accuracy and completeness.