hydra-structlog 0.2.0__tar.gz → 1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydra-structlog
3
- Version: 0.2.0
3
+ Version: 1.0.0
4
4
  Summary: An opinionated hydra plugin to configure logging with structlog
5
5
  Keywords: hydra,structlog,plugin,hydra-zen
6
6
  Author: Sören Zapp
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hydra-structlog"
3
- version = "0.2.0"
3
+ version = "1.0.0"
4
4
  description = "An opinionated hydra plugin to configure logging with structlog"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -42,6 +42,7 @@ dev = [
42
42
  "nox>=2026.2.9",
43
43
  ]
44
44
  tests = [
45
+ "dirty-equals>=0.11",
45
46
  "pytest>=9.0.2",
46
47
  "pytest-cov>=7.1.0",
47
48
  "pytest-randomly>=4.0.1",
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -107,6 +107,6 @@ handlers:
107
107
 
108
108
  root:
109
109
  level: INFO
110
- handlers: [console, file, json]
110
+ handlers: ${hydra-structlog.dev:[console, file, json],[file, json]}
111
111
 
112
112
  disable_existing_loggers: false
@@ -1,16 +1,16 @@
1
1
  import logging
2
2
  from typing import Any, TextIO
3
3
 
4
- try:
4
+ try: # pragma: no cover
5
5
  from tqdm.std import tqdm as std_tqdm # type: ignore[ty:unresolved-import]
6
6
 
7
7
  TqdmClass = type[std_tqdm]
8
- except ImportError:
8
+ except ImportError: # pragma: no cover
9
9
  std_tqdm: Any = None
10
10
  TqdmClass = None
11
11
 
12
12
 
13
- class TqdmStreamHandler(logging.StreamHandler):
13
+ class TqdmStreamHandler(logging.StreamHandler): # pragma: no cover
14
14
  """Handler inheriting from StreamHandler to route logging stream through tqdm."""
15
15
 
16
16
  def __init__(
@@ -30,5 +30,5 @@ class TqdmStreamHandler(logging.StreamHandler):
30
30
  self.flush()
31
31
  except (KeyboardInterrupt, RecursionError, SystemExit):
32
32
  raise
33
- except Exception:
33
+ except Exception: # noqa: BLE001
34
34
  self.handleError(record)
@@ -14,6 +14,21 @@ _excepthook = None
14
14
  _configured_once = False
15
15
 
16
16
 
17
+ def _module_from_filename(filename: str) -> str | None:
18
+ """Obtain the module name from filename through call stack."""
19
+ frame = sys._getframe(1)
20
+ while frame is not None:
21
+ if frame.f_code.co_filename == filename:
22
+ return frame.f_globals.get("__name__")
23
+ frame = frame.f_back
24
+
25
+ # Fallback: Module from sys.modules
26
+ for name, mod in list(sys.modules.items()):
27
+ if getattr(mod, "__file__", None) == filename:
28
+ return name
29
+ return None
30
+
31
+
17
32
  def warning_logger(
18
33
  message: Warning | str,
19
34
  category: type[Warning],
@@ -23,10 +38,20 @@ def warning_logger(
23
38
  line: str | None = None,
24
39
  ) -> None:
25
40
  """Emit Python-native warnings as logging warnings."""
26
- frame = sys._getframe(2)
27
- module = frame.f_globals.get("__name__")
41
+ module = _module_from_filename(filename)
28
42
  logger = logging.getLogger(module)
29
- logger.warning("%s: %s", category.__name__, f"{message} (line {lineno})")
43
+ if not logger.isEnabledFor(logging.WARNING):
44
+ return # pragma: no cover
45
+ record = logger.makeRecord(
46
+ logger.name,
47
+ logging.WARNING,
48
+ filename,
49
+ lineno,
50
+ "%s: %s (line %d)",
51
+ (category.__name__, message, lineno),
52
+ None,
53
+ )
54
+ logger.handle(record)
30
55
 
31
56
 
32
57
  def exception_logger(
@@ -56,7 +81,7 @@ def exception_logger(
56
81
  filename = frame.f_code.co_filename.replace(os.sep, "/")
57
82
  if filename.endswith(skip_files) and tb:
58
83
  exc_traceback = tb
59
- except Exception:
84
+ except Exception: # pragma: no cover # noqa: BLE001, S110
60
85
  pass
61
86
 
62
87
  message = getattr(exc_value, "message", str(exc_value))
@@ -64,7 +89,9 @@ def exception_logger(
64
89
  logger.critical(message, exc_info=exc_info)
65
90
 
66
91
 
67
- def init_plugin(foreign_pre_chain: Sequence[structlog.typing.Processor]) -> None:
92
+ def init_plugin(
93
+ foreign_pre_chain: Sequence[structlog.typing.Processor],
94
+ ) -> None: # pragma: no cover
68
95
  """Configure structlog and warning and exception hooks."""
69
96
  global _excepthook, _showwarning, _configured_once
70
97
 
@@ -1,7 +1,11 @@
1
1
  from hydra.core.config_search_path import ConfigSearchPath
2
2
  from hydra.plugins.search_path_plugin import SearchPathPlugin
3
+ from omegaconf import OmegaConf
4
+
5
+ from .resolvers import dev_switch
3
6
 
4
7
 
5
8
  class HydraStructlogSearchPathPlugin(SearchPathPlugin):
6
9
  def manipulate_search_path(self, search_path: ConfigSearchPath) -> None:
10
+ OmegaConf.register_new_resolver("hydra-structlog.dev", dev_switch)
7
11
  search_path.append("hydra-structlog", "pkg://hydra_structlog.conf")
@@ -0,0 +1,9 @@
1
+ import os
2
+
3
+
4
+ def dev_switch(is_dev, is_not_dev, *, _root_):
5
+ """OmegaConf resolver to select node based on dev environment."""
6
+ # Grab ENV from 'env_set' first and fall back to environment variable
7
+ hydra_env = _root_.get("hydra", {}).get("job", {}).get("env_set", {})
8
+ ENV = hydra_env.get("ENV", os.environ.get("ENV")) or "dev"
9
+ return is_dev if ENV == "dev" else is_not_dev
@@ -1 +0,0 @@
1
- __version__ = "0.2.0"
File without changes