Coverage for lice2/core.py: 100%
46 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-13 11:13 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-13 11:13 +0000
1"""Main core of the application."""
3from __future__ import annotations
5import sys
6from pathlib import Path
7from types import SimpleNamespace
8from typing import Any, Callable, Optional
10import typer
11from rich import print as rprint
12from rich.markup import escape
14from lice2 import __version__
15from lice2.config import check_default_license, settings
16from lice2.constants import LANGS, LICENSES
17from lice2.helpers import (
18 copy_to_clipboard,
19 format_license,
20 generate_header,
21 generate_license,
22 get_context,
23 get_lang,
24 get_local_year,
25 get_metadata,
26 get_suffix,
27 guess_organization,
28 list_languages,
29 list_licenses,
30 list_vars,
31 load_file_template,
32 load_package_template,
33 validate_license,
34 validate_year,
35)
37app = typer.Typer(rich_markup_mode="rich")
40@app.command(
41 help=(
42 "Generates a license template with context variables, and "
43 "optionally write this to a file."
44 ),
45 context_settings={"help_option_names": ["-h", "--help"]},
46)
47def main( # noqa: PLR0913
48 license_name: str = typer.Argument(
49 default=check_default_license(),
50 help=f"The license to generate, one of: {', '.join(LICENSES)}",
51 callback=validate_license,
52 metavar="[license]",
53 ),
54 organization: str = typer.Option(
55 guess_organization(),
56 "--org",
57 "-o",
58 help='Organization, defaults to .gitconfig or os.environ["USER"]',
59 ),
60 project: str = typer.Option(
61 Path.cwd().name,
62 "--proj",
63 "-p",
64 help="Name of project, defaults to name of current directory",
65 ),
66 template_path: Optional[str] = typer.Option(
67 None,
68 "--template",
69 "-t",
70 help="Path to license template file",
71 ),
72 year: Optional[str] = typer.Option(
73 get_local_year(),
74 "--year",
75 "-y",
76 help="Copyright year",
77 callback=validate_year,
78 ),
79 language: Optional[str] = typer.Option(
80 None,
81 "--language",
82 "-l",
83 help=(
84 "Format output for language source file, one of: "
85 f"{', '.join(LANGS.keys())} "
86 f"[dim]{escape('[default: txt]')}[/dim]"
87 ),
88 show_default=False,
89 ),
90 ofile: Optional[str] = typer.Option(
91 "stdout",
92 "--file",
93 "-f",
94 help=(
95 "Name of the output source file (with -l, "
96 "extension can be omitted)"
97 ),
98 ),
99 *,
100 header: bool = typer.Option(
101 False,
102 "--header",
103 help="Generate source file header for specified license",
104 ),
105 clipboard: bool = typer.Option(
106 False,
107 "--clipboard",
108 "-c",
109 help="Copy the generated license to the clipboard",
110 ),
111 show_vars: Optional[bool] = typer.Option(
112 None,
113 "--vars",
114 help="List template variables for specified license",
115 ),
116 show_licenses: bool = typer.Option(
117 False,
118 "--licenses",
119 help="List available license templates and their parameters",
120 ),
121 show_languages: bool = typer.Option(
122 False,
123 "--languages",
124 help="List available source code formatting languages",
125 ),
126 legacy: bool = typer.Option(
127 False,
128 "--legacy",
129 help="Use legacy method to generate license",
130 ),
131 version: bool = typer.Option(
132 False,
133 "--version",
134 "-v",
135 is_eager=True,
136 help="Show version info",
137 ),
138 metadata: bool = typer.Option(
139 False,
140 "--metadata",
141 help=(
142 "Output a JSON string listing all available licenses and "
143 "languages This allows easy integration into other tools."
144 ),
145 ),
146) -> None:
147 """Generate a license file.
149 Can generate a license file, a source file header, or list available
150 licenses, template variables, and source code formatting.
151 """
152 # deal with the '--version' flag first
153 if version:
154 rprint(
155 "\n[green]Lice2 - Generate license files for your projects."
156 f"\n[/green]Version: {__version__} "
157 "\u00a9 2013-2024\n"
158 )
159 raise typer.Exit(0)
161 # get the args into a dict to avoid refactoring all the code...
162 args_base: dict[str, str | bool | None] = {
163 "license": license_name,
164 "header": header,
165 "organization": organization,
166 "project": project,
167 "template_path": template_path,
168 "year": year,
169 "language": language,
170 "ofile": ofile,
171 "clipboard": clipboard or settings.clipboard,
172 "legacy": legacy or settings.legacy,
173 "list_vars": show_vars,
174 "list_licenses": show_licenses,
175 "list_languages": show_languages,
176 }
177 # convert to SimpleNamespace, so we can use dot notation
178 args = SimpleNamespace(**args_base)
180 # get the language if set
181 lang = get_lang(args)
183 actions: list[tuple[bool, Callable[..., None], list[Any]]] = [
184 (metadata, get_metadata, [args]),
185 (args.list_licenses, list_licenses, []),
186 (args.list_languages, list_languages, []),
187 (header, generate_header, [args, lang]),
188 (args.list_vars, list_vars, [args, license_name]),
189 ]
191 # Iterate through the list and call the utility functions based on the
192 # conditions. All the utility functions exit the program after execution.
193 # This saves us from having to write a lot of if-else statements.
194 for condition, func, func_args in actions:
195 if condition:
196 func(*func_args)
198 # create context
199 if args.template_path:
200 template = load_file_template(args.template_path)
201 else:
202 template = load_package_template(license_name)
204 content = generate_license(template, get_context(args))
206 if args.ofile != "stdout":
207 ext = get_suffix(args.ofile)
208 if ext:
209 output = args.ofile
210 out = format_license(
211 content, ext, legacy=args.legacy
212 ) # format license by file suffix
213 else:
214 output = f"{args.ofile}.{lang}" if lang else args.ofile
215 out = format_license(content, lang, legacy=args.legacy)
217 out.seek(0)
218 with Path(output).open(mode="w") as f:
219 f.write(out.getvalue())
220 else:
221 out = format_license(content, lang, legacy=args.legacy)
222 out.seek(0)
223 if not args.clipboard:
224 sys.stdout.write(out.getvalue())
225 else:
226 copy_to_clipboard(out)
228 out.close()
231if __name__ == "__main__":
232 app() # pragma: no cover