custom-python-logger 2.0.13__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.13/custom_python_logger.egg-info → custom_python_logger-3.0.0}/PKG-INFO +1 -1
  2. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/custom_python_logger/logger.py +35 -63
  3. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0/custom_python_logger.egg-info}/PKG-INFO +1 -1
  4. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/custom_python_logger.egg-info/SOURCES.txt +0 -1
  5. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/pyproject.toml +1 -1
  6. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/tests/test_logger.py +4 -4
  7. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/tests/test_logger_pytest.py +12 -11
  8. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/tests/test_usage_example_pytest.py +3 -1
  9. custom_python_logger-2.0.13/requirements.txt +0 -8
  10. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/LICENSE +0 -0
  11. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/MANIFEST.in +0 -0
  12. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/README.md +0 -0
  13. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/custom_python_logger/__init__.py +0 -0
  14. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/custom_python_logger/consts.py +0 -0
  15. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/custom_python_logger.egg-info/dependency_links.txt +0 -0
  16. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/custom_python_logger.egg-info/requires.txt +0 -0
  17. {custom_python_logger-2.0.13 → custom_python_logger-3.0.0}/custom_python_logger.egg-info/top_level.txt +0 -0
  18. {custom_python_logger-2.0.13 → 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.13
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,20 +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
- os.makedirs(log_dir)
91
+ if log_dir := os.path.dirname(log_file_path):
92
+ os.makedirs(log_dir, exist_ok=True)
96
93
 
97
94
  file_handler = logging.FileHandler(log_file_path)
98
-
99
95
  file_handler.setFormatter(log_file_formatter)
100
96
  logger.addHandler(file_handler)
101
97
 
@@ -112,44 +108,6 @@ def add_console_handler_if_specified(logger: Logger, console_output: bool, log_f
112
108
  logger.addHandler(console_handler)
113
109
 
114
110
 
115
- def configure_logging(
116
- log_format: str,
117
- utc: bool,
118
- log_file: bool = False,
119
- log_file_path: str | None = None,
120
- console_output: bool = True,
121
- ) -> None:
122
- """
123
- Configure global logging settings.
124
-
125
- Args:
126
- log_format: Format string for log messages
127
- utc: Whether to use UTC time for log timestamps
128
- log_file: Whether to log to a file
129
- log_file_path: Path to log file (if None, no file logging)
130
- console_output: Whether to output logs to console
131
- """
132
- if utc:
133
- logging.Formatter.converter = time.gmtime
134
-
135
- root_logger = logging.getLogger()
136
-
137
- clear_existing_handlers(logger=root_logger)
138
-
139
- add_file_handler_if_specified(
140
- logger=root_logger,
141
- log_file=log_file,
142
- log_file_path=log_file_path,
143
- log_format=log_format,
144
- )
145
-
146
- add_console_handler_if_specified(
147
- logger=root_logger,
148
- console_output=console_output,
149
- log_format=log_format,
150
- )
151
-
152
-
153
111
  def get_logger(name: str, log_level: int | None = None, extra: dict | None = None) -> CustomLoggerAdapter:
154
112
  custom_logger = logging.getLogger(CUSTOM_LOGGER)
155
113
  full_name = f"{CUSTOM_LOGGER}.{name}"
@@ -162,18 +120,18 @@ def get_logger(name: str, log_level: int | None = None, extra: dict | None = Non
162
120
  return new_logger
163
121
 
164
122
 
165
- def build_logger(
123
+ def build_logger( # pylint: disable=R0913
166
124
  project_name: str,
167
125
  extra: dict[str, Any] | None = None,
168
126
  log_format: str = "%(asctime)s | %(levelname)-9s | l.%(levelno)s | %(name)s | %(filename)s:%(lineno)s | %(message)s", # pylint: disable=C0301
169
127
  log_level: int = logging.DEBUG,
170
128
  log_file: bool = False,
171
- log_file_path: str = None,
129
+ log_file_path: str | None = None,
172
130
  console_output: bool = True,
173
131
  utc: bool = False,
174
- ) -> CustomLoggerAdapter | Logger:
132
+ ) -> CustomLoggerAdapter:
175
133
  """
176
- Get a named logger with optional extra context.
134
+ Configure global logging settings and get a named logger with optional extra context.
177
135
 
178
136
  Args:
179
137
  project_name: Name of the project
@@ -187,17 +145,31 @@ def build_logger(
187
145
  Returns:
188
146
  Configured logger
189
147
  """
190
- if not log_file_path:
191
- log_file_path = f"{get_project_path_by_file()}/logs/{project_name}.log"
192
- log_file_path = log_file_path.lower().replace(" ", "_")
193
148
 
194
- configure_logging(
195
- log_format=log_format,
196
- log_file=log_file,
197
- 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,
198
158
  console_output=console_output,
199
- utc=utc,
159
+ log_format=log_format,
200
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
+
201
173
  logger = CustomLoggerAdapter(logging.getLogger(CUSTOM_LOGGER), extra)
202
174
  logger.setLevel(log_level)
203
175
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: custom-python-logger
3
- Version: 2.0.13
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.13"
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