Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d1febc5

Browse files
committed
Consistent type conversion with arg: T = None.
Prior to Python 3.11 that syntax was considered same as `arg: T|None = None` and with unions we don't look at the default value at all if `T` isn't known. This commit makes behavior with Python 3.11 consistent with how conversion works with older Python versions. Fixes #4626. Might be better not to look at the default values in general if an argument has type information. That would be a backwards incompatible change, though, and needs to wait for a major version.
1 parent 63c82a3 commit d1febc5

File tree

7 files changed

+48
-5
lines changed

7 files changed

+48
-5
lines changed

atest/robot/keywords/type_conversion/annotations.robot

+4-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,10 @@ Invalid kwonly
210210
Return value annotation causes no error
211211
Check Test Case ${TESTNAME}
212212

213-
None as default
213+
None as default with known type
214+
Check Test Case ${TESTNAME}
215+
216+
None as default with unknown type
214217
Check Test Case ${TESTNAME}
215218

216219
Forward references

atest/robot/keywords/type_conversion/annotations_with_typing.robot

+3
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ Invalid Set
9999
None as default
100100
Check Test Case ${TESTNAME}
101101

102+
None as default with Any
103+
Check Test Case ${TESTNAME}
104+
102105
Forward references
103106
Check Test Case ${TESTNAME}
104107

atest/testdata/keywords/type_conversion/Annotations.py

+4
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ def none_as_default(argument: list = None, expected=None):
212212
_validate_type(argument, expected)
213213

214214

215+
def none_as_default_with_unknown_type(argument: Unknown = None, expected=None):
216+
_validate_type(argument, expected)
217+
218+
215219
def forward_referenced_concrete_type(argument: 'int', expected=None):
216220
_validate_type(argument, expected)
217221

atest/testdata/keywords/type_conversion/AnnotationsWithTyping.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import (Dict, List, Mapping, MutableMapping, MutableSet, MutableSequence,
1+
from typing import (Any, Dict, List, Mapping, MutableMapping, MutableSet, MutableSequence,
22
Set, Sequence, Tuple, Union)
33
try:
44
from typing_extensions import TypedDict
@@ -117,6 +117,10 @@ def none_as_default(argument: List = None, expected=None):
117117
_validate_type(argument, expected)
118118

119119

120+
def none_as_default_with_any(argument: Any = None, expected=None):
121+
_validate_type(argument, expected)
122+
123+
120124
def forward_reference(argument: 'List', expected=None):
121125
_validate_type(argument, expected)
122126

atest/testdata/keywords/type_conversion/annotations.robot

+10-2
Original file line numberDiff line numberDiff line change
@@ -561,12 +561,20 @@ Invalid kwonly
561561
Return value annotation causes no error
562562
Return value annotation 42 42
563563

564-
None as default
564+
None as default with known type
565565
None as default
566566
None as default [] []
567567

568+
None as default with unknown type
569+
[Documentation] `a: T = None` was same as `a: T|None = None` prior to Python 3.11.
570+
... With unions we don't look at the default if `T` isn't a known type
571+
... and that behavior is preserved for backwards compatiblity.
572+
None as default with unknown type
573+
None as default with unknown type hi! 'hi!'
574+
None as default with unknown type ${42} 42
575+
None as default with unknown type None 'None'
576+
568577
Forward references
569-
[Tags] require-py3.5
570578
Forward referenced concrete type 42 42
571579
Forward referenced ABC [] []
572580

atest/testdata/keywords/type_conversion/annotations_with_typing.robot

+9
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,15 @@ None as default
177177
None as default
178178
None as default [1, 2, 3, 4] [1, 2, 3, 4]
179179

180+
None as default with Any
181+
[Documentation] `a: Any = None` was same as `a: Any|None = None` prior to Python 3.11.
182+
... With unions we don't look at the default in this case and that
183+
... behavior is preserved for backwards compatiblity.
184+
None as default with Any
185+
None as default with Any hi! 'hi!'
186+
None as default with Any ${42} 42
187+
None as default with Any None 'None'
188+
180189
Forward references
181190
Forward reference [1, 2, 3, 4] [1, 2, 3, 4]
182191
Forward ref with types [1, '2', 3, 4.0] [1, 2, 3, 4]

src/robot/running/arguments/argumentconverter.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def _convert(self, name, value):
6565
return converter.convert(name, value)
6666
except ValueError as err:
6767
conversion_error = err
68-
if name in spec.defaults:
68+
if self._convert_based_on_defaults(name, spec, bool(conversion_error)):
6969
converter = TypeConverter.converter_for(type(spec.defaults[name]),
7070
languages=self._languages)
7171
if converter:
@@ -77,3 +77,15 @@ def _convert(self, name, value):
7777
if conversion_error:
7878
raise conversion_error
7979
return value
80+
81+
def _convert_based_on_defaults(self, name, spec, has_known_type):
82+
if name not in spec.defaults:
83+
return False
84+
# Handle `arg: T = None` consistently with different Python versions
85+
# regardless is `T` a known type or not. Prior to 3.11 this syntax was
86+
# considered same as `arg: Union[T, None] = None` and with unions we
87+
# don't look at the possible default value if `T` is not known.
88+
# https://github.com/robotframework/robotframework/issues/4626
89+
return (name not in spec.types
90+
or spec.defaults[name] is not None
91+
or has_known_type)

0 commit comments

Comments
 (0)