def test_ActionJsonSchema(self): parser = ArgumentParser(prog='app', default_meta=False, error_handler=None) parser.add_argument('--op1', action=ActionJsonSchema(schema=schema1)) parser.add_argument('--op2', action=ActionJsonSchema(schema=schema2)) parser.add_argument('--op3', action=ActionJsonSchema(schema=schema3)) parser.add_argument('--cfg', action=ActionConfigFile) op1_val = [1, 2, 3, 4] op2_val = {'k1': 'one', 'k2': 2, 'k3': 3.3} self.assertEqual(op1_val, parser.parse_args(['--op1', str(op1_val)]).op1) self.assertRaises(ParserError, lambda: parser.parse_args(['--op1', '[1, "two"]'])) self.assertRaises(ParserError, lambda: parser.parse_args(['--op1', '[1.5, 2]'])) self.assertEqual(op2_val, parser.parse_args(['--op2', str(op2_val)]).op2) self.assertEqual(17, parser.parse_args(['--op2', '{"k2": 2}']).op2['k3']) self.assertRaises(ParserError, lambda: parser.parse_args(['--op2', '{"k1": 1}'])) self.assertRaises(ParserError, lambda: parser.parse_args(['--op2', '{"k2": "2"}'])) self.assertRaises(ParserError, lambda: parser.parse_args(['--op2', '{"k4": 4}'])) op1_file = os.path.join(self.tmpdir, 'op1.json') op2_file = os.path.join(self.tmpdir, 'op2.json') cfg1_file = os.path.join(self.tmpdir, 'cfg1.yaml') cfg3_file = os.path.join(self.tmpdir, 'cfg3.yaml') cfg2_str = 'op1:\n '+str(op1_val)+'\nop2:\n '+str(op2_val)+'\n' with open(op1_file, 'w') as f: f.write(str(op1_val)) with open(op2_file, 'w') as f: f.write(str(op2_val)) with open(cfg1_file, 'w') as f: f.write('op1:\n '+op1_file+'\nop2:\n '+op2_file+'\n') with open(cfg3_file, 'w') as f: f.write('op3:\n n1:\n - '+str(op2_val)+'\n') cfg = parser.parse_path(cfg1_file) self.assertEqual(op1_val, cfg['op1']) self.assertEqual(op2_val, cfg['op2']) cfg = parser.parse_string(cfg2_str) self.assertEqual(op1_val, cfg['op1']) self.assertEqual(op2_val, cfg['op2']) cfg = parser.parse_args(['--cfg', cfg3_file]) self.assertEqual(op2_val, cfg.op3['n1'][0]) parser.check_config(cfg, skip_none=True) if os.name == 'posix' and platform.python_implementation() == 'CPython': os.chmod(op1_file, 0) self.assertRaises(ParserError, lambda: parser.parse_path(cfg1_file))
def _adapt_types(val, annotation, subschemas, reverse=False, instantiate_classes=False): def validate_adapt(v, subschema): if subschema is not None: subannotation, subvalidator, subsubschemas = subschema if reverse: v = ActionJsonSchema._adapt_types(v, subannotation, subsubschemas, reverse, instantiate_classes) else: try: if subvalidator is not None and not instantiate_classes: subvalidator.validate(v) v = ActionJsonSchema._adapt_types( v, subannotation, subsubschemas, reverse, instantiate_classes) except jsonschemaValidationError: pass return v if subschemas is None: subschemas = [] if _issubclass(annotation, Enum): if reverse and isinstance(val, annotation): val = val.name elif not reverse and val in annotation.__members__: val = annotation[val] elif _issubclass(annotation, Path): if reverse and isinstance(val, annotation): val = str(val) elif not reverse: val = annotation(val) elif not hasattr(annotation, '__origin__'): if not reverse and \ not _issubclass(annotation, (str, int, float)) and \ isinstance(val, dict) and \ 'class_path' in val: try: val_class = import_object(val['class_path']) assert _issubclass( val_class, annotation), 'Not a subclass of ' + annotation.__name__ if 'init_args' in val: from jsonargparse import ArgumentParser parser = ArgumentParser(error_handler=None, parse_as_dict=True) parser.add_class_arguments(val_class) parser.check_config(val['init_args']) if instantiate_classes: init_args = parser.instantiate_subclasses( val['init_args']) val = val_class(**init_args) # pylint: disable=not-a-mapping elif instantiate_classes: val = val_class() except (ImportError, ModuleNotFound, AttributeError, AssertionError, ParserError) as ex: raise ParserError('Problem with given class_path "' + val['class_path'] + '" :: ' + str(ex)) from ex return val elif annotation.__origin__ == Union: for subschema in subschemas: val = validate_adapt(val, subschema) elif annotation.__origin__ in {Tuple, tuple, Set, set} and isinstance( val, (list, tuple, set)): 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 not reverse: val = tuple(val) if annotation.__origin__ in {Tuple, tuple } else set(val) elif annotation.__origin__ in { List, list, Set, set, Iterable, Sequence } and isinstance(val, list): for n, v in enumerate(val): for subschema in subschemas: val[n] = validate_adapt(v, subschema) elif annotation.__origin__ in {Dict, dict} and isinstance(val, dict): if annotation.__args__[0] == int: cast = str if reverse else int val = {cast(k): v for k, v in val.items()} if annotation.__args__[1] not in typesmap: for k, v in val.items(): for subschema in subschemas: val[k] = validate_adapt(v, subschema) return val