custom-python-logger 2.0.14__tar.gz → 3.0.0__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.0}/PKG-INFO +1 -1
  2. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/custom_python_logger/logger.py +35 -66
  3. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0/custom_python_logger.egg-info}/PKG-INFO +1 -1
  4. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/custom_python_logger.egg-info/SOURCES.txt +0 -1
  5. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/pyproject.toml +1 -1
  6. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/tests/test_logger.py +4 -4
  7. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/tests/test_logger_pytest.py +12 -11
  8. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/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.0}/LICENSE +0 -0
  11. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/MANIFEST.in +0 -0
  12. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/README.md +0 -0
  13. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/custom_python_logger/__init__.py +0 -0
  14. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/custom_python_logger/consts.py +0 -0
  15. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/custom_python_logger.egg-info/dependency_links.txt +0 -0
  16. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/custom_python_logger.egg-info/requires.txt +0 -0
  17. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/custom_python_logger.egg-info/top_level.txt +0 -0
  18. {custom_python_logger-2.0.14 → custom_python_logger-3.0.0}/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.0
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)
@@ -82,23 +82,16 @@ def clear_existing_handlers(logger: Logger) -> None:
82
82
 
83
83
  def add_file_handler_if_specified(
84
84
  logger: Logger,
85
- log_file: bool,
86
85
  log_file_path: str | None,
87
86
  log_format: str,
88
87
  ) -> None:
89
- if log_file and log_file_path is not None:
88
+ if log_file_path is not None:
90
89
  log_file_formatter = logging.Formatter(log_format)
91
90
 
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
91
+ if log_dir := os.path.dirname(log_file_path):
92
+ os.makedirs(log_dir, exist_ok=True)
99
93
 
100
94
  file_handler = logging.FileHandler(log_file_path)
101
-
102
95
  file_handler.setFormatter(log_file_formatter)
103
96
  logger.addHandler(file_handler)
104
97
 
@@ -115,44 +108,6 @@ def add_console_handler_if_specified(logger: Logger, console_output: bool, log_f
115
108
  logger.addHandler(console_handler)
116
109
 
117
110
 
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,
147
- )
148
-
149
- add_console_handler_if_specified(
150
- logger=root_logger,
151
- console_output=console_output,
152
- log_format=log_format,
153
- )
154
-
155
-
156
111
  def get_logger(name: str, log_level: int | None = None, extra: dict | None = None) -> CustomLoggerAdapter:
157
112
  custom_logger = logging.getLogger(CUSTOM_LOGGER)
158
113
  full_name = f"{CUSTOM_LOGGER}.{name}"
@@ -165,18 +120,18 @@ def get_logger(name: str, log_level: int | None = None, extra: dict | None = Non
165
120
  return new_logger
166
121
 
167
122
 
168
- def build_logger(
123
+ def build_logger( # pylint: disable=R0913
169
124
  project_name: str,
170
125
  extra: dict[str, Any] | None = None,
171
126
  log_format: str = "%(asctime)s | %(levelname)-9s | l.%(levelno)s | %(name)s | %(filename)s:%(lineno)s | %(message)s", # pylint: disable=C0301
172
127
  log_level: int = logging.DEBUG,
173
128
  log_file: bool = False,
174
- log_file_path: str = None,
129
+ log_file_path: str | None = None,
175
130
  console_output: bool = True,
176
131
  utc: bool = False,
177
- ) -> CustomLoggerAdapter | Logger:
132
+ ) -> CustomLoggerAdapter:
178
133
  """
179
- Get a named logger with optional extra context.
134
+ Configure global logging settings and get a named logger with optional extra context.
180
135
 
181
136
  Args:
182
137
  project_name: Name of the project
@@ -190,17 +145,31 @@ def build_logger(
190
145
  Returns:
191
146
  Configured logger
192
147
  """
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
148
 
197
- configure_logging(
198
- log_format=log_format,
199
- log_file=log_file,
200
- log_file_path=log_file_path,
149
+ if utc:
150
+ logging.Formatter.converter = time.gmtime
151
+
152
+ root_logger = logging.getLogger()
153
+
154
+ clear_existing_handlers(logger=root_logger)
155
+
156
+ add_console_handler_if_specified(
157
+ logger=root_logger,
201
158
  console_output=console_output,
202
- utc=utc,
159
+ log_format=log_format,
203
160
  )
161
+
162
+ if log_file:
163
+ if not log_file_path:
164
+ log_file_path = f"{get_project_path_by_file()}/logs/{project_name}.log"
165
+ log_file_path = log_file_path.lower().replace(" ", "_")
166
+
167
+ add_file_handler_if_specified(
168
+ logger=root_logger,
169
+ log_file_path=log_file_path,
170
+ log_format=log_format,
171
+ )
172
+
204
173
  logger = CustomLoggerAdapter(logging.getLogger(CUSTOM_LOGGER), extra)
205
174
  logger.setLevel(log_level)
206
175
 
@@ -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.0
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.0"
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