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 <[email protected]>
+
+- 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/[email protected]
+
+ - 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/[email protected]
+
+ - 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: [email protected]
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: [email protected]
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 = [email protected]
-home-page = https://github.com/jd/daiquiri
+author_email = [email protected]
+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