mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Add _ConstEvaluator, evaluate_* getters, format validation
- Implement _ConstEvaluator type in _typing module with STRING format support via typing_type_repr (port of _Py_typing_type_repr) - Add evaluate_bound, evaluate_constraints, evaluate_default pygetset properties to TypeVar, ParamSpec, TypeVarTuple - Emit format validation in evaluator scopes (compile_type_param_bound_or_default, TypeAlias value scopes) so evaluators raise NotImplementedError for unsupported formats - Add non-default-after-default SyntaxError in scan_type_params - Fix ParamSpec default_value to use Mutex for proper caching - Fix TypeVar constructor: evaluate_constraints set to None instead of constraints tuple for eager-constructed TypeVars - Pass format=1 (FORMAT_VALUE) to all lazy evaluator calls - Remove 6 expectedFailure markers from test_type_params
This commit is contained in:
5
Lib/test/test_type_params.py
vendored
5
Lib/test/test_type_params.py
vendored
@@ -1374,13 +1374,11 @@ class DefaultsTest(unittest.TestCase):
|
||||
Ts, = ns["Alias"].__type_params__
|
||||
self.assertEqual(Ts.__default__, next(iter(ns["default"])))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxError not raised
|
||||
def test_nondefault_after_default(self):
|
||||
check_syntax_error(self, "def func[T=int, U](): pass", "non-default type parameter 'U' follows default type parameter")
|
||||
check_syntax_error(self, "class C[T=int, U]: pass", "non-default type parameter 'U' follows default type parameter")
|
||||
check_syntax_error(self, "type A[T=int, U] = int", "non-default type parameter 'U' follows default type parameter")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + defined
|
||||
def test_lazy_evaluation(self):
|
||||
ns = run_code("""
|
||||
type Alias[T = Undefined, *U = Undefined, **V = Undefined] = int
|
||||
@@ -1431,7 +1429,6 @@ class DefaultsTest(unittest.TestCase):
|
||||
|
||||
|
||||
class TestEvaluateFunctions(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'TypeAliasType' object has no attribute 'evaluate_value'
|
||||
def test_general(self):
|
||||
type Alias = int
|
||||
Alias2 = TypeAliasType("Alias2", int)
|
||||
@@ -1459,7 +1456,6 @@ class TestEvaluateFunctions(unittest.TestCase):
|
||||
self.assertIs(annotationlib.call_evaluate_function(case, annotationlib.Format.FORWARDREF), int)
|
||||
self.assertEqual(annotationlib.call_evaluate_function(case, annotationlib.Format.STRING), 'int')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_constraints(self):
|
||||
def f[T: (int, str)](): pass
|
||||
T, = f.__type_params__
|
||||
@@ -1471,7 +1467,6 @@ class TestEvaluateFunctions(unittest.TestCase):
|
||||
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.FORWARDREF), (int, str))
|
||||
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.STRING), '(int, str)')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'TypeVar' object has no attribute 'evaluate_bound'
|
||||
def test_const_evaluator(self):
|
||||
T = TypeVar("T", bound=int)
|
||||
self.assertEqual(repr(T.evaluate_bound), "<constevaluator <class 'int'>>")
|
||||
|
||||
@@ -2461,6 +2461,10 @@ impl Compiler {
|
||||
self.push_symbol_table()?;
|
||||
let inner_key = self.symbol_table_stack.len() - 1;
|
||||
self.enter_scope("TypeAlias", CompilerScope::TypeParams, inner_key, lineno)?;
|
||||
// Evaluator takes a positional-only format parameter
|
||||
self.current_code_info().metadata.argcount = 1;
|
||||
self.current_code_info().metadata.posonlyargcount = 1;
|
||||
self.emit_format_validation()?;
|
||||
self.compile_expression(value)?;
|
||||
emit!(self, Instruction::ReturnValue);
|
||||
let value_code = self.exit_scope();
|
||||
@@ -2494,6 +2498,10 @@ impl Compiler {
|
||||
let key = self.symbol_table_stack.len() - 1;
|
||||
let lineno = self.get_source_line_number().get().to_u32();
|
||||
self.enter_scope("TypeAlias", CompilerScope::TypeParams, key, lineno)?;
|
||||
// Evaluator takes a positional-only format parameter
|
||||
self.current_code_info().metadata.argcount = 1;
|
||||
self.current_code_info().metadata.posonlyargcount = 1;
|
||||
self.emit_format_validation()?;
|
||||
|
||||
let prev_ctx = self.ctx;
|
||||
self.ctx = CompileContext {
|
||||
@@ -2635,6 +2643,12 @@ impl Compiler {
|
||||
// Enter scope with the type parameter name
|
||||
self.enter_scope(name, CompilerScope::TypeParams, key, lineno)?;
|
||||
|
||||
// Evaluator takes a positional-only format parameter
|
||||
self.current_code_info().metadata.argcount = 1;
|
||||
self.current_code_info().metadata.posonlyargcount = 1;
|
||||
|
||||
self.emit_format_validation()?;
|
||||
|
||||
// TypeParams scope is function-like
|
||||
let prev_ctx = self.ctx;
|
||||
self.ctx = CompileContext {
|
||||
|
||||
@@ -1485,6 +1485,8 @@ impl SymbolTableBuilder {
|
||||
CompilerScope::TypeParams,
|
||||
self.line_index_start(value.range()),
|
||||
);
|
||||
// Evaluator takes a format parameter
|
||||
self.register_name("format", SymbolUsage::Parameter, TextRange::default())?;
|
||||
if in_class {
|
||||
if let Some(table) = self.tables.last_mut() {
|
||||
table.can_see_class_scope = true;
|
||||
@@ -1990,6 +1992,8 @@ impl SymbolTableBuilder {
|
||||
let in_class = self.tables.last().is_some_and(|t| t.can_see_class_scope);
|
||||
let line_number = self.line_index_start(expr.range());
|
||||
self.enter_scope(scope_name, CompilerScope::TypeParams, line_number);
|
||||
// Evaluator takes a format parameter
|
||||
self.register_name("format", SymbolUsage::Parameter, TextRange::default())?;
|
||||
|
||||
if in_class {
|
||||
if let Some(table) = self.tables.last_mut() {
|
||||
@@ -2015,11 +2019,15 @@ impl SymbolTableBuilder {
|
||||
fn scan_type_params(&mut self, type_params: &ast::TypeParams) -> SymbolTableResult {
|
||||
// Check for duplicate type parameter names
|
||||
let mut seen_names: IndexSet<&str> = IndexSet::default();
|
||||
// Check for non-default type parameter after default type parameter
|
||||
let mut default_seen = false;
|
||||
for type_param in &type_params.type_params {
|
||||
let (name, range) = match type_param {
|
||||
ast::TypeParam::TypeVar(tv) => (tv.name.as_str(), tv.range),
|
||||
ast::TypeParam::ParamSpec(ps) => (ps.name.as_str(), ps.range),
|
||||
ast::TypeParam::TypeVarTuple(tvt) => (tvt.name.as_str(), tvt.range),
|
||||
let (name, range, has_default) = match type_param {
|
||||
ast::TypeParam::TypeVar(tv) => (tv.name.as_str(), tv.range, tv.default.is_some()),
|
||||
ast::TypeParam::ParamSpec(ps) => (ps.name.as_str(), ps.range, ps.default.is_some()),
|
||||
ast::TypeParam::TypeVarTuple(tvt) => {
|
||||
(tvt.name.as_str(), tvt.range, tvt.default.is_some())
|
||||
}
|
||||
};
|
||||
if !seen_names.insert(name) {
|
||||
return Err(SymbolTableError {
|
||||
@@ -2031,6 +2039,21 @@ impl SymbolTableBuilder {
|
||||
),
|
||||
});
|
||||
}
|
||||
if has_default {
|
||||
default_seen = true;
|
||||
} else if default_seen {
|
||||
return Err(SymbolTableError {
|
||||
error: format!(
|
||||
"non-default type parameter '{}' follows default type parameter",
|
||||
name
|
||||
),
|
||||
location: Some(
|
||||
self.source_file
|
||||
.to_source_code()
|
||||
.source_location(range.start(), PositionEncoding::Utf8),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Register .type_params as a type parameter (automatically becomes cell variable)
|
||||
|
||||
@@ -10,7 +10,7 @@ pub(crate) mod typevar {
|
||||
common::lock::PyMutex,
|
||||
function::{FuncArgs, PyComparisonValue},
|
||||
protocol::PyNumberMethods,
|
||||
stdlib::typing::call_typing_func_object,
|
||||
stdlib::typing::{call_typing_func_object, decl::constevaluator_alloc},
|
||||
types::{AsNumber, Comparable, Constructor, Iterable, PyComparisonOp, Representable},
|
||||
};
|
||||
|
||||
@@ -98,7 +98,7 @@ pub(crate) mod typevar {
|
||||
return Ok(constraints.clone());
|
||||
}
|
||||
let r = if !vm.is_none(&self.evaluate_constraints) {
|
||||
*constraints = self.evaluate_constraints.call((), vm)?;
|
||||
*constraints = self.evaluate_constraints.call((1i32,), vm)?;
|
||||
constraints.clone()
|
||||
} else {
|
||||
vm.ctx.empty_tuple.clone().into()
|
||||
@@ -113,7 +113,7 @@ pub(crate) mod typevar {
|
||||
return Ok(bound.clone());
|
||||
}
|
||||
let r = if !vm.is_none(&self.evaluate_bound) {
|
||||
*bound = self.evaluate_bound.call((), vm)?;
|
||||
*bound = self.evaluate_bound.call((1i32,), vm)?;
|
||||
bound.clone()
|
||||
} else {
|
||||
vm.ctx.none()
|
||||
@@ -139,20 +139,55 @@ pub(crate) mod typevar {
|
||||
#[pygetset]
|
||||
fn __default__(&self, vm: &VirtualMachine) -> PyResult {
|
||||
let mut default_value = self.default_value.lock();
|
||||
// Check if default_value is NoDefault (not just None)
|
||||
if !default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(default_value.clone());
|
||||
}
|
||||
let evaluate_default = self.evaluate_default.lock();
|
||||
if !vm.is_none(&evaluate_default) {
|
||||
*default_value = evaluate_default.call((), vm)?;
|
||||
*default_value = evaluate_default.call((1i32,), vm)?;
|
||||
Ok(default_value.clone())
|
||||
} else {
|
||||
// Return NoDefault singleton
|
||||
Ok(vm.ctx.typing_no_default.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn evaluate_bound(&self, vm: &VirtualMachine) -> PyResult {
|
||||
if !vm.is_none(&self.evaluate_bound) {
|
||||
return Ok(self.evaluate_bound.clone());
|
||||
}
|
||||
let bound = self.bound.lock();
|
||||
if !vm.is_none(&bound) {
|
||||
return Ok(constevaluator_alloc(bound.clone(), vm));
|
||||
}
|
||||
Ok(vm.ctx.none())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn evaluate_constraints(&self, vm: &VirtualMachine) -> PyResult {
|
||||
if !vm.is_none(&self.evaluate_constraints) {
|
||||
return Ok(self.evaluate_constraints.clone());
|
||||
}
|
||||
let constraints = self.constraints.lock();
|
||||
if !vm.is_none(&constraints) {
|
||||
return Ok(constevaluator_alloc(constraints.clone(), vm));
|
||||
}
|
||||
Ok(vm.ctx.none())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn evaluate_default(&self, vm: &VirtualMachine) -> PyResult {
|
||||
let evaluate_default = self.evaluate_default.lock();
|
||||
if !vm.is_none(&evaluate_default) {
|
||||
return Ok(evaluate_default.clone());
|
||||
}
|
||||
let default_value = self.default_value.lock();
|
||||
if !default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(constevaluator_alloc(default_value.clone(), vm));
|
||||
}
|
||||
Ok(vm.ctx.none())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __typing_subst__(
|
||||
zelf: crate::PyRef<Self>,
|
||||
@@ -333,7 +368,7 @@ pub(crate) mod typevar {
|
||||
return Err(vm.new_type_error("Constraints cannot be used with bound"));
|
||||
}
|
||||
let constraints_tuple = vm.ctx.new_tuple(constraints);
|
||||
(constraints_tuple.clone().into(), constraints_tuple.into())
|
||||
(constraints_tuple.into(), vm.ctx.none())
|
||||
} else {
|
||||
(vm.ctx.none(), vm.ctx.none())
|
||||
};
|
||||
@@ -403,7 +438,7 @@ pub(crate) mod typevar {
|
||||
pub struct ParamSpec {
|
||||
name: PyObjectRef,
|
||||
bound: Option<PyObjectRef>,
|
||||
default_value: PyObjectRef,
|
||||
default_value: parking_lot::Mutex<PyObjectRef>,
|
||||
evaluate_default: PyMutex<PyObjectRef>,
|
||||
covariant: bool,
|
||||
contravariant: bool,
|
||||
@@ -465,23 +500,30 @@ pub(crate) mod typevar {
|
||||
|
||||
#[pygetset]
|
||||
fn __default__(&self, vm: &VirtualMachine) -> PyResult {
|
||||
// Check if default_value is NoDefault (not just None)
|
||||
if !self.default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(self.default_value.clone());
|
||||
let mut default_value = self.default_value.lock();
|
||||
if !default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(default_value.clone());
|
||||
}
|
||||
// handle evaluate_default
|
||||
let evaluate_default = self.evaluate_default.lock();
|
||||
if !vm.is_none(&evaluate_default) {
|
||||
let default_value = evaluate_default.call((), vm)?;
|
||||
return Ok(default_value);
|
||||
*default_value = evaluate_default.call((1i32,), vm)?;
|
||||
Ok(default_value.clone())
|
||||
} else {
|
||||
Ok(vm.ctx.typing_no_default.clone().into())
|
||||
}
|
||||
// Return NoDefault singleton
|
||||
Ok(vm.ctx.typing_no_default.clone().into())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn evaluate_default(&self, _vm: &VirtualMachine) -> PyObjectRef {
|
||||
self.evaluate_default.lock().clone()
|
||||
fn evaluate_default(&self, vm: &VirtualMachine) -> PyResult {
|
||||
let evaluate_default = self.evaluate_default.lock();
|
||||
if !vm.is_none(&evaluate_default) {
|
||||
return Ok(evaluate_default.clone());
|
||||
}
|
||||
let default_value = self.default_value.lock();
|
||||
if !default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(constevaluator_alloc(default_value.clone(), vm));
|
||||
}
|
||||
Ok(vm.ctx.none())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -494,8 +536,7 @@ pub(crate) mod typevar {
|
||||
if !vm.is_none(&self.evaluate_default.lock()) {
|
||||
return true;
|
||||
}
|
||||
// Check if default_value is not NoDefault
|
||||
!self.default_value.is(&vm.ctx.typing_no_default)
|
||||
!self.default_value.lock().is(&vm.ctx.typing_no_default)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -596,7 +637,7 @@ pub(crate) mod typevar {
|
||||
let paramspec = Self {
|
||||
name,
|
||||
bound,
|
||||
default_value,
|
||||
default_value: parking_lot::Mutex::new(default_value),
|
||||
evaluate_default: PyMutex::new(vm.ctx.none()),
|
||||
covariant,
|
||||
contravariant,
|
||||
@@ -627,7 +668,7 @@ pub(crate) mod typevar {
|
||||
Self {
|
||||
name,
|
||||
bound: None,
|
||||
default_value: vm.ctx.typing_no_default.clone().into(),
|
||||
default_value: parking_lot::Mutex::new(vm.ctx.typing_no_default.clone().into()),
|
||||
evaluate_default: PyMutex::new(vm.ctx.none()),
|
||||
covariant: false,
|
||||
contravariant: false,
|
||||
@@ -655,27 +696,37 @@ pub(crate) mod typevar {
|
||||
#[pygetset]
|
||||
fn __default__(&self, vm: &VirtualMachine) -> PyResult {
|
||||
let mut default_value = self.default_value.lock();
|
||||
// Check if default_value is NoDefault (not just None)
|
||||
if !default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(default_value.clone());
|
||||
}
|
||||
let evaluate_default = self.evaluate_default.lock();
|
||||
if !vm.is_none(&evaluate_default) {
|
||||
*default_value = evaluate_default.call((), vm)?;
|
||||
*default_value = evaluate_default.call((1i32,), vm)?;
|
||||
Ok(default_value.clone())
|
||||
} else {
|
||||
// Return NoDefault singleton
|
||||
Ok(vm.ctx.typing_no_default.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn evaluate_default(&self, vm: &VirtualMachine) -> PyResult {
|
||||
let evaluate_default = self.evaluate_default.lock();
|
||||
if !vm.is_none(&evaluate_default) {
|
||||
return Ok(evaluate_default.clone());
|
||||
}
|
||||
let default_value = self.default_value.lock();
|
||||
if !default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(constevaluator_alloc(default_value.clone(), vm));
|
||||
}
|
||||
Ok(vm.ctx.none())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn has_default(&self, vm: &VirtualMachine) -> bool {
|
||||
if !vm.is_none(&self.evaluate_default.lock()) {
|
||||
return true;
|
||||
}
|
||||
let default_value = self.default_value.lock();
|
||||
// Check if default_value is not NoDefault
|
||||
!default_value.is(&vm.ctx.typing_no_default)
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ pub(crate) mod decl {
|
||||
builtins::{PyGenericAlias, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, type_},
|
||||
function::FuncArgs,
|
||||
protocol::{PyMappingMethods, PyNumberMethods},
|
||||
types::{AsMapping, AsNumber, Constructor, Iterable, Representable},
|
||||
types::{AsMapping, AsNumber, Callable, Constructor, Iterable, Representable},
|
||||
};
|
||||
|
||||
#[pyfunction]
|
||||
@@ -84,6 +84,103 @@ pub(crate) mod decl {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "_ConstEvaluator", module = "_typing")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(crate) struct ConstEvaluator {
|
||||
value: PyObjectRef,
|
||||
}
|
||||
|
||||
#[pyclass(with(Constructor, Callable, Representable), flags(IMMUTABLETYPE))]
|
||||
impl ConstEvaluator {}
|
||||
|
||||
impl Constructor for ConstEvaluator {
|
||||
type Args = FuncArgs;
|
||||
|
||||
fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
Err(vm.new_type_error("cannot create '_typing._ConstEvaluator' instances".to_owned()))
|
||||
}
|
||||
|
||||
fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
|
||||
unreachable!("ConstEvaluator cannot be instantiated from Python")
|
||||
}
|
||||
}
|
||||
|
||||
/// annotationlib.Format.STRING = 4
|
||||
const ANNOTATE_FORMAT_STRING: i32 = 4;
|
||||
|
||||
impl Callable for ConstEvaluator {
|
||||
type Args = FuncArgs;
|
||||
|
||||
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let (format,): (i32,) = args.bind(vm)?;
|
||||
let value = &zelf.value;
|
||||
if format == ANNOTATE_FORMAT_STRING {
|
||||
return typing_type_repr_value(value, vm);
|
||||
}
|
||||
Ok(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// String representation of a type for annotation purposes.
|
||||
/// Equivalent of _Py_typing_type_repr.
|
||||
fn typing_type_repr(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
// Ellipsis
|
||||
if obj.is(&vm.ctx.ellipsis) {
|
||||
return Ok("...".to_owned());
|
||||
}
|
||||
// NoneType -> "None"
|
||||
if obj.is(&vm.ctx.types.none_type.as_object()) {
|
||||
return Ok("None".to_owned());
|
||||
}
|
||||
// Generic aliases (has __origin__ and __args__) -> repr
|
||||
let has_origin = obj.get_attr("__origin__", vm).is_ok();
|
||||
let has_args = obj.get_attr("__args__", vm).is_ok();
|
||||
if has_origin && has_args {
|
||||
return Ok(obj.repr(vm)?.to_string());
|
||||
}
|
||||
// Has __qualname__ and __module__
|
||||
if let Ok(qualname) = obj.get_attr("__qualname__", vm) {
|
||||
if let Ok(module) = obj.get_attr("__module__", vm) {
|
||||
if !vm.is_none(&module) {
|
||||
if let Some(module_str) = module.downcast_ref::<crate::builtins::PyStr>() {
|
||||
if module_str.as_str() == "builtins" {
|
||||
return Ok(qualname.str(vm)?.to_string());
|
||||
}
|
||||
return Ok(format!("{}.{}", module_str.as_str(), qualname.str(vm)?));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback to repr
|
||||
Ok(obj.repr(vm)?.to_string())
|
||||
}
|
||||
|
||||
/// Format a value as a string for ANNOTATE_FORMAT_STRING.
|
||||
/// Handles tuples specially by wrapping in parentheses.
|
||||
fn typing_type_repr_value(value: &PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
if let Ok(tuple) = value.try_to_ref::<PyTuple>(vm) {
|
||||
let mut parts = Vec::with_capacity(tuple.len());
|
||||
for item in tuple.iter() {
|
||||
parts.push(typing_type_repr(item, vm)?);
|
||||
}
|
||||
Ok(vm.ctx.new_str(format!("({})", parts.join(", "))).into())
|
||||
} else {
|
||||
Ok(vm.ctx.new_str(typing_type_repr(value, vm)?).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Representable for ConstEvaluator {
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let value_repr = zelf.value.repr(vm)?;
|
||||
Ok(format!("<constevaluator {}>", value_repr))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn constevaluator_alloc(value: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
ConstEvaluator { value }.into_ref(&vm.ctx).into()
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name, module = "typing")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
@@ -140,7 +237,8 @@ pub(crate) mod decl {
|
||||
if let Some(value) = cached {
|
||||
return Ok(value);
|
||||
}
|
||||
let value = self.compute_value.call((), vm)?;
|
||||
// Call evaluator with format=1 (FORMAT_VALUE)
|
||||
let value = self.compute_value.call((1i32,), vm)?;
|
||||
*self.cached_value.lock() = Some(value.clone());
|
||||
Ok(value)
|
||||
}
|
||||
@@ -193,20 +291,12 @@ pub(crate) mod decl {
|
||||
vm.ctx.none()
|
||||
}
|
||||
|
||||
/// Returns the evaluator for the alias value.
|
||||
#[pygetset]
|
||||
fn evaluate_value(&self, vm: &VirtualMachine) -> PyResult {
|
||||
if self.is_lazy {
|
||||
// Lazy path: return the compute function directly
|
||||
return Ok(self.compute_value.clone());
|
||||
}
|
||||
// Eager path: wrap value in a ConstEvaluator
|
||||
let value = self.compute_value.clone();
|
||||
Ok(vm
|
||||
.new_function("_ConstEvaluator", move |_args: FuncArgs| -> PyResult {
|
||||
Ok(value.clone())
|
||||
})
|
||||
.into())
|
||||
Ok(constevaluator_alloc(self.compute_value.clone(), vm))
|
||||
}
|
||||
|
||||
/// Check type_params ordering: non-default params must precede default params.
|
||||
|
||||
Reference in New Issue
Block a user