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

67 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-05 11:05 +0000

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 

16import argparse 

17import logging 

18import os 

19import sys 

20# locals 

21try: 

22 from ._version import __version__ 

23except ImportError: 

24 from ghmdlib.libs._version import __version__ 

25 

26 

27class 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. 

138argparser = ArgParser()