Coverage for /var/devmt/py/ghmdlib_0.1.0/ghmdlib/libs/argparser.py: 45%

1#!/usr/bin/env python3

2# -*- coding: utf-8 -*-

3"""

4:Purpose: This module provides command line argument parsing

5 functionality.

6

7:Platform: Linux/Windows | Python 3.10+

8:Developer: J Berendt

9:Email: development@s3dev.uk

10

11:Comments: n/a

12

13"""

14# pylint: disable=line-too-long

15

16 import argparse

17 import logging

18 import os

19 import sys

20# locals

21 try:

22 from ._version import __version__

23 except ImportError:

24 from ghmdlib.libs._version import __version__

25

26

27 class ArgParser:

28 """Project command line argument parser."""

29

30 # Program usage and help strings.

31 _proj = 'ghmdlib'

32 _desc = 'A Markdown to GitHub-style HTML file conversion utility.'

33 _vers = __version__

34 _h_path = 'Path(s) to the Markdown files to be converted.'

35 _h_dark = 'Use a dark HTML theme. (Default)'

36 _h_lght = 'Use a light HTML theme.\n\n'

37 # Newlines added to provide visual separation.

38 _h_down = 'Download the latest CSS files from GitHub. These files are used for offline mode.'

39 _h_embd = 'Embed the CSS into the HTML file instead of using the <link> tag.'

40 _h_ngfm = 'Use plain Markdown mode instead of GitHub Flavored Markdown (gfm).'

41 _h_ofln = ('Keep offline.\n'

42 '- Uses cached CSS files and local libraries for the HTML conversion\n'

43 ' rather than the GitHub API.')

44 _h_prev= 'Auto-open each converted HTML file in a web browser for viewing.\n\n'

45 # Newlines added to provide visual separation.

46 _h_dbug = 'Display debugging output to the terminal.'

47 _h_help = 'Display this help and usage, then exit.'

48 _h_vers = 'Display the version, then exit.'

49

50 def __init__(self):

51 """Project argument parser class initialiser."""

52 self._args = None

53 self._epil = f'{self._proj} v{self._vers}'

54

55 @property

56 def args(self):

57 """Accessor to parsed arguments."""

58 return self._args

59

60 def parse(self):

61 """Parse command line arguments."""

62 argp = argparse.ArgumentParser(prog=self._proj,

63 description=self._desc,

64 epilog=self._epil,

65 formatter_class=argparse.RawTextHelpFormatter,

66 add_help=False)

67 # Order matters here as it affects the display -->

68 # If the download utility is called, the PATH is not required.

69 if '--download' not in sys.argv:

70 argp.add_argument('PATH', help=self._h_path, nargs='+')

71 # Themes

72 argp.add_argument('--dark', help=self._h_dark, action='store_true')

73 argp.add_argument('--light', help=self._h_lght, action='store_true')

74 # Options

75 argp.add_argument('--download', help=self._h_down, action='store_true')

76 argp.add_argument('--embed-css', help=self._h_embd, action='store_true')

77 argp.add_argument('--no-gfm', help=self._h_ngfm, action='store_true')

78 argp.add_argument('--offline', help=self._h_ofln, action='store_true')

79 argp.add_argument('--preview', help=self._h_prev, action='store_true')

80 # Other

81 argp.add_argument('-d', '--debug', help=self._h_dbug, action='store_true')

82 argp.add_argument('-h', '--help', help=self._h_help, action='help')

83 argp.add_argument('-V', '--version', help=self._h_vers, action='version', version=self._epil)

84 self._args = argp.parse_args()

85 self._set_logger()

86 try:

87 self._verify_args()

88 logging.debug('CLI arguments: %s', self._args)

89 except ValueError as err:

90 argp.print_help()

91 print('')

92 logging.critical(err)

93 print('')

94 sys.exit(1)

95

96 def _set_logger(self):

97 """Set the debugging level based on the CLI argument.

98

99 The default logging level is set using the ``project.log_level``

100 key in ``config.toml``. However, if the ``--debug`` argument is

101 passed, the log level is set to 10 (DEBUG).

102

103 :Levels:

104 - 10: DEBUG

105 - 20: INFO

106 - 30: WARNING

107 - 40: ERROR

108 - 50: CRITICAL

109

110 """

111 level = logging.DEBUG if self._args.debug else logging.WARNING

112 logging.basicConfig(level=level, format="[%(levelname)s]: %(message)s")

113

114 def _verify_args(self) -> None:

115 """Verify the provided arguments are valid.

116

117 If no themes have been selected, the default theme is set to

118 dark.

119

120 Raises:

121 FileNotFoundError: If any provided PATH does not exist.

122 ValueError: If any argument validation fails.

123

124 """

125 if not self.args.light:

126 self.args.dark = True

127 if hasattr(self.args, 'PATH'): # Only required if not --download

128 for file in self.args.PATH:

129 if not os.path.exists(file):

130 msg = f'The following file cannot be found: {file}'

131 raise FileNotFoundError(msg)

132 if not os.path.splitext(file)[1].lower() == '.md':

133 msg = 'All files are expected to have a Markdown (.md or .MD) file extension.'

134 raise ValueError(msg)

135

136

137# Make the arg parser accessible as an import.

138 argparser = ArgParser()