Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-daiquiri for openSUSE:Factory checked in at 2022-12-02 15:47:30 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-daiquiri (Old) and /work/SRC/openSUSE:Factory/.python-daiquiri.new.1835 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-daiquiri" Fri Dec 2 15:47:30 2022 rev:12 rq:1039551 version:3.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-daiquiri/python-daiquiri.changes 2022-11-17 17:24:49.865169102 +0100 +++ /work/SRC/openSUSE:Factory/.python-daiquiri.new.1835/python-daiquiri.changes 2022-12-02 15:47:34.164617325 +0100 @@ -1,0 +2,19 @@ +Fri Dec 2 01:09:04 UTC 2022 - Yogalakshmi Arunachalam <yarunacha...@suse.com> + +- Update to 3.2.1 + * chore: add custom LogRecord classes to avoid ignore + * fix(output): correct Datadog handler class + * fix: use _ version + +- Update to 3.2.0 + * chore: type everything + * refactor: remove Python 2 support leftover + * chore: configure black + +- Update to 3.1.1 + * fix: add missing py.typed + +- Update to 3.1.0 + * fix: update setup.cfg + +------------------------------------------------------------------- Old: ---- daiquiri-3.0.1.tar.gz New: ---- daiquiri-3.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-daiquiri.spec ++++++ --- /var/tmp/diff_new_pack.pwAUPS/_old 2022-12-02 15:47:34.680620216 +0100 +++ /var/tmp/diff_new_pack.pwAUPS/_new 2022-12-02 15:47:34.684620239 +0100 @@ -18,7 +18,7 @@ %global skip_python2 1 Name: python-daiquiri -Version: 3.0.1 +Version: 3.2.1 Release: 0 Summary: Library to configure Python logging License: Apache-2.0 ++++++ daiquiri-3.0.1.tar.gz -> daiquiri-3.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/.circleci/config.yml new/daiquiri-3.2.1/.circleci/config.yml --- old/daiquiri-3.0.1/.circleci/config.yml 2020-11-06 10:56:59.000000000 +0100 +++ new/daiquiri-3.2.1/.circleci/config.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,58 +0,0 @@ -version: 2.1 - -commands: - tox: - parameters: - target: - type: string - steps: - - checkout - - run: - command: | - sudo pip install tox - tox -e << parameters.target >> - -jobs: - pep8: - docker: - - image: circleci/python:3.8 - steps: - - tox: {target: pep8} - docs: - docker: - - image: circleci/python:3.8 - steps: - - tox: {target: docs} - py36: - docker: - - image: circleci/python:3.6 - steps: - - tox: {target: py36} - py37: - docker: - - image: circleci/python:3.7 - steps: - - tox: {target: py37} - py38: - docker: - - image: circleci/python:3.8 - steps: - - tox: {target: py38} - py39: - docker: - - image: circleci/python:3.9 - steps: - - tox: {target: py39} - - -workflows: - version: 2 - - test: - jobs: - - pep8 - - py36 - - py37 - - py38 - - py39 - - docs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/.github/workflows/ci.yaml new/daiquiri-3.2.1/.github/workflows/ci.yaml --- old/daiquiri-3.0.1/.github/workflows/ci.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/daiquiri-3.2.1/.github/workflows/ci.yaml 2022-06-21 17:37:53.000000000 +0200 @@ -0,0 +1,46 @@ +name: CI +permissions: read-all + +on: + pull_request: + branches: + - main + +jobs: + test: + strategy: + matrix: + python: ["3.7", "3.8", "3.9", "3.10"] + + runs-on: ubuntu-20.04 + steps: + - name: Checkout ðï¸ + uses: actions/checkout@v2.4.0 + + - name: Setup Python ð§ + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + + - name: Install tox + run: pip install tox + + - name: Test ð + run: tox -e py$(echo ${{ matrix.python }} | tr -d .) + + pep8: + runs-on: ubuntu-20.04 + steps: + - name: Checkout ðï¸ + uses: actions/checkout@v2.4.0 + + - name: Setup Python ð§ + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox + + - name: Test ð + run: tox -e pep8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/.mergify.yml new/daiquiri-3.2.1/.mergify.yml --- old/daiquiri-3.0.1/.mergify.yml 2021-08-31 15:24:50.000000000 +0200 +++ new/daiquiri-3.2.1/.mergify.yml 2022-06-22 11:43:52.000000000 +0200 @@ -1,21 +1,25 @@ pull_request_rules: - name: automatic merge conditions: - - base=master - - check-success=test - - label!=work-in-progress + - base=main + - "#approved-reviews-by>=1" + - and: &CheckRuns + - "check-success=pep8" + - "check-success=test (3.7)" + - "check-success=test (3.8)" + - "check-success=test (3.9)" + - "check-success=test (3.10)" + actions: - merge: - strict: "smart" + queue: + name: default + - name: dismiss reviews conditions: [] actions: dismiss_reviews: {} - - name: automatic merge from maintainer - conditions: - - author=jd - - check-success=test - - label!=work-in-progress - actions: - merge: - strict: "smart" + + +queue_rules: + - name: default + conditions: [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/PKG-INFO new/daiquiri-3.2.1/PKG-INFO --- old/daiquiri-3.0.1/PKG-INFO 2021-08-31 15:26:18.387275700 +0200 +++ new/daiquiri-3.2.1/PKG-INFO 2022-07-01 09:24:39.595489500 +0200 @@ -1,22 +1,21 @@ Metadata-Version: 2.1 Name: daiquiri -Version: 3.0.1 +Version: 3.2.1 Summary: Library to configure Python logging easily -Home-page: https://github.com/jd/daiquiri +Home-page: https://github.com/Mergifyio/daiquiri Author: Julien Danjou Author-email: jul...@danjou.info License: Apache 2.0 -Platform: UNKNOWN Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Description-Content-Type: text/x-rst Provides-Extra: test Provides-Extra: systemd @@ -26,9 +25,6 @@ daiquiri -- Python logging setup helper ======================================= -.. image:: https://circleci.com/gh/jd/daiquiri.svg?style=svg - :target: https://circleci.com/gh/jd/daiquiri - .. image:: https://img.shields.io/pypi/v/daiquiri.svg :target: https://pypi.python.org/pypi/daiquiri :alt: Latest Version @@ -39,6 +35,4 @@ You can read the whole documentation at http://daiquiri.readthedocs.io/ * Free software: Apache license -* Source: https://github.com/jd/daiquiri - - +* Source: https://github.com/Mergifyio/daiquiri diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/README.rst new/daiquiri-3.2.1/README.rst --- old/daiquiri-3.0.1/README.rst 2019-08-06 18:42:24.000000000 +0200 +++ new/daiquiri-3.2.1/README.rst 2022-06-22 11:40:00.000000000 +0200 @@ -2,9 +2,6 @@ daiquiri -- Python logging setup helper ======================================= -.. image:: https://circleci.com/gh/jd/daiquiri.svg?style=svg - :target: https://circleci.com/gh/jd/daiquiri - .. image:: https://img.shields.io/pypi/v/daiquiri.svg :target: https://pypi.python.org/pypi/daiquiri :alt: Latest Version @@ -15,4 +12,4 @@ You can read the whole documentation at http://daiquiri.readthedocs.io/ * Free software: Apache license -* Source: https://github.com/jd/daiquiri +* Source: https://github.com/Mergifyio/daiquiri diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri/__init__.py new/daiquiri-3.2.1/daiquiri/__init__.py --- old/daiquiri-3.0.1/daiquiri/__init__.py 2021-08-31 15:24:50.000000000 +0200 +++ new/daiquiri-3.2.1/daiquiri/__init__.py 2022-07-01 09:24:32.000000000 +0200 @@ -9,16 +9,24 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +import collections.abc import logging import logging.config import logging.handlers import sys import traceback +import types as _ptypes +import typing from daiquiri import output +if typing.TYPE_CHECKING: + _KeywordArgumentAdapterBase = logging.LoggerAdapter[logging.Logger] +else: + _KeywordArgumentAdapterBase = logging.LoggerAdapter -class KeywordArgumentAdapter(logging.LoggerAdapter): + +class KeywordArgumentAdapter(_KeywordArgumentAdapterBase): """Logger adapter to add keyword arguments to log record's extra data. Keywords passed to the log call are added to the "extra" @@ -33,10 +41,13 @@ """ - def process(self, msg, kwargs): + def process( + self, msg: typing.Any, kwargs: "collections.abc.MutableMapping[str, typing.Any]" + ) -> typing.Tuple[typing.Any, "collections.abc.MutableMapping[str, typing.Any]"]: # Make a new extra dictionary combining the values we were # given when we were constructed and anything from kwargs. - extra = self.extra.copy() + if self.extra is not None: + extra = dict(self.extra) if "extra" in kwargs: extra.update(kwargs.pop("extra")) # Move any unknown keyword arguments into the extra @@ -49,14 +60,10 @@ kwargs["extra"] = extra return msg, kwargs - if sys.version_info.major == 2: - - def setLevel(self, level): - """Set the specified level on the underlying logger.""" - self.logger.setLevel(level) - -def getLogger(name=None, **kwargs): +def getLogger( + name: typing.Optional[str] = None, **kwargs: typing.Any +) -> KeywordArgumentAdapter: """Build a logger with the given name. :param name: The name for the logger. This is usually the module @@ -67,12 +74,12 @@ def setup( - level=logging.WARNING, - outputs=[output.STDERR], - program_name=None, - capture_warnings=True, - set_excepthook=True, -): + level: int = logging.WARNING, + outputs: typing.Iterable[typing.Union[output.Output, str]] = [output.STDERR], + program_name: typing.Optional[str] = None, + capture_warnings: bool = True, + set_excepthook: bool = True, +) -> None: """Set up Python logging. This sets up basic handlers for Python logging. @@ -91,9 +98,10 @@ # Add configured handlers for out in outputs: if isinstance(out, str): - out = output.preconfigured.get(out) - if out is None: + if out not in output.preconfigured: raise RuntimeError("Output {} is not available".format(out)) + out = output.preconfigured[out] + out.add_to_logger(root_logger) root_logger.setLevel(level) @@ -101,7 +109,11 @@ if set_excepthook: program_logger = logging.getLogger(program_name) - def logging_excepthook(exc_type, value, tb): + def logging_excepthook( + exc_type: typing.Optional[typing.Type[BaseException]], + value: typing.Optional[BaseException], + tb: typing.Optional[_ptypes.TracebackType], + ) -> None: program_logger.critical( "".join(traceback.format_exception(exc_type, value, tb)) ) @@ -112,18 +124,28 @@ logging.captureWarnings(True) -def parse_and_set_default_log_levels(default_log_levels, separator="="): +def parse_and_set_default_log_levels( + default_log_levels: typing.Iterable[str], separator: str = "=" +) -> None: """Set default log levels for some loggers. :param default_log_levels: List of strings with format <logger_name><separator><log_level> """ - return set_default_log_levels( - (pair.split(separator, 1) for pair in default_log_levels) - ) + levels = [] + for pair in default_log_levels: + result = pair.split(separator, 1) + if len(result) != 2: + raise ValueError("Wrong log level format: `%s`" % result) + levels.append(typing.cast(typing.Tuple[str, str], tuple(result))) + return set_default_log_levels(levels) -def set_default_log_levels(loggers_and_log_levels): +def set_default_log_levels( + loggers_and_log_levels: typing.Iterable[ + typing.Tuple[typing.Optional[str], typing.Union[str, int]] + ] +) -> None: """Set default log levels for some loggers. :param loggers_and_log_levels: List of tuple (logger name, level). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri/formatter.py new/daiquiri-3.2.1/daiquiri/formatter.py --- old/daiquiri-3.0.1/daiquiri/formatter.py 2020-11-06 13:43:33.000000000 +0100 +++ new/daiquiri-3.2.1/daiquiri/formatter.py 2022-07-01 09:24:32.000000000 +0200 @@ -12,9 +12,12 @@ """Formatters.""" import logging +import typing from pythonjsonlogger import jsonlogger +from daiquiri import types + DEFAULT_FORMAT = ( "%(asctime)s [%(process)d] %(color)s%(levelname)-8.8s " @@ -41,7 +44,7 @@ COLOR_STOP = "\033[0m" - def add_color(self, record): + def add_color(self, record: types.ColoredLogRecord) -> None: """Add color to a record.""" if getattr(record, "_stream_is_a_tty", False): record.color = self.LEVEL_COLORS[record.levelno] @@ -50,13 +53,14 @@ record.color = "" record.color_stop = "" - def remove_color(self, record): + def remove_color(self, record: types.ColoredLogRecord) -> None: """Remove color from a record.""" del record.color del record.color_stop - def format(self, record): + def format(self, record: logging.LogRecord) -> str: """Format a record.""" + record = typing.cast(types.ColoredLogRecord, record) self.add_color(record) s = super(ColorFormatter, self).format(record) self.remove_color(record) @@ -102,14 +106,14 @@ def __init__( self, - keywords=None, - extras_template="[{0}: {1}]", - extras_separator=" ", - extras_prefix=" ", - extras_suffix="", - *args, - **kwargs - ): + keywords: typing.Optional[typing.Set[str]] = None, + extras_template: str = "[{0}: {1}]", + extras_separator: str = " ", + extras_prefix: str = " ", + extras_suffix: str = "", + *args: typing.Any, + **kwargs: typing.Any, + ) -> None: self.keywords = set() if keywords is None else keywords self.extras_template = extras_template self.extras_separator = extras_separator @@ -117,7 +121,7 @@ self.extras_suffix = extras_suffix super(ExtrasFormatter, self).__init__(*args, **kwargs) - def add_extras(self, record): + def add_extras(self, record: types.ExtrasLogRecord) -> None: if not hasattr(record, "_daiquiri_extra_keys"): record.extras = "" return @@ -131,10 +135,11 @@ extras = self.extras_prefix + extras + self.extras_suffix record.extras = extras - def remove_extras(self, record): + def remove_extras(self, record: types.ExtrasLogRecord) -> None: del record.extras - def format(self, record): + def format(self, record: logging.LogRecord) -> str: + record = typing.cast(types.ExtrasLogRecord, record) self.add_extras(record) s = super(ExtrasFormatter, self).format(record) self.remove_extras(record) @@ -144,24 +149,30 @@ class ColorExtrasFormatter(ColorFormatter, ExtrasFormatter): """Combines functionality of ColorFormatter and ExtrasFormatter.""" - def format(self, record): + def format(self, record: logging.LogRecord) -> str: + record = typing.cast(types.ColoredLogRecord, record) self.add_color(record) s = ExtrasFormatter.format(self, record) self.remove_color(record) return s -class DatadogFormatter(jsonlogger.JsonFormatter): - def __init__(self): +class DatadogFormatter(jsonlogger.JsonFormatter): # type: ignore[misc] + def __init__(self) -> None: super(DatadogFormatter, self).__init__(timestamp=True) - def add_fields(self, log_record, record, message_dict): + def add_fields( + self, + log_record: typing.Dict[str, typing.Any], + record: logging.LogRecord, + message_dict: typing.Dict[str, str], + ) -> None: super(DatadogFormatter, self).add_fields(log_record, record, message_dict) log_record["status"] = record.levelname.lower() log_record["logger"] = { "name": record.name, } - if record.exc_info: + if record.exc_info and record.exc_info[0]: log_record["error"] = { "kind": record.exc_info[0].__name__, "stack": message_dict.get("stack_info"), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri/handlers.py new/daiquiri-3.2.1/daiquiri/handlers.py --- old/daiquiri-3.0.1/daiquiri/handlers.py 2020-11-06 13:43:29.000000000 +0100 +++ new/daiquiri-3.2.1/daiquiri/handlers.py 2022-07-01 09:24:32.000000000 +0200 @@ -14,16 +14,21 @@ import logging import logging.config import logging.handlers +import typing try: from systemd import journal except ImportError: journal = None + + try: import syslog except ImportError: - syslog = None + syslog = None # type: ignore[assignment] + +from daiquiri import types # This is a copy of the numerical constants from syslog.h. The # definition of these goes back at least 20 years, and is specifically @@ -42,7 +47,9 @@ class SyslogHandler(logging.Handler): """Syslog based handler. Only available on UNIX-like platforms.""" - def __init__(self, program_name, facility=None): + def __init__( + self, program_name: str, facility: typing.Optional[int] = None + ) -> None: # Default values always get evaluated, for which reason we avoid # using 'syslog' directly, which may not be available. facility = facility if facility is not None else syslog.LOG_USER @@ -51,7 +58,7 @@ super(SyslogHandler, self).__init__() syslog.openlog(program_name, 0, facility) - def emit(self, record): + def emit(self, record: logging.LogRecord) -> None: priority = SYSLOG_MAP.get(record.levelname, 7) message = self.format(record) syslog.syslog(priority, message) @@ -60,13 +67,15 @@ class JournalHandler(logging.Handler): """Journald based handler. Only available on platforms using systemd.""" - def __init__(self, program_name): + program_name: str + + def __init__(self, program_name: str) -> None: if not journal: raise RuntimeError("Systemd bindings do not exist") super(JournalHandler, self).__init__() self.program_name = program_name - def emit(self, record): + def emit(self, record: logging.LogRecord) -> None: priority = SYSLOG_MAP.get(record.levelname, 7) message = self.format(record) @@ -89,6 +98,7 @@ extras["EXCEPTION_INFO"] = record.exc_info if hasattr(record, "_daiquiri_extra_keys"): + record = typing.cast(types.ExtrasLogRecord, record) for k in record._daiquiri_extra_keys: if k != "_daiquiri_extra_keys": extras[k.upper()] = getattr(record, k) @@ -96,10 +106,17 @@ journal.send(message, **extras) -class TTYDetectorStreamHandler(logging.StreamHandler): +if typing.TYPE_CHECKING: + _TTYDetectorStreamHandlerBase = logging.StreamHandler[typing.Any] +else: + _TTYDetectorStreamHandlerBase = logging.StreamHandler + + +class TTYDetectorStreamHandler(_TTYDetectorStreamHandlerBase): """Stream handler that adds a hint in the record if the stream is a TTY.""" - def format(self, record): + def format(self, record: logging.LogRecord) -> str: + record = typing.cast(types.TTYDetectionLogRecord, record) if hasattr(self.stream, "isatty"): try: record._stream_is_a_tty = self.stream.isatty() @@ -116,20 +133,20 @@ class PlainTextSocketHandler(logging.handlers.SocketHandler): """Socket handler that uses format and encode the record.""" - def __init__(self, hostname, port, encoding="utf-8"): + def __init__(self, hostname: str, port: int, encoding: str = "utf-8") -> None: self.encoding = encoding super(PlainTextSocketHandler, self).__init__(hostname, port) - def makePickle(self, record): + def makePickle(self, record: logging.LogRecord) -> bytes: return self.format(record).encode(self.encoding) + b"\n" class PlainTextDatagramHandler(logging.handlers.DatagramHandler): """Socket handler that uses format and encode the record.""" - def __init__(self, hostname, port, encoding="utf-8"): + def __init__(self, hostname: str, port: int, encoding: str = "utf-8") -> None: self.encoding = encoding super(PlainTextDatagramHandler, self).__init__(hostname, port) - def makePickle(self, record): + def makePickle(self, record: logging.LogRecord) -> bytes: return self.format(record).encode(self.encoding) + b"\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri/output.py new/daiquiri-3.2.1/daiquiri/output.py --- old/daiquiri-3.0.1/daiquiri/output.py 2020-11-06 13:43:33.000000000 +0100 +++ new/daiquiri-3.2.1/daiquiri/output.py 2022-07-01 09:06:31.000000000 +0200 @@ -13,41 +13,49 @@ import inspect import logging import logging.handlers -import numbers import os import sys +import typing try: import syslog except ImportError: - syslog = None + syslog = None # type: ignore[assignment] from daiquiri import formatter from daiquiri import handlers -def get_program_name(): +def get_program_name() -> str: """Return the name of the running program.""" return os.path.basename(inspect.stack()[-1][1]) -class Output(object): +class Output: """Generic log output.""" - def __init__(self, handler, formatter=formatter.TEXT_FORMATTER, level=None): + def __init__( + self, + handler: logging.Handler, + formatter: logging.Formatter = formatter.TEXT_FORMATTER, + level: typing.Optional[int] = None, + ): self.handler = handler self.handler.setFormatter(formatter) if level is not None: self.handler.setLevel(level) - def add_to_logger(self, logger): + def add_to_logger(self, logger: logging.Logger) -> None: """Add this output to a logger.""" logger.addHandler(self.handler) def _get_log_file_path( - logfile=None, logdir=None, program_name=None, logfile_suffix=".log" -): + logfile: typing.Optional[str] = None, + logdir: typing.Optional[str] = None, + program_name: typing.Optional[str] = None, + logfile_suffix: str = ".log", +) -> str: ret_path = None if not logdir: @@ -71,12 +79,12 @@ def __init__( self, - filename=None, - directory=None, - suffix=".log", - program_name=None, - formatter=formatter.TEXT_FORMATTER, - level=None, + filename: typing.Optional[str] = None, + directory: typing.Optional[str] = None, + suffix: str = ".log", + program_name: typing.Optional[str] = None, + formatter: logging.Formatter = formatter.TEXT_FORMATTER, + level: typing.Optional[int] = None, ): """Log file output. @@ -101,16 +109,18 @@ class RotatingFile(Output): """Output to a file, rotating after a certain size.""" + handler: logging.handlers.RotatingFileHandler + def __init__( self, - filename=None, - directory=None, - suffix=".log", - program_name=None, - formatter=formatter.TEXT_FORMATTER, - level=None, - max_size_bytes=0, - backup_count=0, + filename: typing.Optional[str] = None, + directory: typing.Optional[str] = None, + suffix: str = ".log", + program_name: typing.Optional[str] = None, + formatter: logging.Formatter = formatter.TEXT_FORMATTER, + level: typing.Optional[int] = None, + max_size_bytes: int = 0, + backup_count: int = 0, ): """Rotating log file output. @@ -139,7 +149,7 @@ ) super(RotatingFile, self).__init__(handler, formatter, level) - def do_rollover(self): + def do_rollover(self) -> None: """Manually forces a log file rotation.""" return self.handler.doRollover() @@ -147,16 +157,20 @@ class TimedRotatingFile(Output): """Rotating log file output, triggered by a fixed interval.""" + handler: logging.handlers.TimedRotatingFileHandler + def __init__( self, - filename=None, - directory=None, - suffix=".log", - program_name=None, - formatter=formatter.TEXT_FORMATTER, - level=None, - interval=datetime.timedelta(hours=24), - backup_count=0, + filename: typing.Optional[str] = None, + directory: typing.Optional[str] = None, + suffix: str = ".log", + program_name: typing.Optional[str] = None, + formatter: logging.Formatter = formatter.TEXT_FORMATTER, + level: typing.Optional[int] = None, + interval: typing.Union[float, int, datetime.timedelta] = datetime.timedelta( + hours=24 + ), + backup_count: int = 0, ): """Rotating log file output, triggered by a fixed interval. @@ -182,24 +196,26 @@ handler = logging.handlers.TimedRotatingFileHandler( logpath, when="S", - interval=self._timedelta_to_seconds(interval), + interval=int(self._timedelta_to_seconds(interval)), backupCount=backup_count, ) super(TimedRotatingFile, self).__init__(handler, formatter, level) - def do_rollover(self): + def do_rollover(self) -> None: """Manually forces a log file rotation.""" return self.handler.doRollover() @staticmethod - def _timedelta_to_seconds(td): + def _timedelta_to_seconds( + td: typing.Union[float, int, datetime.timedelta] + ) -> float: """Convert a datetime.timedelta object into a seconds interval. :param td: datetime.timedelta :return: time in seconds :rtype: int """ - if isinstance(td, numbers.Real): + if isinstance(td, (int, float)): td = datetime.timedelta(seconds=td) return td.total_seconds() @@ -208,8 +224,11 @@ """Generic stream output.""" def __init__( - self, stream=sys.stderr, formatter=formatter.TEXT_FORMATTER, level=None - ): + self, + stream: typing.TextIO = sys.stderr, + formatter: logging.Formatter = formatter.TEXT_FORMATTER, + level: typing.Optional[int] = None, + ) -> None: super(Stream, self).__init__( handlers.TTYDetectorStreamHandler(stream), formatter, level ) @@ -221,9 +240,12 @@ class Journal(Output): def __init__( - self, program_name=None, formatter=formatter.TEXT_FORMATTER, level=None - ): - program_name = program_name or get_program_name + self, + program_name: typing.Optional[str] = None, + formatter: logging.Formatter = formatter.TEXT_FORMATTER, + level: typing.Optional[int] = None, + ) -> None: + program_name = program_name or get_program_name() super(Journal, self).__init__( handlers.JournalHandler(program_name), formatter, level ) @@ -232,11 +254,11 @@ class Syslog(Output): def __init__( self, - program_name=None, - facility="user", - formatter=formatter.TEXT_FORMATTER, - level=None, - ): + program_name: typing.Optional[str] = None, + facility: str = "user", + formatter: logging.Formatter = formatter.TEXT_FORMATTER, + level: typing.Optional[int] = None, + ) -> None: if syslog is None: # FIXME(jd) raise something more specific raise RuntimeError("syslog is not available on this platform") @@ -250,7 +272,7 @@ ) @staticmethod - def _find_facility(facility): + def _find_facility(facility: str) -> int: # NOTE(jd): Check the validity of facilities at run time as they differ # depending on the OS and Python version being used. valid_facilities = [ @@ -291,17 +313,19 @@ % ", ".join("'%s'" % fac for fac in valid_facilities) ) - return getattr(syslog, facility) + return int(getattr(syslog, facility)) class Datadog(Output): def __init__( self, - hostname="127.0.0.1", - port=10518, - formatter=formatter.DATADOG_FORMATTER, - level=None, - handler_class=handlers.PlainTextSocketHandler, + hostname: str = "127.0.0.1", + port: int = 10518, + formatter: logging.Formatter = formatter.DATADOG_FORMATTER, + level: typing.Optional[int] = None, + handler_class: typing.Type[ + logging.handlers.SocketHandler + ] = handlers.PlainTextSocketHandler, ): super(Datadog, self).__init__( handler_class(hostname, port), @@ -310,7 +334,8 @@ ) -preconfigured = { +# FIXME(jd): Is this useful? Remove it? +preconfigured: typing.Dict[str, typing.Union[Stream, Output]] = { "stderr": STDERR, "stdout": STDOUT, } @@ -318,5 +343,5 @@ if syslog is not None: preconfigured["syslog"] = Syslog() -if handlers.journal is not None: +if handlers.journal is not None: # type: ignore[attr-defined] preconfigured["journal"] = Journal() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri/tests/test_daiquiri.py new/daiquiri-3.2.1/daiquiri/tests/test_daiquiri.py --- old/daiquiri-3.0.1/daiquiri/tests/test_daiquiri.py 2021-08-31 15:24:50.000000000 +0200 +++ new/daiquiri-3.2.1/daiquiri/tests/test_daiquiri.py 2022-06-30 09:29:14.000000000 +0200 @@ -19,17 +19,17 @@ class TestDaiquiri(unittest.TestCase): - def tearDown(self): + def tearDown(self) -> None: # Be sure to reset the warning capture logging.captureWarnings(False) super(TestDaiquiri, self).tearDown() - def test_setup(self): + def test_setup(self) -> None: daiquiri.setup() daiquiri.setup(level=logging.DEBUG) daiquiri.setup(program_name="foobar") - def test_setup_json_formatter(self): + def test_setup_json_formatter(self) -> None: stream = io.StringIO() daiquiri.setup( outputs=( @@ -41,7 +41,7 @@ daiquiri.getLogger(__name__).warning("foobar") self.assertEqual({"message": "foobar"}, json.loads(stream.getvalue())) - def test_setup_json_formatter_with_extras(self): + def test_setup_json_formatter_with_extras(self) -> None: stream = io.StringIO() daiquiri.setup( outputs=( @@ -55,11 +55,11 @@ {"message": "foobar", "foo": "bar"}, json.loads(stream.getvalue()) ) - def test_get_logger_set_level(self): + def test_get_logger_set_level(self) -> None: logger = daiquiri.getLogger(__name__) logger.setLevel(logging.DEBUG) - def test_capture_warnings(self): + def test_capture_warnings(self) -> None: stream = io.StringIO() daiquiri.setup(outputs=(daiquiri.output.Stream(stream),)) warnings.warn("omg!") @@ -71,7 +71,7 @@ line, ) - def test_no_capture_warnings(self): + def test_no_capture_warnings(self) -> None: stream = io.StringIO() daiquiri.setup( outputs=(daiquiri.output.Stream(stream),), capture_warnings=False @@ -79,28 +79,28 @@ warnings.warn("omg!") self.assertEqual("", stream.getvalue()) - def test_set_default_log_levels(self): + def test_set_default_log_levels(self) -> None: daiquiri.set_default_log_levels((("amqp", "debug"), ("urllib3", "warn"))) - def test_parse_and_set_default_log_levels(self): + def test_parse_and_set_default_log_levels(self) -> None: daiquiri.parse_and_set_default_log_levels(("urllib3=warn", "foobar=debug")) - def test_string_as_setup_outputs_arg(self): + def test_string_as_setup_outputs_arg(self) -> None: daiquiri.setup(outputs=("stderr", "stdout")) - if daiquiri.handlers.syslog is not None: + if daiquiri.handlers.syslog is not None: # type: ignore[attr-defined] daiquiri.setup(outputs=("syslog",)) - if daiquiri.handlers.journal is not None: + if daiquiri.handlers.journal is not None: # type: ignore[attr-defined] daiquiri.setup(outputs=("journal",)) - def test_special_kwargs(self): + def test_special_kwargs(self) -> None: daiquiri.getLogger(__name__).info( "foobar", foo="bar", exc_info=True, stack_info=True ) -def test_extra_with_two_loggers(): +def test_extra_with_two_loggers() -> None: stream = io.StringIO() daiquiri.setup(outputs=(daiquiri.output.Stream(stream),)) log1 = daiquiri.getLogger("foobar") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri/tests/test_formatter.py new/daiquiri-3.2.1/daiquiri/tests/test_formatter.py --- old/daiquiri-3.0.1/daiquiri/tests/test_formatter.py 2020-11-06 13:43:29.000000000 +0100 +++ new/daiquiri-3.2.1/daiquiri/tests/test_formatter.py 2022-06-30 09:29:14.000000000 +0200 @@ -17,8 +17,12 @@ class TestColorExtrasFormatter(unittest.TestCase): + logger: daiquiri.KeywordArgumentAdapter + stream: io.StringIO + handler: daiquiri.handlers.TTYDetectorStreamHandler + @classmethod - def setUpClass(cls): + def setUpClass(cls) -> None: cls.logger = daiquiri.getLogger("my_module") cls.logger.setLevel(logging.INFO) cls.stream = io.StringIO() @@ -26,7 +30,7 @@ cls.logger.logger.addHandler(cls.handler) super(TestColorExtrasFormatter, cls).setUpClass() - def setUp(self): + def setUp(self) -> None: # Couldn't get readline() to return anything no matter what I tried, so # getvalue() is the only way to see what's in the stream. However this # requires the stream to be reset every time. @@ -35,7 +39,7 @@ self.handler.stream = self.stream super(TestColorExtrasFormatter, self).setUp() - def test_no_keywords(self): + def test_no_keywords(self) -> None: format_string = "%(levelname)s %(name)s%(extras)s: %(message)s" formatter = daiquiri.formatter.ColorExtrasFormatter(fmt=format_string) self.handler.setFormatter(formatter) @@ -43,7 +47,7 @@ self.logger.info("test message") self.assertEqual(self.stream.getvalue(), "INFO my_module: test message\n") - def test_no_keywords_with_extras(self): + def test_no_keywords_with_extras(self) -> None: format_string = "%(levelname)s %(name)s%(extras)s: %(message)s" formatter = daiquiri.formatter.ColorExtrasFormatter(fmt=format_string) self.handler.setFormatter(formatter) @@ -53,10 +57,11 @@ self.stream.getvalue(), "INFO my_module [test: a]: test message\n" ) - def test_empty_keywords(self): + def test_empty_keywords(self) -> None: format_string = "%(levelname)s %(name)s%(extras)s: %(message)s" formatter = daiquiri.formatter.ColorExtrasFormatter( - fmt=format_string, keywords=[] + fmt=format_string, + keywords=set(), ) self.handler.setFormatter(formatter) @@ -65,20 +70,20 @@ self.stream.getvalue(), "INFO my_module [test: a]: test message\n" ) - def test_keywords_no_extras(self): + def test_keywords_no_extras(self) -> None: format_string = "%(levelname)s %(name)s" " %(test)s%(extras)s: %(message)s" formatter = daiquiri.formatter.ColorExtrasFormatter( - fmt=format_string, keywords=["test"] + fmt=format_string, keywords={"test"} ) self.handler.setFormatter(formatter) self.logger.info("test message", test="a") self.assertEqual(self.stream.getvalue(), "INFO my_module a: test message\n") - def test_keywords_with_extras(self): + def test_keywords_with_extras(self) -> None: format_string = "%(levelname)s %(name)s" " %(test)s%(extras)s: %(message)s" formatter = daiquiri.formatter.ColorExtrasFormatter( - fmt=format_string, keywords=["test"] + fmt=format_string, keywords={"test"} ) self.handler.setFormatter(formatter) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri/tests/test_output.py new/daiquiri-3.2.1/daiquiri/tests/test_output.py --- old/daiquiri-3.0.1/daiquiri/tests/test_output.py 2020-11-06 13:43:29.000000000 +0100 +++ new/daiquiri-3.2.1/daiquiri/tests/test_output.py 2022-06-30 09:29:14.000000000 +0200 @@ -12,6 +12,7 @@ import json import logging import syslog +import typing import unittest from datetime import timedelta from unittest import mock @@ -21,13 +22,13 @@ class DatadogMatcher(object): - def __init__(self, expected): + def __init__(self, expected: typing.Any) -> None: self.expected = expected - def __eq__(self, other): - return json.loads(other.decode()[:-1]) == self.expected + def __eq__(self, other: typing.Any) -> bool: + return bool(json.loads(other.decode()[:-1]) == self.expected) - def __repr__(self): + def __repr__(self) -> str: return ( "b'" + json.dumps(self.expected, default=lambda x: "unserializable") @@ -36,14 +37,14 @@ class TestOutput(unittest.TestCase): - def test_find_facility(self): + def test_find_facility(self) -> None: self.assertEqual(syslog.LOG_USER, output.Syslog._find_facility("user")) self.assertEqual(syslog.LOG_LOCAL1, output.Syslog._find_facility("log_local1")) self.assertEqual(syslog.LOG_LOCAL2, output.Syslog._find_facility("LOG_local2")) self.assertEqual(syslog.LOG_LOCAL3, output.Syslog._find_facility("LOG_LOCAL3")) self.assertEqual(syslog.LOG_LOCAL4, output.Syslog._find_facility("LOCaL4")) - def test_get_log_file_path(self): + def test_get_log_file_path(self) -> None: self.assertEqual("foobar.log", output._get_log_file_path("foobar.log")) self.assertEqual( "/var/log/foo/foobar.log", @@ -64,7 +65,7 @@ ), ) - def test_timedelta_seconds(self): + def test_timedelta_seconds(self) -> None: fn = output.TimedRotatingFile._timedelta_to_seconds hour = 60 * 60 # seconds * minutes @@ -72,12 +73,13 @@ timedelta(hours=1), timedelta(minutes=60), timedelta(seconds=hour), - hour, - float(hour), ] for t in one_hour: self.assertEqual(hour, fn(t)) + assert hour == fn(float(hour)) + assert hour == fn(hour) + error_cases = [ "string", ["some", "list"], @@ -88,10 +90,10 @@ ("tuple",), {"dict": "mapping"}, ] - for t in error_cases: + for t in error_cases: # type: ignore[assignment] self.assertRaises(AttributeError, fn, t) - def test_datadog(self): + def test_datadog(self) -> None: with mock.patch("socket.socket") as mock_socket: socket_instance = mock_socket.return_value daiquiri.setup(outputs=(daiquiri.output.Datadog(),), level=logging.DEBUG) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri/types.py new/daiquiri-3.2.1/daiquiri/types.py --- old/daiquiri-3.0.1/daiquiri/types.py 1970-01-01 01:00:00.000000000 +0100 +++ new/daiquiri-3.2.1/daiquiri/types.py 2022-07-01 09:24:32.000000000 +0200 @@ -0,0 +1,18 @@ +import logging +import typing + + +class ColoredLogRecord(logging.LogRecord): + color: str + color_stop: str + + +class ExtrasLogRecord(logging.LogRecord): + extras_prefix: str + extras_suffix: str + extras: str + _daiquiri_extra_keys: typing.Set[str] + + +class TTYDetectionLogRecord(logging.LogRecord): + _stream_is_a_tty: bool diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri.egg-info/PKG-INFO new/daiquiri-3.2.1/daiquiri.egg-info/PKG-INFO --- old/daiquiri-3.0.1/daiquiri.egg-info/PKG-INFO 2021-08-31 15:26:18.000000000 +0200 +++ new/daiquiri-3.2.1/daiquiri.egg-info/PKG-INFO 2022-07-01 09:24:39.000000000 +0200 @@ -1,22 +1,21 @@ Metadata-Version: 2.1 Name: daiquiri -Version: 3.0.1 +Version: 3.2.1 Summary: Library to configure Python logging easily -Home-page: https://github.com/jd/daiquiri +Home-page: https://github.com/Mergifyio/daiquiri Author: Julien Danjou Author-email: jul...@danjou.info License: Apache 2.0 -Platform: UNKNOWN Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Description-Content-Type: text/x-rst Provides-Extra: test Provides-Extra: systemd @@ -26,9 +25,6 @@ daiquiri -- Python logging setup helper ======================================= -.. image:: https://circleci.com/gh/jd/daiquiri.svg?style=svg - :target: https://circleci.com/gh/jd/daiquiri - .. image:: https://img.shields.io/pypi/v/daiquiri.svg :target: https://pypi.python.org/pypi/daiquiri :alt: Latest Version @@ -39,6 +35,4 @@ You can read the whole documentation at http://daiquiri.readthedocs.io/ * Free software: Apache license -* Source: https://github.com/jd/daiquiri - - +* Source: https://github.com/Mergifyio/daiquiri diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/daiquiri.egg-info/SOURCES.txt new/daiquiri-3.2.1/daiquiri.egg-info/SOURCES.txt --- old/daiquiri-3.0.1/daiquiri.egg-info/SOURCES.txt 2021-08-31 15:26:18.000000000 +0200 +++ new/daiquiri-3.2.1/daiquiri.egg-info/SOURCES.txt 2022-07-01 09:24:39.000000000 +0200 @@ -2,14 +2,17 @@ .mergify.yml LICENSE README.rst +pyproject.toml setup.cfg setup.py tox.ini -.circleci/config.yml +.github/workflows/ci.yaml daiquiri/__init__.py daiquiri/formatter.py daiquiri/handlers.py daiquiri/output.py +daiquiri/py.typed +daiquiri/types.py daiquiri.egg-info/PKG-INFO daiquiri.egg-info/SOURCES.txt daiquiri.egg-info/dependency_links.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/pyproject.toml new/daiquiri-3.2.1/pyproject.toml --- old/daiquiri-3.0.1/pyproject.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/daiquiri-3.2.1/pyproject.toml 2022-06-30 09:29:14.000000000 +0200 @@ -0,0 +1,14 @@ +[tool.black] +target-version = ['py37', 'py38', 'py39', 'py310'] + +[tool.mypy] +files = "daiquiri" +show_error_codes = true +strict = true + +[[tool.mypy.overrides]] +module = [ + "pythonjsonlogger", + "systemd", +] +ignore_missing_imports = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/setup.cfg new/daiquiri-3.2.1/setup.cfg --- old/daiquiri-3.0.1/setup.cfg 2021-08-31 15:26:18.388026200 +0200 +++ new/daiquiri-3.2.1/setup.cfg 2022-07-01 09:24:39.595755000 +0200 @@ -5,8 +5,8 @@ long_description = file:README.rst long_description_content_type = text/x-rst author = Julien Danjou -author-email = jul...@danjou.info -home-page = https://github.com/jd/daiquiri +author_email = jul...@danjou.info +home_page = https://github.com/Mergifyio/daiquiri classifier = Intended Audience :: Information Technology Intended Audience :: System Administrators @@ -14,10 +14,10 @@ Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 [options] install_requires = @@ -25,6 +25,9 @@ packages = daiquiri +[options.package_data] +daiquiri = py.typed + [options.extras_require] test = pytest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/daiquiri-3.0.1/tox.ini new/daiquiri-3.2.1/tox.ini --- old/daiquiri-3.0.1/tox.ini 2020-11-06 13:43:33.000000000 +0100 +++ new/daiquiri-3.2.1/tox.ini 2022-06-30 09:29:14.000000000 +0200 @@ -1,5 +1,5 @@ [tox] -envlist = py36,py37,py38,py39,pep8,docs +envlist = py37,py38,py39,p310,pep8,docs [testenv] whitelist_externals = sh @@ -10,9 +10,12 @@ sh -c "rm errors.log everything.log" [testenv:pep8] +basepython = python3.10 deps = + {[testenv]deps} black flake8 + mypy flake8-import-order flake8-blind-except flake8-builtins @@ -23,6 +26,7 @@ commands = black --check . flake8 + mypy [flake8] show-source = True @@ -33,4 +37,4 @@ [testenv:docs] deps = sphinx -commands = python setup.py build_sphinx +commands = python setup.py build_sphinx \ No newline at end of file