inspy-logger 3.0.dev2__tar.gz → 3.0.dev3__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.
@@ -1,25 +1,25 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: inspy-logger
3
- Version: 3.0.dev2
3
+ Version: 3.0.dev3
4
4
  Summary: Colorable, scalable logger for CLI
5
5
  Home-page: https://github.com/Inspyre-Softworks/inSPy-Logger
6
6
  License: MIT
7
7
  Keywords: cli,color,logging,log,terminal,console,colorama,colorlog,rich
8
8
  Author: Taylor-Jayde Blackstone
9
9
  Author-email: tayjaybabee@gmail.com
10
- Requires-Python: >=3.7,<4.0
10
+ Requires-Python: >=3.9,<4.0
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Natural Language :: English
15
15
  Classifier: Operating System :: OS Independent
16
16
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.7
18
- Classifier: Programming Language :: Python :: 3.8
19
17
  Classifier: Programming Language :: Python :: 3.9
20
18
  Classifier: Programming Language :: Python :: 3.10
21
19
  Classifier: Programming Language :: Python :: 3.11
22
20
  Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.7
22
+ Classifier: Programming Language :: Python :: 3.8
23
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
24
  Classifier: Topic :: System :: Logging
25
25
  Classifier: Topic :: Utilities
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env python3
2
2
  import inspect
3
+ import os
3
4
  import logging
4
5
  from rich.logging import RichHandler
5
6
  from inspy_logger.__about__ import __prog__ as PROG_NAME
6
7
  from inspy_logger.helpers import find_variable_in_call_stack
7
8
 
9
+
8
10
  # Let's set up some constants.
9
11
  LEVEL_MAP = [
10
12
  ('debug', logging.DEBUG),
@@ -144,8 +146,7 @@ class Logger:
144
146
  child.set_level(file_level=file_level)
145
147
 
146
148
  def get_child(self, name=None, console_level=None, file_level=None):
147
- self.logger.debug("Getting child logger")
148
- console_level = console_level or DEFAULT_LOGGING_LEVEL
149
+ console_level = console_level or self.__console_level
149
150
  caller_frame = inspect.stack()[1]
150
151
 
151
152
  if name is None:
@@ -159,7 +160,7 @@ class Logger:
159
160
  if child.logger.name == child_logger_name:
160
161
  return child
161
162
 
162
- child_logger = Logger(child_logger_name, console_level, file_level, parent=self)
163
+ child_logger = Logger(name=child_logger_name, console_level=console_level, file_level=file_level, parent=self)
163
164
  self.children.append(child_logger)
164
165
  return child_logger
165
166
 
@@ -195,6 +196,19 @@ class Logger:
195
196
 
196
197
  return results
197
198
 
199
+ def debug(self, message):
200
+ self.logger.debug(message)
201
+
202
+ def info(self, message):
203
+ self.logger.info(message)
204
+
205
+ def warning(self, message):
206
+ self.logger.warning(message)
207
+
208
+ def error(self, message):
209
+ self.logger.error(message)
210
+
211
+
198
212
  def __repr__(self):
199
213
  name = self.name
200
214
  hex_id = hex(id(self))
@@ -208,119 +222,59 @@ class Logger:
208
222
  if parent_part.endswith('|'):
209
223
  parent_part = str(parent_part[:-2])
210
224
 
211
- return f'<Logger: {name} at {hex_id}{parent_part}>'
212
-
213
-
214
- found_level = find_variable_in_call_stack('INSPY_LOG_LEVEL', DEFAULT_LOGGING_LEVEL)
215
-
216
-
217
- LOG_DEVICE = Logger(PROG_NAME, found_level)
218
- MOD_LOG_DEVICE = LOG_DEVICE.get_child("log_engine", found_level)
219
- MOD_LOGGER = MOD_LOG_DEVICE.logger
220
- MOD_LOGGER.debug(f"Started logger for {__name__}.")
221
-
222
- add_child = LOG_DEVICE.get_child
223
-
224
- InspyLogger = Logger
225
-
226
- def _get_parent_logging_device():
227
- """
228
- Determines the parent logging device by inspecting the caller's log_device or parent_log_device attribute.
229
-
230
- Returns:
231
- Logger: The parent logging device.
232
- """
233
- MOD_LOGGER.debug("Determining parent logging device")
234
- caller_frame = inspect.currentframe().f_back
235
- caller_locals = caller_frame.f_locals
225
+ return f'<Logger: {name} w/ level {self.logger.level} at {hex_id}{parent_part}>'
236
226
 
237
- if "logger" in caller_locals:
238
- return caller_locals["logger"]
239
- elif "parent_log_device" in caller_locals:
240
- return caller_locals["parent_log_device"]
241
- else:
242
- raise ValueError("Unable to determine the parent logging device.")
243
227
 
228
+ @classmethod
229
+ def create_logger_for_caller(cls):
230
+ """
231
+ Creates a logger for the module that calls this method.
244
232
 
245
- class Loggable:
246
- """
247
- A metaclass to enhance classes with logging capabilities. Classes that inherit from
248
- 'Loggable' can instantly access a logger without manually setting it up. This logger
249
- is derived from a parent logger, ensuring consistent logging behavior and hierarchy.
250
-
251
- Attributes:
252
- - log_device: The logger device associated with the instance of the class.
253
- """
254
-
255
- def __init__(self, parent_log_device=None, **kwargs):
256
- self.parent_log_device = parent_log_device
257
- self.__log_name = self.__class__.__name__
258
- if self.parent_log_device is not None:
259
- self.__log_device = self.parent_log_device.get_child(
260
- self.__class__.__name__
261
- )
233
+ Returns:
234
+ Logger: An instance of the Logger class for the calling module.
235
+ """
236
+ frame = inspect.currentframe().f_back
237
+ #print(frame)
238
+ #print(dir(frame))
239
+ #print(dir(inspect.getmodule(frame)))
240
+ #print(inspect.getmodule(frame).__name__)
241
+ module_path = cls._determine_module_path(frame)
242
+ if module_path:
243
+ return cls(module_path)
262
244
  else:
263
- self.__log_device = _get_parent_logging_device().get_child(
264
- self.__class__.__name__
265
- )
266
-
267
- @property
268
- def log_device(self):
269
- return self.__log_device
270
-
271
- @log_device.setter
272
- def log_device(self, new):
273
- if not isinstance(new, Logger):
274
- raise TypeError('log_device must be of type "Logger"')
275
-
276
- self.__log_device = new
245
+ raise ValueError("Unable to determine module path for logger creation.")
277
246
 
278
- def create_child_logger(self, name=None, override=False):
247
+ @staticmethod
248
+ def _determine_module_path(frame):
279
249
  """
280
- Creates and returns a child logger of this object's logger.
250
+ Determines the in-project path of the module from the call frame.
281
251
 
282
- Parameters:
283
- name (str, optional): The name of the child logger.
284
- If not provided, the name of the calling function is used.
285
- override (bool, optional): A flag to override the membership check. Defaults to False.
252
+ Args:
253
+ frame: The frame from which to determine the module path.
286
254
 
287
255
  Returns:
288
- Logger: An instance of the Logger class that represents the child logger.
256
+ str: The in-project path of the module.
289
257
  """
290
- if not override:
291
- self.__is_member__()
258
+ module = inspect.getmodule(frame)
259
+ if module:
260
+ base_path = os.path.dirname(os.path.abspath(module.__file__))
261
+ relative_path = os.path.relpath(frame.f_code.co_filename, base_path)
262
+ module_path = relative_path.replace(os.path.sep, '.').rstrip('.py')
263
+ return module_path
264
+ return None
292
265
 
293
- if name is None:
294
- name = inspect.stack()[1][
295
- 3
296
- ] # Get the name of the calling function if no name is provided
297
-
298
- return self.log_device.get_child(name)
299
266
 
300
- def __is_member__(self):
301
- """
302
- Checks whether the caller of this method is a member of the same class.
303
267
 
304
- Raises:
305
- PermissionError: If the caller of this method is not a member of the same class.
306
- """
307
- log_device = self.log_device.get_child("__is_member__")
308
- log = log_device.logger
268
+ found_level = find_variable_in_call_stack('INSPY_LOG_LEVEL', DEFAULT_LOGGING_LEVEL)
309
269
 
310
- current_frame = inspect.currentframe()
311
- log.debug(f"Current frame: {current_frame}")
312
270
 
313
- caller_frame = current_frame.f_back
314
- log.debug(f"Caller frame: {caller_frame}")
271
+ LOG_DEVICE = Logger(PROG_NAME, found_level)
272
+ MOD_LOG_DEVICE = LOG_DEVICE.get_child("log_engine", found_level)
273
+ MOD_LOGGER = MOD_LOG_DEVICE.logger
274
+ MOD_LOGGER.debug(f"Started logger for {__name__}.")
315
275
 
316
- caller_self = caller_frame.f_locals.get("self", None)
317
- log.debug(f"Caller self: {caller_self}")
276
+ add_child = LOG_DEVICE.get_child
318
277
 
319
- log.debug("Checking if caller is a member of this class...")
320
- if not isinstance(caller_self, self.__class__):
321
- raise PermissionError(
322
- "Access denied.\n"
323
- f"Method can only be accessed by members of the same class. {caller_self.__class__.__name__} is not such a member"
324
- )
278
+ InspyLogger = Logger
325
279
 
326
- log.debug(f"Access granted to {caller_self.__class__.__name__}")
280
+ from inspy_logger.helpers.base_classes import Loggable
@@ -0,0 +1,136 @@
1
+ import inspect
2
+ from inspy_logger import MOD_LOGGER, MOD_LOG_DEVICE, Logger
3
+
4
+ import inspect
5
+
6
+
7
+ class LoggableDescriptor:
8
+ """
9
+ Descriptor for accessing a logger specific to a class method.
10
+ """
11
+
12
+ def __get__(self, instance, owner):
13
+ if instance is None:
14
+ # Accessing through the class, not an instance
15
+ return owner.class_logger
16
+
17
+ # Determine the calling method's name
18
+ stack = inspect.stack()
19
+ # Start from 1 to skip the current __get__ frame
20
+ for frame_record in stack[1:]:
21
+ if 'self' in frame_record.frame.f_locals:
22
+ if frame_record.frame.f_locals['self'] is instance:
23
+ method_name = frame_record.function
24
+ break
25
+ else:
26
+ raise Exception("Could not determine the calling method's name.")
27
+
28
+ # Get a child logger named after the class and method
29
+ return instance.log_device.get_child()
30
+
31
+
32
+ def _get_parent_logging_device():
33
+ """
34
+ Determines the parent logging device by inspecting the caller's log_device or parent_log_device attribute.
35
+
36
+ Returns:
37
+ Logger: The parent logging device.
38
+ """
39
+ MOD_LOGGER.debug("Determining parent logging device")
40
+ caller_frame = inspect.currentframe().f_back
41
+ caller_locals = caller_frame.f_locals
42
+
43
+ if "logger" in caller_locals:
44
+ return caller_locals["logger"]
45
+ elif "parent_log_device" in caller_locals:
46
+ return caller_locals["parent_log_device"]
47
+ else:
48
+ raise ValueError("Unable to determine the parent logging device.")
49
+
50
+
51
+ class Loggable:
52
+ """
53
+ A metaclass to enhance classes with logging capabilities. Classes that inherit from
54
+ 'Loggable' can instantly access a logger without manually setting it up. This logger
55
+ is derived from a parent logger, ensuring consistent logging behavior and hierarchy.
56
+
57
+ Attributes:
58
+ - log_device: The logger device associated with the instance of the class.
59
+ """
60
+ method_logger = LoggableDescriptor()
61
+ class_logger = None
62
+
63
+ def __init__(self, parent_log_device=None, **kwargs):
64
+ self.parent_log_device = parent_log_device
65
+ self.__log_name = self.__class__.__name__
66
+ if self.parent_log_device is not None:
67
+ self.__log_device = self.parent_log_device.get_child(self.__log_name)
68
+ else:
69
+ self.__log_device = _get_parent_logging_device().get_child()
70
+
71
+ # Set up class-level logger if it's not already set
72
+ if self.__class__.class_logger is None:
73
+ self.__class__.class_logger = self.__log_device
74
+
75
+ self.method_logger = self.__class__.method_logger.logger
76
+
77
+ @property
78
+ def log_device(self):
79
+ return self.__log_device
80
+
81
+ @log_device.setter
82
+ def log_device(self, new):
83
+ if not isinstance(new, Logger):
84
+ raise TypeError('log_device must be of type "Logger"')
85
+
86
+ self.__log_device = new
87
+
88
+ def create_child_logger(self, name=None, override=False):
89
+ """
90
+ Creates and returns a child logger of this object's logger.
91
+
92
+ Parameters:
93
+ name (str, optional): The name of the child logger.
94
+ If not provided, the name of the calling function is used.
95
+ override (bool, optional): A flag to override the membership check. Defaults to False.
96
+
97
+ Returns:
98
+ Logger: An instance of the Logger class that represents the child logger.
99
+ """
100
+ if not override:
101
+ self.__is_member__()
102
+
103
+ if name is None:
104
+ name = inspect.stack()[1][
105
+ 3
106
+ ] # Get the name of the calling function if no name is provided
107
+ #print(inspect.stack()[1])
108
+ return self.log_device.get_child(name)
109
+
110
+ def __is_member__(self):
111
+ """
112
+ Checks whether the caller of this method is a member of the same class.
113
+
114
+ Raises:
115
+ PermissionError: If the caller of this method is not a member of the same class.
116
+ """
117
+ log_device = self.log_device.get_child("__is_member__")
118
+ log = log_device.logger
119
+
120
+ current_frame = inspect.currentframe()
121
+ log.debug(f"Current frame: {current_frame}")
122
+
123
+ caller_frame = current_frame.f_back
124
+ log.debug(f"Caller frame: {caller_frame}")
125
+
126
+ caller_self = caller_frame.f_locals.get("self", None)
127
+ log.debug(f"Caller self: {caller_self}")
128
+
129
+ log.debug("Checking if caller is a member of this class...")
130
+ if not isinstance(caller_self, self.__class__):
131
+ raise PermissionError(
132
+ "Access denied.\n"
133
+ f"Method can only be accessed by members of the same class. {caller_self.__class__.__name__} is not such a member"
134
+ )
135
+
136
+ log.debug(f"Access granted to {caller_self.__class__.__name__}")
@@ -16,7 +16,7 @@ __VERSION__ = {
16
16
  'minor': 0,
17
17
  'patch': 0,
18
18
  'release': 'dev',
19
- 'release_num': 2
19
+ 'release_num': 3
20
20
  }
21
21
 
22
22
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "inspy-logger"
3
- version = "3.0-dev.2"
3
+ version = "3.0-dev.3"
4
4
  description = "Colorable, scalable logger for CLI"
5
5
  authors = ["Taylor-Jayde Blackstone <tayjaybabee@gmail.com>"]
6
6
  homepage = 'https://github.com/Inspyre-Softworks/inSPy-Logger'
@@ -28,7 +28,7 @@ classifiers = [
28
28
  ]
29
29
 
30
30
  [tool.poetry.dependencies]
31
- python = "^3.7"
31
+ python = ">=3.9,<4.0"
32
32
  colorlog = "^6.7"
33
33
  setuptools-autover = "^1.0.2"
34
34
  luddite = "^1.0.1"
@@ -42,15 +42,22 @@ rich = "^13.5.2"
42
42
 
43
43
  [tool.poetry.dev-dependencies]
44
44
  ptipython = "^1.0.1"
45
- Sphinx = "^5.3.0"
46
- sphinxcontrib-napoleon = "^0.7"
47
- sphinx-rtd-theme = "^1.0.0"
48
45
  asciinema = "^2.2.0"
49
46
  bpython = "^0.23"
50
47
 
51
48
  [tool.poetry.scripts]
52
49
  inspy-logger-tool = "inspy_logger.Scripts.main:main"
53
50
 
51
+ [tool.poetry.group.docs.dependencies]
52
+ sphinx = "^7.2.6"
53
+ sphinx-autodoc-typehints = "^1.25.2"
54
+ sphinxcontrib-napoleon = "^0.7"
55
+ sphinx-rtd-theme = "^2.0.0"
56
+ sphinxcontrib-argdoc = "^0.1.3"
57
+ autoclasstoc = "^1.6.0"
58
+ sphinx-autodoc-annotation = "^1.0.post1"
59
+ sphinxcontrib-autoprogram = "^0.1.8"
60
+
54
61
  [build-system]
55
62
  requires = ["poetry-core>=1.0.0"]
56
63
  build-backend = "poetry.core.masonry.api"