Exemplo n.º 1
0
def instrumentGooey(parser, **kwargs) -> Tuple[wx.App, wx.Frame, RGooey]:
    """
    Context manager used during testing for setup/tear down of the
    WX infrastructure during subTests.

    Weirdness warning: this uses a globally reused wx.App instance.
    """
    from gooey.tests import app
    if app == None:
        raise Exception(
            "App instance has not been created! This is likely due to "
            "you forgetting to add the magical import which makes all these "
            "tests work. See the module doc in gooey.tests.__init__ for guidance"
        )
    buildspec = create_from_parser(parser, "", **gooey_params(**kwargs))
    app, frame = bootstrap._build_app(buildspec, app)
    app.SetTopWindow(frame)
    try:
        # we need to run the main loop temporarily to get it to
        # apply any pending updates from the initial creation.
        # The UI state will be stale otherwise
        # this works because CallLater just enqueues the message to
        # be processed. The MainLoop starts running, picks it up, and
        # then exists
        wx.CallLater(1, app.ExitMainLoop)
        app.MainLoop()
        yield (app, frame, frame._instance)
    finally:
        frame.Destroy()
        del frame
Exemplo n.º 2
0
 def test_valid_font_weights(self):
     """
     Asserting that only valid font-weights are allowable.
     """
     all_valid_weights = range(100, 1001, 100)
     for weight in all_valid_weights:
         parser = ArgumentParser(description="test parser")
         params = gooey_params(terminal_font_weight=weight)
         buildspec = create_from_parser(parser, "", **params)
         self.assertEqual(buildspec['terminal_font_weight'], weight)
Exemplo n.º 3
0
    def test_program_description(self):
        """
        Should use `program_description` if supplied, otherwise
        fallback to the description on the `parser`
        """

        parser = ArgumentParser(description="Parser Description")
        # when supplied explicitly, we assign it as the description
        params = gooey_params(program_description='Custom Description')
        buildspec = create_from_parser(parser, "", **params)
        self.assertEqual(buildspec['program_description'], 'Custom Description')

        # when no explicit program_definition supplied, we fallback to the parser's description
        buildspec = create_from_parser(parser, "", **gooey_params())
        self.assertEqual(buildspec['program_description'], 'Parser Description')

        # if no description is provided anywhere, we just set it to be an empty string.
        blank_parser = ArgumentParser()
        buildspec = create_from_parser(blank_parser, "", **gooey_params())
        self.assertEqual(buildspec['program_description'], '')
Exemplo n.º 4
0
    def test_ignore_gooey(self):
        parser = GooeyParser()
        subs = parser.add_subparsers()
        foo = subs.add_parser('foo')
        foo.add_argument('a')
        foo.add_argument('b')
        foo.add_argument('p')

        bar = subs.add_parser('bar')
        bar.add_argument('a')
        bar.add_argument('b')
        bar.add_argument('z')

        control.bypass_gooey(gooey_params())(parser)
Exemplo n.º 5
0
def Gooey(f=None, **gkwargs):
    """
    Decoration entry point for the Gooey process.
    See types.GooeyParams for kwargs options
    """
    params: GooeyParams = gooey_params(**gkwargs)

    @wraps(f)
    def inner(*args, **kwargs):
        parser_handler = choose_hander(params, gkwargs.get('cli', sys.argv))
        # monkey patch parser
        ArgumentParser.original_parse_args = ArgumentParser.parse_args
        ArgumentParser.parse_args = parser_handler
        # return the wrapped, now monkey-patched, user function
        # to be later invoked
        return f(*args, **kwargs)

    def thunk(func):
        """
        This just handles the case where the decorator is called
        with arguments (i.e. @Gooey(foo=bar) rather than @Gooey).

        Cause python is weird, when a decorator is called (e.g. @decorator())
        rather than just declared (e.g. @decorator), in complete and utter
        defiance of what your lying eyes see, it changes from a higher order
        function, to a function that takes an arbitrary argument *and then*
        returns a higher order function. i.e.

        decorate :: (a -> b) -> (a -> b)
        decorate() :: c -> (a -> b) -> (a -> b)

        wat.
        """
        return Gooey(func, **params)

    return inner if callable(f) else thunk
Exemplo n.º 6
0
    def test_validate_form(self):
        """
        Testing the major validation cases we support.
        """
        writer = MagicMock()
        exit = MagicMock()
        monkey_patch = control.validate_form(gooey_params(),
                                             write=writer,
                                             exit=exit)
        ArgumentParser.original_parse_args = ArgumentParser.parse_args
        ArgumentParser.parse_args = monkey_patch

        parser = GooeyParser()
        # examples:
        # ERROR: mismatched builtin type
        parser.add_argument('a',
                            type=int,
                            gooey_options={'initial_value': 'not-an-int'})
        # ERROR: mismatched custom type
        parser.add_argument('b',
                            type=custom_type,
                            gooey_options={'initial_value': 'not-a-float'})
        # ERROR: missing required positional arg
        parser.add_argument('c')
        # ERROR: missing required 'optional' arg
        parser.add_argument('--oc', required=True)
        # VALID: This is one of the bizarre cases which are possible
        # but don't make much sense. It should pass through as valid
        # because there's no way for us to send a 'not present optional value'
        parser.add_argument('--bo', action='store_true', required=True)
        # ERROR: a required mutex group, with no args supplied.
        # Should flag all as missing.
        group = parser.add_mutually_exclusive_group(required=True)
        group.add_argument('--gp1-a', type=str)
        group.add_argument('--gp1-b', type=str)

        # ERROR: required mutex group with a default option but nothing
        # selected will still fail
        group2 = parser.add_mutually_exclusive_group(required=True)
        group2.add_argument('--gp2-a', type=str)
        group2.add_argument('--gp2-b', type=str, default='Heeeeyyyyy')

        # VALID: now, same as above, but now the option is actually enabled via
        # the initial selection. No error.
        group3 = parser.add_mutually_exclusive_group(
            required=True, gooey_options={'initial_selection': 1})
        group3.add_argument('--gp3-a', type=str)
        group3.add_argument('--gp3-b', type=str, default='Heeeeyyyyy')
        # VALID: optional mutex.
        group4 = parser.add_mutually_exclusive_group()
        group4.add_argument('--gp4-a', type=str)
        group4.add_argument('--gp4-b', type=str)
        # VALID: arg present and type satisfied
        parser.add_argument('ga',
                            type=str,
                            gooey_options={'initial_value': 'whatever'})
        # VALID: arg present and custom type satisfied
        parser.add_argument('gb',
                            type=custom_type,
                            gooey_options={'initial_value': '1234'})
        # VALID: optional
        parser.add_argument('--gc')

        # now we're adding the same
        with instrumentGooey(parser, target='test') as (app, frame, gapp):
            # we start off with no errors
            self.assertFalse(s.has_errors(gapp.fullState()))

            # now we feed our form-validation
            cmd = s.buildFormValidationCmd(gapp.fullState())
            asdf = shlex.split(cmd)[1:]
            parser.parse_args(shlex.split(cmd)[1:])
            assert writer.called
            assert exit.called

        result = deserialize_inbound(writer.call_args[0][0].encode('utf-8'),
                                     'utf-8')
        # Host->Gooey communication is all done over the PublicGooeyState schema
        # as such, we coarsely validate it's shape here
        validate_public_state(result)

        # manually merging the two states back together
        nextState = s.mergeExternalState(gapp.fullState(), result)
        # and now we find that we have errors!
        self.assertTrue(s.has_errors(nextState))
        items = s.activeFormState(nextState)
        self.assertIn('invalid literal', get_by_id(items, 'a')['error'])
        self.assertIn('KABOOM!', get_by_id(items, 'b')['error'])
        self.assertIn('required', get_by_id(items, 'c')['error'])
        self.assertIn('required', get_by_id(items, 'oc')['error'])
        for item in get_by_id(items, 'group_gp1_a_gp1_b')['options']:
            self.assertIsNotNone(item['error'])
        for item in get_by_id(items, 'group_gp2_a_gp2_b')['options']:
            self.assertIsNotNone(item['error'])

        for item in get_by_id(items, 'group_gp3_a_gp3_b')['options']:
            self.assertIsNone(item['error'])
        # should be None, since this one was entirely optional
        for item in get_by_id(items, 'group_gp4_a_gp4_b')['options']:
            self.assertIsNone(item['error'])
        self.assertIsNone(get_by_id(items, 'bo')['error'])
        self.assertIsNone(get_by_id(items, 'ga')['error'])
        self.assertIsNone(get_by_id(items, 'gb')['error'])
        self.assertIsNone(get_by_id(items, 'gc')['error'])
Exemplo n.º 7
0
    def test_subparsers(self):
        """
        Making sure that subparsers are handled correctly and
        all validations still work as expected.
        """
        writer = MagicMock()
        exit = MagicMock()
        monkey_patch = control.validate_form(gooey_params(),
                                             write=writer,
                                             exit=exit)
        ArgumentParser.original_parse_args = ArgumentParser.parse_args
        ArgumentParser.parse_args = monkey_patch

        def build_parser():
            # we build a new parser for each subtest
            # since we monkey patch the hell out of it
            # each time
            parser = GooeyParser()
            subs = parser.add_subparsers()
            foo = subs.add_parser('foo')
            foo.add_argument('a')
            foo.add_argument('b')
            foo.add_argument('p')

            bar = subs.add_parser('bar')
            bar.add_argument('a')
            bar.add_argument('b')
            bar.add_argument('z')
            return parser

        parser = build_parser()
        with instrumentGooey(parser, target='test') as (app, frame, gapp):
            with self.subTest('first subparser'):
                # we start off with no errors
                self.assertFalse(s.has_errors(gapp.fullState()))

                cmd = s.buildFormValidationCmd(gapp.fullState())
                parser.parse_args(shlex.split(cmd)[1:])
                assert writer.called
                assert exit.called

                result = deserialize_inbound(
                    writer.call_args[0][0].encode('utf-8'), 'utf-8')
                nextState = s.mergeExternalState(gapp.fullState(), result)
                # by default, the subparser defined first, 'foo', is selected.
                self.assertIn('foo', nextState['forms'])
                # and we should find its attributes
                expected = {'a', 'b', 'p'}
                actual = {x['id'] for x in nextState['forms']['foo']}
                self.assertEqual(expected, actual)

        parser = build_parser()
        with instrumentGooey(parser, target='test') as (app, frame, gapp):
            with self.subTest('Second subparser'):
                # mocking a 'selection change' event to select
                # the second subparser
                event = MagicMock()
                event.Selection = 1
                gapp.handleSelectAction(event)

                # Flushing our events by running the main loop
                wx.CallLater(1, app.ExitMainLoop)
                app.MainLoop()

                cmd = s.buildFormValidationCmd(gapp.fullState())
                parser.parse_args(shlex.split(cmd)[1:])
                assert writer.called
                assert exit.called

                result = deserialize_inbound(
                    writer.call_args[0][0].encode('utf-8'), 'utf-8')
                nextState = s.mergeExternalState(gapp.fullState(), result)
                # Now our second subparer, 'bar', should be present.
                self.assertIn('bar', nextState['forms'])
                # and we should find its attributes
                expected = {'a', 'b', 'z'}
                actual = {x['id'] for x in nextState['forms']['bar']}
                self.assertEqual(expected, actual)
Exemplo n.º 8
0
 def test_invalid_font_weights_throw_error(self):
     parser = ArgumentParser(description="test parser")
     with self.assertRaises(ValueError):
         invalid_weight = 9123
         params = gooey_params(terminal_font_weight=invalid_weight)
         buildspec = create_from_parser(parser, "", **params)
Exemplo n.º 9
0
 def test_font_weight_defaults_to_normal(self):
     parser = ArgumentParser(description="test parser")
     # no font_weight explicitly provided
     buildspec = create_from_parser(parser, "", **gooey_params())
     self.assertEqual(buildspec['terminal_font_weight'], constants.FONTWEIGHT_NORMAL)