log21 3.2.0__tar.gz → 3.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {log21-3.2.0 → log21-3.3.1}/PKG-INFO +31 -10
- {log21-3.2.0 → log21-3.3.1}/README.md +30 -9
- {log21-3.2.0 → log21-3.3.1}/pyproject.toml +1 -1
- {log21-3.2.0 → log21-3.3.1}/src/log21/__init__.py +4 -2
- {log21-3.2.0 → log21-3.3.1}/src/log21/argparse.py +2 -1
- {log21-3.2.0 → log21-3.3.1}/src/log21/argumentify.py +3 -3
- log21-3.3.1/src/log21/helper_types.py +126 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/_argparse.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/_module_helper.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/colors.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/crash_reporter/__init__.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/crash_reporter/formatters.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/crash_reporter/reporters.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/file_handler.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/formatters.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/levels.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/logger.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/logging_window.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/manager.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/pprint.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/progress_bar.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/stream_handler.py +0 -0
- {log21-3.2.0 → log21-3.3.1}/src/log21/tree_print.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: log21
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.1
|
|
4
4
|
Summary: A simple logging package
|
|
5
5
|
Keywords: python,log,colorize,color,logging,Python3,CodeWriter21
|
|
6
6
|
Author: CodeWriter21(Mehrad Pooryoussof)
|
|
@@ -94,24 +94,45 @@ pip install git+https://github.com/MPCodeWriter21/log21
|
|
|
94
94
|
Changelog
|
|
95
95
|
---------
|
|
96
96
|
|
|
97
|
-
### v3.
|
|
97
|
+
### v3.3.1
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
the way a simple logger handles files.
|
|
99
|
+
Get rid of `invalid NoneType value` error message.
|
|
101
100
|
|
|
102
|
-
|
|
101
|
+
In the previous versions, if you used an optional union type such as `int | float | None`
|
|
102
|
+
which ended with `None`, you'd get an error saying `invalid NoneType value` that didn't
|
|
103
|
+
make any sense to the user. The code has been updated to use the name of the last
|
|
104
|
+
non-None type for the error message in these situations.
|
|
103
105
|
|
|
104
106
|
#### Example
|
|
105
107
|
|
|
106
108
|
```python
|
|
107
109
|
import log21
|
|
110
|
+
from log21.helper_types import FileSize
|
|
108
111
|
|
|
109
|
-
logger = log21.get_logger(
|
|
110
|
-
"My File Logger", show_level=False, show_time=True, file="myapp.log", file_mode="a",
|
|
111
|
-
file_encoding="utf-8"
|
|
112
|
-
)
|
|
113
112
|
|
|
114
|
-
|
|
113
|
+
def main(min_size: FileSize | None = None, max_size: FileSize | None = None) -> None:
|
|
114
|
+
log21.info("Min Size: %s, Max Size: %s", args=(min_size, max_size))
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
log21.argumentify(main)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Before v3.3.1:
|
|
121
|
+
|
|
122
|
+
```shell
|
|
123
|
+
$ python test.py -m Hello
|
|
124
|
+
usage: test.py [-h] [--min-size MIN_SIZE] [--max-size MAX_SIZE]
|
|
125
|
+
|
|
126
|
+
test.py: error: argument --min-size/-m: invalid NoneType value: 'Hello'
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
With v3.3.1 update:
|
|
130
|
+
|
|
131
|
+
```shell
|
|
132
|
+
$ python test.py -m Hello
|
|
133
|
+
usage: test.py [-h] [--min-size MIN_SIZE] [--max-size MAX_SIZE]
|
|
134
|
+
|
|
135
|
+
test.py: error: argument --min-size/-m: invalid FileSize value: 'Hello'
|
|
115
136
|
```
|
|
116
137
|
|
|
117
138
|
[Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
|
|
@@ -69,24 +69,45 @@ pip install git+https://github.com/MPCodeWriter21/log21
|
|
|
69
69
|
Changelog
|
|
70
70
|
---------
|
|
71
71
|
|
|
72
|
-
### v3.
|
|
72
|
+
### v3.3.1
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
the way a simple logger handles files.
|
|
74
|
+
Get rid of `invalid NoneType value` error message.
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
In the previous versions, if you used an optional union type such as `int | float | None`
|
|
77
|
+
which ended with `None`, you'd get an error saying `invalid NoneType value` that didn't
|
|
78
|
+
make any sense to the user. The code has been updated to use the name of the last
|
|
79
|
+
non-None type for the error message in these situations.
|
|
78
80
|
|
|
79
81
|
#### Example
|
|
80
82
|
|
|
81
83
|
```python
|
|
82
84
|
import log21
|
|
85
|
+
from log21.helper_types import FileSize
|
|
83
86
|
|
|
84
|
-
logger = log21.get_logger(
|
|
85
|
-
"My File Logger", show_level=False, show_time=True, file="myapp.log", file_mode="a",
|
|
86
|
-
file_encoding="utf-8"
|
|
87
|
-
)
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
def main(min_size: FileSize | None = None, max_size: FileSize | None = None) -> None:
|
|
89
|
+
log21.info("Min Size: %s, Max Size: %s", args=(min_size, max_size))
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
log21.argumentify(main)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Before v3.3.1:
|
|
96
|
+
|
|
97
|
+
```shell
|
|
98
|
+
$ python test.py -m Hello
|
|
99
|
+
usage: test.py [-h] [--min-size MIN_SIZE] [--max-size MAX_SIZE]
|
|
100
|
+
|
|
101
|
+
test.py: error: argument --min-size/-m: invalid NoneType value: 'Hello'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
With v3.3.1 update:
|
|
105
|
+
|
|
106
|
+
```shell
|
|
107
|
+
$ python test.py -m Hello
|
|
108
|
+
usage: test.py [-h] [--min-size MIN_SIZE] [--max-size MAX_SIZE]
|
|
109
|
+
|
|
110
|
+
test.py: error: argument --min-size/-m: invalid FileSize value: 'Hello'
|
|
90
111
|
```
|
|
91
112
|
|
|
92
113
|
[Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
|
|
@@ -10,6 +10,8 @@ from types import ModuleType as _ModuleType
|
|
|
10
10
|
from typing import (Type as _Type, Union as _Union, Literal as _Literal,
|
|
11
11
|
Mapping as _Mapping, Iterable as _Iterable, Optional as _Optional)
|
|
12
12
|
|
|
13
|
+
import log21.helper_types
|
|
14
|
+
|
|
13
15
|
from . import crash_reporter
|
|
14
16
|
from .colors import (Colors, get_color, get_colors, ansi_escape, closest_color,
|
|
15
17
|
get_color_name)
|
|
@@ -31,7 +33,7 @@ from .stream_handler import StreamHandler, ColorizingStreamHandler
|
|
|
31
33
|
# yapf: enable
|
|
32
34
|
|
|
33
35
|
__author__ = 'CodeWriter21 (Mehrad Pooryoussof)'
|
|
34
|
-
__version__ = '3.
|
|
36
|
+
__version__ = '3.3.1'
|
|
35
37
|
__github__ = 'https://GitHub.com/MPCodeWriter21/log21'
|
|
36
38
|
__all__ = [
|
|
37
39
|
'ColorizingStreamHandler', 'DecolorizingFileHandler', 'ColorizingFormatter',
|
|
@@ -44,7 +46,7 @@ __all__ = [
|
|
|
44
46
|
'log', 'basic_config', 'basicConfig', 'ProgressBar', 'LoggingWindow',
|
|
45
47
|
'LoggingWindowHandler', 'get_logging_window', 'crash_reporter', 'console_reporter',
|
|
46
48
|
'file_reporter', 'argumentify', 'ArgumentError', 'IncompatibleArgumentsError',
|
|
47
|
-
'RequiredArgumentError', 'TooFewArgumentsError'
|
|
49
|
+
'RequiredArgumentError', 'TooFewArgumentsError', 'FileHandler'
|
|
48
50
|
]
|
|
49
51
|
|
|
50
52
|
_manager = Manager()
|
|
@@ -784,7 +784,8 @@ class ColorizingArgumentParser(_argparse.ArgumentParser, _ActionsContainer):
|
|
|
784
784
|
else:
|
|
785
785
|
exception = ValueError()
|
|
786
786
|
for type_ in func_type:
|
|
787
|
-
|
|
787
|
+
if type_ is not type(None):
|
|
788
|
+
name = getattr(type_, '__name__', repr(type_))
|
|
788
789
|
try:
|
|
789
790
|
result = type_(arg_string)
|
|
790
791
|
break
|
|
@@ -451,7 +451,7 @@ def _argumentify(functions: _Dict[str, Callable]) -> None:
|
|
|
451
451
|
args = []
|
|
452
452
|
kwargs = {}
|
|
453
453
|
info = None
|
|
454
|
-
for
|
|
454
|
+
for _name, (function, info) in functions_info.items(): # noqa: B007
|
|
455
455
|
if function == cli_args.func:
|
|
456
456
|
break
|
|
457
457
|
else:
|
|
@@ -475,7 +475,7 @@ def _argumentify(functions: _Dict[str, Callable]) -> None:
|
|
|
475
475
|
|
|
476
476
|
def argumentify(
|
|
477
477
|
entry_point: _Union[Callable, _List[Callable], _Dict[str, Callable]]
|
|
478
|
-
) ->
|
|
478
|
+
) -> _Union[Callable, _List[Callable], _Dict[str, Callable]]:
|
|
479
479
|
"""This function argumentifies one or more functions as the entry point of the
|
|
480
480
|
script.
|
|
481
481
|
|
|
@@ -493,7 +493,7 @@ def argumentify(
|
|
|
493
493
|
12 if __name__ == '__main__':
|
|
494
494
|
13 argumentify(main)
|
|
495
495
|
|
|
496
|
-
$ python argumentified.py Ahmad
|
|
496
|
+
$ python argumentified.py Ahmad Mohammadi --age 20
|
|
497
497
|
Ahmad Ahmadi is 20 years old.
|
|
498
498
|
$ python argumentified.py Mehrad Pooryoussof
|
|
499
499
|
Mehrad Pooryoussof is not yet born.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# log21.helper_types.py
|
|
2
|
+
# CodeWriter21
|
|
3
|
+
"""A collection of useful types meant for using with argument parser to parse CLI
|
|
4
|
+
arguments to more usable formats.
|
|
5
|
+
|
|
6
|
+
+ FileSize: Can take `str` and `int` values. Will convert human inputs such as "121 KB",
|
|
7
|
+
"21MiB", or "4.56 GB" to bytes. Can also be used to represent bytes value in more
|
|
8
|
+
human-readable formats.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# yapf: disable
|
|
12
|
+
|
|
13
|
+
import re as _re
|
|
14
|
+
from math import log as _log
|
|
15
|
+
from typing import Union as _Union, SupportsInt as _SupportsInt
|
|
16
|
+
|
|
17
|
+
# yapf: enable
|
|
18
|
+
|
|
19
|
+
__all__ = ["FileSize"]
|
|
20
|
+
|
|
21
|
+
POWERS = "KMGTPEZYRQ"
|
|
22
|
+
FILE_SIZE_PATTERN = _re.compile(rf"^([+-]?[0-9]+(?:\.[0-9]+)?)\s*(|[{POWERS}])(i?)B$")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FileSize:
|
|
26
|
+
|
|
27
|
+
def __init__(self, value: _Union[int, str]) -> None:
|
|
28
|
+
"""An interface for converting different inputs to file-size (bytes).
|
|
29
|
+
|
|
30
|
+
:param value: int value in bytes or a string such as "100 KB", "20MiB", or "1.23
|
|
31
|
+
GB"
|
|
32
|
+
:raises TypeError: If the value is not of type int or str
|
|
33
|
+
:raises ValueError: If the str value does not match the file-size pattern:
|
|
34
|
+
^([+-]?[0-9]+(?:\\.[0-9]+)?)\\s*(|[KMGTPEZYRQ])(i?)B$
|
|
35
|
+
"""
|
|
36
|
+
if isinstance(value, int):
|
|
37
|
+
self.bytes = value
|
|
38
|
+
elif isinstance(value, str):
|
|
39
|
+
match = FILE_SIZE_PATTERN.match(value)
|
|
40
|
+
if not match:
|
|
41
|
+
raise ValueError(f"Input does not match the file-size pattern: {value}")
|
|
42
|
+
val, prefix, binary = match.groups()
|
|
43
|
+
power = POWERS.index(prefix) + 1
|
|
44
|
+
assert power is not None
|
|
45
|
+
self.bytes = int(float(val) * (1024 if binary else 1000)**power)
|
|
46
|
+
else:
|
|
47
|
+
raise TypeError(f"Input to FileSize() can be int or str, not {type(value)}")
|
|
48
|
+
|
|
49
|
+
def humanize(
|
|
50
|
+
self,
|
|
51
|
+
binary: bool = False,
|
|
52
|
+
gnu: bool = False,
|
|
53
|
+
fmt: str = "%.2f",
|
|
54
|
+
) -> str:
|
|
55
|
+
"""Returns the size in a human readable way."""
|
|
56
|
+
base = 1024 if (gnu or binary) else 1000
|
|
57
|
+
abs_bytes = abs(self.bytes)
|
|
58
|
+
|
|
59
|
+
if abs_bytes == 1 and not gnu:
|
|
60
|
+
return f"{self.bytes} Byte"
|
|
61
|
+
|
|
62
|
+
if abs_bytes < base:
|
|
63
|
+
return f"{self.bytes}B" if gnu else f"{self.bytes} Bytes"
|
|
64
|
+
|
|
65
|
+
power = int(min(_log(abs_bytes, base), len(POWERS)))
|
|
66
|
+
result: str = fmt % (self.bytes / (base**power))
|
|
67
|
+
if gnu:
|
|
68
|
+
return result + POWERS[power - 1]
|
|
69
|
+
result += " " + POWERS[power - 1]
|
|
70
|
+
if binary:
|
|
71
|
+
result += "i"
|
|
72
|
+
result += "B"
|
|
73
|
+
return result
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def KB(self) -> float:
|
|
77
|
+
return self.bytes / 1000
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def MB(self) -> float:
|
|
81
|
+
return self.bytes / 1000_000
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def GB(self) -> float:
|
|
85
|
+
return self.bytes / 1000_000_000
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def KiB(self) -> float:
|
|
89
|
+
return self.bytes / 1024
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def MiB(self) -> float:
|
|
93
|
+
return self.bytes / 1048576
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def GiB(self) -> float:
|
|
97
|
+
return self.bytes / 1073741824
|
|
98
|
+
|
|
99
|
+
def __int__(self) -> int:
|
|
100
|
+
return self.bytes
|
|
101
|
+
|
|
102
|
+
def __eq__(self, value: object) -> bool:
|
|
103
|
+
if not isinstance(value, _SupportsInt):
|
|
104
|
+
return False
|
|
105
|
+
return self.bytes == int(value)
|
|
106
|
+
|
|
107
|
+
def __lt__(self, value: _SupportsInt) -> bool:
|
|
108
|
+
return self.bytes < int(value)
|
|
109
|
+
|
|
110
|
+
def __le__(self, value: _SupportsInt) -> bool:
|
|
111
|
+
return self.bytes <= int(value)
|
|
112
|
+
|
|
113
|
+
def __gt__(self, value: _SupportsInt) -> bool:
|
|
114
|
+
return int(value) < self.bytes
|
|
115
|
+
|
|
116
|
+
def __ge__(self, value: _SupportsInt) -> bool:
|
|
117
|
+
return int(value) <= self.bytes
|
|
118
|
+
|
|
119
|
+
def __add__(self, value: _SupportsInt) -> "FileSize":
|
|
120
|
+
return FileSize(self.bytes + int(value))
|
|
121
|
+
|
|
122
|
+
def __str__(self) -> str:
|
|
123
|
+
return self.humanize(binary=True)
|
|
124
|
+
|
|
125
|
+
def __repr__(self) -> str:
|
|
126
|
+
return f"<{self.__class__.__name__}: '{self!s}'>"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|