Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/turbohtml/__main__.py: 66%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

74 statements  

1""" 

2turbohtml.__main__: a thin ``python -m turbohtml`` front end over the public API. 

3 

4Every subcommand parses argv, reads one input (a file argument or stdin), calls the matching public function, and 

5writes the result (stdout or ``-o``); the transforms themselves live in the C core, so this module only wires argv to 

6them. It gives the toolkit the command-line surface ``html2text``, ``markdownify``, ``htmlmin``, ``inscriptis``, 

7``minify-html``, ``charset-normalizer``, and ``courlan`` each ship and turbohtml lacked. 

8""" 

9 

10from __future__ import annotations 

11 

12import argparse 

13import sys 

14from pathlib import Path 

15from typing import TYPE_CHECKING 

16 

17from . import parse 

18from ._html import Minify 

19from .clean import CSSMinify, minify, minify_css, minify_js, sanitize 

20from .detect import detect 

21 

22if TYPE_CHECKING: 

23 from collections.abc import Sequence 

24 

25__all__ = ["main"] 

26 

27 

28def main(argv: Sequence[str] | None = None) -> int: 

29 """ 

30 Run the ``python -m turbohtml`` command line. 

31 

32 :param argv: the argument vector, excluding the program name; ``None`` reads ``sys.argv``. 

33 :returns: the process exit code -- 0 on success, 1 when the library rejects the input or the file cannot be read. 

34 """ 

35 args = _parser().parse_args(argv) 

36 try: 

37 return args.handler(args) 

38 except (OSError, ValueError, LookupError) as error: 

39 sys.stderr.write(f"turbohtml {args.command}: {error}\n") 

40 return 1 

41 

42 

43def _parser() -> argparse.ArgumentParser: 

44 """Build the argument parser: one subcommand per public entry point, each sharing the input/output arguments.""" 

45 parser = argparse.ArgumentParser(prog="python -m turbohtml", description="Command-line front end for turbohtml.") 

46 shared = argparse.ArgumentParser(add_help=False) 

47 shared.add_argument("file", nargs="?", help="input file; omit or pass - to read stdin") 

48 shared.add_argument("-o", "--output", metavar="FILE", help="write to this file instead of stdout") 

49 commands = parser.add_subparsers(dest="command", required=True) 

50 

51 minify_parser = commands.add_parser("minify", parents=[shared], help="minify an HTML document") 

52 minify_parser.add_argument( 

53 "--minify-css", action="store_true", help="also minify <style> bodies and style attributes" 

54 ) 

55 minify_parser.set_defaults(handler=_run_minify) 

56 

57 commands.add_parser("minify-css", parents=[shared], help="minify a CSS stylesheet").set_defaults( 

58 handler=_run_minify_css 

59 ) 

60 commands.add_parser("minify-js", parents=[shared], help="minify a JavaScript source").set_defaults( 

61 handler=_run_minify_js 

62 ) 

63 commands.add_parser("detect", parents=[shared], help="report the character encoding of bytes").set_defaults( 

64 handler=_run_detect 

65 ) 

66 commands.add_parser("to-markdown", parents=[shared], help="render HTML as Markdown").set_defaults( 

67 handler=_run_to_markdown 

68 ) 

69 commands.add_parser("to-text", parents=[shared], help="render HTML as layout-aware plain text").set_defaults( 

70 handler=_run_to_text 

71 ) 

72 commands.add_parser("sanitize", parents=[shared], help="sanitize HTML against the default policy").set_defaults( 

73 handler=_run_sanitize 

74 ) 

75 return parser 

76 

77 

78def _run_minify(args: argparse.Namespace) -> int: 

79 _write(minify(_read_text(args.file), Minify(minify_css=CSSMinify() if args.minify_css else None)), args.output) 

80 return 0 

81 

82 

83def _run_minify_css(args: argparse.Namespace) -> int: 

84 _write(minify_css(_read_text(args.file)), args.output) 

85 return 0 

86 

87 

88def _run_minify_js(args: argparse.Namespace) -> int: 

89 _write(minify_js(_read_text(args.file)), args.output) 

90 return 0 

91 

92 

93def _run_detect(args: argparse.Namespace) -> int: 

94 if (match := detect(_read_bytes(args.file))).encoding is None: 

95 sys.stderr.write("turbohtml detect: no encoding detected\n") 

96 return 1 

97 _write(f"{match.encoding}\n", args.output) 

98 return 0 

99 

100 

101def _run_to_markdown(args: argparse.Namespace) -> int: 

102 _write(parse(_read_text(args.file)).to_markdown(), args.output) 

103 return 0 

104 

105 

106def _run_to_text(args: argparse.Namespace) -> int: 

107 _write(parse(_read_text(args.file)).to_text(), args.output) 

108 return 0 

109 

110 

111def _run_sanitize(args: argparse.Namespace) -> int: 

112 _write(sanitize(_read_text(args.file)), args.output) 

113 return 0 

114 

115 

116def _read_text(path: str | None) -> str: 

117 """Read text input from ``path``, or stdin when it is ``None`` or ``-``.""" 

118 if path is None or path == "-": 

119 return sys.stdin.read() 

120 return Path(path).read_text(encoding="utf-8") 

121 

122 

123def _read_bytes(path: str | None) -> bytes: 

124 """Read binary input from ``path``, or stdin when it is ``None`` or ``-``.""" 

125 if path is None or path == "-": 

126 return sys.stdin.buffer.read() 

127 return Path(path).read_bytes() 

128 

129 

130def _write(text: str, output: str | None) -> None: 

131 """Write ``text`` to ``output``, or stdout when it is ``None`` or ``-``.""" 

132 if output is None or output == "-": 

133 sys.stdout.write(text) 

134 else: 

135 Path(output).write_text(text, encoding="utf-8") 

136 

137 

138if __name__ == "__main__": 

139 raise SystemExit(main())