|
2 | 2 |
|
3 | 3 | import inspect
|
4 | 4 | import sys
|
5 |
| -from contextlib import contextmanager |
6 |
| -from enum import EnumMeta, IntEnum |
7 | 5 | from functools import wraps
|
8 |
| -from logging import getLevelNamesMapping |
9 | 6 | from pathlib import Path
|
10 | 7 | from time import time
|
11 |
| -from typing import TYPE_CHECKING, Literal, LiteralString, ParamSpec, TypeVar, get_args |
| 8 | +from typing import TYPE_CHECKING, Literal, ParamSpec, TypeVar, get_args |
12 | 9 |
|
13 |
| -from . import logging |
14 |
| -from ._compat import deprecated, old_positionals |
15 |
| -from ._singleton import SingletonMeta |
16 |
| -from .logging import _RootLogger, _set_log_file, _set_log_level |
| 10 | +from .. import logging |
| 11 | +from .._compat import deprecated, old_positionals |
| 12 | +from .._singleton import SingletonMeta |
| 13 | +from ..logging import _RootLogger, _set_log_file, _set_log_level |
| 14 | +from .verbosity import Verbosity |
17 | 15 |
|
18 | 16 | if TYPE_CHECKING:
|
19 |
| - from collections.abc import Callable, Generator, Iterable |
| 17 | + from collections.abc import Callable, Iterable |
20 | 18 | from types import UnionType
|
21 | 19 | from typing import ClassVar, Concatenate, Self, TextIO
|
22 | 20 |
|
| 21 | + from .verbosity import _VerbosityName |
| 22 | + |
23 | 23 | # Collected from the print_* functions in matplotlib.backends
|
24 | 24 | _Format = (
|
25 | 25 | Literal["png", "jpg", "tif", "tiff"] # noqa: PYI030
|
26 | 26 | | Literal["pdf", "ps", "eps", "svg", "svgz", "pgf"]
|
27 | 27 | | Literal["raw", "rgba"]
|
28 | 28 | )
|
29 |
| - _VerbosityName = Literal["error", "warning", "info", "hint", "debug"] |
30 |
| - _LoggingLevelName = Literal["CRITICAL", "ERROR", "WARNING", "INFO", "HINT", "DEBUG"] |
31 | 29 |
|
32 |
| -L = TypeVar("L", bound=LiteralString) |
| 30 | + |
33 | 31 | S = TypeVar("S")
|
34 | 32 | T = TypeVar("T")
|
35 | 33 | P = ParamSpec("P")
|
|
39 | 37 | AnnDataFileFormat = Literal["h5ad", "zarr"]
|
40 | 38 |
|
41 | 39 |
|
42 |
| -_VERBOSITY_TO_LOGLEVEL: dict[int | _VerbosityName, _LoggingLevelName] = { |
43 |
| - "error": "ERROR", |
44 |
| - "warning": "WARNING", |
45 |
| - "info": "INFO", |
46 |
| - "hint": "HINT", |
47 |
| - "debug": "DEBUG", |
48 |
| -} |
49 |
| -_VERBOSITY_TO_LOGLEVEL.update(dict(enumerate(list(_VERBOSITY_TO_LOGLEVEL.values())))) |
50 |
| - |
51 |
| - |
52 |
| -class VerbosityMeta(EnumMeta): |
53 |
| - @property |
54 |
| - @deprecated("Use `Verbosity.warning` instead") |
55 |
| - def warn(cls) -> Verbosity: |
56 |
| - return Verbosity.warning |
57 |
| - |
58 |
| - |
59 |
| -class Verbosity(IntEnum, metaclass=VerbosityMeta): |
60 |
| - """Logging verbosity levels for :attr:`scanpy.settings.verbosity`.""" |
61 |
| - |
62 |
| - error = 0 |
63 |
| - """Error (`0`)""" |
64 |
| - warning = 1 |
65 |
| - """Warning (`1`)""" |
66 |
| - info = 2 |
67 |
| - """Info (`2`)""" |
68 |
| - hint = 3 |
69 |
| - """Hint (`3`)""" |
70 |
| - debug = 4 |
71 |
| - """Debug (`4`)""" |
72 |
| - |
73 |
| - def __eq__(self, other: object) -> bool: |
74 |
| - if isinstance(other, Verbosity): |
75 |
| - return self is other |
76 |
| - if isinstance(other, int): |
77 |
| - return self.value == other |
78 |
| - if isinstance(other, str): |
79 |
| - return self.name == other |
80 |
| - return NotImplemented |
81 |
| - |
82 |
| - @property |
83 |
| - def level(self) -> int: |
84 |
| - """The :ref:`logging level <levels>` corresponding to this verbosity level.""" |
85 |
| - m = getLevelNamesMapping() |
86 |
| - return m[_VERBOSITY_TO_LOGLEVEL[self.name]] |
87 |
| - |
88 |
| - @contextmanager |
89 |
| - def override( |
90 |
| - self, verbosity: Verbosity | _VerbosityName | int |
91 |
| - ) -> Generator[Verbosity, None, None]: |
92 |
| - """Temporarily override verbosity. |
93 |
| -
|
94 |
| - >>> import scanpy as sc |
95 |
| - >>> sc.settings.verbosity = sc.Verbosity.info |
96 |
| - >>> with sc.settings.verbosity.override(settings.verbosity.debug): |
97 |
| - ... sc.settings.verbosity |
98 |
| - <Verbosity.debug: 4> |
99 |
| - >>> sc.settings.verbosity |
100 |
| - <Verbosity.info: 2> |
101 |
| - """ |
102 |
| - settings.verbosity = verbosity |
103 |
| - yield self |
104 |
| - settings.verbosity = self |
105 |
| - |
106 |
| - |
107 | 40 | def _type_check(var: object, name: str, types: type | UnionType) -> None:
|
108 | 41 | if isinstance(var, types):
|
109 | 42 | return
|
@@ -173,25 +106,19 @@ def verbosity(cls) -> Verbosity:
|
173 | 106 |
|
174 | 107 | @verbosity.setter
|
175 | 108 | def verbosity(cls, verbosity: Verbosity | _VerbosityName | int) -> None:
|
176 |
| - verbosity_str_options: list[_VerbosityName] = [ |
177 |
| - v for v in _VERBOSITY_TO_LOGLEVEL if isinstance(v, str) |
178 |
| - ] |
179 |
| - if isinstance(verbosity, Verbosity): |
180 |
| - cls._verbosity = verbosity |
181 |
| - elif isinstance(verbosity, int): |
182 |
| - cls._verbosity = Verbosity(verbosity) |
183 |
| - elif isinstance(verbosity, str): |
184 |
| - verbosity = verbosity.lower() |
185 |
| - if verbosity not in verbosity_str_options: |
186 |
| - msg = ( |
187 |
| - f"Cannot set verbosity to {verbosity}. " |
188 |
| - f"Accepted string values are: {verbosity_str_options}" |
189 |
| - ) |
190 |
| - raise ValueError(msg) |
191 |
| - cls._verbosity = Verbosity(verbosity_str_options.index(verbosity)) |
192 |
| - else: |
193 |
| - _type_check(verbosity, "verbosity", str | int) |
194 |
| - _set_log_level(cls, _VERBOSITY_TO_LOGLEVEL[cls._verbosity.name]) |
| 109 | + try: |
| 110 | + cls._verbosity = ( |
| 111 | + Verbosity[verbosity.lower()] |
| 112 | + if isinstance(verbosity, str) |
| 113 | + else Verbosity(verbosity) |
| 114 | + ) |
| 115 | + except KeyError: |
| 116 | + msg = ( |
| 117 | + f"Cannot set verbosity to {verbosity}. " |
| 118 | + f"Accepted string values are: {Verbosity.__members__.keys()}" |
| 119 | + ) |
| 120 | + raise ValueError(msg) from None |
| 121 | + _set_log_level(cls, cls._verbosity.level) |
195 | 122 |
|
196 | 123 | @property
|
197 | 124 | def N_PCS(cls) -> int:
|
@@ -382,14 +309,14 @@ def logfile(cls) -> TextIO | None:
|
382 | 309 |
|
383 | 310 | @logfile.setter
|
384 | 311 | def logfile(cls, logfile: Path | str | TextIO | None) -> None:
|
385 |
| - if not hasattr(logfile, "write") and logfile: |
| 312 | + if not logfile: # "" or None |
| 313 | + logfile = sys.stdout if cls._is_run_from_ipython() else sys.stderr |
| 314 | + if isinstance(logfile, Path | str): |
386 | 315 | cls.logpath = logfile
|
387 |
| - else: # file object |
388 |
| - if not logfile: # None or '' |
389 |
| - logfile = sys.stdout if cls._is_run_from_ipython() else sys.stderr |
390 |
| - cls._logfile = logfile |
391 |
| - cls._logpath = None |
392 |
| - _set_log_file(cls) |
| 316 | + return |
| 317 | + cls._logfile = logfile |
| 318 | + cls._logpath = None |
| 319 | + _set_log_file(cls) |
393 | 320 |
|
394 | 321 | @property
|
395 | 322 | def categories_to_ignore(cls) -> list[str]:
|
@@ -500,7 +427,7 @@ def _set_figure_params( # noqa: PLR0913
|
500 | 427 | rcParams["figure.facecolor"] = facecolor
|
501 | 428 | rcParams["axes.facecolor"] = facecolor
|
502 | 429 | if scanpy:
|
503 |
| - from .plotting._rcmod import set_rcParams_scanpy |
| 430 | + from ..plotting._rcmod import set_rcParams_scanpy |
504 | 431 |
|
505 | 432 | set_rcParams_scanpy(fontsize=fontsize, color_map=color_map)
|
506 | 433 | if figsize is not None:
|
|
0 commit comments