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.
Files changed (67) hide show
  1. {nuclear-2.2.2 → nuclear-2.2.4}/PKG-INFO +1 -1
  2. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/logging.py +54 -21
  3. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/collections.py +12 -1
  4. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/time.py +5 -0
  5. nuclear-2.2.4/nuclear/version.py +1 -0
  6. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/PKG-INFO +1 -1
  7. nuclear-2.2.2/nuclear/version.py +0 -1
  8. {nuclear-2.2.2 → nuclear-2.2.4}/LICENSE +0 -0
  9. {nuclear-2.2.2 → nuclear-2.2.4}/README.md +0 -0
  10. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/__init__.py +0 -0
  11. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/__init__.py +0 -0
  12. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/args/__init__.py +0 -0
  13. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/args/args_que.py +0 -0
  14. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/args/container.py +0 -0
  15. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/autocomplete/__init__.py +0 -0
  16. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/autocomplete/autocomplete.py +0 -0
  17. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/autocomplete/install.py +0 -0
  18. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/__init__.py +0 -0
  19. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/builder.py +0 -0
  20. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/decorator_builder.py +0 -0
  21. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/rule.py +0 -0
  22. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/rule_factory.py +0 -0
  23. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/builder/typedef.py +0 -0
  24. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/completers/__init__.py +0 -0
  25. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/completers/file.py +0 -0
  26. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/help.py +0 -0
  27. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/__init__.py +0 -0
  28. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/context.py +0 -0
  29. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/error.py +0 -0
  30. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/inject.py +0 -0
  31. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/internal_vars.py +0 -0
  32. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/keyword.py +0 -0
  33. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/matcher.py +0 -0
  34. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/parser.py +0 -0
  35. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/transform.py +0 -0
  36. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/validate.py +0 -0
  37. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/parser/value.py +0 -0
  38. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/types/__init__.py +0 -0
  39. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/types/boolean.py +0 -0
  40. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/types/filesystem.py +0 -0
  41. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/cli/types/time.py +0 -0
  42. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/inspection/__init__.py +0 -0
  43. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/inspection/inspection.py +0 -0
  44. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/py.typed +0 -0
  45. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/shell/__init__.py +0 -0
  46. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/shell/background_cmd.py +0 -0
  47. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/shell/shell_utils.py +0 -0
  48. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/__init__.py +0 -0
  49. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/catch.py +0 -0
  50. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/context_error.py +0 -0
  51. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/sublog/exception.py +0 -0
  52. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/__init__.py +0 -0
  53. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/config.py +0 -0
  54. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/datamodel.py +0 -0
  55. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/env.py +0 -0
  56. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/files.py +0 -0
  57. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/functools.py +0 -0
  58. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/input.py +0 -0
  59. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/regex.py +0 -0
  60. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/strings.py +0 -0
  61. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear/utils/url.py +0 -0
  62. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/SOURCES.txt +0 -0
  63. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/dependency_links.txt +0 -0
  64. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/requires.txt +0 -0
  65. {nuclear-2.2.2 → nuclear-2.2.4}/nuclear.egg-info/top_level.txt +0 -0
  66. {nuclear-2.2.2 → nuclear-2.2.4}/setup.cfg +0 -0
  67. {nuclear-2.2.2 → nuclear-2.2.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nuclear
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: Declarative parser for command line interfaces
5
5
  Home-page: https://github.com/igrek51/nuclear
6
6
  Author: igrek51
@@ -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
- from typing import Dict, Any, Callable
8
- import json
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
- 'init': False,
28
- }
27
+ _log_state = {'init': False}
28
+
29
29
 
30
- def init_logs():
31
- """Configure loggers: formatters, handlers and log levels"""
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 not is_env_flag_enabled('STRUCTURED_LOGGING', 'false'):
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(ColoredFormatter(handler.formatter))
53
+ handler.setFormatter(StructuredFormatter())
44
54
  else:
45
- logging_kwargs['datefmt'] = ISO_DATE_FORMAT
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(StructuredFormatter(handler.formatter))
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 = STRUCTURED_LOGGING
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, plain_formatter):
167
+ def __init__(self, log_time_show: bool, log_level_show: bool):
151
168
  logging.Formatter.__init__(self)
152
- self.plain_formatter = plain_formatter
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
- if record.levelname in self.log_level_templates:
164
- record.levelname = self.log_level_templates[record.levelname].format(record.levelname)
165
- line: str = self.plain_formatter.format(record)
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, plain_formatter):
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"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nuclear
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: Declarative parser for command line interfaces
5
5
  Home-page: https://github.com/igrek51/nuclear
6
6
  Author: igrek51
@@ -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