Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

log21

Package Overview
Dependencies
Maintainers
1
Versions
81
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

log21 - npm Package Compare versions

Comparing version
2.8.1
to
2.9.0
+0
-0
LICENSE.txt

@@ -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")
```
![Basic Logging](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-1.png)
----------------
### 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!')
```
![No argument](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.1.png)
![Help](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.2.png)
![Valid example 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.3.png)
![Valid example 2](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.4.png)
![Valid example 3](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.5.png)
------------------
### 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)
```
![Python print](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.1.png)
![log21 pretty print](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.2.png)
![log21 tree print 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.3.1.png)
![log21 tree print 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.3.2.png)
------------------
### 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
```
![The LoggingWindow](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-4.png)
------------------
### 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))
```
![ProgressBar - Example 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-5.1.gif)
![ProgressBar - Example 2](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-5.2.gif)
------------------
### 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)
```
![Old-Way](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.1.png)
![New-Way](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.2.png)
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})
```
![multi-entry](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.3.png)
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")
```
![Basic Logging](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-1.png)
----------------
### 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!')
```
![No argument](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.1.png)
![Help](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.2.png)
![Valid example 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.3.png)
![Valid example 2](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.4.png)
![Valid example 3](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.5.png)
------------------
### 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)
```
![Python print](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.1.png)
![log21 pretty print](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.2.png)
![log21 tree print 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.3.1.png)
![log21 tree print 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.3.2.png)
------------------
### 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
```
![The LoggingWindow](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-4.png)
------------------
### 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))
```
![ProgressBar - Example 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-5.1.gif)
![ProgressBar - Example 2](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-5.2.gif)
------------------
### 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)
```
![Old-Way](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.1.png)
![New-Way](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.2.png)
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})
```
![multi-entry](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.3.png)
About

@@ -462,4 +87,2 @@ -----

Aparat Channel: [CodeWriter21](https://www.aparat.com/CodeWriter21)
### License

@@ -466,0 +89,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")
```
![Basic Logging](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-1.png)
----------------
### 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!')
```
![No argument](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.1.png)
![Help](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.2.png)
![Valid example 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.3.png)
![Valid example 2](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.4.png)
![Valid example 3](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-2.5.png)
------------------
### 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)
```
![Python print](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.1.png)
![log21 pretty print](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.2.png)
![log21 tree print 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.3.1.png)
![log21 tree print 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-3.3.2.png)
------------------
### 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
```
![The LoggingWindow](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-4.png)
------------------
### 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))
```
![ProgressBar - Example 1](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-5.1.gif)
![ProgressBar - Example 2](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-5.2.gif)
------------------
### 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)
```
![Old-Way](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.1.png)
![New-Way](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.2.png)
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})
```
![multi-entry](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.3.png)
About

@@ -493,4 +118,2 @@ -----

Aparat Channel: [CodeWriter21](https://www.aparat.com/CodeWriter21)
### License

@@ -497,0 +120,0 @@

@@ -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

@@ -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