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
« 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.
7:Platform: Linux/Windows | Python 3.10+
8:Developer: J Berendt
9:Email: development@s3dev.uk
11:Comments: n/a
13"""
14# pylint: disable=line-too-long
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__
27class ArgParser:
28 """Project command line argument parser."""
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.'
50 def __init__(self):
51 """Project argument parser class initialiser."""
52 self._args = None
53 self._epil = f'{self._proj} v{self._vers}'
55 @property
56 def args(self):
57 """Accessor to parsed arguments."""
58 return self._args
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)
96 def _set_logger(self):
97 """Set the debugging level based on the CLI argument.
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).
103 :Levels:
104 - 10: DEBUG
105 - 20: INFO
106 - 30: WARNING
107 - 40: ERROR
108 - 50: CRITICAL
110 """
111 level = logging.DEBUG if self._args.debug else logging.WARNING
112 logging.basicConfig(level=level, format="[%(levelname)s]: %(message)s")
114 def _verify_args(self) -> None:
115 """Verify the provided arguments are valid.
117 If no themes have been selected, the default theme is set to
118 dark.
120 Raises:
121 FileNotFoundError: If any provided PATH does not exist.
122 ValueError: If any argument validation fails.
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)
137# Make the arg parser accessible as an import.
138argparser = ArgParser()