|
| 1 | +Usage Examples of log21 |
| 2 | +======================= |
| 3 | + |
| 4 | +### Basic Logging |
| 5 | + |
| 6 | +```python |
| 7 | +import log21 |
| 8 | + |
| 9 | +log21.print(log21.get_color('#FF0000') + 'This' + log21.get_color((0, 255, 0)) + ' is' + log21.get_color('Blue') + |
| 10 | + ' Blue' + log21.get_colors('BackgroundWhite', 'Black') + ' 8)') |
| 11 | + |
| 12 | +logger = log21.get_logger('My Logger', level_names={21: 'SpecialInfo', log21.WARNING: ' ! ', log21.ERROR: '!!!'}) |
| 13 | +logger.info('You are reading the README.md file...') |
| 14 | + |
| 15 | +logger.log(21, 'Here', '%s', 'GO!', args=('we',)) |
| 16 | + |
| 17 | +logger.setLevel(log21.WARNING) |
| 18 | +logger.warning("We can't log messages with a level less than 30 anymore!") |
| 19 | + |
| 20 | +logger.debug("You won't see this!") |
| 21 | +logger.info("Am I visible?") |
| 22 | + |
| 23 | +logger.error(log21.get_colors('LightRed') + "I'm still here ;1") |
| 24 | +``` |
| 25 | + |
| 26 | + |
| 27 | + |
| 28 | +---------------- |
| 29 | + |
| 30 | +### Argument Parsing (See Also: [Argumentify](https://github.com/MPCodeWriter21/log21#argumentify-check-out-the-manual-way)) |
| 31 | + |
| 32 | +```python |
| 33 | +import log21 |
| 34 | +from log21 import ColorizingArgumentParser, get_logger, get_colors as gc |
| 35 | + |
| 36 | +parser = ColorizingArgumentParser(description="This is a simple example of a ColorizingArgumentParser.", |
| 37 | + colors={'help': 'LightCyan'}) |
| 38 | +parser.add_argument('test1', action='store', help='Test 1') |
| 39 | +parser.add_argument('test2', action='store', help='Test 2') |
| 40 | +parser.add_argument('--optional-arg', '-o', action='store', type=int, help='An optional integer') |
| 41 | +parser.add_argument('--verbose', '-v', action='store_true', help='Increase verbosity.') |
| 42 | + |
| 43 | +args = parser.parse_args() |
| 44 | + |
| 45 | +logger = get_logger('My Logger', level_names={log21.DEBUG: ' ? ', log21.INFO: ' + ', log21.WARNING: ' ! ', |
| 46 | + log21.ERROR: '!!!'}) |
| 47 | + |
| 48 | +if args.verbose: |
| 49 | + logger.setLevel(log21.DEBUG) |
| 50 | +else: |
| 51 | + logger.setLevel(log21.INFO) |
| 52 | + |
| 53 | +logger.debug(gc('LightBlue') + 'Verbose mode on!') |
| 54 | + |
| 55 | +logger.debug('Arguments:\n' |
| 56 | + '\tTest 1: %s\n' |
| 57 | + '\tTest 2: %s\n' |
| 58 | + '\tOptional: %s', args=(args.test1, args.test2, args.optional_arg)) |
| 59 | + |
| 60 | +logger.info(gc('LightGreen') + args.test1) |
| 61 | + |
| 62 | +logger.info(gc('LightWhite') + 'Done!') |
| 63 | + |
| 64 | +``` |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | +------------------ |
| 77 | + |
| 78 | +### Pretty-Printing and Tree-Printing |
| 79 | + |
| 80 | +```python |
| 81 | +import json |
| 82 | +import log21 |
| 83 | + |
| 84 | +data = json.load(open('json.json', 'r')) |
| 85 | + |
| 86 | +# Prints data using python's built-in print function |
| 87 | +print(data) |
| 88 | + |
| 89 | +# Uses `log21.pprint` to print the data |
| 90 | +log21.pprint(data) |
| 91 | + |
| 92 | +# Uses `log21.tree_print` to print the data |
| 93 | +log21.tree_print(data) |
| 94 | +``` |
| 95 | + |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | + |
| 101 | +------------------ |
| 102 | + |
| 103 | +### Logging Window |
| 104 | + |
| 105 | +```python |
| 106 | +import log21 |
| 107 | + |
| 108 | +window = log21.get_logging_window('My Logging Window', width=80) |
| 109 | +window.font = ('Courier New', 9) |
| 110 | + |
| 111 | +# Basic logging |
| 112 | +window.info('This is a basic logging message.') |
| 113 | + |
| 114 | +# Using ANSI and HEX colors |
| 115 | +# List of ANSI colors: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors |
| 116 | +# ANSI color format: \033[<attribute>m |
| 117 | +window.info('\033[91mThis is RED message.') |
| 118 | +window.info('\033[102mThis is message with GREEN background.') |
| 119 | +# HEX color format: \033#<HEX-COLOR>hf (where f represents the foreground color) and |
| 120 | +# \033#<HEX-COLOR>hb (where b represents the background color) |
| 121 | +window.info('\x1b#009900hbThis is a text with GREEN background.') |
| 122 | +window.info('\033#0000FFhf\033[103mThis is message with BLUE foreground and YELLOW background.') |
| 123 | + |
| 124 | +import random, string |
| 125 | + |
| 126 | +# And here is a text with random colors |
| 127 | +text = 'I have random colors XD' |
| 128 | +colored_text = '' |
| 129 | +for character in text: |
| 130 | + color = '\033#' + ''.join(random.choice(string.hexdigits) for _ in range(6)) + 'hf' |
| 131 | + colored_text += color + character |
| 132 | + |
| 133 | +window.error(colored_text) |
| 134 | + |
| 135 | +# See more examples in |
| 136 | +# https://github.com/MPCodeWriter21/log21/blob/066efc1e72542531012d36974bbf6cd4c5941378/log21/LoggingWindow.py#L155 |
| 137 | +# and |
| 138 | +# https://github.com/MPCodeWriter21/log21/blob/066efc1e72542531012d36974bbf6cd4c5941378/log21/__init__.py#L144 |
| 139 | + |
| 140 | +``` |
| 141 | + |
| 142 | + |
| 143 | + |
| 144 | +------------------ |
| 145 | + |
| 146 | +### ProgressBar |
| 147 | + |
| 148 | +```python |
| 149 | +# Example 1 |
| 150 | +import log21, time |
| 151 | + |
| 152 | +# Define a very simple log21 progress bar |
| 153 | +progress_bar = log21.ProgressBar() |
| 154 | + |
| 155 | +# And here is a simple loop that will print the progress bar |
| 156 | +for i in range(100): |
| 157 | + progress_bar(i + 1, 100) |
| 158 | + time.sleep(0.08) |
| 159 | + |
| 160 | +# Example 2 |
| 161 | +import time, random |
| 162 | +from log21 import ProgressBar, get_colors as gc |
| 163 | + |
| 164 | +# Let's customize the progress bar a little bit this time |
| 165 | +progress_bar = ProgressBar( |
| 166 | + width=50, |
| 167 | + fill='#', |
| 168 | + empty='-', |
| 169 | + prefix='[', |
| 170 | + suffix=']', |
| 171 | + colors={'progress in-progress': gc('Bright Red'), 'progress complete': gc('Bright Cyan'), |
| 172 | + 'percentage in-progress': gc('Green'), 'percentage complete': gc('Bright Cyan'), |
| 173 | + 'prefix-color in-progress': gc('Bright White'), 'prefix-color complete': gc('Bright White'), |
| 174 | + 'prefix-color failed': gc('Bright White'), 'suffix-color in-progress': gc('Bright White'), |
| 175 | + 'suffix-color complete': gc('Bright White'), 'suffix-color failed': gc('Bright White')}) |
| 176 | + |
| 177 | +for i in range(84): |
| 178 | + progress_bar(i + 1, 84) |
| 179 | + time.sleep(random.uniform(0.05, 0.21)) |
| 180 | + |
| 181 | +``` |
| 182 | + |
| 183 | + |
| 184 | + |
| 185 | + |
| 186 | +------------------ |
| 187 | + |
| 188 | +### Argumentify (Check out [the manual way](https://github.com/MPCodeWriter21/log21#argument-parsing-see-also-argumentify)) |
| 189 | + |
| 190 | +```python |
| 191 | +# Common Section |
| 192 | +import log21 |
| 193 | + |
| 194 | + |
| 195 | +class ReversedText: |
| 196 | + def __init__(self, text: str): |
| 197 | + self._text = text[::-1] |
| 198 | + |
| 199 | + def __str__(self): |
| 200 | + return self._text |
| 201 | + |
| 202 | + def __repr__(self): |
| 203 | + return f"<{self.__class__.__name__}(text='{self._text}') at {hex(id(self))}>" |
| 204 | + |
| 205 | + |
| 206 | +# Old way |
| 207 | +def main(): |
| 208 | + """Here is my main function""" |
| 209 | + parser = log21.ColorizingArgumentParser() |
| 210 | + parser.add_argument('--positional-arg', '-p', action='store', type=int, |
| 211 | + required=True, help="This argument is positional!") |
| 212 | + parser.add_argument('--optional-arg', '-o', action='store', type=ReversedText, |
| 213 | + help="Whatever you pass here will be REVERSED!") |
| 214 | + parser.add_argument('--arg-with-default', '-a', action='store', default=21, |
| 215 | + help="The default value is 21") |
| 216 | + parser.add_argument('--additional-arg', '-A', action='store', |
| 217 | + help="This one is extra.") |
| 218 | + parser.add_argument('--verbose', '-v', action='store_true', |
| 219 | + help="Increase verbosity") |
| 220 | + args = parser.parse_args() |
| 221 | + |
| 222 | + if args.verbose: |
| 223 | + log21.basic_config(level='DEBUG') |
| 224 | + |
| 225 | + log21.info(f"positional_arg = {args.positional_arg}") |
| 226 | + log21.info(f"optional_arg = {args.optional_arg}") |
| 227 | + log21.debug(f"arg_with_default = {args.arg_with_default}") |
| 228 | + log21.debug(f"additional_arg = {args.additional_arg}") |
| 229 | + |
| 230 | + |
| 231 | +if __name__ == '__main__': |
| 232 | + main() |
| 233 | + |
| 234 | + |
| 235 | +# New way |
| 236 | +def main(positional_arg: int, /, optional_arg: ReversedText, arg_with_default: int = 21, |
| 237 | + additional_arg=None, verbose: bool = False): |
| 238 | + """Some description |
| 239 | +
|
| 240 | + :param positional_arg: This argument is positional! |
| 241 | + :param optional_arg: Whatever you pass here will be REVERSED! |
| 242 | + :param arg_with_default: The default value is 21 |
| 243 | + :param additional_arg: This one is extra. |
| 244 | + :param verbose: Increase verbosity |
| 245 | + """ |
| 246 | + if verbose: |
| 247 | + log21.basic_config(level='DEBUG') |
| 248 | + |
| 249 | + log21.info(f"{positional_arg = }") |
| 250 | + log21.info(f"{optional_arg = !s}") |
| 251 | + log21.debug(f"{arg_with_default = }") |
| 252 | + log21.debug(f"{additional_arg = !s}") |
| 253 | + |
| 254 | + |
| 255 | +if __name__ == '__main__': |
| 256 | + log21.argumentify(main) |
| 257 | +``` |
| 258 | + |
| 259 | + |
| 260 | + |
| 261 | + |
| 262 | +Example with multiple functions as entry-point: |
| 263 | + |
| 264 | +```python |
| 265 | +import ast |
| 266 | +import operator |
| 267 | +from functools import reduce |
| 268 | + |
| 269 | +import log21 |
| 270 | + |
| 271 | +# `safe_eval` Based on https://stackoverflow.com/a/9558001/1113207 |
| 272 | +# Supported Operators |
| 273 | +operators = { |
| 274 | + ast.Add: operator.add, |
| 275 | + ast.Sub: operator.sub, |
| 276 | + ast.Mult: operator.mul, |
| 277 | + ast.Div: operator.truediv, |
| 278 | + ast.FloorDiv: operator.floordiv, |
| 279 | + ast.Pow: operator.pow, |
| 280 | + ast.BitXor: operator.xor, |
| 281 | + ast.USub: operator.neg |
| 282 | +} |
| 283 | + |
| 284 | + |
| 285 | +def safe_eval(expr: str): |
| 286 | + """Safely evaluate a mathematical expression. |
| 287 | +
|
| 288 | + >>> eval_expr('2^6') |
| 289 | + 4 |
| 290 | + >>> eval_expr('2**6') |
| 291 | + 64 |
| 292 | + >>> eval_expr('1 + 2*3**(4^5) / (6 + -7)') |
| 293 | + -5.0 |
| 294 | +
|
| 295 | + :param expr: expression to evaluate |
| 296 | + :raises SyntaxError: on invalid expression |
| 297 | + :return: result of the evaluation |
| 298 | + """ |
| 299 | + try: |
| 300 | + return _eval(ast.parse(expr, mode='eval').body) |
| 301 | + except (TypeError, KeyError, SyntaxError): |
| 302 | + log21.error(f'Invalid expression: {expr}') |
| 303 | + raise |
| 304 | + |
| 305 | + |
| 306 | +def _eval(node: ast.AST): |
| 307 | + """Internal implementation of `safe_eval`. |
| 308 | +
|
| 309 | + :param node: AST node to evaluate |
| 310 | + :raises TypeError: on invalid node |
| 311 | + :raises KeyError: on invalid operator |
| 312 | + :raises ZeroDivisionError: on division by zero |
| 313 | + :raises ValueError: on invalid literal |
| 314 | + :raises SyntaxError: on invalid syntax |
| 315 | + :return: result of the evaluation |
| 316 | + """ |
| 317 | + if isinstance(node, ast.Num): # <number> |
| 318 | + return node.n |
| 319 | + if isinstance(node, ast.BinOp): # <left> <operator> <right> |
| 320 | + return operators[type(node.op)](_eval(node.left), _eval(node.right)) |
| 321 | + if isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1 |
| 322 | + return operators[type(node.op)](_eval(node.operand)) |
| 323 | + raise TypeError(node) |
| 324 | + |
| 325 | + |
| 326 | +# Example code |
| 327 | +def addition(*numbers: float): |
| 328 | + """Addition of numbers. |
| 329 | +
|
| 330 | + Args: |
| 331 | + numbers (float): numbers to add |
| 332 | + """ |
| 333 | + if len(numbers) < 2: |
| 334 | + log21.error('At least two numbers are required! Use `-n`.') |
| 335 | + return |
| 336 | + log21.info(f'Result: {sum(numbers)}') |
| 337 | + |
| 338 | + |
| 339 | +def multiplication(*numbers: float): |
| 340 | + """Multiplication of numbers. |
| 341 | +
|
| 342 | + Args: |
| 343 | + numbers (float): numbers to multiply |
| 344 | + """ |
| 345 | + if len(numbers) < 2: |
| 346 | + log21.error('At least two numbers are required! Use `-n`.') |
| 347 | + return |
| 348 | + log21.info(f'Result: {reduce(lambda x, y: x * y, numbers)}') |
| 349 | + |
| 350 | + |
| 351 | +def calc(*inputs: str, verbose: bool = False): |
| 352 | + """Calculate numbers. |
| 353 | +
|
| 354 | + :param inputs: numbers and operators |
| 355 | + """ |
| 356 | + expression = ' '.join(inputs) |
| 357 | + |
| 358 | + if len(expression) < 3: |
| 359 | + log21.error('At least two numbers and one operator are required! Use `-i`.') |
| 360 | + return |
| 361 | + |
| 362 | + if verbose: |
| 363 | + log21.basic_config(level='DEBUG') |
| 364 | + |
| 365 | + log21.debug(f'Expression: {expression}') |
| 366 | + try: |
| 367 | + log21.info(f'Result: {safe_eval(expression)}') |
| 368 | + except (TypeError, KeyError, SyntaxError): |
| 369 | + pass |
| 370 | + |
| 371 | + |
| 372 | +if __name__ == "__main__": |
| 373 | + log21.argumentify({'add': addition, 'mul': multiplication, 'calc': calc}) |
| 374 | +``` |
| 375 | + |
| 376 | + |
| 377 | + |
| 378 | + |
0 commit comments