Skip to content

Make true CPython subset #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
981c4ed
Make `level_for()` private
tekktrik May 22, 2022
1de0e3e
Make `format` private to not confict with CPython
tekktrik May 22, 2022
9e11346
Transform LoggingHandler to Handler
tekktrik May 22, 2022
06ee366
Convert PrintHandler to StreamHandler with default
tekktrik May 22, 2022
6d5609c
getLogger() default return is None, remove `null_logger`
tekktrik May 22, 2022
5206a8d
Add function for adding logger to global dict
tekktrik May 22, 2022
0c8c08e
getLogger() uses _addLogger(), default return is None
tekktrik May 22, 2022
b925c03
Logger() takes name and level args
tekktrik May 22, 2022
ef98f1b
Fix typeannotation of addHandler()
tekktrik May 22, 2022
d721752
Add hasHandlers() to compensate for changes to functionality
tekktrik May 22, 2022
800c377
Logging methods now account for changes, arg name changes
tekktrik May 22, 2022
393d878
NullLogger is now NullHandler
tekktrik May 22, 2022
69821d2
addHandler() type annotation updated
tekktrik May 22, 2022
e53bcf2
FileHandler moved to __init__,py
tekktrik May 22, 2022
1eae018
adafruit_logging changed to file py_module
tekktrik May 22, 2022
880b850
Reformatted per pre-commit
tekktrik May 22, 2022
7a796da
Linted, reformatted per pre-commit
tekktrik May 22, 2022
30ceea1
Update examples
tekktrik May 22, 2022
a8d1037
Fixed getLogger() docstring
tekktrik May 22, 2022
61fd3cf
Prevent recursive _addLogger() and __init__()
tekktrik May 22, 2022
969f3b0
Change arg names
tekktrik May 22, 2022
15fc22f
Fix simple test example
tekktrik May 22, 2022
75cf4c9
Update file logging example
tekktrik May 22, 2022
335af26
Logger names should be strings
tekktrik May 22, 2022
6faddad
Change arg name for addHandler()
tekktrik May 28, 2022
85ad94c
Use brackets to get logger in getLogger()
tekktrik May 28, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 123 additions & 63 deletions adafruit_logging/__init__.py → adafruit_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
# pylint:disable=invalid-name

import time
import sys

__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Logger.git"
Expand All @@ -63,14 +64,14 @@
"WARNING",
"ERROR",
"CRITICAL",
"level_for",
"LoggingHandler",
"PrintHandler",
"_level_for",
"Handler",
"StreamHandler",
"logger_cache",
"null_logger",
"getLogger",
"Logger",
"NullLogger",
"NullHandler",
"FileHandler",
]


Expand All @@ -87,7 +88,7 @@
globals()[__name] = __value


def level_for(value: int) -> str:
def _level_for(value: int) -> str:
"""Convert a numeric level to the most appropriate name.

:param int value: a numeric level
Expand All @@ -101,63 +102,80 @@ def level_for(value: int) -> str:
return LEVELS[0][1]


class LoggingHandler:
# pylint: disable=too-few-public-methods
class Handler:
"""Abstract logging message handler."""

def format(self, log_level: int, message: str) -> str:
def __init__(self, level=NOTSET): # pylint: disable=undefined-variable
self.level = level
"""Level of the handler; this is currently unused, and
only the level of the logger is used"""

def _format(self, log_level: int, message: str) -> str:
"""Generate a timestamped message.

:param int log_level: the logging level
:param str message: the message to log

"""
return "{0:<0.3f}: {1} - {2}".format(
time.monotonic(), level_for(log_level), message
time.monotonic(), _level_for(log_level), message
)

def emit(self, log_level: int, message: str):
def _emit(self, log_level: int, message: str):
"""Send a message where it should go.
Placeholder for subclass implementations.
"""
raise NotImplementedError()


class PrintHandler(LoggingHandler):
"""Send logging messages to the console by using print."""
# pylint: disable=too-few-public-methods
class StreamHandler(Handler):
"""Send logging messages to a stream, `sys.stderr` (typically
the serial console) by default.

:param stream: The stream to log to, default is `sys.stderr`
"""

def __init__(self, stream=None):
super().__init__()
if stream is None:
stream = sys.stderr
self.stream = stream
"""The stream to log to"""

def emit(self, log_level: int, message: str):
def _emit(self, log_level: int, message: str):
"""Send a message to the console.

:param int log_level: the logging level
:param str message: the message to log

"""
print(self.format(log_level, message))
print(self._format(log_level, message))


# The level module-global variables get created when loaded
# pylint:disable=undefined-variable

logger_cache = {}
null_logger = None


def _addLogger(logger_name: str):
"""Adds the logger if it doesn't already exist"""
if logger_name not in logger_cache:
logger_cache[logger_name] = Logger(logger_name)


# pylint:disable=global-statement
def getLogger(logger_name: str) -> "Logger":
"""Create or retrieve a logger by name.
"""Create or retrieve a logger by name; only retrieves loggers
made using this function

:param str logger_name: The name of the `Logger` to create/retrieve. `None`
will cause the `NullLogger` instance to be returned.
:param str logger_name: The name of the `Logger` to create/retrieve.

"""
global null_logger
if not logger_name or logger_name == "":
if not null_logger:
null_logger = NullLogger()
return null_logger

if logger_name not in logger_cache:
logger_cache[logger_name] = Logger()
return logger_cache[logger_name]
_addLogger(logger_name)
return logger_cache.get(logger_name, None)


# pylint:enable=global-statement
Expand All @@ -166,10 +184,13 @@ def getLogger(logger_name: str) -> "Logger":
class Logger:
"""Provide a logging api."""

def __init__(self):
def __init__(self, name: str, level=NOTSET):
"""Create an instance."""
self._level = NOTSET
self._handler = PrintHandler()
self._level = level
self.name = name
"""The name of the logger, this should be unique for proper
functionality of `getLogger()`"""
self._handler = None

def setLevel(self, log_level: int):
"""Set the logging cutoff level.
Expand All @@ -187,8 +208,9 @@ def getEffectiveLevel(self) -> int:
"""
return self._level

def addHandler(self, handler: LoggingHandler):
def addHandler(self, handler: Handler):
"""Sets the handler of this logger to the specified handler.

*NOTE* this is slightly different from the CPython equivalent which adds
the handler rather than replacing it.

Expand All @@ -197,76 +219,78 @@ def addHandler(self, handler: LoggingHandler):
"""
self._handler = handler

def log(self, log_level: int, format_string: str, *args):
def hasHandlers(self) -> bool:
"""Whether any handlers have been set for this logger"""
return self._handler is not None

def log(self, level: int, msg: str, *args):
"""Log a message.

:param int log_level: the priority level at which to log
:param str format_string: the core message string with embedded
:param int level: the priority level at which to log
:param str msg: the core message string with embedded
formatting directives
:param args: arguments to ``format_string.format()``; can be empty

"""
if log_level >= self._level:
self._handler.emit(log_level, format_string % args)
if level >= self._level:
self._handler._emit(level, msg % args) # pylint: disable=protected-access

def debug(self, format_string: str, *args):
def debug(self, msg: str, *args):
"""Log a debug message.

:param str format_string: the core message string with embedded
:param str fmsg: the core message string with embedded
formatting directives
:param args: arguments to ``format_string.format()``; can be empty

"""
self.log(DEBUG, format_string, *args)
self.log(DEBUG, msg, *args)

def info(self, format_string: str, *args):
def info(self, msg: str, *args):
"""Log a info message.

:param str format_string: the core message string with embedded
:param str msg: the core message string with embedded
formatting directives
:param args: arguments to ``format_string.format()``; can be empty

"""
self.log(INFO, format_string, *args)
self.log(INFO, msg, *args)

def warning(self, format_string: str, *args):
def warning(self, msg: str, *args):
"""Log a warning message.

:param str format_string: the core message string with embedded
:param str msg: the core message string with embedded
formatting directives
:param args: arguments to ``format_string.format()``; can be empty

"""
self.log(WARNING, format_string, *args)
self.log(WARNING, msg, *args)

def error(self, format_string: str, *args):
def error(self, msg: str, *args):
"""Log a error message.

:param str format_string: the core message string with embedded
:param str msg: the core message string with embedded
formatting directives
:param args: arguments to ``format_string.format()``; can be empty

"""
self.log(ERROR, format_string, *args)
self.log(ERROR, msg, *args)

def critical(self, format_string: str, *args):
def critical(self, msg: str, *args):
"""Log a critical message.

:param str format_string: the core message string with embedded
:param str msg: the core message string with embedded
formatting directives
:param args: arguments to ``format_string.format()``; can be empty

"""
self.log(CRITICAL, format_string, *args)
self.log(CRITICAL, msg, *args)


class NullLogger:
"""Provide an empty logger.
This can be used in place of a real logger to more efficiently disable
logging."""
class NullHandler(Handler):
"""Provide an empty log handler.

def __init__(self):
"""Dummy implementation."""
This can be used in place of a real log handler to more efficiently disable
logging."""

def setLevel(self, log_level: int):
"""Dummy implementation."""
Expand All @@ -275,23 +299,59 @@ def getEffectiveLevel(self) -> int:
"""Dummy implementation."""
return NOTSET

def addHandler(self, handler: LoggingHandler):
def addHandler(self, handler: Handler):
"""Dummy implementation."""

def log(self, log_level: int, format_string: str, *args):
"""Dummy implementation."""

def debug(self, format_string: str, *args):
def debug(self, msg: str, *args):
"""Dummy implementation."""

def info(self, format_string: str, *args):
def info(self, msg: str, *args):
"""Dummy implementation."""

def warning(self, format_string: str, *args):
def warning(self, msg: str, *args):
"""Dummy implementation."""

def error(self, format_string: str, *args):
def error(self, msg: str, *args):
"""Dummy implementation."""

def critical(self, format_string: str, *args):
def critical(self, msg: str, *args):
"""Dummy implementation."""

def _emit(self, log_level: int, message: str):
"""Dummy implementation"""


class FileHandler(StreamHandler):
"""File handler for working with log files off of the microcontroller (like
an SD card)

:param str filename: The filename of the log file
:param str mode: Whether to write ('w') or append ('a'); default is to append
"""

def __init__(self, filename: str, mode: str = "a") -> None:
# pylint: disable=consider-using-with
super().__init__(open(filename, mode=mode))

def close(self):
"""Closes the file"""
self.stream.close()

def _format(self, log_level: int, message: str):
"""Generate a string to log

:param level: The level of the message
:param msg: The message to format
"""
return super()._format(log_level, message) + "\r\n"

def _emit(self, log_level: int, message: str):
"""Generate the message and write it to the UART.

:param level: The level of the message
:param msg: The message to log
"""
self.stream.write(self._format(log_level, message))
51 changes: 0 additions & 51 deletions adafruit_logging/extensions.py

This file was deleted.

Loading