Coverage for lice2/core.py: 100%

46 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-13 11:13 +0000

1"""Main core of the application.""" 

2 

3from __future__ import annotations 

4 

5import sys 

6from pathlib import Path 

7from types import SimpleNamespace 

8from typing import Any, Callable, Optional 

9 

10import typer 

11from rich import print as rprint 

12from rich.markup import escape 

13 

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) 

36 

37app = typer.Typer(rich_markup_mode="rich") 

38 

39 

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. 

148 

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) 

160 

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) 

179 

180 # get the language if set 

181 lang = get_lang(args) 

182 

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 ] 

190 

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) 

197 

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) 

203 

204 content = generate_license(template, get_context(args)) 

205 

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) 

216 

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) 

227 

228 out.close() 

229 

230 

231if __name__ == "__main__": 

232 app() # pragma: no cover