Skip to content

Commit

Permalink
Add type annotations to timer.py and logging.py
Browse files Browse the repository at this point in the history
  • Loading branch information
timj committed Sep 28, 2021
1 parent ee94ade commit 851eb71
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 34 deletions.
59 changes: 34 additions & 25 deletions python/lsst/utils/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# Use of this source code is governed by a 3-clause BSD-style
# license that can be found in the LICENSE file.

from __future__ import annotations

__all__ = ("TRACE", "VERBOSE", "getLogger", "LsstLogAdapter")

Expand All @@ -17,6 +18,14 @@
from deprecated.sphinx import deprecated
from contextlib import contextmanager

from typing import (
Any,
Generator,
List,
Optional,
Union,
)

# log level for trace (verbose debug).
TRACE = 5
logging.addLevelName(TRACE, "TRACE")
Expand All @@ -31,12 +40,12 @@ class _F:
Format, supporting str.format() syntax
see using-custom-message-objects in logging-cookbook.html
"""
def __init__(self, fmt, /, *args, **kwargs):
def __init__(self, fmt: str, /, *args: Any, **kwargs: Any):
self.fmt = fmt
self.args = args
self.kwargs = kwargs

def __str__(self):
def __str__(self) -> str:
return self.fmt.format(*self.args, **self.kwargs)


Expand Down Expand Up @@ -72,7 +81,7 @@ class LsstLogAdapter(LoggerAdapter):
VERBOSE = VERBOSE

@contextmanager
def temporary_log_level(self, level):
def temporary_log_level(self, level: Union[int, str]) -> Generator:
"""A context manager that temporarily sets the level of this logger.
Parameters
Expand All @@ -88,11 +97,11 @@ def temporary_log_level(self, level):
self.setLevel(old)

@property
def level(self):
def level(self) -> int:
"""Current level of this logger (``int``)."""
return self.logger.level

def getChild(self, name):
def getChild(self, name: str) -> LsstLogAdapter:
"""Get the named child logger.
Parameters
Expand All @@ -109,27 +118,27 @@ def getChild(self, name):

@deprecated(reason="Use Python Logger compatible isEnabledFor Will be removed after v23.",
version="v23", category=FutureWarning)
def isDebugEnabled(self):
def isDebugEnabled(self) -> bool:
return self.isEnabledFor(self.DEBUG)

@deprecated(reason="Use Python Logger compatible 'name' attribute. Will be removed after v23.",
version="v23", category=FutureWarning)
def getName(self):
def getName(self) -> str:
return self.name

@deprecated(reason="Use Python Logger compatible .level property. Will be removed after v23.",
version="v23", category=FutureWarning)
def getLevel(self):
def getLevel(self) -> int:
return self.logger.level

def fatal(self, msg, *args, **kwargs):
def fatal(self, msg: str, *args: Any, **kwargs: Any) -> None:
# Python does not provide this method in LoggerAdapter but does
# not formally deprecated it in favor of critical() either.
# Provide it without deprecation message for consistency with Python.
# stacklevel=5 accounts for the forwarding of LoggerAdapter.
return self.critical(msg, *args, **kwargs, stacklevel=4)
self.critical(msg, *args, **kwargs, stacklevel=4)

def verbose(self, fmt, *args, **kwargs):
def verbose(self, fmt: str, *args: Any, **kwargs: Any) -> None:
"""Issue a VERBOSE level log message.
Arguments are as for `logging.info`.
Expand All @@ -143,7 +152,7 @@ def verbose(self, fmt, *args, **kwargs):
# itself.
self.log(VERBOSE, fmt, *args, stacklevel=3, **kwargs)

def trace(self, fmt, *args):
def trace(self, fmt: str, *args: Any) -> None:
"""Issue a TRACE level log message.
Arguments are as for `logging.info`.
Expand All @@ -155,36 +164,36 @@ def trace(self, fmt, *args):

@deprecated(reason="Use Python Logger compatible method. Will be removed after v23.",
version="v23", category=FutureWarning)
def tracef(self, fmt, *args, **kwargs):
def tracef(self, fmt: str, *args: Any, **kwargs: Any) -> None:
# Stacklevel is 4 to account for the deprecation wrapper
self.log(TRACE, _F(fmt, *args, **kwargs), stacklevel=4)

@deprecated(reason="Use Python Logger compatible method. Will be removed after v23.",
version="v23", category=FutureWarning)
def debugf(self, fmt, *args, **kwargs):
def debugf(self, fmt: str, *args: Any, **kwargs: Any) -> None:
self.log(logging.DEBUG, _F(fmt, *args, **kwargs), stacklevel=4)

@deprecated(reason="Use Python Logger compatible method. Will be removed after v23.",
version="v23", category=FutureWarning)
def infof(self, fmt, *args, **kwargs):
def infof(self, fmt: str, *args: Any, **kwargs: Any) -> None:
self.log(logging.INFO, _F(fmt, *args, **kwargs), stacklevel=4)

@deprecated(reason="Use Python Logger compatible method. Will be removed after v23.",
version="v23", category=FutureWarning)
def warnf(self, fmt, *args, **kwargs):
def warnf(self, fmt: str, *args: Any, **kwargs: Any) -> None:
self.log(logging.WARNING, _F(fmt, *args, **kwargs), stacklevel=4)

@deprecated(reason="Use Python Logger compatible method. Will be removed after v23.",
version="v23", category=FutureWarning)
def errorf(self, fmt, *args, **kwargs):
def errorf(self, fmt: str, *args: Any, **kwargs: Any) -> None:
self.log(logging.ERROR, _F(fmt, *args, **kwargs), stacklevel=4)

@deprecated(reason="Use Python Logger compatible method. Will be removed after v23.",
version="v23", category=FutureWarning)
def fatalf(self, fmt, *args, **kwargs):
def fatalf(self, fmt: str, *args: Any, **kwargs: Any) -> None:
self.log(logging.CRITICAL, _F(fmt, *args, **kwargs), stacklevel=4)

def setLevel(self, level):
def setLevel(self, level: Union[int, str]) -> None:
"""Set the level for the logger, trapping lsst.log values.
Parameters
Expand All @@ -193,38 +202,38 @@ def setLevel(self, level):
The level to use. If the level looks too big to be a Python
logging level it is assumed to be a lsst.log level.
"""
if level > logging.CRITICAL:
if isinstance(level, int) and level > logging.CRITICAL:
self.logger.warning("Attempting to set level to %d -- looks like an lsst.log level so scaling it"
" accordingly.", level)
level //= 1000

self.logger.setLevel(level)

@property
def handlers(self):
def handlers(self) -> List[logging.Handler]:
"""Log handlers associated with this logger."""
return self.logger.handlers

def addHandler(self, handler):
def addHandler(self, handler: logging.Handler) -> None:
"""Add a handler to this logger.
The handler is forwarded to the underlying logger.
"""
self.logger.addHandler(handler)

def removeHandler(self, handler):
def removeHandler(self, handler: logging.Handler) -> None:
"""Remove the given handler from the underlying logger."""
self.logger.removeHandler(handler)


def getLogger(name=None, logger=None):
def getLogger(name: Optional[str] = None, logger: logging.Logger = None) -> LsstLogAdapter:
"""Get a logger compatible with LSST usage.
Parameters
----------
name : `str`, optional
Name of the logger. Root logger if `None`.
logger : `logging.Logger`
logger : `logging.Logger` or `LsstLogAdapter`
If given the logger is converted to the relevant logger class.
If ``name`` is given the logger is assumed to be a child of the
supplied logger.
Expand Down
26 changes: 17 additions & 9 deletions python/lsst/utils/timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@

from typing import (
Any,
Callable,
Collection,
MutableMapping,
Optional,
Tuple,
)


Expand All @@ -44,9 +48,9 @@ def _add_to_metadata(metadata: MutableMapping, name: str, value: Any) -> None:
try:
try:
# PropertySet should always prefer LongLong for integers
metadata.addLongLong(name, value)
metadata.addLongLong(name, value) # type: ignore
except TypeError:
metadata.add(name, value)
metadata.add(name, value) # type: ignore
except AttributeError:
pass
else:
Expand All @@ -58,7 +62,9 @@ def _add_to_metadata(metadata: MutableMapping, name: str, value: Any) -> None:
metadata[name].append(value)


def logPairs(obj, pairs, logLevel=logging.DEBUG, metadata=None, logger=None):
def logPairs(obj: Any, pairs: Collection[Tuple[str, Any]], logLevel: int = logging.DEBUG,
metadata: Optional[MutableMapping] = None,
logger: Optional[logging.Logger] = None) -> None:
"""Log ``(name, value)`` pairs to ``obj.metadata`` and ``obj.log``
Parameters
Expand Down Expand Up @@ -102,7 +108,8 @@ def logPairs(obj, pairs, logLevel=logging.DEBUG, metadata=None, logger=None):
logging.getLogger("timer." + logger.name).log(logLevel, "; ".join(strList))


def logInfo(obj, prefix, logLevel=logging.DEBUG, metadata=None, logger=None):
def logInfo(obj: Any, prefix: str, logLevel: int = logging.DEBUG,
metadata: Optional[MutableMapping] = None, logger: Optional[logging.Logger] = None) -> None:
"""Log timer information to ``obj.metadata`` and ``obj.log``.
Parameters
Expand All @@ -117,7 +124,7 @@ def logInfo(obj, prefix, logLevel=logging.DEBUG, metadata=None, logger=None):
If `None`, at least one of ``metadata`` or ``logger`` should be passed
or this function will do nothing.
prefix
prefix : `str`
Name prefix, the resulting entries are ``CpuTime``, etc.. For example
`timeMethod` uses ``prefix = Start`` when the method begins and
``prefix = End`` when the method ends.
Expand Down Expand Up @@ -170,8 +177,9 @@ def logInfo(obj, prefix, logLevel=logging.DEBUG, metadata=None, logger=None):
logger=logger)


def timeMethod(_func=None, *, metadata=None, logger=None,
logLevel=logging.DEBUG):
def timeMethod(_func: Optional[Any] = None, *, metadata: Optional[MutableMapping] = None,
logger: Optional[logging.Logger] = None,
logLevel: int = logging.DEBUG) -> Callable:
"""Decorator to measure duration of a method.
Parameters
Expand Down Expand Up @@ -218,9 +226,9 @@ def run(self, ...): # or any other instance method you want to time
pass
"""

def decorator_timer(func):
def decorator_timer(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(self, *args, **keyArgs):
def wrapper(self: Any, *args: Any, **keyArgs: Any) -> Any:
logInfo(obj=self, prefix=func.__name__ + "Start", metadata=metadata, logger=logger,
logLevel=logLevel)
try:
Expand Down

0 comments on commit 851eb71

Please sign in to comment.