Skip to content

Commit 8a54a31

Browse files
committed
Fixed wrong error indentation for subclass in union (pytorch-lightning#17254).
1 parent 06d5ed3 commit 8a54a31

File tree

4 files changed

+43
-7
lines changed

4 files changed

+43
-7
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Fixed
1919
^^^^^
2020
- `str` parameter in subclass incorrectly parsed as dict with implicit `null`
2121
value (`#262 <https://github.com/omni-us/jsonargparse/issues/262>`__).
22+
- Wrong error indentation for subclass in union (`pytorch-lightning#17254
23+
<https://github.com/Lightning-AI/lightning/issues/17254>`__).
2224

2325
Changed
2426
^^^^^^^

jsonargparse/typehints.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,8 @@ def raise_unexpected_value(message: str, val: Any = inspect._empty, exception: O
535535

536536

537537
def raise_union_unexpected_value(uniontype, val: Any, exceptions: List[Exception]) -> NoReturn:
538-
errors = indent_text('- ' + '\n- '.join(map(str, exceptions)))
538+
str_exceptions = [indent_text(str(e), first_line=False) for e in exceptions]
539+
errors = indent_text('- ' + '\n- '.join(str_exceptions))
539540
errors = errors.replace(f'. Got value: {val}', '').replace(f' {val} ', ' ')
540541
subtypes = uniontype.__args__
541542
raise ValueError(
@@ -770,7 +771,7 @@ def adapt_typehints(
770771
val = subclass_spec_as_namespace(val, prev_val)
771772
if not is_subclass_spec(val):
772773
raise_unexpected_value(
773-
f'Not a valid {typehint.__name__}. Got value: {val_input}\n'
774+
f'Not a valid subclass of {typehint.__name__}. Got value: {val_input}\n'
774775
'Subclass types expect one of:\n'
775776
'- a class path (str)\n'
776777
'- a dict with class_path entry\n'
@@ -785,8 +786,8 @@ def adapt_typehints(
785786
val = adapt_class_type(val, serialize, instantiate_classes, sub_add_kwargs, prev_val=prev_val)
786787
except (ImportError, AttributeError, AssertionError, ArgumentError) as ex:
787788
class_path = val if isinstance(val, str) else val['class_path']
788-
error = indent_text(f'\n- {ex}')
789-
raise_unexpected_value(f'Problem with given class_path {class_path!r}:{error}', exception=ex)
789+
error = indent_text(str(ex))
790+
raise_unexpected_value(f'Problem with given class_path {class_path!r}:\n{error}', exception=ex)
790791

791792
return val
792793

jsonargparse/util.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,13 @@ def iter_to_set_str(val, sep=','):
333333
return '{'+sep.join(str(x) for x in val)+'}'
334334

335335

336-
def indent_text(text: str) -> str:
337-
return textwrap.indent(text, ' ')
336+
def indent_text(text: str, first_line: bool = True) -> str:
337+
if first_line:
338+
return textwrap.indent(text, ' ')
339+
lines = text.splitlines()
340+
if len(lines) == 1:
341+
return text
342+
return lines[0] + os.linesep + textwrap.indent(os.linesep.join(lines[1:]), ' ')
338343

339344

340345
def get_private_kwargs(data, **kwargs):

jsonargparse_tests/test_signatures.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ def __init__(self, val: Optional[Union[int, dict]] = None):
11931193
with redirect_stderr(err), self.assertRaises(SystemExit):
11941194
parser.parse_args(['--cls=Class', '--cls.init_args.val=abc'])
11951195
expected = textwrap.dedent('''
1196-
- Parser key "val":
1196+
Parser key "val":
11971197
Does not validate against any of the Union subtypes
11981198
Subtypes: (<class 'int'>, <class 'dict'>, <class 'NoneType'>)
11991199
Errors:
@@ -1207,6 +1207,34 @@ def __init__(self, val: Optional[Union[int, dict]] = None):
12071207
self.assertIn(expected, err.getvalue())
12081208

12091209

1210+
def test_subclass_in_union_error_message_indentation(self):
1211+
class Class:
1212+
def __init__(self, val: int):
1213+
pass
1214+
1215+
with mock_module(Class):
1216+
parser = ArgumentParser()
1217+
parser.add_argument('--union', type=Union[str, Class])
1218+
err = StringIO()
1219+
with redirect_stderr(err), self.assertRaises(SystemExit):
1220+
parser.parse_object({'union': [{'class_path': 'Class', 'init_args': {'val': 'x'}}]})
1221+
expected = textwrap.dedent('''
1222+
Errors:
1223+
- Expected a <class 'str'>
1224+
- Not a valid subclass of Class
1225+
Subclass types expect one of:
1226+
- a class path (str)
1227+
- a dict with class_path entry
1228+
- a dict without class_path but with init_args entry (class path given previously)
1229+
Given value type: <class 'list'>
1230+
Given value: [{'class_path': 'Class', 'init_args': {'val': 'x'}}]
1231+
''').strip()
1232+
expected = textwrap.indent(expected, ' ')
1233+
expected = '\n'.join(expected.splitlines())
1234+
obtained = '\n'.join(err.getvalue().splitlines())
1235+
self.assertIn(expected, obtained)
1236+
1237+
12101238
class SignaturesConfigTests(TempDirTestCase):
12111239

12121240
def test_add_function_arguments_config(self):

0 commit comments

Comments
 (0)