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

thesimj / tomlev / 17790751398

17 Sep 2025 07:55AM UTC coverage: 92.55% (+0.5%) from 92.066%
17790751398

push

github

Mykola Bubelich
Add set and tuple type support with comprehensive tests and bump version to 1.0.4

53 of 55 new or added lines in 2 files covered. (96.36%)

2 existing lines in 1 file now uncovered.

559 of 604 relevant lines covered (92.55%)

0.93 hits per line

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

97.37
/tomlev/cli.py
1
"""
2
MIT License
3

4
Copyright (c) 2025 Nick Bubelich
5

6
Permission is hereby granted, free of charge, to any person obtaining a copy
7
of this software and associated documentation files (the "Software"), to deal
8
in the Software without restriction, including without limitation the rights
9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
copies of the Software, and to permit persons to whom the Software is
11
furnished to do so, subject to the following conditions:
12

13
The above copyright notice and this permission notice shall be included in all
14
copies or substantial portions of the Software.
15

16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
SOFTWARE.
23
"""
24

25
from __future__ import annotations
1✔
26

27
import argparse
1✔
28
import json
1✔
29
from os import environ
1✔
30

31
from .constants import (
1✔
32
    DEFAULT_ENV_FILE,
33
    DEFAULT_SEPARATOR,
34
    DEFAULT_TOML_FILE,
35
    TOMLEV_ENV_FILE,
36
    TOMLEV_TOML_FILE,
37
    VERSION,
38
)
39
from .env_loader import EnvDict, read_env_file
1✔
40
from .errors import ConfigValidationError
1✔
41
from .parser import read_toml
1✔
42

43
__all__ = ["cli_validate", "cli_render", "main"]
1✔
44

45

46
def cli_validate(toml_file: str, env_file: str | None, strict: bool, include_environment: bool, separator: str) -> int:
1✔
47
    """Validate TOML parsing and env substitution without a model.
48

49
    Returns exit code 0 on success, 1 on failure. Prints a concise
50
    success message or a meaningful error to stderr.
51

52
    Args:
53
        toml_file: Path to the TOML file to validate.
54
        env_file: Path to the .env file, or None to skip.
55
        strict: Whether to operate in strict mode.
56
        include_environment: Whether to include system environment variables.
57
        separator: Separator for default values in environment variables.
58

59
    Returns:
60
        Exit code: 0 on success, 1 on failure.
61
    """
62
    # Build env mapping
63
    env: EnvDict = dict(environ) if include_environment else {}
1✔
64
    # Read dotenv
65
    try:
1✔
66
        dotenv = read_env_file(env_file, strict)
1✔
67
    except Exception as e:  # noqa: BLE001  # pragma: no cover - rare environment read errors
68
        print(f"Error reading env file: {e}")
69
        return 1
70
    env.update(dotenv)
1✔
71

72
    # Read TOML and perform substitution
73
    try:
1✔
74
        read_toml(toml_file, env, strict, separator)
1✔
75
    except FileNotFoundError:
1✔
76
        print(f"TOML file not found: {toml_file}")
1✔
77
        return 1
1✔
78
    except ConfigValidationError as e:
1✔
79
        print(str(e))
1✔
80
        return 1
1✔
81
    except Exception as e:  # noqa: BLE001
1✔
82
        print(f"Validation error: {e}")
1✔
83
        return 1
1✔
84

85
    print("Validation successful.")
1✔
86
    return 0
1✔
87

88

89
def cli_render(toml_file: str, env_file: str | None, strict: bool, include_environment: bool, separator: str) -> int:
1✔
90
    """Render TOML configuration as JSON after environment substitution and includes.
91

92
    Returns exit code 0 on success, 1 on failure. Prints the rendered
93
    configuration as pretty-formatted JSON to stdout.
94

95
    Args:
96
        toml_file: Path to the TOML file to render.
97
        env_file: Path to the .env file, or None to skip.
98
        strict: Whether to operate in strict mode.
99
        include_environment: Whether to include system environment variables.
100
        separator: Separator for default values in environment variables.
101

102
    Returns:
103
        Exit code: 0 on success, 1 on failure.
104
    """
105
    # Build env mapping
106
    env: EnvDict = dict(environ) if include_environment else {}
1✔
107
    # Read dotenv
108
    try:
1✔
109
        dotenv = read_env_file(env_file, strict)
1✔
110
    except Exception as e:  # noqa: BLE001  # pragma: no cover - rare environment read errors
111
        print(f"Error reading env file: {e}")
112
        return 1
113
    env.update(dotenv)
1✔
114

115
    # Read TOML and perform substitution
116
    try:
1✔
117
        config = read_toml(toml_file, env, strict, separator)
1✔
118
    except FileNotFoundError:
1✔
119
        print(f"TOML file not found: {toml_file}")
1✔
120
        return 1
1✔
121
    except ConfigValidationError as e:
1✔
UNCOV
122
        print(str(e))
×
UNCOV
123
        return 1
×
124
    except Exception as e:  # noqa: BLE001
1✔
125
        print(f"Render error: {e}")
1✔
126
        return 1
1✔
127

128
    # Output as JSON
129
    print(json.dumps(config, indent=2))
1✔
130
    return 0
1✔
131

132

133
def main(argv: list[str] | None = None) -> int:
1✔
134
    """TomlEv CLI entry point.
135

136
    Commands:
137
    - validate: Validate TOML file with env substitution (schema-less).
138
    - render: Render TOML configuration as JSON with substitution and includes.
139

140
    Args:
141
        argv: Command line arguments. If None, uses sys.argv.
142

143
    Returns:
144
        Exit code: 0 on success, 1 on failure.
145
    """
146
    # Get defaults from environment variables or fall back to constants
147
    default_toml = environ.get(TOMLEV_TOML_FILE, DEFAULT_TOML_FILE)
1✔
148
    default_env = environ.get(TOMLEV_ENV_FILE, DEFAULT_ENV_FILE)
1✔
149

150
    parser = argparse.ArgumentParser(prog="tomlev", description="TomlEv CLI")
1✔
151
    parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {VERSION}")
1✔
152
    sub = parser.add_subparsers(dest="command", required=True)
1✔
153

154
    p_validate = sub.add_parser("validate", help="Validate TOML with env substitution")
1✔
155
    p_validate.add_argument("--toml", default=default_toml, help="Path to TOML file")
1✔
156
    p_validate.add_argument("--env-file", default=default_env, help="Path to .env file (use --no-env-file to disable)")
1✔
157
    p_validate.add_argument("--no-env-file", action="store_true", help="Do not read .env file")
1✔
158
    p_validate.add_argument(
1✔
159
        "--strict", dest="strict", action="store_true", default=True, help="Enable strict mode (default)"
160
    )
161
    p_validate.add_argument("--no-strict", dest="strict", action="store_false", help="Disable strict mode")
1✔
162
    p_validate.add_argument("--no-environ", action="store_true", help="Do not include system environment variables")
1✔
163
    p_validate.add_argument("--separator", default=DEFAULT_SEPARATOR, help="Default separator for ${VAR|-default}")
1✔
164

165
    p_render = sub.add_parser("render", help="Render TOML configuration as JSON")
1✔
166
    p_render.add_argument("--toml", default=default_toml, help="Path to TOML file")
1✔
167
    p_render.add_argument("--env-file", default=default_env, help="Path to .env file (use --no-env-file to disable)")
1✔
168
    p_render.add_argument("--no-env-file", action="store_true", help="Do not read .env file")
1✔
169
    p_render.add_argument(
1✔
170
        "--strict", dest="strict", action="store_true", default=True, help="Enable strict mode (default)"
171
    )
172
    p_render.add_argument("--no-strict", dest="strict", action="store_false", help="Disable strict mode")
1✔
173
    p_render.add_argument("--no-environ", action="store_true", help="Do not include system environment variables")
1✔
174
    p_render.add_argument("--separator", default=DEFAULT_SEPARATOR, help="Default separator for ${VAR|-default}")
1✔
175

176
    args = parser.parse_args(argv)
1✔
177

178
    if args.command == "validate":
1✔
179
        env_file = None if args.no_env_file else args.env_file
1✔
180
        include_environment = not args.no_environ
1✔
181
        return cli_validate(args.toml, env_file, args.strict, include_environment, args.separator)
1✔
182
    elif args.command == "render":
1✔
183
        env_file = None if args.no_env_file else args.env_file
1✔
184
        include_environment = not args.no_environ
1✔
185
        return cli_render(args.toml, env_file, args.strict, include_environment, args.separator)
1✔
186

187
    parser.print_help()  # pragma: no cover - subparsers require a command
188
    return 1  # pragma: no cover - unreachable with required=True
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