• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Hekxsler / pudding / 26163864792

20 May 2026 12:54PM UTC coverage: 92.385% (-0.9%) from 93.254%
26163864792

Pull #5

github

web-flow
Merge f197fa31e into a91d4e7d1
Pull Request #5: Feature/specifiy output

1286 of 1392 relevant lines covered (92.39%)

0.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.15
pudding/_cli.py
1
"""Main script."""
2

3
import argparse
1✔
4
import datetime
1✔
5
import logging
1✔
6
import os
1✔
7
from collections.abc import Sequence
1✔
8
from pathlib import Path
1✔
9

10
from .exceptions import PuddingException
1✔
11
from .util import convert_files
1✔
12
from .version import __version__
1✔
13
from .writer import __all__ as formats
1✔
14

15
DEFAULT_FORMAT = "json"
1✔
16
DESCRIPTION = """
1✔
17
Pudding converts text to a structured format, such as XML, JSON or YAML.
18
For more information see the documentation at https://pudding.readthedocs.io/latest.
19
"""
20

21
format_choices = [f.lower() for f in formats]
1✔
22
logger = logging.getLogger(__name__)
1✔
23

24

25
def build_parser() -> argparse.ArgumentParser:
1✔
26
    """Build argument parser."""
27
    parser = argparse.ArgumentParser(prog="pudding", description=DESCRIPTION)
1✔
28
    parser.add_argument(
1✔
29
        "filename", help="The file or files to convert.", metavar="FILE", nargs="+"
30
    )
31
    parser.add_argument(
1✔
32
        "-s",
33
        "--syntax",
34
        default=None,
35
        help="The file containing the syntax for parsing the input.",
36
        metavar="SYNTAX_FILE",
37
        required=True,
38
    )
39
    parser.add_argument(
1✔
40
        "-f",
41
        "--format",
42
        choices=format_choices,
43
        default=DEFAULT_FORMAT,
44
        help=(
45
            "The output format. "
46
            f"Choices are: {', '.join(format_choices)}. Default is `{DEFAULT_FORMAT}`."
47
        ),
48
        metavar="FORMAT",
49
    )
50
    parser.add_argument(
1✔
51
        "-o",
52
        "--output",
53
        help=(
54
            "If converting a single file this is the name of the output file. "
55
            "When converting multiple files this is the output directory."
56
        ),
57
        metavar="OUTPUT",
58
    )
59
    parser.add_argument("--debug", action="store_true", help="Print debug info.")
1✔
60
    parser.add_argument("-V", "--version", action="version", version=__version__)
1✔
61
    return parser
1✔
62

63

64
def is_valid_inpath(path: str) -> bool:
1✔
65
    """Check if a syntax file path is valid."""
66
    if not os.path.exists(path):
1✔
67
        logger.error("no such file or directory: %s", repr(path))
×
68
        return False
×
69
    if not os.path.isfile(path):
1✔
70
        logger.error("not a valid input file: %s", repr(path))
×
71
        return False
×
72
    return True
1✔
73

74

75
def main(argv: Sequence[str] | None = None) -> int:
1✔
76
    """Check cli arguments."""
77
    args = build_parser().parse_args(argv)
1✔
78

79
    log_level = logging.INFO
1✔
80
    if args.debug:
1✔
81
        log_level = logging.DEBUG
×
82
    logging.basicConfig(
1✔
83
        format="[%(asctime)s] %(levelname)s: %(message)s",
84
        datefmt="%Y-%m-%d %I:%M:%S",
85
        level=log_level,
86
    )
87

88
    if not is_valid_inpath(args.syntax):
1✔
89
        return 2
×
90

91
    ins: list[Path] = []
1✔
92
    outs: list[Path] = []
1✔
93
    if len(args.filename) == 1:
1✔
94
        f = args.filename[0]
1✔
95
        if not is_valid_inpath(f):
1✔
96
            return 2
×
97
        path, _ = os.path.splitext(f)
1✔
98
        ins.append(Path(f))
1✔
99
        if args.output:
1✔
100
            outs.append(Path(args.output))
1✔
101
        else:
102
            outs.append(Path(f"{path}.{args.format.lower()}"))
×
103
    else:
104
        for f in args.filename:
×
105
            if not is_valid_inpath(f):
×
106
                return 2
×
107
            path, _ = os.path.splitext(f)
×
108
            ins.append(Path(f))
×
109
            output_path = Path(f"{path}.{args.format.lower()}")
×
110
            if args.output:
×
111
                outs.append(Path(args.output) / output_path)
×
112
            else:
113
                outs.append(output_path)
×
114

115
    start = datetime.datetime.now()
1✔
116
    try:
1✔
117
        convert_files(Path(args.syntax), ins, outs, args.format)
1✔
118
    except PuddingException as e:
×
119
        logger.error(e)
×
120
        return 1
×
121
    logger.debug("Total: %s", str(datetime.datetime.now() - start))
1✔
122
    return 0
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc