Skip to content

Commit

Permalink
- Tuples with ellipsis are now supported #40.
Browse files Browse the repository at this point in the history
- Fixed nested tuples were not working correctly #40.
- Added unit test for dict type.
- Preparing for release 3.5.0.
  • Loading branch information
mauvilsa committed Feb 12, 2021
1 parent 5b0fe90 commit c5cfadd
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ section of releases.
v3.X.X
------

Added
-----
- Tuples with ellipsis are now supported #40.

Fixed
^^^^^
- Using dict as type incorrectly considered as class requiring class_path.
- Nested tuples were not working correctly #40.


v3.4.1 (2021-02-03)
Expand Down
25 changes: 18 additions & 7 deletions jsonargparse/jsonschema.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,11 @@ def validate_adapt(v, subschema):
if reverse:
val = list(val)
for n, v in enumerate(val):
if n < len(subschemas) and subschemas[n] is not None:
for subschema in subschemas[n]:
val[n] = validate_adapt(v, subschema)
if len(subschemas) == 0:
break
subschema = subschemas[n if n < len(subschemas) else -1]
if subschema is not None:
val[n] = validate_adapt(v, subschema)
if not reverse:
val = tuple(val) if annotation.__origin__ in {Tuple, tuple} else set(val)

Expand Down Expand Up @@ -316,15 +318,24 @@ def _typing_schema(annotation):
return {'anyOf': members}, union_subschemas

elif annotation.__origin__ in {Tuple, tuple}:
has_ellipsis = False
items = []
tuple_subschemas = []
for arg in annotation.__args__:
if arg == Ellipsis:
has_ellipsis = True
break
item, subschemas = ActionJsonSchema._typing_schema(arg)
items.append(item)
tuple_subschemas.append(subschemas)
#if any(a is None for a in items):
# return None, None
return {'type': 'array', 'items': items, 'minItems': len(items), 'maxItems': len(items)}, tuple_subschemas
if arg not in typesmap:
jsonvalidator = import_jsonschema('ActionJsonSchema')[1]
tuple_subschemas.append((arg, jsonvalidator(item), subschemas))
schema = {'type': 'array', 'items': items, 'minItems': len(items)}
if has_ellipsis:
schema['additionalItems'] = items[-1]
else:
schema['maxItems'] = len(items)
return schema, tuple_subschemas

elif annotation.__origin__ in {List, list, Iterable, Sequence, Set, set}:
items, subschemas = ActionJsonSchema._typing_schema(annotation.__args__[0])
Expand Down
30 changes: 30 additions & 0 deletions jsonargparse_tests/jsonschema_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ class MyEnum(Enum):
self.assertEqual([3, MyEnum.ab], parser.parse_args(['--list2=[3, "ab"]']).list2)


def test_dict(self):
parser = ArgumentParser(error_handler=None, parse_as_dict=True)
parser.add_argument('--dict', type=dict)
self.assertEqual({}, parser.parse_args(['--dict={}'])['dict'])
self.assertEqual({'a': 1, 'b': '2'}, parser.parse_args(['--dict={"a":1, "b":"2"}'])['dict'])
self.assertRaises(ParserError, lambda: parser.parse_args(['--dict=1']))


def test_dict_union(self):
class MyEnum(Enum):
ab = 1
Expand All @@ -230,11 +238,33 @@ class MyEnum(Enum):
cfg = parser.parse_args(['--tuple=[2, "a", "b"]'])
self.assertEqual((2, 'a', 'b'), cfg.tuple)
self.assertIsInstance(cfg.tuple[1], Path)
self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[]']))
self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[2, "a", "b", 5]']))
self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[2, "a"]']))
self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=["2", "a", "b"]']))


def test_nested_tuples(self):
parser = ArgumentParser(error_handler=None)
parser.add_argument('--tuple', type=Tuple[Tuple[str, str], Tuple[Tuple[int, float], Tuple[int, float]]])
cfg = parser.parse_args(['--tuple=[["foo", "bar"], [[1, 2.02], [3, 3.09]]]'])
self.assertEqual((('foo', 'bar'), ((1, 2.02), (3, 3.09))), cfg.tuple)


def test_tuple_ellipsis(self):
parser = ArgumentParser(error_handler=None)
parser.add_argument('--tuple', type=Tuple[float, ...])
self.assertEqual((1.2,), parser.parse_args(['--tuple=[1.2]']).tuple)
self.assertEqual((1.2, 3.4), parser.parse_args(['--tuple=[1.2, 3.4]']).tuple)
self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[]']))
self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[2, "a"]']))

parser = ArgumentParser(error_handler=None)
parser.add_argument('--tuple', type=Tuple[Tuple[str, str], Tuple[Tuple[int, float], ...]])
cfg = parser.parse_args(['--tuple=[["foo", "bar"], [[1, 2.02], [3, 3.09]]]'])
self.assertEqual((('foo', 'bar'), ((1, 2.02), (3, 3.09))), cfg.tuple)


def test_class_type(self):
parser = ArgumentParser(error_handler=None, parse_as_dict=True)
parser.add_argument('--op', type=Optional[List[Calendar]])
Expand Down

0 comments on commit c5cfadd

Please sign in to comment.