def test_optional_pep604(self): # New PEP 604 union syntax in Python 3.10+ self.assertTrue(typing_utils.is_optional_type(str | None)) self.assertTrue(typing_utils.is_optional_type(None | str)) self.assertEqual(typing_utils.get_optional_type(str | None), str) self.assertEqual(typing_utils.get_optional_type(None | str), str) self.assertFalse(typing_utils.is_optional_type(int | str))
def get_type_info(tp: type) -> TypeInfo: """ Reduce iterable and optional types to their 'base' types. """ is_final = typing_utils.is_final_type(tp) if is_final: tp = typing_utils.get_final_type(tp) if tp is typing.Any: # This used to be supported, but Python 3.10 raises in get_type_hints() if it encounters a plain Final hint. raise TypeError( 'Plain typing.Final is not valid as a type argument.') is_nullable = typing_utils.is_optional_type(tp) if is_nullable: tp = typing_utils.get_optional_type(tp) is_mapping = typing_utils.is_mapping_type(tp) is_many = typing_utils.is_iterable_type(tp) if is_mapping: tp = typing_utils.get_mapping_value_type(tp) elif is_many: # This must be elif (instead of if), as otherwise we'd reduce mappings twice as they're also iterable tp = typing_utils.get_iterable_element_type(tp) if typing_utils.is_type_variable(tp): tp = typing_utils.get_variable_type_substitute(tp) if tp is typing.Any: tp = None return TypeInfo(is_many, is_mapping, is_final, is_nullable, tp)
def get_type_info( tp: type, default_value_type: typing.Optional[type] = None) -> TypeInfo: """ Reduce iterable and optional types to their 'base' types. """ is_final = typing_utils.is_final_type(tp) if is_final: tp = typing_utils.get_final_type(tp) if tp is typing.Any and default_value_type is not None: tp = default_value_type is_optional = typing_utils.is_optional_type(tp) if is_optional: tp = typing_utils.get_optional_type(tp) is_mapping = typing_utils.is_mapping_type(tp) is_many = typing_utils.is_iterable_type(tp) if is_mapping: tp = typing_utils.get_mapping_value_type(tp) elif is_many: # This must be elif (instead of if), as otherwise we'd reduce mappings twice as they're also iterable tp = typing_utils.get_iterable_element_type(tp) if typing_utils.is_type_variable(tp): tp = typing_utils.get_variable_type_substitute(tp) if tp is typing.Any: tp = None return TypeInfo(is_many, is_mapping, is_final, is_optional, tp)
def test_optional(self): self.assertTrue(typing_utils.is_optional_type(typing.Optional[str])) self.assertTrue(typing_utils.is_optional_type(typing.Union[str, None])) self.assertTrue( typing_utils.is_optional_type(typing.Union[str, typing.Optional[str]])) self.assertTrue( typing_utils.is_optional_type(typing.Union[str, typing.Union[str, None]])) self.assertFalse(typing_utils.is_optional_type(str)) self.assertFalse(typing_utils.is_optional_type(int)) self.assertFalse(typing_utils.is_optional_type(TypingTest)) self.assertEqual(typing_utils.get_optional_type(typing.Optional[str]), str) self.assertEqual( typing_utils.get_optional_type(typing.Union[str, None]), str) self.assertEqual( typing_utils.get_optional_type(typing.Union[None, str]), str) self.assertEqual( typing_utils.get_optional_type(typing.Union[str, typing.Optional[str]]), str) self.assertEqual( typing_utils.get_optional_type(typing.Union[str, typing.Union[str, None]]), str) with self.assertRaises(ValueError): typing_utils.get_optional_type(str)
def test_literal(self): self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b'])) self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', None])) self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', types.Literal['c', 'd']])) self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', types.Literal['c', 'd', None]])) self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', types.Literal[1, 2]])) self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', types.Literal[1, 2, None]])) self.assertFalse(typing_utils.is_optional_type(types.Literal['a', 'b'])) self.assertTrue(typing_utils.is_optional_type(types.Literal['a', 'b', None])) self.assertFalse(typing_utils.is_optional_type(types.Literal['a', 'b', types.Literal['c', 'd']])) self.assertTrue(typing_utils.is_optional_type(types.Literal['a', 'b', types.Literal['c', 'd', None]])) self.assertFalse(typing_utils.is_optional_type(types.Literal['a', 'b', types.Literal[1, 2]])) self.assertTrue(typing_utils.is_optional_type(types.Literal['a', 'b', types.Literal[1, 2, None]])) self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b']), ['a', 'b']) self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', None]), ['a', 'b', None]) self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', types.Literal['c', 'd']]), ['a', 'b', 'c', 'd']) self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', types.Literal['c', 'd', None]]), ['a', 'b', 'c', 'd', None]) self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', types.Literal[1, 2]]), ['a', 'b', 1, 2]) self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', types.Literal[1, 2, None]]), ['a', 'b', 1, 2, None]) with self.assertRaises(ValueError): typing_utils.get_literal_choices(str)
def get_type_info(tp: type) -> TypeInfo: """ Reduce iterable and optional types to their 'base' types. """ is_optional = typing_utils.is_optional_type(tp) if is_optional: tp = typing_utils.get_optional_type(tp) is_mapping = typing_utils.is_mapping_type(tp) is_many = typing_utils.is_iterable_type(tp) if is_mapping: tp = typing_utils.get_mapping_value_type(tp) elif is_many: # This must be elif (instead of if), as otherwise we'd reduce mappings twice tp = typing_utils.get_iterable_element_type(tp) return TypeInfo(is_many, is_mapping, is_optional, tp)