File: test_regressions.py

package info (click to toggle)
python-argh 0.31.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 400 kB
  • sloc: python: 3,231; makefile: 4
file content (263 lines) | stat: -rw-r--r-- 7,871 bytes parent folder | download | duplicates (2)
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
"""
Regression tests
~~~~~~~~~~~~~~~~
"""

import sys
from typing import List, Optional, TextIO

import pytest

import argh

from .base import DebugArghParser, run


def test_regression_issue12():
    """
    Issue #12: @command was broken if there were more than one argument
    to begin with same character (i.e. short option names were inferred
    incorrectly).
    """

    def cmd(*, foo=1, fox=2):
        yield f"foo {foo}, fox {fox}"

    parser = DebugArghParser()
    parser.set_default_command(cmd)

    assert run(parser, "").out == "foo 1, fox 2\n"
    assert run(parser, "--foo 3").out == "foo 3, fox 2\n"
    assert run(parser, "--fox 3").out == "foo 1, fox 3\n"
    assert "unrecognized" in run(parser, "-f 3", exit=True)


def test_regression_issue12_help_flag():
    """
    Issue #12: if an argument starts with "h", e.g. "--host",
    ArgumentError is raised because "--help" is always added by argh
    without decorators.
    """

    def ddos(*, host="localhost"):
        return f"so be it, {host}!"

    # no help → no conflict
    parser = DebugArghParser("PROG", add_help=False)
    parser.set_default_command(ddos)
    assert run(parser, "-h 127.0.0.1").out == "so be it, 127.0.0.1!\n"

    # help added → conflict → short name ignored
    parser = DebugArghParser("PROG", add_help=True)
    parser.set_default_command(ddos)
    assert run(parser, "-h 127.0.0.1", exit=True) == 0


def test_regression_issue27():
    """
    Issue #27: store_true is not set for inferred bool argument.

    :Reason: when @command was refactored, it stopped using @arg, but it is
    it was there that guesses (choices→type, default→type and
    default→action) were made.
    """

    def parrot(*, dead=False):
        return "this parrot is no more" if dead else "beautiful plumage"

    def grenade(*, count=3):
        if count == 3:
            return "Three shall be the number thou shalt count"
        else:
            return "{0!r} is right out".format(count)

    parser = DebugArghParser()
    parser.add_commands([parrot, grenade])

    # default → type (int)
    assert run(parser, "grenade").out == (
        "Three shall be the number " "thou shalt count\n"
    )
    assert run(parser, "grenade --count 5").out == "5 is right out\n"

    # default → action (store_true)
    assert run(parser, "parrot").out == "beautiful plumage\n"
    assert run(parser, "parrot --dead").out == "this parrot is no more\n"


def test_regression_issue31():
    """
    Issue #31: Argh fails with parameter action type 'count' if a default
    value is provided.

    :Reason: assembling._guess() would infer type from default value without
        regard to the action.  _CountAction does not accept argument "type".

    :Solution: restricted type inferring to actions "store" and "append".
    """

    @argh.arg("-v", "--verbose", dest="verbose", action="count", default=0)
    def cmd(**kwargs):
        yield kwargs.get("verbose", -1)

    parser = DebugArghParser()
    parser.set_default_command(cmd)
    assert "0\n" == run(parser, "").out
    assert "1\n" == run(parser, "-v").out
    assert "2\n" == run(parser, "-vv").out


def test_regression_issue47():
    @argh.arg("--foo-bar", default="full")
    def func(foo_bar):
        return "hello"

    parser = DebugArghParser()
    with pytest.raises(argh.assembling.AssemblingError) as excinfo:
        parser.set_default_command(func)
    msg = (
        'func: argument "foo_bar" declared as positional (in function '
        "signature) and optional (via decorator). If you've just migrated "
        "from Argh v.0.29, please check the new default NameMappingPolicy. "
        "Perhaps you need to replace `func(x=1)` with `func(*, x=1)`?"
    )
    assert excinfo.exconly().endswith(msg)


def test_regression_issue76():
    """
    Issue #76: optional arguments defaulting to the empty string break --help.

    This is also tested in integration tests but in a different way.
    """

    def cmd(*, foo=""):
        pass

    parser = DebugArghParser()
    parser.set_default_command(cmd)
    run(parser, "--help", exit=True)


def test_regression_issue104():
    """
    Issue #76: Bug in the way **kwargs is handled

    **kwargs handling was broken in the case that required (no default
    value) positional argument names contained underscores.
    """

    def cmd(foo_foo, bar_bar, *, baz_baz=5, bip_bip=9, **kwargs):
        return "\n".join(
            [str(foo_foo), str(bar_bar), str(baz_baz), str(bip_bip), str(kwargs)]
        )

    parser = DebugArghParser()
    parser.set_default_command(cmd)
    expected = "abc\ndef\n8\n9\n{}\n"
    assert run(parser, "abc def --baz-baz 8").out == expected


def test_regression_issue204():
    """
    Issue #204: `asdict(ParserAddArgumentSpec)` used `deepcopy` which would
    lead to "TypeError: cannot pickle..." if e.g. a default value contained an
    un-pickle-able object.

    We should avoid `deepcopy()` in standard operations.
    """

    def func(*, x: TextIO = sys.stdout) -> None: ...

    parser = DebugArghParser()
    parser.set_default_command(func)


def test_regression_issue208():
    @argh.arg("foo_bar", help="fooooo")
    def func(foo_bar):
        return foo_bar

    parser = DebugArghParser()
    parser.set_default_command(func)


def test_regression_issue212_orig_use_case():
    """
    Issue #212: a combination of nargs with list as default value would result
    in a nested list instead of a flat list.

    Variation: original use case (default value via decorator).
    """

    @argh.arg("paths", nargs="*", default=["one", "two"])
    def func(paths: List[str]):
        return f"{paths}"

    parser = DebugArghParser()
    parser.set_default_command(func)

    assert run(parser, "").out == "['one', 'two']\n"
    assert run(parser, "alpha").out == "['alpha']\n"
    assert run(parser, "alpha beta gamma").out == "['alpha', 'beta', 'gamma']\n"


def test_regression_issue212_funcsig_centric_positional():
    """
    Issue #212: a combination of nargs with list as default value would result
    in a nested list instead of a flat list.

    Variation: default value via function signature (positional).
    """

    @argh.arg("paths", nargs="*")
    def func(paths: Optional[List[str]] = ["one", "two"]):
        return f"{paths}"

    parser = DebugArghParser()
    parser.set_default_command(
        func, name_mapping_policy=argh.assembling.NameMappingPolicy.BY_NAME_IF_KWONLY
    )

    assert run(parser, "").out == "['one', 'two']\n"
    assert run(parser, "alpha").out == "['alpha']\n"
    assert run(parser, "alpha beta gamma").out == "['alpha', 'beta', 'gamma']\n"


def test_regression_issue212_funcsig_centric_named():
    """
    Issue #212: a combination of nargs with list as default value would result
    in a nested list instead of a flat list.

    Variation: default value via function signature (named).
    """

    @argh.arg("--paths", nargs="*")
    def func(*, paths: Optional[List[str]] = ["one", "two"]):
        return f"{paths}"

    parser = DebugArghParser()
    parser.set_default_command(func)

    assert run(parser, "").out == "['one', 'two']\n"
    assert run(parser, "--paths alpha").out == "['alpha']\n"
    assert run(parser, "--paths alpha beta gamma").out == "['alpha', 'beta', 'gamma']\n"


def test_regression_issue224():
    """
    Issue #224: @arg param `dest` was ignored and Argh was unable to map the
    declaration onto the function signature.

    Use case: expose a function argument with a different name in the CLI.
    """

    @argh.arg("-l", dest="list_files")
    def func(*, list_files=False):
        return f"list_files={list_files}"

    parser = DebugArghParser()
    parser.set_default_command(func)

    assert run(parser, "").out == "list_files=False\n"
    assert run(parser, "-l").out == "list_files=True\n"