nuclear 2.2.2__tar.gz → 2.2.4__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.
- {nuclear-2.2.2 → nuclear-2.2.4}/PKG-INFO +1 -1
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/logging.py +54 -21
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/collections.py +12 -1
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/time.py +5 -0
- nuclear-2.2.4/nuclear/version.py +1 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/PKG-INFO +1 -1
- nuclear-2.2.2/nuclear/version.py +0 -1
- {nuclear-2.2.2 → nuclear-2.2.4}/LICENSE +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/README.md +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/args/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/args/args_que.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/args/container.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/autocomplete/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/autocomplete/autocomplete.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/autocomplete/install.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/builder.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/decorator_builder.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/rule.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/rule_factory.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/typedef.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/completers/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/completers/file.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/help.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/context.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/error.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/inject.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/internal_vars.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/keyword.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/matcher.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/parser.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/transform.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/validate.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/value.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/types/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/types/boolean.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/types/filesystem.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/types/time.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/inspection/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/inspection/inspection.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/py.typed +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/shell/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/shell/background_cmd.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/shell/shell_utils.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/catch.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/context_error.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/exception.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/__init__.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/config.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/datamodel.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/env.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/files.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/functools.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/input.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/regex.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/strings.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/url.py +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/SOURCES.txt +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/dependency_links.txt +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/requires.txt +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/top_level.txt +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/setup.cfg +0 -0
- {nuclear-2.2.2 → nuclear-2.2.4}/setup.py +0 -0
|
@@ -1,34 +1,43 @@
|
|
|
1
1
|
from contextlib import contextmanager
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
3
|
import logging
|
|
4
|
+
import json
|
|
4
5
|
import os
|
|
5
6
|
import sys
|
|
6
7
|
import threading
|
|
7
|
-
|
|
8
|
-
import
|
|
8
|
+
import time
|
|
9
|
+
from typing import Dict, Any, Callable, List, Optional
|
|
9
10
|
|
|
10
11
|
from colorama import init, Fore, Style
|
|
11
12
|
|
|
12
13
|
from nuclear.sublog.context_error import ContextError
|
|
13
14
|
from nuclear.sublog.exception import extended_exception_details
|
|
15
|
+
from nuclear.utils.collections import coalesce
|
|
14
16
|
from nuclear.utils.env import is_env_flag_enabled
|
|
15
17
|
from nuclear.utils.strings import strip_ansi_colors
|
|
16
18
|
|
|
17
19
|
LOG_FORMAT = f'{Style.DIM}[%(asctime)s]{Style.RESET_ALL} %(levelname)s %(message)s'
|
|
18
20
|
LOG_DATE_FORMAT = r'%Y-%m-%d %H:%M:%S'
|
|
21
|
+
LOG_DATE_FORMAT_UTC = r'%Y-%m-%d %H:%M:%SZ'
|
|
19
22
|
ISO_DATE_FORMAT = r'%Y-%m-%dT%H:%M:%S.%fZ'
|
|
20
23
|
LOGGING_LOGGER_NAME = 'nuclear.sublog'
|
|
21
|
-
STRUCTURED_LOGGING = is_env_flag_enabled('STRUCTURED_LOGGING', 'false')
|
|
22
24
|
|
|
23
|
-
log_level: str = os.environ.get('LOG_LEVEL', 'debug')
|
|
24
25
|
simultaneous_print_lock = threading.Lock()
|
|
25
26
|
_logging_logger: logging.Logger = logging.getLogger(LOGGING_LOGGER_NAME)
|
|
26
|
-
_log_state = {
|
|
27
|
-
|
|
28
|
-
}
|
|
27
|
+
_log_state = {'init': False}
|
|
28
|
+
|
|
29
29
|
|
|
30
|
-
def init_logs(
|
|
31
|
-
|
|
30
|
+
def init_logs(
|
|
31
|
+
show_time: Optional[bool] = None,
|
|
32
|
+
show_log_level: Optional[bool] = None,
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Configure loggers: formatters, handlers and log levels
|
|
36
|
+
:param show_time: True to display formatted time in each log message,
|
|
37
|
+
False to turn it off, None to use the default
|
|
38
|
+
:param show_log_level: True to display log level in each log message,
|
|
39
|
+
False to turn it off, None to use the default
|
|
40
|
+
"""
|
|
32
41
|
logging_kwargs: Dict[str, Any] = {
|
|
33
42
|
'stream': sys.stdout,
|
|
34
43
|
'format': LOG_FORMAT,
|
|
@@ -37,22 +46,30 @@ def init_logs():
|
|
|
37
46
|
}
|
|
38
47
|
if sys.version_info[1] >= 8:
|
|
39
48
|
logging_kwargs['force'] = True
|
|
40
|
-
if
|
|
49
|
+
if is_env_flag_enabled('NUCLEAR_STRUCTURED_LOGGING', 'false'):
|
|
50
|
+
logging_kwargs['datefmt'] = ISO_DATE_FORMAT
|
|
41
51
|
logging.basicConfig(**logging_kwargs)
|
|
42
52
|
for handler in logging.getLogger().handlers:
|
|
43
|
-
handler.setFormatter(
|
|
53
|
+
handler.setFormatter(StructuredFormatter())
|
|
44
54
|
else:
|
|
45
|
-
|
|
55
|
+
_show_log_time: bool = coalesce(show_time, is_env_flag_enabled('NUCLEAR_LOG_TIME', 'true'))
|
|
56
|
+
_show_log_level: bool = coalesce(show_log_level, is_env_flag_enabled('NUCLEAR_LOG_LEVEL_SHOW', 'true'))
|
|
46
57
|
logging.basicConfig(**logging_kwargs)
|
|
47
58
|
for handler in logging.getLogger().handlers:
|
|
48
|
-
handler.setFormatter(
|
|
59
|
+
handler.setFormatter(ColoredFormatter(_show_log_time, _show_log_level))
|
|
49
60
|
|
|
61
|
+
log_level: str = os.environ.get('NUCLEAR_LOG_LEVEL', 'debug')
|
|
50
62
|
level = _get_logging_level(log_level)
|
|
51
63
|
root_logger = logging.getLogger(LOGGING_LOGGER_NAME)
|
|
52
64
|
root_logger.setLevel(level)
|
|
53
65
|
_log_state['init'] = True
|
|
54
66
|
|
|
55
67
|
|
|
68
|
+
def init_logs_once():
|
|
69
|
+
if not _log_state['init']:
|
|
70
|
+
init_logs()
|
|
71
|
+
|
|
72
|
+
|
|
56
73
|
def get_logger(logger_name: str) -> logging.Logger:
|
|
57
74
|
"""
|
|
58
75
|
Get configured child logger
|
|
@@ -65,7 +82,7 @@ def get_logger(logger_name: str) -> logging.Logger:
|
|
|
65
82
|
class ContextLogger:
|
|
66
83
|
def __init__(self, ctx: Dict[str, Any]):
|
|
67
84
|
self.ctx: Dict[str, Any] = ctx
|
|
68
|
-
self.structured_logging: bool =
|
|
85
|
+
self.structured_logging: bool = is_env_flag_enabled('NUCLEAR_STRUCTURED_LOGGING', 'false')
|
|
69
86
|
self.first_use = True
|
|
70
87
|
|
|
71
88
|
def error(self, message: str, **ctx):
|
|
@@ -147,9 +164,10 @@ def add_context(context_name: str, log: bool = False, **ctx):
|
|
|
147
164
|
|
|
148
165
|
|
|
149
166
|
class ColoredFormatter(logging.Formatter):
|
|
150
|
-
def __init__(self,
|
|
167
|
+
def __init__(self, log_time_show: bool, log_level_show: bool):
|
|
151
168
|
logging.Formatter.__init__(self)
|
|
152
|
-
self.
|
|
169
|
+
self.log_level_show: bool = log_level_show
|
|
170
|
+
self.log_time_show: bool = log_time_show
|
|
153
171
|
|
|
154
172
|
log_level_templates = {
|
|
155
173
|
'CRITICAL': f'{Style.BRIGHT + Fore.RED}CRIT {Style.RESET_ALL}',
|
|
@@ -160,18 +178,33 @@ class ColoredFormatter(logging.Formatter):
|
|
|
160
178
|
}
|
|
161
179
|
|
|
162
180
|
def format(self, record: logging.LogRecord) -> str:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
181
|
+
parts: List[str] = []
|
|
182
|
+
part_time = self.format_time()
|
|
183
|
+
if part_time:
|
|
184
|
+
parts.append(part_time)
|
|
185
|
+
if self.log_level_show:
|
|
186
|
+
part_levelname = self.log_level_templates.get(record.levelname, record.levelname)
|
|
187
|
+
parts.append(part_levelname)
|
|
188
|
+
parts.append(record.msg)
|
|
189
|
+
line = ' '.join(parts)
|
|
166
190
|
if not sys.stdout.isatty():
|
|
167
191
|
line = strip_ansi_colors(line)
|
|
168
192
|
return line
|
|
193
|
+
|
|
194
|
+
def format_time(self) -> Optional[str]:
|
|
195
|
+
if not self.log_time_show:
|
|
196
|
+
return None
|
|
197
|
+
now_tz = datetime.now().astimezone()
|
|
198
|
+
if time.timezone == 0:
|
|
199
|
+
time_formatted = now_tz.strftime(LOG_DATE_FORMAT_UTC)
|
|
200
|
+
else:
|
|
201
|
+
time_formatted = now_tz.strftime(LOG_DATE_FORMAT)
|
|
202
|
+
return f'{Style.DIM}[{time_formatted}]{Style.RESET_ALL}'
|
|
169
203
|
|
|
170
204
|
|
|
171
205
|
class StructuredFormatter(logging.Formatter):
|
|
172
|
-
def __init__(self
|
|
206
|
+
def __init__(self):
|
|
173
207
|
logging.Formatter.__init__(self)
|
|
174
|
-
self.plain_formatter = plain_formatter
|
|
175
208
|
|
|
176
209
|
def format(self, record: logging.LogRecord) -> str:
|
|
177
210
|
timestamp: float = record.created
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Callable, Iterable, TypeVar, List, Union
|
|
1
|
+
from typing import Any, Callable, Iterable, TypeVar, List, Union, Optional
|
|
2
2
|
|
|
3
3
|
T = TypeVar('T')
|
|
4
4
|
|
|
@@ -42,3 +42,14 @@ def flatten(collection: Iterable[Union[T, List[T]]]) -> List[T]:
|
|
|
42
42
|
else:
|
|
43
43
|
flat.append(item)
|
|
44
44
|
return flat
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def filter_not_none(items: List[Optional[T]]) -> List[T]:
|
|
48
|
+
return [item for item in items if item is not None]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def coalesce(*items: Optional[T]) -> T:
|
|
52
|
+
for item in items:
|
|
53
|
+
if item is not None:
|
|
54
|
+
return item
|
|
55
|
+
raise ValueError('all items are None')
|
|
@@ -14,6 +14,11 @@ def now() -> datetime:
|
|
|
14
14
|
return datetime.now(timezone.utc)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def now_tz() -> datetime:
|
|
18
|
+
"""Return current datetime with local timezone set"""
|
|
19
|
+
return datetime.now().astimezone()
|
|
20
|
+
|
|
21
|
+
|
|
17
22
|
def now_timestamp() -> int:
|
|
18
23
|
return datetime_to_timestamp(now())
|
|
19
24
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.2.4"
|
nuclear-2.2.2/nuclear/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.2.2"
|
|
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
|
|
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
|
|
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
|
|
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
|