Skip to content

Commit adddfe6

Browse files
authored
Merge pull request #7078 from youknowone/typing
Implement more typing
2 parents 84a2581 + 42740f7 commit adddfe6

13 files changed

Lines changed: 271 additions & 76 deletions

File tree

‎.cspell.dict/cpython.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ CLASSDEREF
3131
classdict
3232
cmpop
3333
codedepth
34+
constevaluator
3435
CODEUNIT
3536
CONVFUNC
3637
convparam

‎Lib/test/test_re.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2107,7 +2107,6 @@ def test_issue17998(self):
21072107
self.assertEqual(re.compile(pattern, re.S).findall(b'xyz'),
21082108
[b'xyz'], msg=pattern)
21092109

2110-
@unittest.expectedFailure # TODO: RUSTPYTHON
21112110
def test_match_repr(self):
21122111
for string in '[abracadabra]', S('[abracadabra]'):
21132112
m = re.search(r'(.+)(.*?)\1', string)

‎Lib/test/test_type_params.py‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ def inner[X]():
165165
"""
166166
check_syntax_error(self, code)
167167

168-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxError not raised
169168
def test_nonlocal_disallowed_02(self):
170169
code = """
171170
def outer2[T]():
@@ -174,7 +173,6 @@ def inner1():
174173
"""
175174
check_syntax_error(self, textwrap.dedent(code))
176175

177-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxError not raised
178176
def test_nonlocal_disallowed_03(self):
179177
code = """
180178
class Cls[T]:
@@ -268,7 +266,6 @@ def func[A]():
268266
with self.assertRaisesRegex(NameError, "name 'A' is not defined"):
269267
run_code(code)
270268

271-
@unittest.expectedFailure # TODO: RUSTPYTHON; NameError: name 'x' is not defined
272269
def test_method_access_01(self):
273270
ns = run_code("""
274271
class ClassA:
@@ -320,7 +317,6 @@ def funcB[B](self): ...
320317
with self.assertRaisesRegex(NameError, "name 'B' is not defined"):
321318
run_code(code)
322319

323-
@unittest.expectedFailure # TODO: RUSTPYTHON; NameError: name 'x' is not defined
324320
def test_class_scope_interaction_01(self):
325321
ns = run_code("""
326322
class C:
@@ -486,7 +482,6 @@ class Inner[U](make_base([T for _ in (1,)]), make_base(T)):
486482
self.assertEqual(base1.__arg__, [ns["C"].__type_params__[0]])
487483
self.assertEqual(base2.__arg__, "class")
488484

489-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: ~T != 'class'
490485
def test_gen_exp_in_generic_method(self):
491486
code = """
492487
class C[T]:
@@ -1374,13 +1369,11 @@ def test_starred_typevartuple(self):
13741369
Ts, = ns["Alias"].__type_params__
13751370
self.assertEqual(Ts.__default__, next(iter(ns["default"])))
13761371

1377-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxError not raised
13781372
def test_nondefault_after_default(self):
13791373
check_syntax_error(self, "def func[T=int, U](): pass", "non-default type parameter 'U' follows default type parameter")
13801374
check_syntax_error(self, "class C[T=int, U]: pass", "non-default type parameter 'U' follows default type parameter")
13811375
check_syntax_error(self, "type A[T=int, U] = int", "non-default type parameter 'U' follows default type parameter")
13821376

1383-
@unittest.expectedFailure # TODO: RUSTPYTHON; + defined
13841377
def test_lazy_evaluation(self):
13851378
ns = run_code("""
13861379
type Alias[T = Undefined, *U = Undefined, **V = Undefined] = int
@@ -1431,7 +1424,6 @@ def test_symtable_key_regression_name(self):
14311424

14321425

14331426
class TestEvaluateFunctions(unittest.TestCase):
1434-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'TypeAliasType' object has no attribute 'evaluate_value'
14351427
def test_general(self):
14361428
type Alias = int
14371429
Alias2 = TypeAliasType("Alias2", int)
@@ -1459,7 +1451,6 @@ def f[T: int = int, **P = int, *Ts = int](): pass
14591451
self.assertIs(annotationlib.call_evaluate_function(case, annotationlib.Format.FORWARDREF), int)
14601452
self.assertEqual(annotationlib.call_evaluate_function(case, annotationlib.Format.STRING), 'int')
14611453

1462-
@unittest.expectedFailure # TODO: RUSTPYTHON
14631454
def test_constraints(self):
14641455
def f[T: (int, str)](): pass
14651456
T, = f.__type_params__
@@ -1471,7 +1462,6 @@ def f[T: (int, str)](): pass
14711462
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.FORWARDREF), (int, str))
14721463
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.STRING), '(int, str)')
14731464

1474-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'TypeVar' object has no attribute 'evaluate_bound'
14751465
def test_const_evaluator(self):
14761466
T = TypeVar("T", bound=int)
14771467
self.assertEqual(repr(T.evaluate_bound), "<constevaluator <class 'int'>>")

‎Lib/test/test_typing.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8437,7 +8437,6 @@ class Bar(NamedTuple):
84378437
self.assertIsInstance(bar.attr, Vanilla)
84388438
self.assertEqual(bar.attr.name, "attr")
84398439

8440-
@unittest.expectedFailure # TODO: RUSTPYTHON; + Error calling __set_name__ on 'Annoying' instance attr in 'NamedTupleClass'
84418440
def test_setname_raises_the_same_as_on_other_classes(self):
84428441
class CustomException(BaseException): pass
84438442

@@ -9433,7 +9432,6 @@ def test_repr(self):
94339432
self.assertEqual(repr(Match[str]), 'typing.Match[str]')
94349433
self.assertEqual(repr(Match[bytes]), 'typing.Match[bytes]')
94359434

9436-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "type 're\.Match' is not an acceptable base type" does not match "type '_sre.Match' is not an acceptable base type"
94379435
def test_cannot_subclass(self):
94389436
with self.assertRaisesRegex(
94399437
TypeError,

‎crates/codegen/src/compile.rs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2474,6 +2474,10 @@ impl Compiler {
24742474
self.push_symbol_table()?;
24752475
let inner_key = self.symbol_table_stack.len() - 1;
24762476
self.enter_scope("TypeAlias", CompilerScope::TypeParams, inner_key, lineno)?;
2477+
// Evaluator takes a positional-only format parameter
2478+
self.current_code_info().metadata.argcount = 1;
2479+
self.current_code_info().metadata.posonlyargcount = 1;
2480+
self.emit_format_validation()?;
24772481
self.compile_expression(value)?;
24782482
emit!(self, Instruction::ReturnValue);
24792483
let value_code = self.exit_scope();
@@ -2507,6 +2511,10 @@ impl Compiler {
25072511
let key = self.symbol_table_stack.len() - 1;
25082512
let lineno = self.get_source_line_number().get().to_u32();
25092513
self.enter_scope("TypeAlias", CompilerScope::TypeParams, key, lineno)?;
2514+
// Evaluator takes a positional-only format parameter
2515+
self.current_code_info().metadata.argcount = 1;
2516+
self.current_code_info().metadata.posonlyargcount = 1;
2517+
self.emit_format_validation()?;
25102518

25112519
let prev_ctx = self.ctx;
25122520
self.ctx = CompileContext {
@@ -2648,6 +2656,12 @@ impl Compiler {
26482656
// Enter scope with the type parameter name
26492657
self.enter_scope(name, CompilerScope::TypeParams, key, lineno)?;
26502658

2659+
// Evaluator takes a positional-only format parameter
2660+
self.current_code_info().metadata.argcount = 1;
2661+
self.current_code_info().metadata.posonlyargcount = 1;
2662+
2663+
self.emit_format_validation()?;
2664+
26512665
// TypeParams scope is function-like
26522666
let prev_ctx = self.ctx;
26532667
self.ctx = CompileContext {

‎crates/codegen/src/symboltable.rs‎

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,6 @@ impl SymbolTableAnalyzer {
528528
if !self.tables.as_ref().is_empty() {
529529
let scope_depth = self.tables.as_ref().len();
530530
// check if the name is already defined in any outer scope
531-
// therefore
532531
if scope_depth < 2
533532
|| self.found_in_outer_scope(&symbol.name, st_typ)
534533
!= Some(SymbolScope::Free)
@@ -539,6 +538,25 @@ impl SymbolTableAnalyzer {
539538
location: None,
540539
});
541540
}
541+
// Check if the nonlocal binding refers to a type parameter
542+
if symbol.flags.contains(SymbolFlags::NONLOCAL) {
543+
for (symbols, _typ) in self.tables.iter().rev() {
544+
if let Some(sym) = symbols.get(&symbol.name) {
545+
if sym.flags.contains(SymbolFlags::TYPE_PARAM) {
546+
return Err(SymbolTableError {
547+
error: format!(
548+
"nonlocal binding not allowed for type parameter '{}'",
549+
symbol.name
550+
),
551+
location: None,
552+
});
553+
}
554+
if sym.is_bound() {
555+
break;
556+
}
557+
}
558+
}
559+
}
542560
} else {
543561
return Err(SymbolTableError {
544562
error: format!(
@@ -933,7 +951,8 @@ impl SymbolTableBuilder {
933951
/// Creates or reuses the annotation block for the current scope
934952
fn enter_annotation_scope(&mut self, line_number: u32) {
935953
let current = self.tables.last_mut().unwrap();
936-
let can_see_class_scope = current.typ == CompilerScope::Class;
954+
let can_see_class_scope =
955+
current.typ == CompilerScope::Class || current.can_see_class_scope;
937956
let has_conditional = current.has_conditional_annotations;
938957

939958
// Create annotation block if not exists
@@ -1485,6 +1504,8 @@ impl SymbolTableBuilder {
14851504
CompilerScope::TypeParams,
14861505
self.line_index_start(value.range()),
14871506
);
1507+
// Evaluator takes a format parameter
1508+
self.register_name("format", SymbolUsage::Parameter, TextRange::default())?;
14881509
if in_class {
14891510
if let Some(table) = self.tables.last_mut() {
14901511
table.can_see_class_scope = true;
@@ -1990,6 +2011,8 @@ impl SymbolTableBuilder {
19902011
let in_class = self.tables.last().is_some_and(|t| t.can_see_class_scope);
19912012
let line_number = self.line_index_start(expr.range());
19922013
self.enter_scope(scope_name, CompilerScope::TypeParams, line_number);
2014+
// Evaluator takes a format parameter
2015+
self.register_name("format", SymbolUsage::Parameter, TextRange::default())?;
19932016

19942017
if in_class {
19952018
if let Some(table) = self.tables.last_mut() {
@@ -2015,11 +2038,15 @@ impl SymbolTableBuilder {
20152038
fn scan_type_params(&mut self, type_params: &ast::TypeParams) -> SymbolTableResult {
20162039
// Check for duplicate type parameter names
20172040
let mut seen_names: IndexSet<&str> = IndexSet::default();
2041+
// Check for non-default type parameter after default type parameter
2042+
let mut default_seen = false;
20182043
for type_param in &type_params.type_params {
2019-
let (name, range) = match type_param {
2020-
ast::TypeParam::TypeVar(tv) => (tv.name.as_str(), tv.range),
2021-
ast::TypeParam::ParamSpec(ps) => (ps.name.as_str(), ps.range),
2022-
ast::TypeParam::TypeVarTuple(tvt) => (tvt.name.as_str(), tvt.range),
2044+
let (name, range, has_default) = match type_param {
2045+
ast::TypeParam::TypeVar(tv) => (tv.name.as_str(), tv.range, tv.default.is_some()),
2046+
ast::TypeParam::ParamSpec(ps) => (ps.name.as_str(), ps.range, ps.default.is_some()),
2047+
ast::TypeParam::TypeVarTuple(tvt) => {
2048+
(tvt.name.as_str(), tvt.range, tvt.default.is_some())
2049+
}
20232050
};
20242051
if !seen_names.insert(name) {
20252052
return Err(SymbolTableError {
@@ -2031,6 +2058,21 @@ impl SymbolTableBuilder {
20312058
),
20322059
});
20332060
}
2061+
if has_default {
2062+
default_seen = true;
2063+
} else if default_seen {
2064+
return Err(SymbolTableError {
2065+
error: format!(
2066+
"non-default type parameter '{}' follows default type parameter",
2067+
name
2068+
),
2069+
location: Some(
2070+
self.source_file
2071+
.to_source_code()
2072+
.source_location(range.start(), PositionEncoding::Utf8),
2073+
),
2074+
});
2075+
}
20342076
}
20352077

20362078
// Register .type_params as a type parameter (automatically becomes cell variable)

‎crates/vm/src/builtins/interpolation.rs‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use super::{PyStr, PyStrRef, PyTupleRef, PyType, tuple::IntoPyTuple};
1+
use super::{
2+
PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, genericalias::PyGenericAlias,
3+
tuple::IntoPyTuple,
4+
};
25
use crate::{
36
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
47
class::PyClassImpl,
@@ -135,6 +138,11 @@ impl PyInterpolation {
135138
self.format_spec.clone()
136139
}
137140

141+
#[pyclassmethod]
142+
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
143+
PyGenericAlias::from_args(cls, args, vm)
144+
}
145+
138146
#[pymethod]
139147
fn __reduce__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyTupleRef {
140148
let cls = zelf.class().to_owned();

‎crates/vm/src/builtins/memory.rs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,8 +552,7 @@ impl Py<PyMemoryView> {
552552
flags(SEQUENCE)
553553
)]
554554
impl PyMemoryView {
555-
// TODO: Uncomment when Python adds __class_getitem__ to memoryview
556-
// #[pyclassmethod]
555+
#[pyclassmethod]
557556
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
558557
PyGenericAlias::from_args(cls, args, vm)
559558
}

‎crates/vm/src/builtins/template.rs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use super::{PyStr, PyTupleRef, PyType};
2-
use crate::common::lock::LazyLock;
1+
use super::{PyStr, PyTupleRef, PyType, PyTypeRef, genericalias::PyGenericAlias};
32
use crate::{
43
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
54
atomic_func,
65
class::PyClassImpl,
6+
common::lock::LazyLock,
77
function::{FuncArgs, PyComparisonValue},
88
protocol::{PyIterReturn, PySequenceMethods},
99
types::{
@@ -178,6 +178,11 @@ impl PyTemplate {
178178
self.concat(&other, vm)
179179
}
180180

181+
#[pyclassmethod]
182+
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
183+
PyGenericAlias::from_args(cls, args, vm)
184+
}
185+
181186
#[pymethod]
182187
fn __reduce__(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
183188
// Import string.templatelib._template_unpickle

‎crates/vm/src/builtins/type.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1629,7 +1629,7 @@ impl Constructor for PyType {
16291629
// PEP 678: Add a note to the original exception instead of wrapping it
16301630
// (Python 3.12+, gh-77757)
16311631
let note = format!(
1632-
"Error calling __set_name__ on '{}' instance {} in '{}'",
1632+
"Error calling __set_name__ on '{}' instance '{}' in '{}'",
16331633
obj.class().name(),
16341634
name,
16351635
typ.name()

0 commit comments

Comments
 (0)