astral-sh/ruff · error
DynamicClassAnchor::Definition should only be used for assig
Error message
DynamicClassAnchor::Definition should only be used for assignments
What it means
For an assigned dynamic-class call (`C = make_class('C', bases)`), ty defers reading the explicit base classes until needed. `deferred_explicit_bases` re-reads the definition's value node from the parsed module; the expect asserts the definition is an assignment with a value. It fires when a `DynamicClassAnchor::Definition` was created for a definition whose kind yields no value expression.
Source
Thrown at crates/ty_python_semantic/src/types/class/dynamic_literal.rs:213
///
/// Returns `[Unknown]` if the bases iterable is variable-length.
pub(crate) fn explicit_bases(self, db: &'db dyn Db) -> &'db [Type<'db>] {
/// Inner cached function for deferred inference of bases.
/// Only called for assigned calls where inference was deferred.
#[salsa::tracked(returns(deref), cycle_initial=|_, _, _| Box::default(), heap_size=ruff_memory_usage::heap_size)]
fn deferred_explicit_bases<'db>(
db: &'db dyn Db,
definition: Definition<'db>,
) -> Box<[Type<'db>]> {
let program_file = definition.program_file(db);
let python_file = program_file.python_file(db);
let env = ProgramEnvironment::from_file(program_file);
let module = parsed_module(db, python_file).load(db);
let value = definition
.kind(db)
.value(&module)
.expect("DynamicClassAnchor::Definition should only be used for assignments");
let call_expr = value
.as_call_expr()
.expect("Definition value should be a call expression");
let Some(bases_arg) = dynamic_class_bases_argument(&call_expr.arguments) else {
return Box::default();
};
// Use `definition_expression_type` for deferred inference support.
extract_fixed_length_iterable_element_types(db, &env, bases_arg, |expr| {
definition_expression_type(db, definition, expr)
})
.unwrap_or_else(|| Box::from([Type::unknown()]))
}
match self.anchor(db) {
// For dangling calls, bases are stored directly on the anchor.
DynamicClassAnchor::ScopeOffset { explicit_bases, .. } => explicit_bases.as_ref(),View on GitHub (pinned to 15f3fe6b15)
Solutions
- At the site that builds `DynamicClassAnchor::Definition`, only anchor definitions for which `.kind(db).value(&module)` is Some.
- Return empty bases (as the `dynamic_class_bases_argument` None path already does) instead of anchoring unmatched definitions.
- Add an mdtest for the offending assignment shape and run the ty_python_semantic suite.
- File an issue with the reproducer if it happens on stock ty.
Example fix
// before DynamicClassAnchor::Definition(definition) // after: guard at construction, degrade to no explicit bases let value = definition.kind(db).value(&module); let anchor = value.is_some().then(|| DynamicClassAnchor::Definition(definition));
Defensive patterns
Strategy: try-catch
Try / catch
let bases = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
dynamic_class.explicit_bases(db)
}))
.unwrap_or_else(|_| Box::from([Type::unknown()])); // degrade to unknown bases Prevention
- Contributors: after touching dynamic-class detection, run the full ty_python_semantic mdtest suite.
- Keep reproducers as named assignments (`C = make_class('C', ...)`) when filing issues.
- Embedders: catch_unwind around semantic queries and degrade to Type::unknown() so one anchor bug does not kill the run.
When it happens
Trigger: Calling `explicit_bases`/base resolution on a dynamic class anchored via `DynamicClassAnchor::Definition` (dynamic_literal.rs:233) where `definition.kind(db).value(&module)` is None - a non-assignment definition, or a definition resolved against a different module parse.
Common situations: Extending dynamic-class support to new constructs (module-level `type(...)` statements, conditional assignments, aliased targets) and anchoring definitions that are not plain assignments; refactors of definition kinds; anchors resolved after the file was reparsed without invalidation.
Related errors
- Expected `NamedTuple` definition to be an assignment
- dynamic class definitions should only be used for assignment
- anchor should not be NodeIndex::NONE
- scope offset should point to ExprCall
- Definition value should be a call expression
AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20).
Data as JSON: /api/errors/330b09255fca4cac.
Report an issue: GitHub.