custom-python-logger 2.0.14__tar.gz → 3.0.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.
Files changed (18) hide show
  1. {custom_python_logger-2.0.14/custom_python_logger.egg-info → custom_python_logger-3.0.1}/PKG-INFO +1 -1
  2. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/custom_python_logger/logger.py +37 -82
  3. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1/custom_python_logger.egg-info}/PKG-INFO +1 -1
  4. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/custom_python_logger.egg-info/SOURCES.txt +0 -1
  5. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/pyproject.toml +1 -1
  6. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/tests/test_logger.py +4 -4
  7. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/tests/test_logger_pytest.py +12 -11
  8. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/tests/test_usage_example_pytest.py +3 -1
  9. custom_python_logger-2.0.14/requirements.txt +0 -8
  10. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/LICENSE +0 -0
  11. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/MANIFEST.in +0 -0
  12. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/README.md +0 -0
  13. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/custom_python_logger/__init__.py +0 -0
  14. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/custom_python_logger/consts.py +0 -0
  15. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/custom_python_logger.egg-info/dependency_links.txt +0 -0
  16. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/custom_python_logger.egg-info/requires.txt +0 -0
  17. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/custom_python_logger.egg-info/top_level.txt +0 -0
  18. {custom_python_logger-2.0.14 → custom_python_logger-3.0.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: custom-python-logger
3
- Version: 2.0.14
3
+ Version: 3.0.1
4
4
  Summary: A custom logger with color support and additional features.
5
5
  Author: Avi Zaguri
6
6
  License: MIT
@@ -15,7 +15,7 @@ from custom_python_logger.consts import LOG_COLORS, CustomLoggerLevel
15
15
  CUSTOM_LOGGER = "custom_logger"
16
16
 
17
17
 
18
- def json_pretty_format(data: Any, indent: int = 4, sort_keys: bool = True, default: Callable = None) -> str:
18
+ def json_pretty_format(data: Any, indent: int = 4, sort_keys: bool = True, default: Callable | None = None) -> str:
19
19
  return json.dumps(data, indent=indent, sort_keys=sort_keys, default=default)
20
20
 
21
21
 
@@ -38,14 +38,14 @@ def get_project_path_by_file(markers: set[str] | None = None) -> Path:
38
38
  raise RuntimeError(f'Project root with one of the markers: "{markers}" not found.')
39
39
 
40
40
 
41
- def print_before_logger(project_name: str) -> None:
41
+ def print_before_logger(project_name: str, sleep_time: float = 0.3) -> None:
42
42
  main_string = f'Start "{project_name}" Process'
43
43
 
44
44
  number_of_ladder = "#" * len(f"### {main_string} ###")
45
45
  print(f"\n{number_of_ladder}")
46
46
  print(f"### {main_string} ###")
47
47
  print(f"{number_of_ladder}\n")
48
- time.sleep(0.3)
48
+ time.sleep(sleep_time)
49
49
 
50
50
 
51
51
  class CustomLoggerAdapter(logging.LoggerAdapter):
@@ -59,17 +59,17 @@ class CustomLoggerAdapter(logging.LoggerAdapter):
59
59
  kwargs.setdefault("stacklevel", 2)
60
60
  self.log(CustomLoggerLevel.STEP.value, msg, *args, exc_info=False, **kwargs)
61
61
 
62
- def success(self, msg: str, *args, **kwargs) -> None:
62
+ def success(self, msg: str, *args: Any, **kwargs: Any) -> None:
63
63
  logging.addLevelName(CustomLoggerLevel.SUCCESS.value, "SUCCESS")
64
64
  kwargs.setdefault("stacklevel", 2)
65
65
  self.log(CustomLoggerLevel.SUCCESS.value, msg, *args, **kwargs)
66
66
 
67
- def alert(self, msg: str, *args, **kwargs) -> None:
67
+ def alert(self, msg: str, *args: Any, **kwargs: Any) -> None:
68
68
  logging.addLevelName(CustomLoggerLevel.ALERT.value, "ALERT")
69
69
  kwargs.setdefault("stacklevel", 2)
70
70
  self.log(CustomLoggerLevel.ALERT.value, msg, *args, **kwargs)
71
71
 
72
- def trace(self, msg: str, *args, **kwargs) -> None:
72
+ def trace(self, msg: str, *args: Any, **kwargs: Any) -> None:
73
73
  logging.addLevelName(CustomLoggerLevel.TRACE.value, "TRACE")
74
74
  kwargs.setdefault("stacklevel", 2)
75
75
  self.log(CustomLoggerLevel.TRACE.value, msg, *args, **kwargs)
@@ -80,77 +80,27 @@ def clear_existing_handlers(logger: Logger) -> None:
80
80
  logger.removeHandler(handler)
81
81
 
82
82
 
83
- def add_file_handler_if_specified(
84
- logger: Logger,
85
- log_file: bool,
86
- log_file_path: str | None,
87
- log_format: str,
88
- ) -> None:
89
- if log_file and log_file_path is not None:
83
+ def add_file_handler(logger: Logger, log_file_path: str | None, log_format: str) -> None:
84
+ if log_file_path is not None:
90
85
  log_file_formatter = logging.Formatter(log_format)
91
86
 
92
- # Create directory if it doesn't exist
93
- log_dir = os.path.dirname(log_file_path)
94
- if log_dir and not os.path.exists(log_dir):
95
- try:
96
- os.makedirs(log_dir)
97
- except FileExistsError:
98
- pass # Directory was created by another process
87
+ if log_dir := os.path.dirname(log_file_path):
88
+ os.makedirs(log_dir, exist_ok=True)
99
89
 
100
90
  file_handler = logging.FileHandler(log_file_path)
101
-
102
91
  file_handler.setFormatter(log_file_formatter)
103
92
  logger.addHandler(file_handler)
104
93
 
105
94
 
106
- def add_console_handler_if_specified(logger: Logger, console_output: bool, log_format: str) -> None:
107
- if console_output:
108
- log_console_formatter = ColoredFormatter(
109
- "%(log_color)s " + log_format,
110
- log_colors=LOG_COLORS,
111
- )
112
-
113
- console_handler = logging.StreamHandler()
114
- console_handler.setFormatter(log_console_formatter)
115
- logger.addHandler(console_handler)
116
-
117
-
118
- def configure_logging(
119
- log_format: str,
120
- utc: bool,
121
- log_file: bool = False,
122
- log_file_path: str | None = None,
123
- console_output: bool = True,
124
- ) -> None:
125
- """
126
- Configure global logging settings.
127
-
128
- Args:
129
- log_format: Format string for log messages
130
- utc: Whether to use UTC time for log timestamps
131
- log_file: Whether to log to a file
132
- log_file_path: Path to log file (if None, no file logging)
133
- console_output: Whether to output logs to console
134
- """
135
- if utc:
136
- logging.Formatter.converter = time.gmtime
137
-
138
- root_logger = logging.getLogger()
139
-
140
- clear_existing_handlers(logger=root_logger)
141
-
142
- add_file_handler_if_specified(
143
- logger=root_logger,
144
- log_file=log_file,
145
- log_file_path=log_file_path,
146
- log_format=log_format,
95
+ def add_console_handler(logger: Logger, log_format: str) -> None:
96
+ log_console_formatter = ColoredFormatter(
97
+ "%(log_color)s " + log_format,
98
+ log_colors=LOG_COLORS,
147
99
  )
148
100
 
149
- add_console_handler_if_specified(
150
- logger=root_logger,
151
- console_output=console_output,
152
- log_format=log_format,
153
- )
101
+ console_handler = logging.StreamHandler()
102
+ console_handler.setFormatter(log_console_formatter)
103
+ logger.addHandler(console_handler)
154
104
 
155
105
 
156
106
  def get_logger(name: str, log_level: int | None = None, extra: dict | None = None) -> CustomLoggerAdapter:
@@ -165,18 +115,18 @@ def get_logger(name: str, log_level: int | None = None, extra: dict | None = Non
165
115
  return new_logger
166
116
 
167
117
 
168
- def build_logger(
118
+ def build_logger( # pylint: disable=R0913
169
119
  project_name: str,
170
120
  extra: dict[str, Any] | None = None,
171
121
  log_format: str = "%(asctime)s | %(levelname)-9s | l.%(levelno)s | %(name)s | %(filename)s:%(lineno)s | %(message)s", # pylint: disable=C0301
172
122
  log_level: int = logging.DEBUG,
173
123
  log_file: bool = False,
174
- log_file_path: str = None,
124
+ log_file_path: str | None = None,
175
125
  console_output: bool = True,
176
126
  utc: bool = False,
177
- ) -> CustomLoggerAdapter | Logger:
127
+ ) -> CustomLoggerAdapter:
178
128
  """
179
- Get a named logger with optional extra context.
129
+ Configure global logging settings and get a named logger with optional extra context.
180
130
 
181
131
  Args:
182
132
  project_name: Name of the project
@@ -190,17 +140,22 @@ def build_logger(
190
140
  Returns:
191
141
  Configured logger
192
142
  """
193
- if not log_file_path:
194
- log_file_path = f"{get_project_path_by_file()}/logs/{project_name}.log"
195
- log_file_path = log_file_path.lower().replace(" ", "_")
196
-
197
- configure_logging(
198
- log_format=log_format,
199
- log_file=log_file,
200
- log_file_path=log_file_path,
201
- console_output=console_output,
202
- utc=utc,
203
- )
143
+
144
+ if utc:
145
+ logging.Formatter.converter = time.gmtime
146
+
147
+ root_logger = logging.getLogger()
148
+ clear_existing_handlers(logger=root_logger)
149
+
150
+ if console_output:
151
+ add_console_handler(logger=root_logger, log_format=log_format)
152
+
153
+ if log_file:
154
+ if not log_file_path:
155
+ log_file_path = f"{get_project_path_by_file()}/logs/{project_name}.log"
156
+ log_file_path = log_file_path.lower().replace(" ", "_")
157
+ add_file_handler(logger=root_logger, log_file_path=log_file_path, log_format=log_format)
158
+
204
159
  logger = CustomLoggerAdapter(logging.getLogger(CUSTOM_LOGGER), extra)
205
160
  logger.setLevel(log_level)
206
161
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: custom-python-logger
3
- Version: 2.0.14
3
+ Version: 3.0.1
4
4
  Summary: A custom logger with color support and additional features.
5
5
  Author: Avi Zaguri
6
6
  License: MIT
@@ -2,7 +2,6 @@ LICENSE
2
2
  MANIFEST.in
3
3
  README.md
4
4
  pyproject.toml
5
- requirements.txt
6
5
  custom_python_logger/__init__.py
7
6
  custom_python_logger/consts.py
8
7
  custom_python_logger/logger.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "custom-python-logger"
7
- version = "2.0.14"
7
+ version = "3.0.1"
8
8
  description = "A custom logger with color support and additional features."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -4,17 +4,17 @@ from custom_python_logger import build_logger
4
4
 
5
5
 
6
6
  class TestLogger(unittest.TestCase):
7
- def test_logger_creation(self):
7
+ def test_logger_creation(self) -> None:
8
8
  logger = build_logger(project_name="TestProject")
9
9
  self.assertIsNotNone(logger)
10
- self.assertEqual(logger.name, "root")
10
+ self.assertEqual(logger.name, "custom_logger.TestProject")
11
11
 
12
- def test_step_log(self):
12
+ def test_step_log(self) -> None:
13
13
  logger = build_logger(project_name="TestProject")
14
14
  logger.step("Testing step log")
15
15
  self.assertTrue(True) # pylint: disable=W1503
16
16
 
17
- def test_exception_log(self):
17
+ def test_exception_log(self) -> None:
18
18
  logger = build_logger(project_name="TestProject")
19
19
  try:
20
20
  raise ValueError("Test exception")
@@ -3,6 +3,7 @@ import logging
3
3
  import os
4
4
  import tempfile
5
5
  import time
6
+ from collections.abc import Generator
6
7
 
7
8
  import pytest
8
9
 
@@ -10,13 +11,13 @@ from custom_python_logger import CustomLoggerAdapter, build_logger, json_pretty_
10
11
 
11
12
 
12
13
  @pytest.fixture
13
- def temp_log_file():
14
+ def temp_log_file() -> Generator[str, None, None]:
14
15
  with tempfile.NamedTemporaryFile(delete=False) as f:
15
16
  yield f.name
16
17
  os.remove(f.name)
17
18
 
18
19
 
19
- def test_logger_creation():
20
+ def test_logger_creation() -> None:
20
21
  logger = build_logger(project_name="PytestTest")
21
22
  assert logger is not None
22
23
  assert isinstance(logger, CustomLoggerAdapter)
@@ -24,7 +25,7 @@ def test_logger_creation():
24
25
  assert hasattr(logger, "exception")
25
26
 
26
27
 
27
- def test_step_log(caplog):
28
+ def test_step_log(caplog: pytest.LogCaptureFixture) -> None:
28
29
  logger = build_logger(project_name="PytestTest", console_output=False)
29
30
  if not isinstance(logger, CustomLoggerAdapter):
30
31
  raise AssertionError("Logger is not a CustomLoggerAdapter")
@@ -36,7 +37,7 @@ def test_step_log(caplog):
36
37
  assert any("STEP" in r.levelname for r in caplog.records)
37
38
 
38
39
 
39
- def test_step_log_2(caplog):
40
+ def test_step_log_2(caplog: pytest.LogCaptureFixture) -> None:
40
41
  logger = build_logger(project_name="TestProject", console_output=False)
41
42
  if not isinstance(logger, CustomLoggerAdapter):
42
43
  raise AssertionError("Logger is not a CustomLoggerAdapter")
@@ -47,7 +48,7 @@ def test_step_log_2(caplog):
47
48
  assert any("STEP" in r.levelname for r in caplog.records)
48
49
 
49
50
 
50
- def test_exception_log(caplog):
51
+ def test_exception_log(caplog: pytest.LogCaptureFixture) -> None:
51
52
  logger = build_logger(project_name="PytestTest", console_output=False)
52
53
  logging.getLogger().addHandler(caplog.handler)
53
54
  with caplog.at_level(logging.ERROR):
@@ -59,7 +60,7 @@ def test_exception_log(caplog):
59
60
  assert any("EXCEPTION" in r.levelname for r in caplog.records)
60
61
 
61
62
 
62
- def test_exception_log_2(caplog):
63
+ def test_exception_log_2(caplog: pytest.LogCaptureFixture) -> None:
63
64
  logger = build_logger(project_name="TestProject", console_output=False)
64
65
  logging.getLogger().addHandler(caplog.handler)
65
66
  with caplog.at_level(logging.ERROR):
@@ -71,7 +72,7 @@ def test_exception_log_2(caplog):
71
72
  assert any("EXCEPTION" in r.levelname for r in caplog.records)
72
73
 
73
74
 
74
- def test_log_to_file(temp_log_file): # pylint: disable=W0621
75
+ def test_log_to_file(temp_log_file: str) -> None: # pylint: disable=W0621
75
76
  logger = build_logger(project_name="FileTest", log_file=True, log_file_path=temp_log_file)
76
77
  logger.info("File log message")
77
78
  time.sleep(0.1)
@@ -80,7 +81,7 @@ def test_log_to_file(temp_log_file): # pylint: disable=W0621
80
81
  assert "File log message" in content
81
82
 
82
83
 
83
- def test_utc_logging(temp_log_file): # pylint: disable=W0621
84
+ def test_utc_logging(temp_log_file: str) -> None: # pylint: disable=W0621
84
85
  logger = build_logger(project_name="UTCTest", log_file=True, log_file_path=temp_log_file, utc=True)
85
86
  logger.info("UTC log message")
86
87
  time.sleep(0.1)
@@ -92,7 +93,7 @@ def test_utc_logging(temp_log_file): # pylint: disable=W0621
92
93
  assert now_utc in content
93
94
 
94
95
 
95
- def test_extra_context(caplog):
96
+ def test_extra_context(caplog: pytest.LogCaptureFixture) -> None:
96
97
  logger = build_logger(project_name="ExtraTest", extra={"user": "pytest"}, console_output=False)
97
98
  logging.getLogger().addHandler(caplog.handler)
98
99
  with caplog.at_level(logging.INFO):
@@ -101,13 +102,13 @@ def test_extra_context(caplog):
101
102
  # The extra field is not in the default format, but test that logger works with extra
102
103
 
103
104
 
104
- def test_json_pretty_format():
105
+ def test_json_pretty_format() -> None:
105
106
  data = {"a": 1, "b": 2}
106
107
  result = json_pretty_format(data)
107
108
  assert "{" in result and "a" in result and "b" in result
108
109
 
109
110
 
110
- def test_yaml_pretty_format():
111
+ def test_yaml_pretty_format() -> None:
111
112
  data = {"a": 1, "b": 2}
112
113
  result = yaml_pretty_format(data)
113
114
  assert "a:" in result and "b:" in result
@@ -1,7 +1,9 @@
1
+ import pytest
2
+
1
3
  from usage_example.example_1 import main
2
4
 
3
5
 
4
- def test_usage_example_runs(monkeypatch):
6
+ def test_usage_example_runs(monkeypatch: pytest.MonkeyPatch) -> None:
5
7
  # Patch print and time.sleep to avoid side effects
6
8
  monkeypatch.setattr("builtins.print", lambda *a, **k: None)
7
9
  monkeypatch.setattr("time.sleep", lambda x: None)
@@ -1,8 +0,0 @@
1
- setuptools
2
- wheel
3
- colorlog
4
- python-dotenv
5
- pre-commit
6
- pytest
7
- pathlib
8
- PyYAML