Coverage for hookee/util.py: 93.85%

1 import inspect

2 import json

3 import logging

4 import os

5 import sys

6

7 import click

8

9 __author__ = "Alex Laird"

10 __copyright__ = "Copyright 2023, Alex Laird"

11 __version__ = "2.0.8"

12

13 from defusedxml.minidom import parseString

14

15 logger = logging.getLogger(__name__)

16

17

18 class PrintUtil:

19 """

20 An object that provides helper methods for logging output. If :class:`~hookee.conf.Config`'s ``click_logging`` is

21 ``True`` (which will happen by default if a :class:`click.Context` is found to be active), this logging will be

22 done through ``click``, otherwise the ``hookee`` logger will be used.

23

24 If ``click_logging`` is disabled, output sent through this utility can still be interacted with by ensuring the a

25 logger is setup. For example, this would add a handler to the ``hookee`` logger that just logs output back to

26 the console:

27

28 .. code-block:: python

29

30 import logging

31

32 logger = logging.getLogger("hookee")

33 logger.setLevel(logging.INFO)

34 logging.getLogger().addHandler(logging.StreamHandler())

35

36 :var config: The ``hookee`` configuration.

37 :vartype config: Config

38 """

39

40 def __init__(self, config):

41 self.config = config

42

43 @property

44 def console_width(self):

45 return self.config.get("console_width")

46

47 @property

48 def header_color(self):

49 return self.config.get("header_color")

50

51 @property

52 def default_color(self):

53 return self.config.get("default_color")

54

55 @property

56 def request_color(self):

57 return self.config.get("request_color")

58

59 def print_config_update(self, msg):

60 self.print_basic("\n--> {}\n".format(msg), color=self.header_color)

61

62 def print_open_header(self, title, delimiter="-", color=None):

63 """

64 Log an opening header with a title and a new line before and after.

65

66 :param title: The header title.

67 :type title: str

68 :param delimiter: The title of the XML blob.

69 :type delimiter: str, optional

70 :param color: The color to make the text.

71 :type color: str, optional

72 """

73 if color is None:

74 color = self.header_color

75

76 width = int((self.console_width - len(title)) / 2)

77

78 self.print_basic()

79 self.print_basic("{}{}{}".format(delimiter * width, title, delimiter * width), color=color, bold=True)

80 self.print_basic()

81

82 def print_close_header(self, delimiter="-", color=None, blank_line=True):

83 """

84 Log a closing header with an optional new line before.

85

86 :param delimiter: The title of the XML blob.

87 :type delimiter: str

88 :param color: The color to make the text.

89 :type color: str, optional

90 :param blank_line: ``True`` if a blank line should precede the closing header.

91 :type blank_line: bool

92 """

93 if color is None:

94 color = self.header_color

95

96 if blank_line:

97 self.print_basic()

98 self.print_basic(delimiter * self.console_width, color=color, bold=True)

99

100 def print_dict(self, title, data, color=None):

101 """

102 Log formatted dictionary data.

103

104 :param title: The title of the XML blob.

105 :type title: str

106 :param data: A dictionary.

107 :type data: dict

108 :param color: The color to make the text.

109 :type color: str, optional

110 """

111 if color is None:

112 color = self.default_color

113

114 self.print_basic("{}: {}".format(title, json.dumps(data, indent=4)), color=color)

115

116 def print_xml(self, title, data, color=None):

117 """

118 Log formatted XML.

119

120 :param title: The title of the XML blob.

121 :type title: str

122 :param data: An XML string.

123 :type data: str

124 :param color: The color to make the text.

125 :type color: str, optional

126 """

127 if color is None:

128 color = self.default_color

129

130 self.print_basic("{}: {}".format(title, parseString(data).toprettyxml()), color=color)

131

132 def print_basic(self, msg="", color=None, bold=False, print_when_logging=False):

133 """

134 Log a basic message. The message will be logged via ``click``, if ``click_logging`` is enabled in

135 :class:`~hookee.conf.Config`, or appended to the logger.

136

137 :param msg: The update to print.

138 :type msg: str, optional

139 :param color: The color to make the text.

140 :type color: str, optional

141 :param bold: ``True`` if the output should be bold.

142 :type bold: bool, optional

143 :param print_when_logging: ``True`` if, when ``click_logging`` is ``False``, ``msg`` should print to the

144 console instead of appending to the logger.

145 :type print_when_logging: bool, optional

146 """

147 if color is None:

148 color = self.default_color

149

150 if self.config.click_logging:

151 click.secho(msg, fg=color, bold=bold)

152 elif not print_when_logging:

153 logger.info(msg)

154 else:

155 print(msg)

156

157

158 def python36_gte():

159 """

160 Check if running on a Python 3.6 or higher interpreter.

161

162 :return: ``True`` if Python 3.6 or higher.

163 :rtype: bool

164 """

165 return sys.version_info >= (3, 6)

166

167

168 def get_functions(mod):

169 """

170 Get a list of functions for the given module.

171

172 :param mod: The module to inspect for functions.

173 :type mod: types.ModuleType

174 :return: The list of functions.

175 :rtype: list[types.FunctionType]

176 """

177 return [o[0] for o in inspect.getmembers(mod, inspect.isfunction)]

178

179

180 def get_args(func):

181 """

182 Get a list of args for the given function.

183

184 :param func: The function to inspect for args.

185 :type func: types.FunctionType

186 :return: The list of args.

187 :rtype: list[str]

188 """

189 return inspect.getfullargspec(func)[0]

190

191

192 def get_module_name(module):

193 """

194 Get the name of the module from the basename of its path.

195

196 :param module: The module.

197 :type module: types.ModuleType

198 :return: The base name of the module.

199 :rtype: str

200 """

201 return os.path.splitext(os.path.basename(module.__file__))[0]