Skip to content

Commit 411f549

Browse files
committed
support | operation between typing.Union and strings
Adds support for performing '|' operation between Union objects and strings, e.g. forward type references. For example following code: from typing import Union U1 = Union[int, str] U1 | "float" The result of the operation above becomes: int | str | ForwardRef('float')
1 parent 3d99a79 commit 411f549

File tree

3 files changed

+33
-9
lines changed

3 files changed

+33
-9
lines changed

Lib/test/test_typing.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2281,7 +2281,6 @@ class Ints(enum.IntEnum):
22812281
self.assertEqual(Union[Literal[1], Literal[Ints.B], Literal[True]].__args__,
22822282
(Literal[1], Literal[Ints.B], Literal[True]))
22832283

2284-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: types.UnionType[int, str] | float != types.UnionType[int, str, float]
22852284
def test_allow_non_types_in_or(self):
22862285
# gh-140348: Test that using | with a Union object allows things that are
22872286
# not allowed by is_unionable().

crates/vm/src/builtins/type.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,12 +2048,7 @@ pub(crate) fn call_slot_new(
20482048
}
20492049

20502050
pub(crate) fn or_(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
2051-
if !union_::is_unionable(zelf.clone(), vm) || !union_::is_unionable(other.clone(), vm) {
2052-
return Ok(vm.ctx.not_implemented());
2053-
}
2054-
2055-
let tuple = PyTuple::new_ref(vec![zelf, other], &vm.ctx);
2056-
union_::make_union(&tuple, vm)
2051+
union_::or_op(zelf, other, vm)
20572052
}
20582053

20592054
fn take_next_base(bases: &mut [Vec<PyTypeRef>]) -> Option<PyTypeRef> {

crates/vm/src/builtins/union.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
convert::ToPyObject,
99
function::PyComparisonValue,
1010
protocol::{PyMappingMethods, PyNumberMethods},
11-
stdlib::typing::TypeAliasType,
11+
stdlib::typing::{TypeAliasType, call_typing_func_object},
1212
types::{AsMapping, AsNumber, Comparable, GetAttr, Hashable, PyComparisonOp, Representable},
1313
};
1414
use alloc::fmt;
@@ -193,7 +193,7 @@ impl PyUnion {
193193
}
194194
}
195195

196-
pub fn is_unionable(obj: PyObjectRef, vm: &VirtualMachine) -> bool {
196+
fn is_unionable(obj: PyObjectRef, vm: &VirtualMachine) -> bool {
197197
let cls = obj.class();
198198
cls.is(vm.ctx.types.none_type)
199199
|| obj.downcastable::<PyType>()
@@ -202,6 +202,36 @@ pub fn is_unionable(obj: PyObjectRef, vm: &VirtualMachine) -> bool {
202202
|| obj.downcast_ref::<TypeAliasType>().is_some()
203203
}
204204

205+
fn type_check(arg: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
206+
// Fast path to avoid calling into typing.py
207+
if is_unionable(arg.clone(), vm) {
208+
return Ok(arg);
209+
}
210+
let message_str: PyObjectRef = vm
211+
.ctx
212+
.new_str("Union[arg, ...]: each arg must be a type.")
213+
.into();
214+
call_typing_func_object(vm, "_type_check", (arg, message_str))
215+
}
216+
217+
fn has_union_operands(a: PyObjectRef, b: PyObjectRef, vm: &VirtualMachine) -> bool {
218+
let union_type = vm.ctx.types.union_type;
219+
a.class().is(union_type) || b.class().is(union_type)
220+
}
221+
222+
pub fn or_op(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
223+
if !has_union_operands(zelf.clone(), other.clone(), vm)
224+
&& (!is_unionable(zelf.clone(), vm) || !is_unionable(other.clone(), vm))
225+
{
226+
return Ok(vm.ctx.not_implemented());
227+
}
228+
229+
let left = type_check(zelf, vm)?;
230+
let right = type_check(other, vm)?;
231+
let tuple = PyTuple::new_ref(vec![left, right], &vm.ctx);
232+
make_union(&tuple, vm)
233+
}
234+
205235
fn make_parameters(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
206236
let parameters = genericalias::make_parameters(args, vm);
207237
let result = dedup_and_flatten_args(&parameters, vm)?;

0 commit comments

Comments
 (0)