cxppython 0.0.1__tar.gz → 0.0.3__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 (27) hide show
  1. {cxppython-0.0.1 → cxppython-0.0.3}/PKG-INFO +8 -1
  2. cxppython-0.0.3/README.md +8 -0
  3. cxppython-0.0.3/cxppython/__init__.py +3 -0
  4. cxppython-0.0.3/cxppython/core/__init__.py +0 -0
  5. cxppython-0.0.3/cxppython/core/config.py +248 -0
  6. {cxppython-0.0.1 → cxppython-0.0.3}/cxppython/main.py +2 -1
  7. cxppython-0.0.3/cxppython/utils/__init__.py +0 -0
  8. cxppython-0.0.3/cxppython/utils/btlogging/__init__.py +11 -0
  9. cxppython-0.0.3/cxppython/utils/btlogging/console.py +73 -0
  10. cxppython-0.0.3/cxppython/utils/btlogging/defines.py +11 -0
  11. cxppython-0.0.3/cxppython/utils/btlogging/format.py +214 -0
  12. cxppython-0.0.3/cxppython/utils/btlogging/helpers.py +71 -0
  13. cxppython-0.0.3/cxppython/utils/btlogging/loggingmachine.py +664 -0
  14. cxppython-0.0.3/cxppython/utils/database/__init__.py +2 -0
  15. cxppython-0.0.3/cxppython/utils/database/mysql.py +103 -0
  16. cxppython-0.0.3/cxppython/utils/easy_imports.py +37 -0
  17. {cxppython-0.0.1 → cxppython-0.0.3}/cxppython.egg-info/PKG-INFO +8 -1
  18. cxppython-0.0.3/cxppython.egg-info/SOURCES.txt +22 -0
  19. {cxppython-0.0.1 → cxppython-0.0.3}/setup.py +2 -2
  20. cxppython-0.0.1/README.md +0 -1
  21. cxppython-0.0.1/cxppython/__init__.py +0 -1
  22. cxppython-0.0.1/cxppython.egg-info/SOURCES.txt +0 -10
  23. {cxppython-0.0.1 → cxppython-0.0.3}/LICENSE +0 -0
  24. {cxppython-0.0.1 → cxppython-0.0.3}/cxppython.egg-info/dependency_links.txt +0 -0
  25. {cxppython-0.0.1 → cxppython-0.0.3}/cxppython.egg-info/requires.txt +0 -0
  26. {cxppython-0.0.1 → cxppython-0.0.3}/cxppython.egg-info/top_level.txt +0 -0
  27. {cxppython-0.0.1 → cxppython-0.0.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cxppython
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: A python utils package
5
5
  Home-page: https://github.com/yourusername/my_package
6
6
  Author: cxp
@@ -25,3 +25,10 @@ Dynamic: requires-python
25
25
  Dynamic: summary
26
26
 
27
27
  # cxppython
28
+ ### install build
29
+ - python -m pip install --upgrade pip setuptools wheel twine
30
+ -
31
+ ### build
32
+ - python setup.py sdist bdist_wheel
33
+ ### upload
34
+ - twine upload dist/*
@@ -0,0 +1,8 @@
1
+ # cxppython
2
+ ### install build
3
+ - python -m pip install --upgrade pip setuptools wheel twine
4
+ -
5
+ ### build
6
+ - python setup.py sdist bdist_wheel
7
+ ### upload
8
+ - twine upload dist/*
@@ -0,0 +1,3 @@
1
+ from .utils.btlogging import logging
2
+ from .utils.easy_imports import *
3
+ from .utils.database import db
File without changes
@@ -0,0 +1,248 @@
1
+ """Implementation of the config class, which manages the configuration of different Bittensor modules.
2
+
3
+ Example:
4
+ import argparse
5
+ import bittensor as bt
6
+
7
+ parser = argparse.ArgumentParser('Miner')
8
+ bt.Axon.add_args(parser)
9
+ bt.Subtensor.add_args(parser)
10
+ bt.Async_subtensor.add_args(parser)
11
+ bt.Wallet.add_args(parser)
12
+ bt.logging.add_args(parser)
13
+ bt.PriorityThreadPoolExecutor.add_args(parser)
14
+ config = bt.config(parser)
15
+
16
+ print(config)
17
+ """
18
+
19
+ import argparse
20
+ import os
21
+ import sys
22
+ from copy import deepcopy
23
+ from typing import Any, TypeVar, Type, Optional
24
+
25
+ import yaml
26
+ from munch import DefaultMunch
27
+
28
+
29
+ def _filter_keys(obj):
30
+ """Filters keys from an object, excluding private and certain internal properties."""
31
+ if isinstance(obj, dict):
32
+ return {
33
+ k: _filter_keys(v)
34
+ for k, v in obj.items()
35
+ if not k.startswith("__") and not k.startswith("_Config__is_set")
36
+ }
37
+ elif isinstance(obj, (Config, DefaultMunch)):
38
+ return _filter_keys(obj.toDict())
39
+ return obj
40
+
41
+
42
+ class InvalidConfigFile(Exception):
43
+ """Raised when there's an error loading the config file."""
44
+
45
+
46
+ class Config(DefaultMunch):
47
+ """Manages configuration for Bittensor modules with nested namespace support."""
48
+
49
+ def __init__(
50
+ self,
51
+ parser: argparse.ArgumentParser = None,
52
+ args: Optional[list[str]] = None,
53
+ strict: bool = False,
54
+ default: Any = None,
55
+ ) -> None:
56
+ super().__init__(default)
57
+ self.__is_set = {}
58
+
59
+ if parser is None:
60
+ return
61
+
62
+ self._add_default_arguments(parser)
63
+ args = args or sys.argv[1:]
64
+ self._validate_required_args(parser, args)
65
+
66
+ config_params = self._parse_args(args, parser, strict=False)
67
+ config_path = self._get_config_path(config_params)
68
+ strict = strict or getattr(config_params, "strict", False)
69
+
70
+ if config_path:
71
+ self._load_config_file(parser, config_path)
72
+
73
+ params = self._parse_args(args, parser, strict)
74
+ self._build_config_tree(params)
75
+ self._detect_set_parameters(parser, args)
76
+
77
+ def __str__(self) -> str:
78
+ """String representation without private keys, optimized to avoid deepcopy."""
79
+ cleaned = _filter_keys(self.toDict())
80
+ return "\n" + yaml.dump(cleaned, sort_keys=False, default_flow_style=False)
81
+
82
+ def __repr__(self) -> str:
83
+ """String representation of the Config."""
84
+ return self.__str__()
85
+
86
+ def _validate_required_args(
87
+ self, parser: argparse.ArgumentParser, args: list[str]
88
+ ) -> None:
89
+ """Validates required arguments are present."""
90
+ missing = self._find_missing_required_args(parser, args)
91
+ if missing:
92
+ raise ValueError(f"Missing required arguments: {', '.join(missing)}")
93
+
94
+ def _find_missing_required_args(
95
+ self, parser: argparse.ArgumentParser, args: list[str]
96
+ ) -> list[str]:
97
+ """Identifies missing required arguments."""
98
+ required = {a.dest for a in parser._actions if a.required}
99
+ provided = {a.split("=")[0].lstrip("-") for a in args if a.startswith("-")}
100
+ return list(required - provided)
101
+
102
+ def _get_config_path(self, params: DefaultMunch) -> Optional[str]:
103
+ """Gets Config path from parameters."""
104
+ return getattr(params, "config", None)
105
+
106
+ def _load_config_file(self, parser: argparse.ArgumentParser, path: str) -> None:
107
+ """Loads Config from YAML file."""
108
+ try:
109
+ with open(os.path.expanduser(path)) as f:
110
+ config = yaml.safe_load(f)
111
+ print(f"Loading config from: {path}")
112
+ parser.set_defaults(**config)
113
+ except Exception as e:
114
+ raise InvalidConfigFile(f"Error loading config: {e}") from e
115
+
116
+ def _build_config_tree(self, params: DefaultMunch) -> None:
117
+ """Builds nested Config structure."""
118
+ for key, value in params.items():
119
+ if key in ["__is_set"]:
120
+ continue
121
+ current = self
122
+ parts = key.split(".")
123
+ for part in parts[:-1]:
124
+ current = current.setdefault(part, Config())
125
+ current[parts[-1]] = value
126
+
127
+ def _detect_set_parameters(
128
+ self, parser: argparse.ArgumentParser, args: list[str]
129
+ ) -> None:
130
+ """Detects which parameters were explicitly set."""
131
+ temp_parser = self._create_non_default_parser(parser)
132
+ detected = self._parse_args(args, temp_parser, strict=False)
133
+ self.__is_set = DefaultMunch(**{k: True for k in detected.keys()})
134
+
135
+ def _create_non_default_parser(
136
+ self, original: argparse.ArgumentParser
137
+ ) -> argparse.ArgumentParser:
138
+ """Creates a parser that ignores default values."""
139
+ parser = deepcopy(original)
140
+ for action in parser._actions:
141
+ action.default = argparse.SUPPRESS
142
+ return parser
143
+
144
+ @staticmethod
145
+ def _parse_args(
146
+ args: list[str], parser: argparse.ArgumentParser, strict: bool
147
+ ) -> DefaultMunch:
148
+ """Parses args with error handling."""
149
+ try:
150
+ if strict:
151
+ result = parser.parse_args(args)
152
+ return DefaultMunch.fromDict(vars(result))
153
+
154
+ result, unknown = parser.parse_known_args(args)
155
+ for arg in unknown:
156
+ if arg.startswith("--") and (name := arg[2:]) in vars(result):
157
+ setattr(result, name, True)
158
+ return DefaultMunch.fromDict(vars(result))
159
+ except Exception:
160
+ raise ValueError("Invalid arguments provided.")
161
+
162
+ def __deepcopy__(self, memo) -> "Config":
163
+ """Creates a deep copy that maintains Config type."""
164
+ new_config = Config()
165
+ memo[id(self)] = new_config
166
+
167
+ for key, value in self.items():
168
+ new_config[key] = deepcopy(value, memo)
169
+
170
+ new_config.__is_set = deepcopy(self.__is_set, memo)
171
+ return new_config
172
+
173
+ def merge(self, other: "Config") -> None:
174
+ """Merges another Config into this one."""
175
+ self.update(self._merge_dicts(self, other))
176
+ self.__is_set.update(other.__is_set)
177
+
178
+ @staticmethod
179
+ def _merge_dicts(a: DefaultMunch, b: DefaultMunch) -> DefaultMunch:
180
+ """Recursively merges two Config objects."""
181
+ result = deepcopy(a)
182
+ for key, value in b.items():
183
+ if key in result:
184
+ if isinstance(result[key], DefaultMunch) and isinstance(
185
+ value, DefaultMunch
186
+ ):
187
+ result[key] = Config._merge_dicts(result[key], value)
188
+ else:
189
+ result[key] = deepcopy(value)
190
+ else:
191
+ result[key] = deepcopy(value)
192
+ return result
193
+
194
+ def is_set(self, param_name: str) -> bool:
195
+ """Checks if a parameter was explicitly set."""
196
+ return self.__is_set.get(param_name, False)
197
+
198
+ def to_dict(self) -> dict:
199
+ """Returns the configuration as a dictionary."""
200
+ return self.toDict()
201
+
202
+ def _add_default_arguments(self, parser: argparse.ArgumentParser) -> None:
203
+ """Adds default arguments to the Config parser."""
204
+ arguments = [
205
+ (
206
+ "--config",
207
+ {
208
+ "type": str,
209
+ "help": "If set, defaults are overridden by passed file.",
210
+ "default": False,
211
+ },
212
+ ),
213
+ (
214
+ "--strict",
215
+ {
216
+ "action": "store_true",
217
+ "help": "If flagged, config will check that only exact arguments have been set.",
218
+ "default": False,
219
+ },
220
+ ),
221
+ (
222
+ "--no_version_checking",
223
+ {
224
+ "action": "store_true",
225
+ "help": "Set `true to stop cli version checking.",
226
+ "default": False,
227
+ },
228
+ ),
229
+ ]
230
+
231
+ for arg_name, kwargs in arguments:
232
+ try:
233
+ parser.add_argument(arg_name, **kwargs)
234
+ except argparse.ArgumentError:
235
+ # this can fail if argument has already been added.
236
+ pass
237
+
238
+
239
+ T = TypeVar("T", bound="DefaultConfig")
240
+
241
+
242
+ class DefaultConfig(Config):
243
+ """A Config with a set of default values."""
244
+
245
+ @classmethod
246
+ def default(cls: Type[T]) -> T:
247
+ """Get default config."""
248
+ raise NotImplementedError("Function default is not implemented.")
@@ -1,6 +1,7 @@
1
1
  def say_hello():
2
2
  print("Hello from my_package!")
3
-
3
+ def say_hello2():
4
+ print("Hello from my_package!")
4
5
 
5
6
  if __name__ == '__main__':
6
7
  say_hello()
File without changes
@@ -0,0 +1,11 @@
1
+ """
2
+ btlogging sub-package standardized logging for Bittensor.
3
+
4
+ This module provides logging functionality for the Bittensor package. It includes custom loggers, handlers, and
5
+ formatters to ensure consistent logging throughout the project.
6
+ """
7
+
8
+ from .loggingmachine import LoggingMachine
9
+
10
+
11
+ logging = LoggingMachine(LoggingMachine.config())
@@ -0,0 +1,73 @@
1
+ """
2
+ BittensorConsole class gives the ability to log messages to the terminal without changing Bittensor logging level.
3
+
4
+ Example:
5
+ from bittensor import logging
6
+
7
+ # will be logged
8
+ logging.console.info("info message")
9
+ logging.console.error("error message")
10
+ logging.console.success("success message")
11
+ logging.console.warning("warning message")
12
+ logging.console.critical("critical message")
13
+
14
+ # will not be logged
15
+ logging.info("test info")
16
+ """
17
+
18
+ from functools import wraps
19
+ from typing import Callable, TYPE_CHECKING
20
+
21
+ from .helpers import all_loggers
22
+
23
+ if TYPE_CHECKING:
24
+ from .loggingmachine import LoggingMachine
25
+
26
+
27
+ def _print_wrapper(func: "Callable"):
28
+ @wraps(func)
29
+ def wrapper(self: "BittensorConsole", *args, **kwargs):
30
+ """A wrapper function to temporarily set the logger level to debug."""
31
+ old_logger_level = self.logger.get_level()
32
+ self.logger.set_console()
33
+ func(self, *args, **kwargs)
34
+
35
+ for logger in all_loggers():
36
+ logger.setLevel(old_logger_level)
37
+
38
+ return wrapper
39
+
40
+
41
+ class BittensorConsole:
42
+ def __init__(self, logger: "LoggingMachine"):
43
+ self.logger = logger
44
+
45
+ @_print_wrapper
46
+ def debug(self, message: str):
47
+ """Logs a DEBUG message to the console."""
48
+ self.logger.debug(message)
49
+
50
+ @_print_wrapper
51
+ def info(self, message: str):
52
+ """Logs a INFO message to the console."""
53
+ self.logger.info(message)
54
+
55
+ @_print_wrapper
56
+ def success(self, message: str):
57
+ """Logs a SUCCESS message to the console."""
58
+ self.logger.success(message)
59
+
60
+ @_print_wrapper
61
+ def warning(self, message: str):
62
+ """Logs a WARNING message to the console."""
63
+ self.logger.warning(message)
64
+
65
+ @_print_wrapper
66
+ def error(self, message: str):
67
+ """Logs a ERROR message to the console."""
68
+ self.logger.error(message)
69
+
70
+ @_print_wrapper
71
+ def critical(self, message: str):
72
+ """Logs a CRITICAL message to the console."""
73
+ self.logger.critical(message)
@@ -0,0 +1,11 @@
1
+ """Btlogging constant definition module."""
2
+
3
+ BASE_LOG_FORMAT = "%(asctime)s | %(levelname)s | %(message)s"
4
+ TRACE_LOG_FORMAT = (
5
+ f"%(asctime)s | %(levelname)s | %(name)s:%(filename)s:%(lineno)s | %(message)s"
6
+ )
7
+ DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
8
+ BITTENSOR_LOGGER_NAME = "cxppy"
9
+ DEFAULT_LOG_FILE_NAME = "cxppy.log"
10
+ DEFAULT_MAX_ROTATING_LOG_FILE_SIZE = 25 * 1024 * 1024
11
+ DEFAULT_LOG_BACKUP_COUNT = 10
@@ -0,0 +1,214 @@
1
+ """
2
+ btlogging.format module
3
+
4
+ This module defines custom logging formatters for the Bittensor project.
5
+ """
6
+
7
+ import logging
8
+ import time
9
+ from typing import Optional
10
+ from colorama import init, Fore, Back, Style
11
+
12
+ init(wrap=False)
13
+
14
+ TRACE_LEVEL_NUM: int = 5
15
+ SUCCESS_LEVEL_NUM: int = 21
16
+
17
+
18
+ def _trace(self, message: str, *args, **kws):
19
+ if self.isEnabledFor(TRACE_LEVEL_NUM):
20
+ self._log(TRACE_LEVEL_NUM, message, args, **kws)
21
+
22
+
23
+ def _success(self, message: str, *args, **kws):
24
+ if self.isEnabledFor(SUCCESS_LEVEL_NUM):
25
+ self._log(SUCCESS_LEVEL_NUM, message, args, **kws)
26
+
27
+
28
+ logging.SUCCESS = SUCCESS_LEVEL_NUM
29
+ logging.addLevelName(SUCCESS_LEVEL_NUM, "SUCCESS")
30
+ logging.Logger.success = _success
31
+
32
+ logging.TRACE = TRACE_LEVEL_NUM
33
+ logging.addLevelName(TRACE_LEVEL_NUM, "TRACE")
34
+ logging.Logger.trace = _trace
35
+
36
+ emoji_map: dict[str, str] = {
37
+ ":white_heavy_check_mark:": "✅",
38
+ ":cross_mark:": "❌",
39
+ ":satellite:": "🛰️",
40
+ ":warning:": "⚠️",
41
+ ":arrow_right:": "➡️",
42
+ ":hourglass:": "⏳",
43
+ }
44
+
45
+
46
+ color_map: dict[str, str] = {
47
+ "[red]": Fore.RED,
48
+ "[/red]": Style.RESET_ALL,
49
+ "[blue]": Fore.BLUE,
50
+ "[/blue]": Style.RESET_ALL,
51
+ "[green]": Fore.GREEN,
52
+ "[/green]": Style.RESET_ALL,
53
+ "[magenta]": Fore.MAGENTA,
54
+ "[/magenta]": Style.RESET_ALL,
55
+ "[yellow]": Fore.YELLOW,
56
+ "[/yellow]": Style.RESET_ALL,
57
+ "[orange]": Fore.YELLOW,
58
+ "[/orange]": Style.RESET_ALL,
59
+ }
60
+
61
+
62
+ log_level_color_prefix: dict[int, str] = {
63
+ logging.NOTSET: Fore.RESET,
64
+ logging.TRACE: Fore.MAGENTA,
65
+ logging.DEBUG: Fore.BLUE,
66
+ logging.INFO: Fore.WHITE,
67
+ logging.SUCCESS: Fore.GREEN,
68
+ logging.WARNING: Fore.YELLOW,
69
+ logging.ERROR: Fore.RED,
70
+ logging.CRITICAL: Back.RED,
71
+ }
72
+
73
+
74
+ LOG_FORMATS: dict[int, str] = {
75
+ level: f"{Fore.BLUE}%(asctime)s{Fore.RESET} | {Style.BRIGHT}{color}%(levelname)s\033[0m | %(message)s"
76
+ for level, color in log_level_color_prefix.items()
77
+ }
78
+
79
+ LOG_TRACE_FORMATS: dict[int, str] = {
80
+ level: f"{Fore.BLUE}%(asctime)s{Fore.RESET}"
81
+ f" | {Style.BRIGHT}{color}%(levelname)s{Fore.RESET}{Back.RESET}{Style.RESET_ALL}"
82
+ f" | %(name)s:%(filename)s:%(lineno)s"
83
+ f" | %(message)s"
84
+ for level, color in log_level_color_prefix.items()
85
+ }
86
+
87
+ DEFAULT_LOG_FORMAT: str = (
88
+ f"{Fore.BLUE}%(asctime)s{Fore.RESET} | "
89
+ f"{Style.BRIGHT}{Fore.WHITE}%(levelname)s{Style.RESET_ALL} | "
90
+ f"%(name)s:%(filename)s:%(lineno)s | %(message)s"
91
+ )
92
+
93
+ DEFAULT_TRACE_FORMAT: str = (
94
+ f"{Fore.BLUE}%(asctime)s{Fore.RESET} | "
95
+ f"{Style.BRIGHT}{Fore.WHITE}%(levelname)s{Style.RESET_ALL} | "
96
+ f"%(name)s:%(filename)s:%(lineno)s | %(message)s"
97
+ )
98
+
99
+
100
+ class BtStreamFormatter(logging.Formatter):
101
+ """
102
+ A custom logging formatter for the Bittensor project that overrides the time formatting to include milliseconds,
103
+ centers the level name, and applies custom log formats, emojis, and colors.
104
+ """
105
+
106
+ def __init__(self, *args, **kwargs):
107
+ super().__init__(*args, **kwargs)
108
+ self.trace = False
109
+
110
+ def formatTime(self, record, datefmt: Optional[str] = None) -> str:
111
+ """
112
+ Override formatTime to add milliseconds.
113
+
114
+ Args:
115
+ record (logging.LogRecord): The log record.
116
+ datefmt (Optional[str]): The date format string.
117
+
118
+ Returns:
119
+ s (str): The formatted time string with milliseconds.
120
+ """
121
+
122
+ created = self.converter(record.created)
123
+ if datefmt:
124
+ s = time.strftime(datefmt, created)
125
+ else:
126
+ s = time.strftime("%Y-%m-%d %H:%M:%S", created)
127
+ s += f".{int(record.msecs):03d}"
128
+ return s
129
+
130
+ def format(self, record: "logging.LogRecord") -> str:
131
+ """
132
+ Override format to apply custom formatting including emojis and colors.
133
+
134
+ This method saves the original format, applies custom formatting based on the log level and trace flag, replaces
135
+ text with emojis and colors, and then returns the formatted log record.
136
+
137
+ Args:
138
+ record (logging.LogRecord): The log record.
139
+
140
+ Returns:
141
+ result (str): The formatted log record.
142
+ """
143
+
144
+ format_orig = self._style._fmt
145
+ record.levelname = f"{record.levelname:^16}"
146
+
147
+ if record.levelno not in LOG_FORMATS:
148
+ self._style._fmt = (
149
+ DEFAULT_TRACE_FORMAT if self.trace else DEFAULT_LOG_FORMAT
150
+ )
151
+ else:
152
+ if self.trace is True:
153
+ self._style._fmt = LOG_TRACE_FORMATS[record.levelno]
154
+ else:
155
+ self._style._fmt = LOG_FORMATS[record.levelno]
156
+
157
+ for text, emoji in emoji_map.items():
158
+ record.msg = record.msg.replace(text, emoji)
159
+ # Apply color specifiers
160
+ for text, color in color_map.items():
161
+ record.msg = record.msg.replace(text, color)
162
+
163
+ result = super().format(record)
164
+ self._style._fmt = format_orig
165
+
166
+ return result
167
+
168
+ def set_trace(self, state: bool = True):
169
+ """Change formatter state."""
170
+ self.trace = state
171
+
172
+
173
+ class BtFileFormatter(logging.Formatter):
174
+ """
175
+ BtFileFormatter
176
+
177
+ A custom logging formatter for the Bittensor project that overrides the time formatting to include milliseconds and
178
+ centers the level name.
179
+ """
180
+
181
+ def formatTime(
182
+ self, record: "logging.LogRecord", datefmt: Optional[str] = None
183
+ ) -> str:
184
+ """
185
+ Override formatTime to add milliseconds.
186
+
187
+ Args:
188
+ record (logging.LogRecord): The log record.
189
+ datefmt (Optional[str]): The date format string.
190
+
191
+ Returns:
192
+ s (str): The formatted time string with milliseconds.
193
+ """
194
+
195
+ created = self.converter(record.created)
196
+ if datefmt:
197
+ s = time.strftime(datefmt, created)
198
+ else:
199
+ s = time.strftime("%Y-%m-%d %H:%M:%S", created)
200
+ s += f".{int(record.msecs):03d}"
201
+ return s
202
+
203
+ def format(self, record: "logging.LogRecord") -> str:
204
+ """
205
+ Override format to center the level name.
206
+
207
+ Args:
208
+ record (logging.LogRecord): The log record.
209
+
210
+ Returns:
211
+ formatted record (str): The formatted log record.
212
+ """
213
+ record.levelname = f"{record.levelname:^10}"
214
+ return super().format(record)
@@ -0,0 +1,71 @@
1
+ """
2
+ btlogging.helpers module provides helper functions for the Bittensor logging system.
3
+ """
4
+
5
+ import logging
6
+ from typing import Generator
7
+
8
+
9
+ def all_loggers() -> Generator["logging.Logger", None, None]:
10
+ """Generator that yields all logger instances in the application.
11
+
12
+ Iterates through the logging root manager's logger dictionary and yields all active `Logger` instances. It skips
13
+ placeholders and other types that are not instances of `Logger`.
14
+
15
+ Yields:
16
+ logger (logging.Logger): An active logger instance.
17
+ """
18
+ for logger in logging.root.manager.loggerDict.values():
19
+ if isinstance(logger, logging.PlaceHolder):
20
+ continue
21
+ # In some versions of Python, the values in loggerDict might be
22
+ # LoggerAdapter instances instead of Logger instances.
23
+ # We check for Logger instances specifically.
24
+ if isinstance(logger, logging.Logger):
25
+ yield logger
26
+ else:
27
+ # If it's not a Logger instance, it could be a LoggerAdapter or
28
+ # another form that doesn't directly offer logging methods.
29
+ # This branch can be extended to handle such cases as needed.
30
+ pass
31
+
32
+
33
+ def all_logger_names() -> Generator[str, None, None]:
34
+ """
35
+ Generate the names of all active loggers.
36
+
37
+ This function iterates through the logging root manager's logger dictionary and yields the names of all active
38
+ `Logger` instances. It skips placeholders and other types that are not instances of `Logger`.
39
+
40
+ Yields:
41
+ name (str): The name of an active logger.
42
+ """
43
+ for name, logger in logging.root.manager.loggerDict.items():
44
+ if isinstance(logger, logging.PlaceHolder):
45
+ continue
46
+ # In some versions of Python, the values in loggerDict might be
47
+ # LoggerAdapter instances instead of Logger instances.
48
+ # We check for Logger instances specifically.
49
+ if isinstance(logger, logging.Logger):
50
+ yield name
51
+ else:
52
+ # If it's not a Logger instance, it could be a LoggerAdapter or
53
+ # another form that doesn't directly offer logging methods.
54
+ # This branch can be extended to handle such cases as needed.
55
+ pass
56
+
57
+
58
+ def get_max_logger_name_length() -> int:
59
+ """
60
+ Calculate and return the length of the longest logger name.
61
+
62
+ This function iterates through all active logger names and determines the length of the longest name.
63
+
64
+ Returns:
65
+ max_length (int): The length of the longest logger name.
66
+ """
67
+ max_length = 0
68
+ for name in all_logger_names():
69
+ if len(name) > max_length:
70
+ max_length = len(name)
71
+ return max_length