Skip to content

Python 3.7 Compatibility Architecture

Audience: core and adapter maintainers. Decision: ADR 011. Contract: compatibility/python.json.

Why this profile exists

DCC applications embed Python and usually cannot adopt a newer interpreter without upgrading the entire host. Python 3.7 therefore remains an LTS target for Maya 2022, Blender 2.83, MotionBuilder 2022, and studio-owned hosts.

The architecture separates three concerns that must not be conflated:

  1. source-level Python 3.7 compatibility;
  2. native CPython 3.7 wheel compatibility; and
  3. a pure-Python fallback for platforms without a native wheel.

Build profiles

text
                         dcc-mcp-core source
                                  |
              +-------------------+-------------------+
              |                   |                   |
              v                   v                   v
       native_py37           lite_py37              abi3
       cp37-cp37m            py3-none-any           cp38-abi3
       includes _core        excludes _core         includes _core
       Python 3.7            Python 3.7             Python 3.8+

native_py37

The native profile uses the PyO3 series pinned in the compatibility contract and does not enable abi3-py38. Linux and Windows wheels are built separately for CPython 3.7. This is the full package and the authoritative LTS proof.

lite_py37

The lite profile packages the Python tree without a compiled extension. Public entry points select pure-Python or sidecar-backed implementations when _core is absent. It is a portability fallback, not a replacement for the native profile.

The lite factory provides local metadata discovery through list_skills(), search_skills(), and get_skill() so explicit and environment-provided SKILL.md paths remain observable. The Rust sidecar is dispatch-only: it does not host a SkillCatalog, advertise those skills to the gateway, or activate declarative skill packages. Calls to load_skill()/unload_skill() therefore fail explicitly instead of reporting false success. Use a native Python 3.7 wheel when gateway discovery or declarative skill activation is required.

abi3

The modern profile enables abi3-py38. One wheel per platform serves all maintained Python versions from 3.8 upward.

Import boundary

Modules that support the lite profile must treat _core as optional:

python
try:
    from dcc_mcp_core._core import BlockingDispatcher
except ImportError:
    from dcc_mcp_core.host._fallback import BlockingDispatcher

Use this pattern only at an ownership boundary. Do not scatter broad except ImportError blocks through business logic; they can hide unrelated missing imports. Keep fallback selection in the facade or factory responsible for that capability.

For type-only imports, use TYPE_CHECKING:

python
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from dcc_mcp_core._core import ToolRegistry

Python 3.7 typing behavior

from __future__ import annotations postpones annotation evaluation. That lets many modern annotation expressions compile on Python 3.7, but it does not make their runtime evaluation safe. Code that calls typing.get_type_hints or stores evaluated aliases must use a compatibility layer.

Python 3.7's typing module lacks several runtime APIs used by newer code, including Literal, Protocol, and runtime_checkable. Shared behavior belongs in dcc_mcp_core._typing_compat; do not duplicate private fallbacks in individual modules.

Source rules

Shipped modules and Python 3.7 tests must avoid grammar introduced after 3.7:

  • assignment expressions (:=);
  • positional-only parameter markers (/);
  • debug f-strings (f"{value=}");
  • structural pattern matching; and
  • other syntax rejected by a real CPython 3.7 compiler.

PEP 585 and PEP 604 annotation expressions require extra care: postponed annotations can make them importable, while later evaluation can still fail. Runtime smoke and tests provide the proof; a compile-only check does not.

CI layers

text
compatibility/python.json
          |
          +-- projection check (metadata, PyO3, workflows, docs)
          +-- real CPython 3.7 syntax check
          +-- native Linux wheel -> content check -> runtime smoke -> full suite
          +-- native Windows wheel -> content check -> runtime smoke
          +-- lite wheel -> content check -> fallback runtime smoke
          `-- stable aggregate status

The aggregate Python 3.7 compatibility status must be required in the repository ruleset. Its if: always() behavior ensures that skipped and cancelled dependencies cannot look green.

Adding a Python-facing feature

  1. Decide whether the feature is available in native 3.7, lite 3.7, or both.
  2. Keep Rust extension imports behind the owning facade when lite is supported.
  3. Add the critical import to compatibility/python.json when it belongs to the guaranteed runtime surface.
  4. Add native behavior tests. Add lite behavior tests when a fallback exists.
  5. Run vx just check-python-support and vx just check-py37-syntax.
  6. Let CI prove native Linux/Windows wheel construction.

Local commands

Use vx for the parser gate. Wheel building/runtime validation requires a full CPython 3.7 installation with pip and development/import libraries; set PYTHON37 to that executable. See Maya 2022 Support for PowerShell.

bash
# Static contract and real-parser checks
vx just check-python-support
vx just check-py37-syntax

# Native wheel
PYTHON37="${PYTHON37:-python3.7}"
vx just build-py37 -i "$PYTHON37"
"$PYTHON37" scripts/ci/check_python_wheel.py --profile native_py37 --platform linux-x86_64 \
  'dist/dcc_mcp_core-*-cp37-cp37m-*.whl'

# Lite wheel
"$PYTHON37" scripts/build_py37_pure_wheel.py
"$PYTHON37" scripts/ci/check_python_wheel.py --profile lite_py37 --platform any \
  'dist/dcc_mcp_core-*-py3-none-any.whl'

Operational guidance

  • Prefer the native wheel when one exists for the DCC platform.
  • Use lite only when the platform has no supported native artifact or when the adapter intentionally delegates execution to dcc-mcp-server.
  • Never work around an incompatible native build by publishing only lite; that turns a release failure into a silent capability loss.
  • A PyO3 upgrade must prove native Python 3.7 in the same change.

Deprecation

There is no date-based removal. A future change must supersede ADR 011, ship in a major release, provide at least 180 days of notice, and document adapter migrations.

Released under the MIT License.