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:
- source-level Python 3.7 compatibility;
- native CPython 3.7 wheel compatibility; and
- a pure-Python fallback for platforms without a native wheel.
Build profiles
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:
try:
from dcc_mcp_core._core import BlockingDispatcher
except ImportError:
from dcc_mcp_core.host._fallback import BlockingDispatcherUse 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:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from dcc_mcp_core._core import ToolRegistryPython 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
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 statusThe 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
- Decide whether the feature is available in native 3.7, lite 3.7, or both.
- Keep Rust extension imports behind the owning facade when lite is supported.
- Add the critical import to
compatibility/python.jsonwhen it belongs to the guaranteed runtime surface. - Add native behavior tests. Add lite behavior tests when a fallback exists.
- Run
vx just check-python-supportandvx just check-py37-syntax. - 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.
# 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.