rats-apps 0.11.1.dev20250527164358__py3-none-any.whl → 0.11.1.dev20250527164753__py3-none-any.whl

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.
rats/cli/_container.py CHANGED
@@ -55,11 +55,11 @@ class Container(Protocol):
55
55
 
56
56
  commands = get_class_commands(type(self))
57
57
  tates = commands.annotations
58
+ logger.debug(f"[{type(self)}] commands: {tates}")
58
59
 
59
60
  for tate in tates:
60
61
  method = getattr(self, tate.name)
61
62
  params = list(reversed(getattr(method, "__click_params__", [])))
62
- logger.debug(tate.namespace)
63
63
  for command in tate.groups:
64
64
  if tate.namespace == "commands":
65
65
  group.add_command(
rats/logs/__init__.py CHANGED
@@ -7,16 +7,62 @@ handle configuring the python logging libraries, with a few configuration option
7
7
  !!! warning
8
8
  We expose the logging functionality through a [rats.apps.AppContainer][] in order to leverage
9
9
  the built in plugin system to give users the ability to adjust the default settings, but this
10
- entire application should be lightweight and should not contain very complex logic, avoiding
11
- logic that is very time consuming or has a chance of failing with confusing errors.
10
+ application should be lightweight and should not contain very complex logic, avoiding logic
11
+ that is very time consuming or has a chance of failing with confusing errors.
12
+
13
+ ## Verbosity Env Variables
14
+
15
+ To help develop modules, logging output can be configured with the `DEBUG_LOGS_*`, `QUIET_LOGS_*`,
16
+ and `LEVEL_LOGS_*` environment variables. The suffix of the environment variables match the module
17
+ name wanting to be configured.
18
+
19
+ ### Module Loggers
20
+
21
+ Environment variables named `DEBUG_LOGS_*` cause logs from the given module to be shown; the
22
+ `QUIET_LOGS_*` environment variables silence logs emitted by the module; and `LEVEL_LOGS_*` allows
23
+ a specific [logging-level](https://docs.python.org/3/library/logging.html#logging-levels) to be
24
+ used.
25
+
26
+ ```
27
+ $ rats-ci fix
28
+ All checks passed!
29
+ 63 files left unchanged
30
+ ran 2 fix commands
31
+ ```
32
+
33
+ ```bash
34
+ # Enable DEBUG level logs for the `rats.*` modules
35
+ export DEBUG_LOGS_RATS="1"
36
+ # Enable WARNING level logs for the `rats.projects.*` modules
37
+ export LEVEL_LOGS_RATS_PROJECTS="WARNING"
38
+ # Disable logs for `rats.cli.*` modules
39
+ export QUIET_LOGS_RATS_CLI="1"
40
+ ```
41
+ ```
42
+ $ rats-ci fix
43
+ 2025-05-27 00:49:45 DEBUG [rats.logs._app:96]: done configuring logging
44
+ All checks passed!
45
+ 63 files left unchanged
46
+ ran 2 fix commands
47
+ ```
48
+
49
+ ### Root Logger
50
+
51
+ The root logger can be configured with the `DEBUG_LOGS`, `QUIET_LOGS`, and `LEVEL_LOGS` environment
52
+ variables.
53
+
54
+ ```bash
55
+ # Enable WARNING level logs for all logs
56
+ export LEVEL_LOGS="WARNING"
57
+ ```
12
58
 
13
- If the logging options made available through this module are far from what is desired, instead
14
- of adding flags and options to this module, we recommend configuring logging in your own code.
15
59
  """
16
60
 
17
61
  from ._app import AppConfigs, ConfigureApplication
62
+ from ._showwarning import showwarning
18
63
 
19
64
  __all__ = [
20
65
  "AppConfigs",
21
66
  "ConfigureApplication",
67
+ "showwarning",
22
68
  ]
rats/logs/_app.py CHANGED
@@ -1,10 +1,13 @@
1
1
  import logging.config
2
+ import os
2
3
  import warnings
3
4
  from collections.abc import Iterator
4
5
  from typing import Any
5
6
 
6
7
  from rats import apps
7
8
 
9
+ from ._showwarning import showwarning
10
+
8
11
  logger = logging.getLogger(__name__)
9
12
  LoggerConfigEntry = tuple[str, dict[str, Any]]
10
13
 
@@ -61,12 +64,12 @@ class ConfigureApplication(apps.AppContainer, apps.PluginMixin):
61
64
  "colored": {
62
65
  "()": "colorlog.ColoredFormatter",
63
66
  "format": (
64
- "%(log_color)s%(asctime)s %(levelname)-8s [%(name)s][%(lineno)d]: "
67
+ "%(log_color)s%(asctime)s %(levelname)-8s [%(name)s:%(lineno)d]: "
65
68
  "%(message)s%(reset)s"
66
69
  ),
67
70
  "datefmt": "%Y-%m-%d %H:%M:%S",
68
71
  "log_colors": {
69
- "DEBUG": "white",
72
+ "DEBUG": "cyan",
70
73
  "INFO": "green",
71
74
  "WARNING": "yellow",
72
75
  "ERROR": "red,",
@@ -88,9 +91,65 @@ class ConfigureApplication(apps.AppContainer, apps.PluginMixin):
88
91
  # enable deprecation warnings by default
89
92
  logging.captureWarnings(True)
90
93
  warnings.simplefilter("default", DeprecationWarning)
94
+ # our modified `showwarning` method logs warnings with the module logger that emitted it
95
+ warnings.showwarning = showwarning
91
96
  logger.debug("done configuring logging")
92
97
 
93
98
  @apps.fallback_group(AppConfigs.LOGGERS)
94
99
  def _default_loggers(self) -> Iterator[LoggerConfigEntry]:
95
- yield "", {"level": "INFO", "handlers": ["console"]}
96
- yield "azure", {"level": "WARNING", "handlers": ["console"]}
100
+ logger_mapping = {
101
+ "": "INFO",
102
+ "azure": "WARNING",
103
+ "py.warnings": "CRITICAL",
104
+ }
105
+ searched_prefixes = [
106
+ "DEBUG_LOGS",
107
+ "QUIET_LOGS",
108
+ "LEVEL_LOGS",
109
+ ]
110
+ for name in os.environ.keys():
111
+ env_prefix = name[0:10]
112
+ if env_prefix not in searched_prefixes:
113
+ # this isn't a logging config env
114
+ continue
115
+
116
+ # our above prefixes are conveniently all 10 characters
117
+ # everything after the prefix is the logger name, skip the underscore after the prefix
118
+ logger_name = name.lower()[11:].replace("_", ".")
119
+ if logger_name not in logger_mapping:
120
+ # the default here is ignored
121
+ logger_mapping[logger_name] = "INFO"
122
+
123
+ for name, default in logger_mapping.items():
124
+ yield self._build_logger_entry(name, default)
125
+
126
+ def _build_logger_entry(self, name: str, default: str = "INFO") -> LoggerConfigEntry:
127
+ # https://docs.python.org/3/library/logging.html#logging-levels
128
+ valid_levels = [
129
+ "NOTSET",
130
+ "DEBUG",
131
+ "INFO",
132
+ "WARNING",
133
+ "ERROR",
134
+ "CRITICAL",
135
+ ]
136
+ suffix = f"_{'_'.join(name.split('.')).upper()}" if name != "" else ""
137
+
138
+ debug_env_name = f"DEBUG_LOGS{suffix}"
139
+ quiet_env_name = f"QUIET_LOGS{suffix}"
140
+ level_env_name = f"LEVEL_LOGS{suffix}"
141
+ if os.environ.get(level_env_name):
142
+ # user specified exactly what log level is wanted for this module
143
+ lvl = os.environ[level_env_name].upper()
144
+ if lvl not in valid_levels:
145
+ raise ValueError(f"invalid log level specified: {level_env_name}={lvl}")
146
+ return name, {"level": lvl, "handlers": ["console"], "propagate": False}
147
+ elif os.environ.get(debug_env_name):
148
+ # show as many logs as possible
149
+ return name, {"level": "DEBUG", "handlers": ["console"], "propagate": False}
150
+ elif os.environ.get(quiet_env_name):
151
+ # only show critical logs when QUIET_LOGS is enabled
152
+ return name, {"level": "CRITICAL", "handlers": ["console"], "propagate": False}
153
+ else:
154
+ # set default logging to INFO
155
+ return name, {"level": default, "handlers": ["console"], "propagate": False}
@@ -0,0 +1,51 @@
1
+ import logging
2
+ import sys
3
+ import warnings
4
+ from pathlib import Path
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ def showwarning(
10
+ message: Warning | str,
11
+ category: type[Warning],
12
+ filename: str,
13
+ lineno: int,
14
+ file: str | None = None,
15
+ line: str | None = None,
16
+ ) -> None:
17
+ """
18
+ Handler that logs warnings using the module logger from the module that emitted the warning.
19
+
20
+ ```python
21
+ import warnings
22
+ from rats.logs import showwarning
23
+
24
+ warnings.showwarning = showwarning
25
+ warnings.warn("This is a custom warning", UserWarning)
26
+ ```
27
+
28
+ Args:
29
+ message: The warning message instance or string.
30
+ category: The category of the warning (e.g., [DeprecationWarning][], [UserWarning][]).
31
+ filename: The path to the file where the warning originated.
32
+ lineno: The line number in the file where the warning originated.
33
+ file: Ignored. Included for compatibility with the standard warnings.showwarning signature.
34
+ line: The line of source code to be included in the warning message, if available.
35
+ """
36
+ if file is not None:
37
+ raise ValueError(file)
38
+
39
+ formatted_message = warnings.formatwarning(message, category, filename, lineno, line)
40
+
41
+ for _module_name, module in sys.modules.items():
42
+ module_path = getattr(module, "__file__", None)
43
+ if module_path and Path(filename).is_file() and Path(module_path).samefile(filename):
44
+ module_name = _module_name
45
+ break
46
+ else:
47
+ # unsure what module to use, but we can default to "py.warnings" like the original handler
48
+ module_name = "py.warnings"
49
+
50
+ source_logger = logging.getLogger(module_name)
51
+ source_logger.warning(formatted_message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rats-apps
3
- Version: 0.11.1.dev20250527164358
3
+ Version: 0.11.1.dev20250527164753
4
4
  Summary: research analysis tools for building applications
5
5
  License: MIT
6
6
  Keywords: pipelines,machine learning,research
@@ -26,12 +26,13 @@ rats/cli/__main__.py,sha256=WeldAKjA3Kmz9ZRnZVd3G8Ud66Y_gSRDLIPNE1JyhV0,1418
26
26
  rats/cli/_annotations.py,sha256=-B2Y1bYjPbeTNTBMZzMdAU92qu9AyutKHN_NdFy-xhA,2964
27
27
  rats/cli/_click_app.py,sha256=Jvs6OqNC4Yoe6kbc2tCzjzUieNFa1n_zwXPsdo971Wc,1450
28
28
  rats/cli/_command.py,sha256=kyU3UqqF9aiTTaFvlQFBKDLXvArQS1QgjoQqlMbKzok,597
29
- rats/cli/_container.py,sha256=FIQBqi9PTNpE2P52qkfGET51BczdD-JvcWXLTjCNPDI,3512
29
+ rats/cli/_container.py,sha256=BoZLOu-eT4SzweNwMepWHd4PyxQAg--_p-rM7XkQNAA,3529
30
30
  rats/cli/_functions.py,sha256=BNmgWVquQUEqJAYsed_l8vLnlLP7u3XC1TDyEFI1AiU,1552
31
31
  rats/cli/_plugin.py,sha256=o-pmEqU6mVH3QoRfRBrbG-XRTWCzt6pLKtSV3-5VSx0,1144
32
32
  rats/cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- rats/logs/__init__.py,sha256=dSO-V2eu2W7w4w7oRau-UOfXXIkyZ2bHB6-kaasbeFM,970
34
- rats/logs/_app.py,sha256=SoTxk6g84tygOP_346NwXPcG1o6KZLd7lIgayq5vMdc,3239
33
+ rats/logs/__init__.py,sha256=_ZdkBmvW1yr5UOIB_m4OldNsUxK6NTDunNW4mvgQmpI,2115
34
+ rats/logs/_app.py,sha256=EVjSjSaxJIL5LMJZf4yo9EpUBkkavaVXyg42cDsUt3I,5702
35
+ rats/logs/_showwarning.py,sha256=g5Gq0I7GoHgABI4eCobq6xd4NaKuLGJFyK-SCvegDHA,1726
35
36
  rats/logs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
37
  rats/runtime/__init__.py,sha256=f9aEnynHuePP9OP5XCtB-6nCBvNWWlQQLTzuxFEueoQ,2150
37
38
  rats/runtime/__main__.py,sha256=y01yOymsL075poX95pc02sJR1HD0pDNFRZdpOdi0R6Y,79
@@ -54,7 +55,7 @@ rats_e2e/runtime/_data.py,sha256=3d1F_JO2gEOPUjBp_KYMP3TefyneiG_ktlJjdIIYUy8,125
54
55
  rats_e2e/runtime/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
56
  rats_resources/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
57
  rats_resources/runtime/example-context.yaml,sha256=eiLsNFquFfkIpUhxUCQLzLigH21QF2F00fzA_e_aOKk,215
57
- rats_apps-0.11.1.dev20250527164358.dist-info/METADATA,sha256=pxkR1NF65RfrYcBemvO87N8bYjEdLprXgvi0NQ9ygYw,889
58
- rats_apps-0.11.1.dev20250527164358.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
59
- rats_apps-0.11.1.dev20250527164358.dist-info/entry_points.txt,sha256=Gf6bPwxIVjWd3Xx71upZo7eDJA5cujniLew6fxJMgA4,117
60
- rats_apps-0.11.1.dev20250527164358.dist-info/RECORD,,
58
+ rats_apps-0.11.1.dev20250527164753.dist-info/METADATA,sha256=kgJQUzcaMgofZndS9ETn14z0uY0ZE50-Y1Yh8pqVqYA,889
59
+ rats_apps-0.11.1.dev20250527164753.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
60
+ rats_apps-0.11.1.dev20250527164753.dist-info/entry_points.txt,sha256=Gf6bPwxIVjWd3Xx71upZo7eDJA5cujniLew6fxJMgA4,117
61
+ rats_apps-0.11.1.dev20250527164753.dist-info/RECORD,,