Skip to content

Clean up PLC0415 noqa tech debt (inline imports) #2994

@snopoke

Description

@snopoke

Goal

Remove the 303 # noqa: PLC0415 suppression comments added when the import-outside-top-level ruff rule was enabled. Each suppressed import should either be moved to module level (the common case) or kept as a lazy import with a documented reason (circular import avoidance or startup-time reduction per docs/agents/django_performance.md).

Context

PR #2993 enabled PLC0415 to prevent new inline imports, but suppressed all 308 existing violations with # noqa: PLC0415 as a baseline. The policy from AGENTS.md is clear:

Don't use local imports for any reason other than to avoid circular imports or as a means to reduce startup time (reserved for specific imports)

So for each noqa comment:

  • If the import is inside a function/method purely for convenience → move it to the top of the file
  • If it's avoiding a circular import → keep the noqa, add a brief comment explaining why
  • If it's lazy-loading a heavy library (see docs/agents/django_performance.md) → keep the noqa, it's intentional

The heavy libraries that should stay lazy: langchain_google_vertexai, langchain_google_genai, langchain_anthropic, langchain_openai, boto3 (these are also enforced by TID253).

ALWAYS run the test before completing to surface any import issues.

Counts by app (total: 303):

  • 68 service_providers
  • 37 utils
  • 35 pipelines
  • 20 experiments
  • 18 evaluations
  • 15 human_annotations
  • 14 chat
  • 12 help
  • 12 web
  • 10 teams
  • 8 trace, 7 events, 7 documents, 6 channels
  • smaller apps: api, assistants, chatbots, data_migrations, users, dashboard, participants, custom_actions, files, sso, ocs_notifications, audit

Tasks

Task 1: service_providers (68 violations)

  • Task 1

Clean up the 68 # noqa: PLC0415 comments in apps/service_providers/. Key files: llm_service/main.py, models.py, messaging_service.py, speech_service.py, llm_service/runnables.py, llm_service/utils.py. The lazy-loaded LLM provider imports in llm_service/main.py are intentional (keep with note); others should be moved to module level unless they're avoiding circular imports.

Task 2: utils + pipelines (72 violations)

  • Task 2

Clean up 37 violations in apps/utils/ and 35 in apps/pipelines/. Key files: utils/deletion.py, utils/restricted_http.py, utils/tests/, pipelines/models.py, pipelines/repository.py.

Task 3: experiments + evaluations + human_annotations (53 violations)

  • Task 3

Clean up 20 violations in apps/experiments/, 18 in apps/evaluations/, and 15 in apps/human_annotations/.

Task 4: chat + help + web + teams (48 violations)

  • Task 4

Clean up 14 in apps/chat/, 12 in apps/help/, 12 in apps/web/, 10 in apps/teams/.

Task 5: Remaining apps (30 violations)

  • Task 5

Clean up the remaining ~30 violations across trace, events, documents, channels, api, assistants, chatbots, data_migrations, users, dashboard, participants, custom_actions, files, sso, ocs_notifications, audit.

Learnings

  • Task 2 (utils + pipelines): All remaining noqa annotations were already properly documented from previous work — no new changes needed. The remaining 41 annotations are all justified (circular imports, test setup requirements, Django LOGGING startup).
  • Task 3 (experiments/evaluations/human_annotations): Most violations were convenience imports safely moved to top level. Circular imports were kept with descriptive comments. The langchain_core.messages import in experiments/tasks.py was kept lazy to avoid langchain import on startup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions