def test_class_type_subclass_nested_help(self): class Class: def __init__(self, cal: Calendar, p1: int = 0): self.cal = cal parser = ArgumentParser() parser.add_argument('--op', type=Class) for pattern in [r'[\s=]', r'\s']: with self.subTest('" "' if '=' in pattern else '"="'), mock_module( Class) as module: out = StringIO() args = re.split( pattern, f'--op.help={module}.Class --op.init_args.cal.help=TextCalendar' ) with redirect_stdout(out), self.assertRaises(SystemExit): parser.parse_args(args) self.assertIn('--op.init_args.cal.init_args.firstweekday', out.getvalue()) with self.subTest('invalid'), mock_module(Class) as module: err = StringIO() with redirect_stderr(err), self.assertRaises(SystemExit): parser.parse_args( [f'--op.help={module}.Class', '--op.init_args.p1=1']) self.assertIn('Expected a nested --*.help option', err.getvalue())
def test_class_path_override_with_default_config_files(self): class MyCalendar(Calendar): def __init__(self, *args, param: str = '0', **kwargs): super().__init__(*args, **kwargs) with mock_module(MyCalendar) as module: config = { 'class_path': f'{module}.MyCalendar', 'init_args': { 'firstweekday': 2, 'param': '1' }, } config_path = os.path.join(self.tmpdir, 'config.yaml') with open(config_path, 'w') as f: json.dump({'cal': config}, f) parser = ArgumentParser(error_handler=None, default_config_files=[config_path]) parser.add_argument('--cal', type=Optional[Calendar]) cfg = parser.instantiate_classes(parser.get_defaults()) self.assertIsInstance(cfg['cal'], MyCalendar) cfg = parser.parse_args([ '--cal={"class_path": "calendar.Calendar", "init_args": {"firstweekday": 3}}' ]) self.assertEqual(type(parser.instantiate_classes(cfg)['cal']), Calendar)
def test_dump_skip_default(self): class MyCalendar(Calendar): def __init__(self, *args, param: str = '0', **kwargs): super().__init__(*args, **kwargs) with mock_module(MyCalendar) as module: parser = ArgumentParser() parser.add_argument('--g1.op1', default=123) parser.add_argument('--g1.op2', default='abc') parser.add_argument('--g2.op1', default=4.5) parser.add_argument('--g2.op2', type=Calendar, default=lazy_instance(Calendar, firstweekday=2)) cfg = parser.get_defaults() dump = parser.dump(cfg, skip_default=True) self.assertEqual(dump, '{}\n') cfg.g2.op2.class_path = f'{module}.MyCalendar' dump = parser.dump(cfg, skip_default=True) self.assertEqual( dump, 'g2:\n op2:\n class_path: jsonargparse_tests.MyCalendar\n init_args:\n firstweekday: 2\n' ) cfg.g2.op2.init_args.firstweekday = 0 dump = parser.dump(cfg, skip_default=True) self.assertEqual( dump, 'g2:\n op2:\n class_path: jsonargparse_tests.MyCalendar\n')
def test_class_type_subclass_given_by_name_issue_84(self): class LocalCalendar(Calendar): pass parser = ArgumentParser() parser.add_argument('--op', type=Union[Calendar, GzipFile, None]) cfg = parser.parse_args(['--op=TextCalendar']) self.assertEqual(cfg.op.class_path, 'calendar.TextCalendar') out = StringIO() parser.print_help(out) for class_path in [ 'calendar.Calendar', 'calendar.TextCalendar', 'gzip.GzipFile' ]: self.assertIn(class_path, out.getvalue()) self.assertNotIn('LocalCalendar', out.getvalue()) class HTMLCalendar(Calendar): pass with mock_module(HTMLCalendar) as module: err = StringIO() with redirect_stderr(err), self.assertRaises(SystemExit): parser.parse_args(['--op.help=HTMLCalendar']) self.assertIn('Give the full class path to avoid ambiguity', err.getvalue()) self.assertIn(f'{module}.HTMLCalendar', err.getvalue())
def test_subcommand_with_subclass_default_override_lightning_issue_10859( self): class Arch: def __init__(self, a: int = 1): pass class ArchB(Arch): def __init__(self, a: int = 2, b: int = 3): pass class ArchC(Arch): def __init__(self, a: int = 4, c: int = 5): pass parser = ArgumentParser(error_handler=None) parser_subcommands = parser.add_subcommands() subparser = ArgumentParser() subparser.add_argument('--arch', type=Arch) with mock_module(Arch, ArchB, ArchC) as module: default = {'class_path': f'{module}.ArchB'} value = { 'class_path': f'{module}.ArchC', 'init_args': { 'a': 10, 'c': 11 } } subparser.set_defaults(arch=default) parser_subcommands.add_subcommand('fit', subparser) cfg = parser.parse_args(['fit', f'--arch={json.dumps(value)}']) self.assertEqual(cfg.fit.arch.as_dict(), value)
def test_linking_deep_targets_mapping(self): class D: pass class A: def __init__(self, d: D) -> None: self.d = d class BSuper: pass class BSub(BSuper): def __init__(self, a_map: Mapping[str, A]) -> None: self.a_map = a_map class C: def fn(self) -> D: return D() with mock_module(D, A, BSuper, BSub, C) as module: config = { "b": { "class_path": f"{module}.BSub", "init_args": { "a_map": { "name": { "class_path": f"{module}.A", }, }, }, }, "c": {}, } config_path = os.path.join(self.tmpdir, 'config.yaml') with open(config_path, 'w') as f: yaml.safe_dump(config, f) parser = ArgumentParser() parser.add_argument("--config", action=ActionConfigFile) parser.add_subclass_arguments(BSuper, nested_key="b", required=True) parser.add_class_arguments(C, nested_key="c") parser.link_arguments("c", "b.init_args.a_map.name.init_args.d", compute_fn=C.fn, apply_on="instantiate") config = parser.parse_args(["--config", config_path]) config_init = parser.instantiate_classes(config) self.assertIsInstance(config_init["b"].a_map["name"].d, D) config_init = parser.instantiate_classes(config) self.assertIsInstance(config_init["b"].a_map["name"].d, D)
def test_object_path_serializer_reimport_differs(self): class MyClass: pass with mock_module(MyClass) as module: class FakeMyClass: pass FakeMyClass.__module__ = module FakeMyClass.__qualname__ = MyClass.__qualname__ self.assertRaises(ValueError, lambda: object_path_serializer(FakeMyClass))
def test_serialize_class_method_path(self): class MyClass: @staticmethod def my_method1(): pass def my_method2(self): pass with mock_module(MyClass) as module: self.assertEqual(object_path_serializer(MyClass.my_method1), f'{module}.MyClass.my_method1') self.assertEqual(object_path_serializer(MyClass.my_method2), f'{module}.MyClass.my_method2')
def test_class_type_required_params(self): class MyCal(Calendar): def __init__(self, p1: int, p2: str): pass with mock_module(MyCal) as module: parser = ArgumentParser(error_handler=None) parser.add_argument('--op', type=MyCal, default=lazy_instance(MyCal)) cfg = parser.get_defaults() self.assertEqual(cfg.op.class_path, f'{module}.MyCal') self.assertEqual(cfg.op.init_args, Namespace(p1=None, p2=None)) self.assertRaises( ParserError, lambda: parser.parse_args([f'--op={module}.MyCal']))
def test_typed_Callable_with_function_path(self): def my_func_1(p: int) -> str: return str(p) def my_func_2(p: str) -> int: return int(p) parser = ArgumentParser(error_handler=None) parser.add_argument('--callable', type=Callable[[int], str]) with mock_module(my_func_1, my_func_2) as module: cfg = parser.parse_args([f'--callable={module}.my_func_1']) self.assertEqual(my_func_1, cfg.callable) cfg = parser.parse_args([f'--callable={module}.my_func_2']) self.assertEqual( my_func_2, cfg.callable) # Currently callable types are ignored
def test_subclass_type_config_file(self): class A: def __init__(self, p1: str = 'a default'): self.p1 = p1 class B: def __init__(self, a: A = A()): self.a = a class C: def __init__(self, a: A = lazy_instance(A), b: B = None): self.a = a self.b = b def cmd_a(self): print(self.a.p1) def cmd_b(self): print(self.b.a.p1) with mock_module(A, B) as module: a_yaml = { 'class_path': f'{module}.A', 'init_args': {'p1': 'a yaml'} } with open('config.yaml', 'w') as f: f.write('a: a.yaml\n') with open('a.yaml', 'w') as f: f.write(yaml.safe_dump(a_yaml)) out = StringIO() with redirect_stdout(out): CLI(C, args=['--config=config.yaml', 'cmd_a']) self.assertEqual('a yaml\n', out.getvalue()) with open('config.yaml', 'w') as f: f.write('a: a.yaml\nb: b.yaml\n') with open('b.yaml', 'w') as f: f.write(f'class_path: {module}.B\ninit_args:\n a: a.yaml\n') out = StringIO() with redirect_stdout(out): CLI(C, args=['--config=config.yaml', 'cmd_b']) self.assertEqual('a yaml\n', out.getvalue())
def test_mapping_class_typehint(self): class A: pass class B: def __init__( self, class_map: Mapping[str, A], int_list: List[int], ): self.class_map = class_map self.int_list = int_list with mock_module(A, B) as module: parser = ArgumentParser(error_handler=None) parser.add_class_arguments(B, 'b') config = { 'b': { 'class_map': { 'one': { 'class_path': f'{module}.A' }, }, 'int_list': [1], }, } cfg = parser.parse_object(config) self.assertEqual(cfg.b.class_map, {'one': Namespace(class_path=f'{module}.A')}) self.assertEqual(cfg.b.int_list, [1]) cfg_init = parser.instantiate_classes(cfg) self.assertIsInstance(cfg_init.b, B) self.assertIsInstance(cfg_init.b.class_map, dict) self.assertIsInstance(cfg_init.b.class_map['one'], A) config['b']['int_list'] = config['b']['class_map'] self.assertRaises(ParserError, lambda: parser.parse_object(config))
def test_class_type_subclass_nested_init_args(self): class Class: def __init__(self, cal: Calendar, p1: int = 0): self.cal = cal for full in ['init_args.', '']: with self.subTest('full' if full else 'short'), mock_module( Class) as module: parser = ArgumentParser() parser.add_argument('--op', type=Class) cfg = parser.parse_args([ f'--op={module}.Class', f'--op.{full}p1=1', f'--op.{full}cal=calendar.TextCalendar', f'--op.{full}cal.{full}firstweekday=2', ]) self.assertEqual(cfg.op.class_path, f'{module}.Class') self.assertEqual(cfg.op.init_args.p1, 1) self.assertEqual(cfg.op.init_args.cal.class_path, 'calendar.TextCalendar') self.assertEqual(cfg.op.init_args.cal.init_args, Namespace(firstweekday=2))
def test_Callable_with_class_path(self): class MyFunc1: def __init__(self, p1: int = 1): self.p1 = p1 def __call__(self): return self.p1 class MyFunc2(MyFunc1): pass parser = ArgumentParser(error_handler=None) parser.add_argument('--callable', type=Callable) with mock_module(MyFunc1, MyFunc2) as module: value = {'class_path': f'{module}.MyFunc2', 'init_args': {'p1': 1}} cfg = parser.parse_args([f'--callable={module}.MyFunc2']) self.assertEqual(cfg.callable.as_dict(), value) value = {'class_path': f'{module}.MyFunc1', 'init_args': {'p1': 2}} cfg = parser.parse_args([f'--callable={json.dumps(value)}']) self.assertEqual(cfg.callable.as_dict(), value) self.assertEqual( yaml.safe_load(parser.dump(cfg))['callable'], value) cfg_init = parser.instantiate_classes(cfg) self.assertIsInstance(cfg_init.callable, MyFunc1) self.assertEqual(cfg_init.callable(), 2) self.assertRaises(ParserError, lambda: parser.parse_args(['--callable={}'])) self.assertRaises( ParserError, lambda: parser.parse_args( ['--callable=jsonargparse.SUPPRESS'])) self.assertRaises( ParserError, lambda: parser.parse_args(['--callable=calendar.Calendar'])) value = {'class_path': f'{module}.MyFunc1', 'key': 'val'} self.assertRaises( ParserError, lambda: parser.parse_args([f'--callable={json.dumps(value)}']))
def test_class_type_without_defaults(self): class MyCal(Calendar): def __init__(self, p1: int = 1, p2: str = '2'): pass parser = ArgumentParser(error_handler=None) parser.add_argument('--op', type=MyCal) with mock_module(MyCal) as module: cfg = parser.parse_args( [f'--op.class_path={module}.MyCal', '--op.init_args.p1=3'], defaults=False) self.assertEqual( cfg.op, Namespace(class_path=f'{module}.MyCal', init_args=Namespace(p1=3))) cfg = parser.parse_args([ '--op.class_path', f'{module}.MyCal', '--op.init_args.p1', '3' ], defaults=False) self.assertEqual( cfg.op, Namespace(class_path=f'{module}.MyCal', init_args=Namespace(p1=3)))