|
| 1 | +import re |
| 2 | +from argparse import ArgumentParser, HelpFormatter |
| 3 | +from contextlib import nullcontext |
| 4 | +from functools import partial |
| 5 | +from io import StringIO |
1 | 6 | from os.path import join
|
| 7 | +from shlex import split |
2 | 8 | from sys import executable
|
| 9 | +import pytest |
| 10 | +from line_profiler.cli_utils import add_argument |
| 11 | + |
| 12 | + |
| 13 | +@pytest.fixture |
| 14 | +def parser(): |
| 15 | + """ |
| 16 | + Argument parser with the following boolean flags: |
| 17 | +
|
| 18 | + -f, -F, --foo -> foo |
| 19 | + -b, --bar -> bar |
| 20 | + -B, --baz -> baz |
| 21 | + --no-spam -> spam (negated) |
| 22 | + --ham -> ham |
| 23 | + -c -> c |
| 24 | + """ |
| 25 | + parser = ArgumentParser( |
| 26 | + formatter_class=partial(HelpFormatter, |
| 27 | + max_help_position=float('inf'), |
| 28 | + width=float('inf'))) |
| 29 | + # Normal boolean flag (w/2 short forms) |
| 30 | + # -> adds 3 actions (long, short, long-negated) |
| 31 | + add_argument(parser, '-f', '-F', '--foo', action='store_true') |
| 32 | + # Boolean flag w/o parenthetical remark in help text |
| 33 | + # -> adds 3 actions (long, short, long-negated) |
| 34 | + add_argument(parser, '-b', '--bar', action='store_true', help='Set `bar`') |
| 35 | + # Boolean flag w/parenthetical remark in help text |
| 36 | + # -> adds 3 actions (long, short, long-negated) |
| 37 | + add_argument(parser, '-B', '--baz', |
| 38 | + action='store_true', help='Set `baz` (BAZ)') |
| 39 | + # Negative boolean flag |
| 40 | + # -> adds 1 action (long-negated) |
| 41 | + add_argument(parser, '--no-spam', |
| 42 | + action='store_false', dest='spam', help='Set `spam` to false') |
| 43 | + # Boolean flag w/o short form |
| 44 | + # -> adds 2 actions (long, long-negated) |
| 45 | + add_argument(parser, '--ham', action='store_true', help='Set `ham`') |
| 46 | + # Short-form-only boolean flag |
| 47 | + # -> adds 1 action (short) |
| 48 | + add_argument(parser, '-e', |
| 49 | + action='store_true', dest='eggs', help='Set `eggs`') |
| 50 | + yield parser |
| 51 | + |
| 52 | + |
| 53 | +def test_boolean_argument_help_text(parser): |
| 54 | + """ |
| 55 | + Test the help texts generated from boolean arguments added by |
| 56 | + `line_profiler.cli_utils.add_argument(action=...)`. |
| 57 | + """ |
| 58 | + assert len(parser._actions) == 14 # One extra option from `--help` |
| 59 | + with StringIO() as sio: |
| 60 | + parser.print_help(sio) |
| 61 | + help_text = sio.getvalue() |
| 62 | + matches = partial(re.search, string=help_text, flags=re.MULTILINE) |
| 63 | + assert matches(r'^ --foo \[.*\] +' |
| 64 | + + re.escape('(Short forms: -f, -F)') |
| 65 | + + '$') |
| 66 | + assert matches(r'^ --bar \[.*\] +' |
| 67 | + + re.escape('Set `bar` (Short form: -b)') |
| 68 | + + '$') |
| 69 | + assert matches(r'^ --baz \[.*\] +' |
| 70 | + + re.escape('Set `baz` (BAZ; short form: -B)') |
| 71 | + + '$') |
| 72 | + assert matches(r'^ --no-spam \[.*\] +' |
| 73 | + + re.escape('Set `spam` to false') |
| 74 | + + '$') |
| 75 | + assert matches(r'^ --ham \[.*\] +' |
| 76 | + + re.escape('Set `ham`') |
| 77 | + + '$') |
| 78 | + assert matches(r'^ -e +' |
| 79 | + + re.escape('Set `eggs`') |
| 80 | + + '$') |
| 81 | + |
| 82 | + |
| 83 | +@pytest.mark.parametrize( |
| 84 | + ('args', 'foo', 'bar', 'baz', 'spam', 'ham', 'eggs', 'expect_error'), |
| 85 | + [('--foo q', *((None,) * 6), True), # Can't parse `q` into boolean |
| 86 | + ('-fbB' # Test short-flag concatenation |
| 87 | + ' --ham=', # Empty string -> set to false |
| 88 | + True, True, True, None, False, None, False), |
| 89 | + ('--foo' # No-arg -> set to true |
| 90 | + ' --bar=0' # Falsy arg -> set to false |
| 91 | + ' --no-baz' # No-arg (negated flag) -> set to false |
| 92 | + ' --no-spam=no' # Falsy arg (negated flag) -> set to true |
| 93 | + ' --ham=on' # Truey arg -> set to true |
| 94 | + ' -e', # No-arg -> set to true |
| 95 | + True, False, False, True, True, True, False)]) |
| 96 | +def test_boolean_argument_parsing( |
| 97 | + parser, capsys, args, foo, bar, baz, spam, ham, eggs, expect_error): |
| 98 | + """ |
| 99 | + Test the handling of boolean flags. |
| 100 | + """ |
| 101 | + if expect_error: |
| 102 | + ctx = pytest.raises(SystemExit) |
| 103 | + match_stderr = 'usage: .* error: argument' |
| 104 | + else: |
| 105 | + ctx = nullcontext() |
| 106 | + match_stderr = '^$' |
| 107 | + with ctx: |
| 108 | + result = vars(parser.parse_args(split(args))) |
| 109 | + stderr = capsys.readouterr().err |
| 110 | + assert re.match(match_stderr, stderr, flags=re.DOTALL) |
| 111 | + if expect_error: |
| 112 | + return |
| 113 | + expected = dict(foo=foo, bar=bar, baz=baz, spam=spam, ham=ham, eggs=eggs) |
| 114 | + assert result == expected |
3 | 115 |
|
4 | 116 |
|
5 | 117 | def test_cli():
|
|
0 commit comments