File: parser_context.py

package info (click to toggle)
python-invoke 1.4.1%2Bds-0.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,704 kB
  • sloc: python: 11,377; makefile: 18; sh: 12
file content (320 lines) | stat: -rw-r--r-- 12,087 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
import copy

from pytest import raises

from invoke.parser import Argument, Context
from invoke.tasks import task
from invoke.collection import Collection


class Context_:
    "ParserContext"  # meh

    def may_have_a_name(self):
        c = Context(name="taskname")
        assert c.name == "taskname"

    def may_have_aliases(self):
        c = Context(name="realname", aliases=("othername", "yup"))
        assert "othername" in c.aliases

    def may_give_arg_list_at_init_time(self):
        a1 = Argument("foo")
        a2 = Argument("bar")
        c = Context(name="name", args=(a1, a2))
        assert c.args["foo"] is a1

    # TODO: reconcile this sort of test organization with the .flags oriented
    # tests within 'add_arg'.  Some of this behavior is technically driven by
    # add_arg.
    class args:
        def setup(self):
            self.c = Context(
                args=(
                    Argument("foo"),
                    Argument(names=("bar", "biz")),
                    Argument("baz", attr_name="wat"),
                )
            )

        def exposed_as_dict(self):
            assert "foo" in self.c.args.keys()

        def exposed_as_Lexicon(self):
            assert self.c.args.bar == self.c.args["bar"]

        def args_dict_includes_all_arg_names(self):
            for x in ("foo", "bar", "biz"):
                assert x in self.c.args

        def argument_attr_names_appear_in_args_but_not_flags(self):
            # Both appear as "Python-facing" args
            for x in ("baz", "wat"):
                assert x in self.c.args
            # But attr_name is for Python access only and isn't shown to the
            # parser.
            assert "wat" not in self.c.flags

    class add_arg:
        def setup(self):
            self.c = Context()

        def can_take_Argument_instance(self):
            a = Argument(names=("foo",))
            self.c.add_arg(a)
            assert self.c.args["foo"] is a

        def can_take_name_arg(self):
            self.c.add_arg("foo")
            assert "foo" in self.c.args

        def can_take_kwargs_for_single_Argument(self):
            self.c.add_arg(names=("foo", "bar"))
            assert "foo" in self.c.args and "bar" in self.c.args

        def raises_ValueError_on_duplicate(self):
            self.c.add_arg(names=("foo", "bar"))
            with raises(ValueError):
                self.c.add_arg(name="bar")

        def adds_flaglike_name_to_dot_flags(self):
            "adds flaglike name to .flags"
            self.c.add_arg("foo")
            assert "--foo" in self.c.flags

        def adds_all_names_to_dot_flags(self):
            "adds all names to .flags"
            self.c.add_arg(names=("foo", "bar"))
            assert "--foo" in self.c.flags
            assert "--bar" in self.c.flags

        def adds_true_bools_to_inverse_flags(self):
            self.c.add_arg(name="myflag", default=True, kind=bool)
            assert "--myflag" in self.c.flags
            assert "--no-myflag" in self.c.inverse_flags
            assert self.c.inverse_flags["--no-myflag"] == "--myflag"

        def inverse_flags_works_right_with_task_driven_underscored_names(self):
            # Use a Task here instead of creating a raw argument, we're partly
            # testing Task.get_arguments()' transform of underscored names
            # here. Yes that makes this an integration test, but it's nice to
            # test it here at this level & not just in cli tests.
            @task
            def mytask(c, underscored_option=True):
                pass

            self.c.add_arg(mytask.get_arguments()[0])
            flags = self.c.inverse_flags["--no-underscored-option"]
            assert flags == "--underscored-option"

        def turns_single_character_names_into_short_flags(self):
            self.c.add_arg("f")
            assert "-f" in self.c.flags
            assert "--f" not in self.c.flags

        def adds_positional_args_to_positional_args(self):
            self.c.add_arg(name="pos", positional=True)
            assert self.c.positional_args[0].name == "pos"

        def positional_args_empty_when_none_given(self):
            assert len(self.c.positional_args) == 0

        def positional_args_filled_in_order(self):
            self.c.add_arg(name="pos1", positional=True)
            assert self.c.positional_args[0].name == "pos1"
            self.c.add_arg(name="abc", positional=True)
            assert self.c.positional_args[1].name == "abc"

        def positional_arg_modifications_affect_args_copy(self):
            self.c.add_arg(name="hrm", positional=True)
            assert self.c.args["hrm"].value == self.c.positional_args[0].value
            self.c.positional_args[0].value = 17
            assert self.c.args["hrm"].value == self.c.positional_args[0].value

    class deepcopy:
        "__deepcopy__"

        def setup(self):
            self.arg = Argument("--boolean")
            self.orig = Context(
                name="mytask", args=(self.arg,), aliases=("othername",)
            )
            self.new = copy.deepcopy(self.orig)

        def returns_correct_copy(self):
            assert self.new is not self.orig
            assert self.new.name == "mytask"
            assert "othername" in self.new.aliases

        def includes_arguments(self):
            assert len(self.new.args) == 1
            assert self.new.args["--boolean"] is not self.arg

        def modifications_to_copied_arguments_do_not_touch_originals(self):
            new_arg = self.new.args["--boolean"]
            new_arg.value = True
            assert new_arg.value
            assert not self.arg.value

    class help_for:
        def setup(self):
            # Normal, non-task/collection related Context
            self.vanilla = Context(
                args=(Argument("foo"), Argument("bar", help="bar the baz"))
            )
            # Task/Collection generated Context
            # (will expose flags n such)
            @task(help={"otherarg": "other help"}, optional=["optval"])
            def mytask(c, myarg, otherarg, optval, intval=5):
                pass

            col = Collection(mytask)
            self.tasked = col.to_contexts()[0]

        def raises_ValueError_for_non_flag_values(self):
            with raises(ValueError):
                self.vanilla.help_for("foo")

        def vanilla_no_helpstr(self):
            assert self.vanilla.help_for("--foo") == ("--foo=STRING", "")

        def vanilla_with_helpstr(self):
            result = self.vanilla.help_for("--bar")
            assert result == ("--bar=STRING", "bar the baz")

        def task_driven_with_helpstr(self):
            result = self.tasked.help_for("--otherarg")
            assert result == ("-o STRING, --otherarg=STRING", "other help")

        # Yes, the next 3 tests are identical in form, but technically they
        # test different behaviors. HERPIN' AN' DERPIN'
        def task_driven_no_helpstr(self):
            result = self.tasked.help_for("--myarg")
            assert result == ("-m STRING, --myarg=STRING", "")

        def short_form_before_long_form(self):
            result = self.tasked.help_for("--myarg")
            assert result == ("-m STRING, --myarg=STRING", "")

        def equals_sign_for_long_form_only(self):
            result = self.tasked.help_for("--myarg")
            assert result == ("-m STRING, --myarg=STRING", "")

        def kind_to_placeholder_map(self):
            # Strings
            helpfor = self.tasked.help_for("--myarg")
            assert helpfor == ("-m STRING, --myarg=STRING", "")
            # Ints
            helpfor = self.tasked.help_for("--intval")
            assert helpfor == ("-i INT, --intval=INT", "")
            # TODO: others

        def shortflag_inputs_work_too(self):
            m = self.tasked.help_for("-m")
            myarg = self.tasked.help_for("--myarg")
            assert m == myarg

        def optional_values_use_brackets(self):
            result = self.tasked.help_for("--optval")
            assert result == ("-p [STRING], --optval[=STRING]", "")

        def underscored_args(self):
            c = Context(args=(Argument("i_have_underscores", help="yup"),))
            result = c.help_for("--i-have-underscores")
            assert result == ("--i-have-underscores=STRING", "yup")

        def true_default_args(self):
            c = Context(args=(Argument("truthy", kind=bool, default=True),))
            assert c.help_for("--truthy") == ("--[no-]truthy", "")

    class help_tuples:
        def returns_list_of_help_tuples(self):
            # Walks own list of flags/args, ensures resulting map to help_for()
            # TODO: consider redoing help_for to be more flexible on input --
            # arg value or flag; or even Argument objects. ?
            @task(help={"otherarg": "other help"})
            def mytask(c, myarg, otherarg):
                pass

            c = Collection(mytask).to_contexts()[0]
            expected = [c.help_for("--myarg"), c.help_for("--otherarg")]
            assert c.help_tuples() == expected

        def _assert_order(self, name_tuples, expected_flag_order):
            c = Context(args=[Argument(names=x) for x in name_tuples])
            expected = [c.help_for(x) for x in expected_flag_order]
            assert c.help_tuples() == expected

        def sorts_alphabetically_by_shortflag_first(self):
            # Where shortflags exist, they take precedence
            self._assert_order(
                [("zarg", "a"), ("arg", "z")], ["--zarg", "--arg"]
            )

        def case_ignored_during_sorting(self):
            self._assert_order(
                [("a",), ("B",)],
                # In raw cmp() uppercase would come before lowercase,
                # and we'd get ['-B', '-a']
                ["-a", "-B"],
            )

        def lowercase_wins_when_values_identical_otherwise(self):
            self._assert_order([("V",), ("v",)], ["-v", "-V"])

        def sorts_alphabetically_by_longflag_when_no_shortflag(self):
            # Where no shortflag, sorts by longflag
            self._assert_order(
                [("otherarg",), ("longarg",)], ["--longarg", "--otherarg"]
            )

        def sorts_heterogenous_help_output_with_longflag_only_options_first(
            self
        ):  # noqa
            # When both of the above mix, long-flag-only options come first.
            # E.g.:
            #   --alpha
            #   --beta
            #   -a, --aaaagh
            #   -b, --bah
            #   -c
            self._assert_order(
                [("c",), ("a", "aaagh"), ("b", "bah"), ("beta",), ("alpha",)],
                ["--alpha", "--beta", "-a", "-b", "-c"],
            )

        def mixed_corelike_options(self):
            self._assert_order(
                [
                    ("V", "version"),
                    ("c", "collection"),
                    ("h", "help"),
                    ("l", "list"),
                    ("r", "root"),
                ],
                ["-c", "-h", "-l", "-r", "-V"],
            )

    class missing_positional_args:
        def represents_positional_args_missing_values(self):
            arg1 = Argument("arg1", positional=True)
            arg2 = Argument("arg2", positional=False)
            arg3 = Argument("arg3", positional=True)
            c = Context(name="foo", args=(arg1, arg2, arg3))
            assert c.missing_positional_args == [arg1, arg3]
            c.positional_args[0].value = "wat"
            assert c.missing_positional_args == [arg3]
            c.positional_args[1].value = "hrm"
            assert c.missing_positional_args == []

    class str:
        "__str__"

        def with_no_args_output_is_simple(self):
            assert str(Context("foo")) == "<parser/Context 'foo'>"

        def args_show_as_repr(self):
            string = str(Context("bar", args=[Argument("arg1")]))
            assert (
                string == "<parser/Context 'bar': {'arg1': <Argument: arg1>}>"
            )  # noqa