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)
Ejemplo n.º 7
0
    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))
Ejemplo n.º 8
0
    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
Ejemplo n.º 11
0
    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)))