log21
Advanced tools
+0
-0
@@ -0,0 +0,0 @@ Apache License |
+5
-382
| Metadata-Version: 2.1 | ||
| Name: log21 | ||
| Version: 2.8.1 | ||
| Version: 2.9.0 | ||
| Summary: A simple logging package that helps you log colorized messages in Windows console. | ||
@@ -95,10 +95,7 @@ Author-email: "CodeWriter21(Mehrad Pooryoussof)" <CodeWriter21@gmail.com> | ||
| ### 2.8.1 | ||
| ### 2.9.0 | ||
| + Fixed Carriage Return Handling. | ||
| + Fixed setting level using `log21.basic_config` | ||
| + Added more configuration for developer tools to the `pyproject.toml` file. | ||
| + Added pre-commit. | ||
| + Added `<<` and `>>` (left shift and right shift operators) to `log21.Logger.Logger`. | ||
| [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md) | ||
| [Full CHANGELOG](CHANGELOG.md) | ||
@@ -109,377 +106,5 @@ | ||
| ### Basic Logging | ||
| See [Examples.md](EXAMPLES.md) | ||
| ```python | ||
| import log21 | ||
| log21.print(log21.get_color('#FF0000') + 'This' + log21.get_color((0, 255, 0)) + ' is' + log21.get_color('Blue') + | ||
| ' Blue' + log21.get_colors('BackgroundWhite', 'Black') + ' 8)') | ||
| logger = log21.get_logger('My Logger', level_names={21: 'SpecialInfo', log21.WARNING: ' ! ', log21.ERROR: '!!!'}) | ||
| logger.info('You are reading the README.md file...') | ||
| logger.log(21, 'Here', '%s', 'GO!', args=('we',)) | ||
| logger.setLevel(log21.WARNING) | ||
| logger.warning("We can't log messages with a level less than 30 anymore!") | ||
| logger.debug("You won't see this!") | ||
| logger.info("Am I visible?") | ||
| logger.error(log21.get_colors('LightRed') + "I'm still here ;1") | ||
| ``` | ||
|  | ||
| ---------------- | ||
| ### Argument Parsing (See Also: [Argumentify](https://github.com/MPCodeWriter21/log21#argumentify-check-out-the-manual-way)) | ||
| ```python | ||
| import log21 | ||
| from log21 import ColorizingArgumentParser, get_logger, get_colors as gc | ||
| parser = ColorizingArgumentParser(description="This is a simple example of a ColorizingArgumentParser.", | ||
| colors={'help': 'LightCyan'}) | ||
| parser.add_argument('test1', action='store', help='Test 1') | ||
| parser.add_argument('test2', action='store', help='Test 2') | ||
| parser.add_argument('--optional-arg', '-o', action='store', type=int, help='An optional integer') | ||
| parser.add_argument('--verbose', '-v', action='store_true', help='Increase verbosity.') | ||
| args = parser.parse_args() | ||
| logger = get_logger('My Logger', level_names={log21.DEBUG: ' ? ', log21.INFO: ' + ', log21.WARNING: ' ! ', | ||
| log21.ERROR: '!!!'}) | ||
| if args.verbose: | ||
| logger.setLevel(log21.DEBUG) | ||
| else: | ||
| logger.setLevel(log21.INFO) | ||
| logger.debug(gc('LightBlue') + 'Verbose mode on!') | ||
| logger.debug('Arguments:\n' | ||
| '\tTest 1: %s\n' | ||
| '\tTest 2: %s\n' | ||
| '\tOptional: %s', args=(args.test1, args.test2, args.optional_arg)) | ||
| logger.info(gc('LightGreen') + args.test1) | ||
| logger.info(gc('LightWhite') + 'Done!') | ||
| ``` | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
| ------------------ | ||
| ### Pretty-Printing and Tree-Printing | ||
| ```python | ||
| import json | ||
| import log21 | ||
| data = json.load(open('json.json', 'r')) | ||
| # Prints data using python's built-in print function | ||
| print(data) | ||
| # Uses `log21.pprint` to print the data | ||
| log21.pprint(data) | ||
| # Uses `log21.tree_print` to print the data | ||
| log21.tree_print(data) | ||
| ``` | ||
|  | ||
|  | ||
|  | ||
|  | ||
| ------------------ | ||
| ### Logging Window | ||
| ```python | ||
| import log21 | ||
| window = log21.get_logging_window('My Logging Window', width=80) | ||
| window.font = ('Courier New', 9) | ||
| # Basic logging | ||
| window.info('This is a basic logging message.') | ||
| # Using ANSI and HEX colors | ||
| # List of ANSI colors: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors | ||
| # ANSI color format: \033[<attribute>m | ||
| window.info('\033[91mThis is RED message.') | ||
| window.info('\033[102mThis is message with GREEN background.') | ||
| # HEX color format: \033#<HEX-COLOR>hf (where f represents the foreground color) and | ||
| # \033#<HEX-COLOR>hb (where b represents the background color) | ||
| window.info('\x1b#009900hbThis is a text with GREEN background.') | ||
| window.info('\033#0000FFhf\033[103mThis is message with BLUE foreground and YELLOW background.') | ||
| import random, string | ||
| # And here is a text with random colors | ||
| text = 'I have random colors XD' | ||
| colored_text = '' | ||
| for character in text: | ||
| color = '\033#' + ''.join(random.choice(string.hexdigits) for _ in range(6)) + 'hf' | ||
| colored_text += color + character | ||
| window.error(colored_text) | ||
| # See more examples in | ||
| # https://github.com/MPCodeWriter21/log21/blob/066efc1e72542531012d36974bbf6cd4c5941378/log21/LoggingWindow.py#L155 | ||
| # and | ||
| # https://github.com/MPCodeWriter21/log21/blob/066efc1e72542531012d36974bbf6cd4c5941378/log21/__init__.py#L144 | ||
| ``` | ||
|  | ||
| ------------------ | ||
| ### ProgressBar | ||
| ```python | ||
| # Example 1 | ||
| import log21, time | ||
| # Define a very simple log21 progress bar | ||
| progress_bar = log21.ProgressBar() | ||
| # And here is a simple loop that will print the progress bar | ||
| for i in range(100): | ||
| progress_bar(i + 1, 100) | ||
| time.sleep(0.08) | ||
| # Example 2 | ||
| import time, random | ||
| from log21 import ProgressBar, get_colors as gc | ||
| # Let's customize the progress bar a little bit this time | ||
| progress_bar = ProgressBar( | ||
| width=50, | ||
| fill='#', | ||
| empty='-', | ||
| prefix='[', | ||
| suffix=']', | ||
| colors={'progress in-progress': gc('Bright Red'), 'progress complete': gc('Bright Cyan'), | ||
| 'percentage in-progress': gc('Green'), 'percentage complete': gc('Bright Cyan'), | ||
| 'prefix-color in-progress': gc('Bright White'), 'prefix-color complete': gc('Bright White'), | ||
| 'prefix-color failed': gc('Bright White'), 'suffix-color in-progress': gc('Bright White'), | ||
| 'suffix-color complete': gc('Bright White'), 'suffix-color failed': gc('Bright White')}) | ||
| for i in range(84): | ||
| progress_bar(i + 1, 84) | ||
| time.sleep(random.uniform(0.05, 0.21)) | ||
| ``` | ||
|  | ||
|  | ||
| ------------------ | ||
| ### Argumentify (Check out [the manual way](https://github.com/MPCodeWriter21/log21#argument-parsing-see-also-argumentify)) | ||
| ```python | ||
| # Common Section | ||
| import log21 | ||
| class ReversedText: | ||
| def __init__(self, text: str): | ||
| self._text = text[::-1] | ||
| def __str__(self): | ||
| return self._text | ||
| def __repr__(self): | ||
| return f"<{self.__class__.__name__}(text='{self._text}') at {hex(id(self))}>" | ||
| # Old way | ||
| def main(): | ||
| """Here is my main function""" | ||
| parser = log21.ColorizingArgumentParser() | ||
| parser.add_argument('--positional-arg', '-p', action='store', type=int, | ||
| required=True, help="This argument is positional!") | ||
| parser.add_argument('--optional-arg', '-o', action='store', type=ReversedText, | ||
| help="Whatever you pass here will be REVERSED!") | ||
| parser.add_argument('--arg-with-default', '-a', action='store', default=21, | ||
| help="The default value is 21") | ||
| parser.add_argument('--additional-arg', '-A', action='store', | ||
| help="This one is extra.") | ||
| parser.add_argument('--verbose', '-v', action='store_true', | ||
| help="Increase verbosity") | ||
| args = parser.parse_args() | ||
| if args.verbose: | ||
| log21.basic_config(level='DEBUG') | ||
| log21.info(f"positional_arg = {args.positional_arg}") | ||
| log21.info(f"optional_arg = {args.optional_arg}") | ||
| log21.debug(f"arg_with_default = {args.arg_with_default}") | ||
| log21.debug(f"additional_arg = {args.additional_arg}") | ||
| if __name__ == '__main__': | ||
| main() | ||
| # New way | ||
| def main(positional_arg: int, /, optional_arg: ReversedText, arg_with_default: int = 21, | ||
| additional_arg=None, verbose: bool = False): | ||
| """Some description | ||
| :param positional_arg: This argument is positional! | ||
| :param optional_arg: Whatever you pass here will be REVERSED! | ||
| :param arg_with_default: The default value is 21 | ||
| :param additional_arg: This one is extra. | ||
| :param verbose: Increase verbosity | ||
| """ | ||
| if verbose: | ||
| log21.basic_config(level='DEBUG') | ||
| log21.info(f"{positional_arg = }") | ||
| log21.info(f"{optional_arg = !s}") | ||
| log21.debug(f"{arg_with_default = }") | ||
| log21.debug(f"{additional_arg = !s}") | ||
| if __name__ == '__main__': | ||
| log21.argumentify(main) | ||
| ``` | ||
|  | ||
|  | ||
| Example with multiple functions as entry-point: | ||
| ```python | ||
| import ast | ||
| import operator | ||
| from functools import reduce | ||
| import log21 | ||
| # `safe_eval` Based on https://stackoverflow.com/a/9558001/1113207 | ||
| # Supported Operators | ||
| operators = { | ||
| ast.Add: operator.add, | ||
| ast.Sub: operator.sub, | ||
| ast.Mult: operator.mul, | ||
| ast.Div: operator.truediv, | ||
| ast.FloorDiv: operator.floordiv, | ||
| ast.Pow: operator.pow, | ||
| ast.BitXor: operator.xor, | ||
| ast.USub: operator.neg | ||
| } | ||
| def safe_eval(expr: str): | ||
| """Safely evaluate a mathematical expression. | ||
| >>> eval_expr('2^6') | ||
| 4 | ||
| >>> eval_expr('2**6') | ||
| 64 | ||
| >>> eval_expr('1 + 2*3**(4^5) / (6 + -7)') | ||
| -5.0 | ||
| :param expr: expression to evaluate | ||
| :raises SyntaxError: on invalid expression | ||
| :return: result of the evaluation | ||
| """ | ||
| try: | ||
| return _eval(ast.parse(expr, mode='eval').body) | ||
| except (TypeError, KeyError, SyntaxError): | ||
| log21.error(f'Invalid expression: {expr}') | ||
| raise | ||
| def _eval(node: ast.AST): | ||
| """Internal implementation of `safe_eval`. | ||
| :param node: AST node to evaluate | ||
| :raises TypeError: on invalid node | ||
| :raises KeyError: on invalid operator | ||
| :raises ZeroDivisionError: on division by zero | ||
| :raises ValueError: on invalid literal | ||
| :raises SyntaxError: on invalid syntax | ||
| :return: result of the evaluation | ||
| """ | ||
| if isinstance(node, ast.Num): # <number> | ||
| return node.n | ||
| if isinstance(node, ast.BinOp): # <left> <operator> <right> | ||
| return operators[type(node.op)](_eval(node.left), _eval(node.right)) | ||
| if isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1 | ||
| return operators[type(node.op)](_eval(node.operand)) | ||
| raise TypeError(node) | ||
| # Example code | ||
| def addition(*numbers: float): | ||
| """Addition of numbers. | ||
| Args: | ||
| numbers (float): numbers to add | ||
| """ | ||
| if len(numbers) < 2: | ||
| log21.error('At least two numbers are required! Use `-n`.') | ||
| return | ||
| log21.info(f'Result: {sum(numbers)}') | ||
| def multiplication(*numbers: float): | ||
| """Multiplication of numbers. | ||
| Args: | ||
| numbers (float): numbers to multiply | ||
| """ | ||
| if len(numbers) < 2: | ||
| log21.error('At least two numbers are required! Use `-n`.') | ||
| return | ||
| log21.info(f'Result: {reduce(lambda x, y: x * y, numbers)}') | ||
| def calc(*inputs: str, verbose: bool = False): | ||
| """Calculate numbers. | ||
| :param inputs: numbers and operators | ||
| """ | ||
| expression = ' '.join(inputs) | ||
| if len(expression) < 3: | ||
| log21.error('At least two numbers and one operator are required! Use `-i`.') | ||
| return | ||
| if verbose: | ||
| log21.basic_config(level='DEBUG') | ||
| log21.debug(f'Expression: {expression}') | ||
| try: | ||
| log21.info(f'Result: {safe_eval(expression)}') | ||
| except (TypeError, KeyError, SyntaxError): | ||
| pass | ||
| if __name__ == "__main__": | ||
| log21.argumentify({'add': addition, 'mul': multiplication, 'calc': calc}) | ||
| ``` | ||
|  | ||
| About | ||
@@ -493,4 +118,2 @@ ----- | ||
| Aparat Channel: [CodeWriter21](https://www.aparat.com/CodeWriter21) | ||
| ### License | ||
@@ -497,0 +120,0 @@ |
+1
-1
@@ -29,3 +29,3 @@ [build-system] | ||
| ] | ||
| version = "2.8.1" | ||
| version = "2.9.0" | ||
@@ -32,0 +32,0 @@ [tool.setuptools.packages.find] |
+4
-381
@@ -64,10 +64,7 @@ log21 | ||
| ### 2.8.1 | ||
| ### 2.9.0 | ||
| + Fixed Carriage Return Handling. | ||
| + Fixed setting level using `log21.basic_config` | ||
| + Added more configuration for developer tools to the `pyproject.toml` file. | ||
| + Added pre-commit. | ||
| + Added `<<` and `>>` (left shift and right shift operators) to `log21.Logger.Logger`. | ||
| [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md) | ||
| [Full CHANGELOG](CHANGELOG.md) | ||
@@ -78,377 +75,5 @@ | ||
| ### Basic Logging | ||
| See [Examples.md](EXAMPLES.md) | ||
| ```python | ||
| import log21 | ||
| log21.print(log21.get_color('#FF0000') + 'This' + log21.get_color((0, 255, 0)) + ' is' + log21.get_color('Blue') + | ||
| ' Blue' + log21.get_colors('BackgroundWhite', 'Black') + ' 8)') | ||
| logger = log21.get_logger('My Logger', level_names={21: 'SpecialInfo', log21.WARNING: ' ! ', log21.ERROR: '!!!'}) | ||
| logger.info('You are reading the README.md file...') | ||
| logger.log(21, 'Here', '%s', 'GO!', args=('we',)) | ||
| logger.setLevel(log21.WARNING) | ||
| logger.warning("We can't log messages with a level less than 30 anymore!") | ||
| logger.debug("You won't see this!") | ||
| logger.info("Am I visible?") | ||
| logger.error(log21.get_colors('LightRed') + "I'm still here ;1") | ||
| ``` | ||
|  | ||
| ---------------- | ||
| ### Argument Parsing (See Also: [Argumentify](https://github.com/MPCodeWriter21/log21#argumentify-check-out-the-manual-way)) | ||
| ```python | ||
| import log21 | ||
| from log21 import ColorizingArgumentParser, get_logger, get_colors as gc | ||
| parser = ColorizingArgumentParser(description="This is a simple example of a ColorizingArgumentParser.", | ||
| colors={'help': 'LightCyan'}) | ||
| parser.add_argument('test1', action='store', help='Test 1') | ||
| parser.add_argument('test2', action='store', help='Test 2') | ||
| parser.add_argument('--optional-arg', '-o', action='store', type=int, help='An optional integer') | ||
| parser.add_argument('--verbose', '-v', action='store_true', help='Increase verbosity.') | ||
| args = parser.parse_args() | ||
| logger = get_logger('My Logger', level_names={log21.DEBUG: ' ? ', log21.INFO: ' + ', log21.WARNING: ' ! ', | ||
| log21.ERROR: '!!!'}) | ||
| if args.verbose: | ||
| logger.setLevel(log21.DEBUG) | ||
| else: | ||
| logger.setLevel(log21.INFO) | ||
| logger.debug(gc('LightBlue') + 'Verbose mode on!') | ||
| logger.debug('Arguments:\n' | ||
| '\tTest 1: %s\n' | ||
| '\tTest 2: %s\n' | ||
| '\tOptional: %s', args=(args.test1, args.test2, args.optional_arg)) | ||
| logger.info(gc('LightGreen') + args.test1) | ||
| logger.info(gc('LightWhite') + 'Done!') | ||
| ``` | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
| ------------------ | ||
| ### Pretty-Printing and Tree-Printing | ||
| ```python | ||
| import json | ||
| import log21 | ||
| data = json.load(open('json.json', 'r')) | ||
| # Prints data using python's built-in print function | ||
| print(data) | ||
| # Uses `log21.pprint` to print the data | ||
| log21.pprint(data) | ||
| # Uses `log21.tree_print` to print the data | ||
| log21.tree_print(data) | ||
| ``` | ||
|  | ||
|  | ||
|  | ||
|  | ||
| ------------------ | ||
| ### Logging Window | ||
| ```python | ||
| import log21 | ||
| window = log21.get_logging_window('My Logging Window', width=80) | ||
| window.font = ('Courier New', 9) | ||
| # Basic logging | ||
| window.info('This is a basic logging message.') | ||
| # Using ANSI and HEX colors | ||
| # List of ANSI colors: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors | ||
| # ANSI color format: \033[<attribute>m | ||
| window.info('\033[91mThis is RED message.') | ||
| window.info('\033[102mThis is message with GREEN background.') | ||
| # HEX color format: \033#<HEX-COLOR>hf (where f represents the foreground color) and | ||
| # \033#<HEX-COLOR>hb (where b represents the background color) | ||
| window.info('\x1b#009900hbThis is a text with GREEN background.') | ||
| window.info('\033#0000FFhf\033[103mThis is message with BLUE foreground and YELLOW background.') | ||
| import random, string | ||
| # And here is a text with random colors | ||
| text = 'I have random colors XD' | ||
| colored_text = '' | ||
| for character in text: | ||
| color = '\033#' + ''.join(random.choice(string.hexdigits) for _ in range(6)) + 'hf' | ||
| colored_text += color + character | ||
| window.error(colored_text) | ||
| # See more examples in | ||
| # https://github.com/MPCodeWriter21/log21/blob/066efc1e72542531012d36974bbf6cd4c5941378/log21/LoggingWindow.py#L155 | ||
| # and | ||
| # https://github.com/MPCodeWriter21/log21/blob/066efc1e72542531012d36974bbf6cd4c5941378/log21/__init__.py#L144 | ||
| ``` | ||
|  | ||
| ------------------ | ||
| ### ProgressBar | ||
| ```python | ||
| # Example 1 | ||
| import log21, time | ||
| # Define a very simple log21 progress bar | ||
| progress_bar = log21.ProgressBar() | ||
| # And here is a simple loop that will print the progress bar | ||
| for i in range(100): | ||
| progress_bar(i + 1, 100) | ||
| time.sleep(0.08) | ||
| # Example 2 | ||
| import time, random | ||
| from log21 import ProgressBar, get_colors as gc | ||
| # Let's customize the progress bar a little bit this time | ||
| progress_bar = ProgressBar( | ||
| width=50, | ||
| fill='#', | ||
| empty='-', | ||
| prefix='[', | ||
| suffix=']', | ||
| colors={'progress in-progress': gc('Bright Red'), 'progress complete': gc('Bright Cyan'), | ||
| 'percentage in-progress': gc('Green'), 'percentage complete': gc('Bright Cyan'), | ||
| 'prefix-color in-progress': gc('Bright White'), 'prefix-color complete': gc('Bright White'), | ||
| 'prefix-color failed': gc('Bright White'), 'suffix-color in-progress': gc('Bright White'), | ||
| 'suffix-color complete': gc('Bright White'), 'suffix-color failed': gc('Bright White')}) | ||
| for i in range(84): | ||
| progress_bar(i + 1, 84) | ||
| time.sleep(random.uniform(0.05, 0.21)) | ||
| ``` | ||
|  | ||
|  | ||
| ------------------ | ||
| ### Argumentify (Check out [the manual way](https://github.com/MPCodeWriter21/log21#argument-parsing-see-also-argumentify)) | ||
| ```python | ||
| # Common Section | ||
| import log21 | ||
| class ReversedText: | ||
| def __init__(self, text: str): | ||
| self._text = text[::-1] | ||
| def __str__(self): | ||
| return self._text | ||
| def __repr__(self): | ||
| return f"<{self.__class__.__name__}(text='{self._text}') at {hex(id(self))}>" | ||
| # Old way | ||
| def main(): | ||
| """Here is my main function""" | ||
| parser = log21.ColorizingArgumentParser() | ||
| parser.add_argument('--positional-arg', '-p', action='store', type=int, | ||
| required=True, help="This argument is positional!") | ||
| parser.add_argument('--optional-arg', '-o', action='store', type=ReversedText, | ||
| help="Whatever you pass here will be REVERSED!") | ||
| parser.add_argument('--arg-with-default', '-a', action='store', default=21, | ||
| help="The default value is 21") | ||
| parser.add_argument('--additional-arg', '-A', action='store', | ||
| help="This one is extra.") | ||
| parser.add_argument('--verbose', '-v', action='store_true', | ||
| help="Increase verbosity") | ||
| args = parser.parse_args() | ||
| if args.verbose: | ||
| log21.basic_config(level='DEBUG') | ||
| log21.info(f"positional_arg = {args.positional_arg}") | ||
| log21.info(f"optional_arg = {args.optional_arg}") | ||
| log21.debug(f"arg_with_default = {args.arg_with_default}") | ||
| log21.debug(f"additional_arg = {args.additional_arg}") | ||
| if __name__ == '__main__': | ||
| main() | ||
| # New way | ||
| def main(positional_arg: int, /, optional_arg: ReversedText, arg_with_default: int = 21, | ||
| additional_arg=None, verbose: bool = False): | ||
| """Some description | ||
| :param positional_arg: This argument is positional! | ||
| :param optional_arg: Whatever you pass here will be REVERSED! | ||
| :param arg_with_default: The default value is 21 | ||
| :param additional_arg: This one is extra. | ||
| :param verbose: Increase verbosity | ||
| """ | ||
| if verbose: | ||
| log21.basic_config(level='DEBUG') | ||
| log21.info(f"{positional_arg = }") | ||
| log21.info(f"{optional_arg = !s}") | ||
| log21.debug(f"{arg_with_default = }") | ||
| log21.debug(f"{additional_arg = !s}") | ||
| if __name__ == '__main__': | ||
| log21.argumentify(main) | ||
| ``` | ||
|  | ||
|  | ||
| Example with multiple functions as entry-point: | ||
| ```python | ||
| import ast | ||
| import operator | ||
| from functools import reduce | ||
| import log21 | ||
| # `safe_eval` Based on https://stackoverflow.com/a/9558001/1113207 | ||
| # Supported Operators | ||
| operators = { | ||
| ast.Add: operator.add, | ||
| ast.Sub: operator.sub, | ||
| ast.Mult: operator.mul, | ||
| ast.Div: operator.truediv, | ||
| ast.FloorDiv: operator.floordiv, | ||
| ast.Pow: operator.pow, | ||
| ast.BitXor: operator.xor, | ||
| ast.USub: operator.neg | ||
| } | ||
| def safe_eval(expr: str): | ||
| """Safely evaluate a mathematical expression. | ||
| >>> eval_expr('2^6') | ||
| 4 | ||
| >>> eval_expr('2**6') | ||
| 64 | ||
| >>> eval_expr('1 + 2*3**(4^5) / (6 + -7)') | ||
| -5.0 | ||
| :param expr: expression to evaluate | ||
| :raises SyntaxError: on invalid expression | ||
| :return: result of the evaluation | ||
| """ | ||
| try: | ||
| return _eval(ast.parse(expr, mode='eval').body) | ||
| except (TypeError, KeyError, SyntaxError): | ||
| log21.error(f'Invalid expression: {expr}') | ||
| raise | ||
| def _eval(node: ast.AST): | ||
| """Internal implementation of `safe_eval`. | ||
| :param node: AST node to evaluate | ||
| :raises TypeError: on invalid node | ||
| :raises KeyError: on invalid operator | ||
| :raises ZeroDivisionError: on division by zero | ||
| :raises ValueError: on invalid literal | ||
| :raises SyntaxError: on invalid syntax | ||
| :return: result of the evaluation | ||
| """ | ||
| if isinstance(node, ast.Num): # <number> | ||
| return node.n | ||
| if isinstance(node, ast.BinOp): # <left> <operator> <right> | ||
| return operators[type(node.op)](_eval(node.left), _eval(node.right)) | ||
| if isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1 | ||
| return operators[type(node.op)](_eval(node.operand)) | ||
| raise TypeError(node) | ||
| # Example code | ||
| def addition(*numbers: float): | ||
| """Addition of numbers. | ||
| Args: | ||
| numbers (float): numbers to add | ||
| """ | ||
| if len(numbers) < 2: | ||
| log21.error('At least two numbers are required! Use `-n`.') | ||
| return | ||
| log21.info(f'Result: {sum(numbers)}') | ||
| def multiplication(*numbers: float): | ||
| """Multiplication of numbers. | ||
| Args: | ||
| numbers (float): numbers to multiply | ||
| """ | ||
| if len(numbers) < 2: | ||
| log21.error('At least two numbers are required! Use `-n`.') | ||
| return | ||
| log21.info(f'Result: {reduce(lambda x, y: x * y, numbers)}') | ||
| def calc(*inputs: str, verbose: bool = False): | ||
| """Calculate numbers. | ||
| :param inputs: numbers and operators | ||
| """ | ||
| expression = ' '.join(inputs) | ||
| if len(expression) < 3: | ||
| log21.error('At least two numbers and one operator are required! Use `-i`.') | ||
| return | ||
| if verbose: | ||
| log21.basic_config(level='DEBUG') | ||
| log21.debug(f'Expression: {expression}') | ||
| try: | ||
| log21.info(f'Result: {safe_eval(expression)}') | ||
| except (TypeError, KeyError, SyntaxError): | ||
| pass | ||
| if __name__ == "__main__": | ||
| log21.argumentify({'add': addition, 'mul': multiplication, 'calc': calc}) | ||
| ``` | ||
|  | ||
| About | ||
@@ -462,4 +87,2 @@ ----- | ||
| Aparat Channel: [CodeWriter21](https://www.aparat.com/CodeWriter21) | ||
| ### License | ||
@@ -466,0 +89,0 @@ |
+0
-0
@@ -0,0 +0,0 @@ [egg_info] |
| Metadata-Version: 2.1 | ||
| Name: log21 | ||
| Version: 2.8.1 | ||
| Version: 2.9.0 | ||
| Summary: A simple logging package that helps you log colorized messages in Windows console. | ||
@@ -95,10 +95,7 @@ Author-email: "CodeWriter21(Mehrad Pooryoussof)" <CodeWriter21@gmail.com> | ||
| ### 2.8.1 | ||
| ### 2.9.0 | ||
| + Fixed Carriage Return Handling. | ||
| + Fixed setting level using `log21.basic_config` | ||
| + Added more configuration for developer tools to the `pyproject.toml` file. | ||
| + Added pre-commit. | ||
| + Added `<<` and `>>` (left shift and right shift operators) to `log21.Logger.Logger`. | ||
| [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md) | ||
| [Full CHANGELOG](CHANGELOG.md) | ||
@@ -109,377 +106,5 @@ | ||
| ### Basic Logging | ||
| See [Examples.md](EXAMPLES.md) | ||
| ```python | ||
| import log21 | ||
| log21.print(log21.get_color('#FF0000') + 'This' + log21.get_color((0, 255, 0)) + ' is' + log21.get_color('Blue') + | ||
| ' Blue' + log21.get_colors('BackgroundWhite', 'Black') + ' 8)') | ||
| logger = log21.get_logger('My Logger', level_names={21: 'SpecialInfo', log21.WARNING: ' ! ', log21.ERROR: '!!!'}) | ||
| logger.info('You are reading the README.md file...') | ||
| logger.log(21, 'Here', '%s', 'GO!', args=('we',)) | ||
| logger.setLevel(log21.WARNING) | ||
| logger.warning("We can't log messages with a level less than 30 anymore!") | ||
| logger.debug("You won't see this!") | ||
| logger.info("Am I visible?") | ||
| logger.error(log21.get_colors('LightRed') + "I'm still here ;1") | ||
| ``` | ||
|  | ||
| ---------------- | ||
| ### Argument Parsing (See Also: [Argumentify](https://github.com/MPCodeWriter21/log21#argumentify-check-out-the-manual-way)) | ||
| ```python | ||
| import log21 | ||
| from log21 import ColorizingArgumentParser, get_logger, get_colors as gc | ||
| parser = ColorizingArgumentParser(description="This is a simple example of a ColorizingArgumentParser.", | ||
| colors={'help': 'LightCyan'}) | ||
| parser.add_argument('test1', action='store', help='Test 1') | ||
| parser.add_argument('test2', action='store', help='Test 2') | ||
| parser.add_argument('--optional-arg', '-o', action='store', type=int, help='An optional integer') | ||
| parser.add_argument('--verbose', '-v', action='store_true', help='Increase verbosity.') | ||
| args = parser.parse_args() | ||
| logger = get_logger('My Logger', level_names={log21.DEBUG: ' ? ', log21.INFO: ' + ', log21.WARNING: ' ! ', | ||
| log21.ERROR: '!!!'}) | ||
| if args.verbose: | ||
| logger.setLevel(log21.DEBUG) | ||
| else: | ||
| logger.setLevel(log21.INFO) | ||
| logger.debug(gc('LightBlue') + 'Verbose mode on!') | ||
| logger.debug('Arguments:\n' | ||
| '\tTest 1: %s\n' | ||
| '\tTest 2: %s\n' | ||
| '\tOptional: %s', args=(args.test1, args.test2, args.optional_arg)) | ||
| logger.info(gc('LightGreen') + args.test1) | ||
| logger.info(gc('LightWhite') + 'Done!') | ||
| ``` | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
| ------------------ | ||
| ### Pretty-Printing and Tree-Printing | ||
| ```python | ||
| import json | ||
| import log21 | ||
| data = json.load(open('json.json', 'r')) | ||
| # Prints data using python's built-in print function | ||
| print(data) | ||
| # Uses `log21.pprint` to print the data | ||
| log21.pprint(data) | ||
| # Uses `log21.tree_print` to print the data | ||
| log21.tree_print(data) | ||
| ``` | ||
|  | ||
|  | ||
|  | ||
|  | ||
| ------------------ | ||
| ### Logging Window | ||
| ```python | ||
| import log21 | ||
| window = log21.get_logging_window('My Logging Window', width=80) | ||
| window.font = ('Courier New', 9) | ||
| # Basic logging | ||
| window.info('This is a basic logging message.') | ||
| # Using ANSI and HEX colors | ||
| # List of ANSI colors: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors | ||
| # ANSI color format: \033[<attribute>m | ||
| window.info('\033[91mThis is RED message.') | ||
| window.info('\033[102mThis is message with GREEN background.') | ||
| # HEX color format: \033#<HEX-COLOR>hf (where f represents the foreground color) and | ||
| # \033#<HEX-COLOR>hb (where b represents the background color) | ||
| window.info('\x1b#009900hbThis is a text with GREEN background.') | ||
| window.info('\033#0000FFhf\033[103mThis is message with BLUE foreground and YELLOW background.') | ||
| import random, string | ||
| # And here is a text with random colors | ||
| text = 'I have random colors XD' | ||
| colored_text = '' | ||
| for character in text: | ||
| color = '\033#' + ''.join(random.choice(string.hexdigits) for _ in range(6)) + 'hf' | ||
| colored_text += color + character | ||
| window.error(colored_text) | ||
| # See more examples in | ||
| # https://github.com/MPCodeWriter21/log21/blob/066efc1e72542531012d36974bbf6cd4c5941378/log21/LoggingWindow.py#L155 | ||
| # and | ||
| # https://github.com/MPCodeWriter21/log21/blob/066efc1e72542531012d36974bbf6cd4c5941378/log21/__init__.py#L144 | ||
| ``` | ||
|  | ||
| ------------------ | ||
| ### ProgressBar | ||
| ```python | ||
| # Example 1 | ||
| import log21, time | ||
| # Define a very simple log21 progress bar | ||
| progress_bar = log21.ProgressBar() | ||
| # And here is a simple loop that will print the progress bar | ||
| for i in range(100): | ||
| progress_bar(i + 1, 100) | ||
| time.sleep(0.08) | ||
| # Example 2 | ||
| import time, random | ||
| from log21 import ProgressBar, get_colors as gc | ||
| # Let's customize the progress bar a little bit this time | ||
| progress_bar = ProgressBar( | ||
| width=50, | ||
| fill='#', | ||
| empty='-', | ||
| prefix='[', | ||
| suffix=']', | ||
| colors={'progress in-progress': gc('Bright Red'), 'progress complete': gc('Bright Cyan'), | ||
| 'percentage in-progress': gc('Green'), 'percentage complete': gc('Bright Cyan'), | ||
| 'prefix-color in-progress': gc('Bright White'), 'prefix-color complete': gc('Bright White'), | ||
| 'prefix-color failed': gc('Bright White'), 'suffix-color in-progress': gc('Bright White'), | ||
| 'suffix-color complete': gc('Bright White'), 'suffix-color failed': gc('Bright White')}) | ||
| for i in range(84): | ||
| progress_bar(i + 1, 84) | ||
| time.sleep(random.uniform(0.05, 0.21)) | ||
| ``` | ||
|  | ||
|  | ||
| ------------------ | ||
| ### Argumentify (Check out [the manual way](https://github.com/MPCodeWriter21/log21#argument-parsing-see-also-argumentify)) | ||
| ```python | ||
| # Common Section | ||
| import log21 | ||
| class ReversedText: | ||
| def __init__(self, text: str): | ||
| self._text = text[::-1] | ||
| def __str__(self): | ||
| return self._text | ||
| def __repr__(self): | ||
| return f"<{self.__class__.__name__}(text='{self._text}') at {hex(id(self))}>" | ||
| # Old way | ||
| def main(): | ||
| """Here is my main function""" | ||
| parser = log21.ColorizingArgumentParser() | ||
| parser.add_argument('--positional-arg', '-p', action='store', type=int, | ||
| required=True, help="This argument is positional!") | ||
| parser.add_argument('--optional-arg', '-o', action='store', type=ReversedText, | ||
| help="Whatever you pass here will be REVERSED!") | ||
| parser.add_argument('--arg-with-default', '-a', action='store', default=21, | ||
| help="The default value is 21") | ||
| parser.add_argument('--additional-arg', '-A', action='store', | ||
| help="This one is extra.") | ||
| parser.add_argument('--verbose', '-v', action='store_true', | ||
| help="Increase verbosity") | ||
| args = parser.parse_args() | ||
| if args.verbose: | ||
| log21.basic_config(level='DEBUG') | ||
| log21.info(f"positional_arg = {args.positional_arg}") | ||
| log21.info(f"optional_arg = {args.optional_arg}") | ||
| log21.debug(f"arg_with_default = {args.arg_with_default}") | ||
| log21.debug(f"additional_arg = {args.additional_arg}") | ||
| if __name__ == '__main__': | ||
| main() | ||
| # New way | ||
| def main(positional_arg: int, /, optional_arg: ReversedText, arg_with_default: int = 21, | ||
| additional_arg=None, verbose: bool = False): | ||
| """Some description | ||
| :param positional_arg: This argument is positional! | ||
| :param optional_arg: Whatever you pass here will be REVERSED! | ||
| :param arg_with_default: The default value is 21 | ||
| :param additional_arg: This one is extra. | ||
| :param verbose: Increase verbosity | ||
| """ | ||
| if verbose: | ||
| log21.basic_config(level='DEBUG') | ||
| log21.info(f"{positional_arg = }") | ||
| log21.info(f"{optional_arg = !s}") | ||
| log21.debug(f"{arg_with_default = }") | ||
| log21.debug(f"{additional_arg = !s}") | ||
| if __name__ == '__main__': | ||
| log21.argumentify(main) | ||
| ``` | ||
|  | ||
|  | ||
| Example with multiple functions as entry-point: | ||
| ```python | ||
| import ast | ||
| import operator | ||
| from functools import reduce | ||
| import log21 | ||
| # `safe_eval` Based on https://stackoverflow.com/a/9558001/1113207 | ||
| # Supported Operators | ||
| operators = { | ||
| ast.Add: operator.add, | ||
| ast.Sub: operator.sub, | ||
| ast.Mult: operator.mul, | ||
| ast.Div: operator.truediv, | ||
| ast.FloorDiv: operator.floordiv, | ||
| ast.Pow: operator.pow, | ||
| ast.BitXor: operator.xor, | ||
| ast.USub: operator.neg | ||
| } | ||
| def safe_eval(expr: str): | ||
| """Safely evaluate a mathematical expression. | ||
| >>> eval_expr('2^6') | ||
| 4 | ||
| >>> eval_expr('2**6') | ||
| 64 | ||
| >>> eval_expr('1 + 2*3**(4^5) / (6 + -7)') | ||
| -5.0 | ||
| :param expr: expression to evaluate | ||
| :raises SyntaxError: on invalid expression | ||
| :return: result of the evaluation | ||
| """ | ||
| try: | ||
| return _eval(ast.parse(expr, mode='eval').body) | ||
| except (TypeError, KeyError, SyntaxError): | ||
| log21.error(f'Invalid expression: {expr}') | ||
| raise | ||
| def _eval(node: ast.AST): | ||
| """Internal implementation of `safe_eval`. | ||
| :param node: AST node to evaluate | ||
| :raises TypeError: on invalid node | ||
| :raises KeyError: on invalid operator | ||
| :raises ZeroDivisionError: on division by zero | ||
| :raises ValueError: on invalid literal | ||
| :raises SyntaxError: on invalid syntax | ||
| :return: result of the evaluation | ||
| """ | ||
| if isinstance(node, ast.Num): # <number> | ||
| return node.n | ||
| if isinstance(node, ast.BinOp): # <left> <operator> <right> | ||
| return operators[type(node.op)](_eval(node.left), _eval(node.right)) | ||
| if isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1 | ||
| return operators[type(node.op)](_eval(node.operand)) | ||
| raise TypeError(node) | ||
| # Example code | ||
| def addition(*numbers: float): | ||
| """Addition of numbers. | ||
| Args: | ||
| numbers (float): numbers to add | ||
| """ | ||
| if len(numbers) < 2: | ||
| log21.error('At least two numbers are required! Use `-n`.') | ||
| return | ||
| log21.info(f'Result: {sum(numbers)}') | ||
| def multiplication(*numbers: float): | ||
| """Multiplication of numbers. | ||
| Args: | ||
| numbers (float): numbers to multiply | ||
| """ | ||
| if len(numbers) < 2: | ||
| log21.error('At least two numbers are required! Use `-n`.') | ||
| return | ||
| log21.info(f'Result: {reduce(lambda x, y: x * y, numbers)}') | ||
| def calc(*inputs: str, verbose: bool = False): | ||
| """Calculate numbers. | ||
| :param inputs: numbers and operators | ||
| """ | ||
| expression = ' '.join(inputs) | ||
| if len(expression) < 3: | ||
| log21.error('At least two numbers and one operator are required! Use `-i`.') | ||
| return | ||
| if verbose: | ||
| log21.basic_config(level='DEBUG') | ||
| log21.debug(f'Expression: {expression}') | ||
| try: | ||
| log21.info(f'Result: {safe_eval(expression)}') | ||
| except (TypeError, KeyError, SyntaxError): | ||
| pass | ||
| if __name__ == "__main__": | ||
| log21.argumentify({'add': addition, 'mul': multiplication, 'calc': calc}) | ||
| ``` | ||
|  | ||
| About | ||
@@ -493,4 +118,2 @@ ----- | ||
| Aparat Channel: [CodeWriter21](https://www.aparat.com/CodeWriter21) | ||
| ### License | ||
@@ -497,0 +120,0 @@ |
+19
-17
@@ -9,20 +9,20 @@ # log21.__init__.py | ||
| from log21 import CrashReporter | ||
| from log21.Colors import (Colors, get_color, get_colors, ansi_escape, closest_color, | ||
| get_color_name) | ||
| from log21.Levels import (INFO, WARN, DEBUG, ERROR, FATAL, INPUT, NOTSET, WARNING, | ||
| CRITICAL) | ||
| from log21.Logger import Logger | ||
| from log21.PPrint import PrettyPrinter, pformat | ||
| from log21.Manager import Manager | ||
| from log21.Argparse import ColorizingArgumentParser | ||
| from log21.TreePrint import TreePrint, tree_format | ||
| from log21.Formatters import ColorizingFormatter, DecolorizingFormatter, _Formatter | ||
| from log21.Argumentify import argumentify | ||
| from log21.FileHandler import FileHandler, DecolorizingFileHandler | ||
| from log21.ProgressBar import ProgressBar | ||
| from log21.LoggingWindow import LoggingWindow, LoggingWindowHandler | ||
| from log21.StreamHandler import StreamHandler, ColorizingStreamHandler | ||
| from . import CrashReporter | ||
| from .Colors import (Colors, get_color, get_colors, ansi_escape, closest_color, | ||
| get_color_name) | ||
| from .Levels import INFO, WARN, DEBUG, ERROR, FATAL, INPUT, NOTSET, WARNING, CRITICAL | ||
| from .Logger import Logger | ||
| from .PPrint import PrettyPrinter, pformat | ||
| from .Manager import Manager | ||
| from .Argparse import ColorizingArgumentParser | ||
| from .TreePrint import TreePrint, tree_format | ||
| from .Formatters import ColorizingFormatter, DecolorizingFormatter, _Formatter | ||
| from .Argumentify import argumentify | ||
| from .FileHandler import FileHandler, DecolorizingFileHandler | ||
| from .ProgressBar import ProgressBar | ||
| from .LoggingWindow import LoggingWindow, LoggingWindowHandler | ||
| from .StreamHandler import StreamHandler, ColorizingStreamHandler | ||
| __author__ = 'CodeWriter21 (Mehrad Pooryoussof)' | ||
| __author__ = 'CodeWriter21 (Mehrad Pooryoussof)' | ||
| __version__ = '2.9.0' | ||
| __github__ = 'Https://GitHub.com/MPCodeWriter21/log21' | ||
@@ -630,4 +630,6 @@ __all__ = [ | ||
| endl = '\n' | ||
| console_reporter = CrashReporter.ConsoleReporter() | ||
| file_reporter = CrashReporter.FileReporter(file='.crash_report.log') |
@@ -0,0 +0,0 @@ # log21.Argparse.py |
@@ -0,0 +0,0 @@ # log21.Argparse.py |
@@ -0,0 +0,0 @@ # log21.Colors.py |
@@ -0,0 +0,0 @@ # log21.CrashReporter.__init__.py |
@@ -0,0 +0,0 @@ # log21.CrashReporter.Formatters.py |
@@ -0,0 +0,0 @@ # log21.CrashReporter.Reporters.py |
@@ -0,0 +0,0 @@ # log21.FileHandler.py |
@@ -0,0 +0,0 @@ # log21.Formatters.py |
@@ -0,0 +0,0 @@ # log21.Levels.py |
+115
-9
@@ -5,5 +5,6 @@ # log21.Logger.py | ||
| import re as _re | ||
| import sys as _sys | ||
| import logging as _logging | ||
| from types import MethodType as _MethodType | ||
| from typing import (Union as _Union, Literal as _Literal, Mapping, | ||
| from typing import (Any, List, Union as _Union, Literal as _Literal, Mapping, | ||
| Callable as _Callable, Optional as _Optional, Sequence as _Sequence) | ||
@@ -44,3 +45,3 @@ from getpass import getpass as _getpass | ||
| raise TypeError( | ||
| "handlers must be a list of logging.Handler objects" | ||
| 'handlers must be a list of logging.Handler objects' | ||
| ) | ||
@@ -66,3 +67,3 @@ for handler in handlers: | ||
| if _raiseExceptions: | ||
| raise TypeError("level must be an integer") | ||
| raise TypeError('level must be an integer') | ||
| return | ||
@@ -167,4 +168,5 @@ if self.isEnabledFor(level): | ||
| To pass exception information, use the keyword argument exc_info with a true | ||
| value, e.g. | ||
| value. | ||
| Usage example: | ||
| age = logger.input("Enter your age: ") | ||
@@ -177,6 +179,8 @@ """ | ||
| def getpass(self, *msg, args: tuple = (), end='', **kwargs): | ||
| """ | ||
| """Takes a password input from the user. | ||
| :return: | ||
| :param msg: The message to display to the user. | ||
| :param args: The arguments to pass to the message. | ||
| :param end: The ending character to append to the message. | ||
| :return: The password. | ||
| """ | ||
@@ -221,3 +225,3 @@ msg = ' '.join([str(m) for m in msg]) + end | ||
| name: str, | ||
| errors: _Literal["raise", "ignore", "handle", "force"] = "raise" | ||
| errors: _Literal['raise', 'ignore', 'handle', 'force'] = 'raise' | ||
| ) -> str: | ||
@@ -285,3 +289,3 @@ """Adds a new method to the logger with a specific level and name. | ||
| level_names: Mapping[int, str], | ||
| errors: _Literal["raise", "ignore", "handle", "force"] = "raise" | ||
| errors: _Literal['raise', 'ignore', 'handle', 'force'] = 'raise' | ||
| ) -> None: | ||
@@ -297,3 +301,105 @@ """Adds new methods to the logger with specific levels and names. | ||
| def __lshift__(self, obj): | ||
| """Prints the object to the output stream. | ||
| This operator is meant to make the Logger object be usable in a | ||
| std::cout-like way. | ||
| :param obj: The object to print. | ||
| :return: The Logger object. | ||
| """ | ||
| logger = self | ||
| found = 0 | ||
| while logger: | ||
| for handler in logger.handlers: | ||
| if (isinstance(handler, _logging.StreamHandler) | ||
| and hasattr(handler.stream, 'write') | ||
| and hasattr(handler.stream, 'flush')): | ||
| found = found + 1 | ||
| handler.stream.write(str(obj)) | ||
| handler.stream.flush() | ||
| if not logger.propagate: | ||
| break | ||
| logger = logger.parent | ||
| if found == 0: | ||
| _sys.stderr.write( | ||
| f"No handlers could be found for logger \"{self.name}\"\n" | ||
| ) | ||
| return self | ||
| def __rshift__(self, obj: List[Any]): | ||
| """A way of receiving input from the stdin. | ||
| This operator is meant to make a std::cin-like operation possible in Python. | ||
| Usage examples: | ||
| >>> import log21 | ||
| >>> cout = cin = log21.get_logger() | ||
| >>> | ||
| >>> # Example 1 | ||
| >>> # Get three inputs of type: str, str or None, and float | ||
| >>> data = [str, None, float] # first name, last name and age | ||
| >>> cout << "Please enter a first name, last name and age(separated by space): " | ||
| Please enter a first name, last name and age(separated by space): | ||
| >>> cin >> data; | ||
| M 21 | ||
| >>> name = data[0] + (data[1] if data[1] is not None else '') | ||
| >>> age = data[2] | ||
| >>> cout << name << " is " << age << " years old." << log21.endl; | ||
| M is 21.0 years old. | ||
| >>> | ||
| >>> # Example 2 | ||
| >>> # Get any number of inputs | ||
| >>> data = [] | ||
| >>> cout << "Enter something: "; | ||
| Enter something: | ||
| >>> cin >> data; | ||
| What ever man 1 2 3 ! | ||
| >>> cout << "Here are the items you chose: " << data << log21.endl; | ||
| Here are the items you chose: ['What', 'ever', 'man', '1', '2', '3', '!'] | ||
| >>> | ||
| >>> # Example 3 | ||
| >>> # Get two inputs of type int with defaults: 1280 and 720 | ||
| >>> data = [1280, 720] | ||
| >>> cout << "Enter the width and the height: "; | ||
| Enter the width and the height: | ||
| >>> cin >> data; | ||
| 500 | ||
| >>> cout << "Width: " << data[0] << " Height: " << data[1] << log21.endl; | ||
| Width: 500 Height: 720 | ||
| >>> | ||
| :param obj: The object to redirect the output to. | ||
| :return: The Logger object. | ||
| """ | ||
| n = len(obj) - 1 | ||
| if n >= 0: | ||
| data = [] | ||
| while n >= 0: | ||
| tmp = _sys.stdin.readline()[:-1].split(' ', maxsplit=n) | ||
| if tmp: | ||
| data.extend(tmp) | ||
| else: | ||
| data.append('') | ||
| n -= len(tmp) | ||
| tmp = [] | ||
| for i, item in enumerate(data): | ||
| if obj[i] is None: | ||
| tmp.append(item or None) | ||
| elif isinstance(obj[i], type): | ||
| try: | ||
| tmp.append(obj[i](item)) | ||
| except ValueError: | ||
| tmp.append(obj[i]()) | ||
| else: | ||
| try: | ||
| tmp.append(obj[i].__class__(item)) | ||
| except ValueError: | ||
| tmp.append(obj[i]) | ||
| obj[:] = tmp | ||
| else: | ||
| obj[:] = _sys.stdin.readline()[:-1].split() | ||
| return self | ||
| def _add_one(name: str) -> str: | ||
@@ -300,0 +406,0 @@ """Add one to the end of a string. |
@@ -0,0 +0,0 @@ # log21.LoggingWindow.py |
@@ -0,0 +0,0 @@ # log21.Manager.py |
@@ -0,0 +0,0 @@ # log21.PPrint.py |
@@ -0,0 +0,0 @@ # log21.ProgressBar.py |
@@ -0,0 +0,0 @@ # log21.StreamHandler.py |
@@ -0,0 +0,0 @@ # log21.TreePrint.py |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
5648
1.82%279267
-8.11%