Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ insert_final_newline = true
trim_trailing_whitespace = true

[*.{py,ini}]
max_line_length = 79
max_line_length = 120

[*.{yml,rst}]
indent_size = 2
Expand Down
12 changes: 0 additions & 12 deletions .idea/codeStyleSettings.xml

This file was deleted.

17 changes: 0 additions & 17 deletions .idea/dictionaries/penguinolog.xml

This file was deleted.

2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ logging-modules=logging
[FORMAT]

# Maximum number of characters on a single line.
max-line-length=80
max-line-length=120

# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
CHANGELOG
=========
Version 3.3.0
-------------
* Type hints and stubs
* PEP0518

Version 3.2.0
-------------
* Return logwrap function back with 2 branches: with arguments and not due to
Expand Down
2 changes: 1 addition & 1 deletion logwrap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
pretty_str
)

PY3 = sys.version_info[:2] > (3, 0)
PY3 = sys.version_info[:2] > (3, 0) # type: bool

# pylint: disable=no-name-in-module
if PY3: # pragma: no cover
Expand Down
61 changes: 55 additions & 6 deletions logwrap/_class_decorator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Copyright 2017 Alexey Stepanov aka penguinolog
#!/usr/bin/env python

# Copyright 2017-2018 Alexey Stepanov aka penguinolog
##
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
Expand All @@ -15,19 +17,55 @@
"""Base class for decorators."""

from __future__ import absolute_import
from __future__ import print_function

import abc
import functools
import sys
import typing # noqa # pylint: disable=unused-import

PY3 = sys.version_info[:2] > (3, 0)
PY3 = sys.version_info[:2] > (3, 0) # type: bool


class BaseDecorator(object):
"""Base class for decorators.

Implements wrapping and __call__, wrapper getter is abstract.

Note:
wrapper getter is called only on function call,
if decorator used without braces.

Usage example:

>>> class TestDecorator(BaseDecorator):
... def _get_function_wrapper(self, func):
... print('Wrapping: {}'.format(func.__name__))
... @functools.wraps(func)
... def wrapper(*args, **kwargs):
... print('call_function: {}'.format(func.__name__))
... return func(*args, **kwargs)
... return wrapper

>>> @TestDecorator
... def func_no_init():
... pass
>>> func_no_init()
Wrapping: func_no_init
call_function: func_no_init
>>> isinstance(func_no_init, TestDecorator)
True
>>> func_no_init._func is func_no_init.__wrapped__
True

>>> @TestDecorator()
... def func_init():
... pass
Wrapping: func_init
>>> func_init()
call_function: func_init
>>> isinstance(func_init, TestDecorator)
False
"""

def __init__(
Expand All @@ -40,19 +78,19 @@ def __init__(
:type func: typing.Optional[typing.Callable]
"""
# pylint: disable=assigning-non-slot
self.__func = func
self.__func = func # type: typing.Optional[typing.Callable]
if self.__func is not None:
functools.update_wrapper(self, self.__func)
if not PY3: # pragma: no cover
self.__wrapped__ = self.__func
self.__wrapped__ = self.__func # type: typing.Callable
# pylint: enable=assigning-non-slot
# noinspection PyArgumentList
super(BaseDecorator, self).__init__()

@property
def _func(
self
): # type: (BaseDecorator) -> typing.Optional[typing.Callable]
): # type: () -> typing.Optional[typing.Callable]
"""Get wrapped function.

:rtype: typing.Optional[typing.Callable]
Expand All @@ -72,7 +110,11 @@ def _get_function_wrapper(
"""
raise NotImplementedError() # pragma: no cover

def __call__(self, *args, **kwargs):
def __call__(
self,
*args, # type: typing.Any
**kwargs # type: typing.Any
): # type: (...) -> typing.Any
"""Main decorator getter."""
args = list(args)
wrapped = self.__func or args.pop(0)
Expand All @@ -88,3 +130,10 @@ def __repr__(self):
func=self.__func,
id=id(self)
) # pragma: no cover


# 8<----------------------------------------------------------------------------

if __name__ == '__main__':
import doctest # pragma: no cover
doctest.testmod(verbose=True) # pragma: no cover
8 changes: 8 additions & 0 deletions logwrap/_class_decorator.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import typing

PY3: bool

class BaseDecorator:
__wrapped__: typing.Optional[typing.Callable] = ...
def __init__(self, func: typing.Optional[typing.Callable]=...) -> None: ...
def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> typing.Any: ...
17 changes: 6 additions & 11 deletions logwrap/_log_wrap2.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@
__all__ = ('logwrap', 'LogWrap')


DEFAULT_DECORATOR_ARGUMENT = typing.Union[logging.Logger, typing.Callable]
BLACKLISTED_EXCEPTIONS_ARGUMENT = typing.Optional[typing.List[Exception]]


class LogWrap(_log_wrap_shared.BaseLogWrap):
"""LogWrap."""

Expand Down Expand Up @@ -84,13 +80,13 @@ def wrapper(*args, **kwargs):

# pylint: disable=unexpected-keyword-arg, no-value-for-parameter
def logwrap(
log=_log_wrap_shared.logger, # type: DEFAULT_DECORATOR_ARGUMENT
log=_log_wrap_shared.logger, # type: typing.Union[logging.Logger, typing.Callable]
log_level=logging.DEBUG, # type: int
exc_level=logging.ERROR, # type: int
max_indent=20, # type: int
spec=None, # type: typing.Optional[typing.Callable]
blacklisted_names=None, # type: typing.Optional[typing.List[str]]
blacklisted_exceptions=None, # type: BLACKLISTED_EXCEPTIONS_ARGUMENT
blacklisted_exceptions=None, # type: typing.Optional[typing.List[Exception]]
log_call_args=True, # type: bool
log_call_args_on_exc=True, # type: bool
log_result_obj=True, # type: bool
Expand All @@ -112,9 +108,8 @@ def logwrap(
Note: this object should provide fully compatible signature
with decorated function, or arguments bind will be failed!
:type spec: typing.Optional[typing.Callable]
:param blacklisted_names: list of exception,
which should be re-raised without
producing log record.
:param blacklisted_names: Blacklisted argument names.
Arguments with this names will be skipped in log.
:type blacklisted_names: typing.Optional[typing.List[str]]
:param blacklisted_exceptions: list of exception,
which should be re-raised without
Expand All @@ -130,9 +125,9 @@ def logwrap(
:rtype: _log_wrap_shared.BaseLogWrap
"""
if isinstance(log, logging.Logger):
log, func = log, None
func = None
else:
log, func = _log_wrap_shared.logger, log
log, func = _log_wrap_shared.logger, log # type: logging.Logger, typing.Callable

wrapper = LogWrap(
log=log,
Expand Down
18 changes: 18 additions & 0 deletions logwrap/_log_wrap2.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import logging
import typing
from . import _log_wrap_shared

class LogWrap(_log_wrap_shared.BaseLogWrap): ...

def logwrap(
log: typing.Union[logging.Logger, typing.Callable]=...,
log_level: int=...,
exc_level: int=...,
max_indent: int=...,
spec: typing.Optional[typing.Callable]=...,
blacklisted_names: typing.Optional[typing.List[str]]=...,
blacklisted_exceptions: typing.Optional[typing.List[Exception]]=...,
log_call_args: bool=...,
log_call_args_on_exc: bool=...,
log_result_obj: bool=...
) -> typing.Union[LogWrap, typing.Callable]: ...
16 changes: 6 additions & 10 deletions logwrap/_log_wrap3.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@
__all__ = ('logwrap', 'LogWrap')


DEFAULT_DECORATOR_ARGUMENT = typing.Union[logging.Logger, typing.Callable]


class LogWrap(_log_wrap_shared.BaseLogWrap):
"""Python 3.3+ version of LogWrap."""

Expand All @@ -55,7 +52,7 @@ def _get_function_wrapper(
:return: wrapped coroutine or function
:rtype: typing.Callable
"""
sig = inspect.signature(obj=self._spec or func)
sig = inspect.signature(self._spec or func)

# pylint: disable=missing-docstring
# noinspection PyCompatibility,PyMissingOrEmptyDocstring
Expand Down Expand Up @@ -112,7 +109,7 @@ def wrapper(*args, **kwargs):

# pylint: disable=unexpected-keyword-arg, no-value-for-parameter
def logwrap(
log: DEFAULT_DECORATOR_ARGUMENT = _log_wrap_shared.logger,
log: typing.Union[logging.Logger, typing.Callable] = _log_wrap_shared.logger,
log_level: int = logging.DEBUG,
exc_level: int = logging.ERROR,
max_indent: int = 20,
Expand Down Expand Up @@ -140,9 +137,8 @@ def logwrap(
Note: this object should provide fully compatible signature
with decorated function, or arguments bind will be failed!
:type spec: typing.Optional[typing.Callable]
:param blacklisted_names: list of exception,
which should be re-raised without
producing log record.
:param blacklisted_names: Blacklisted argument names.
Arguments with this names will be skipped in log.
:type blacklisted_names: typing.Optional[typing.Iterable[str]]
:param blacklisted_exceptions: list of exception,
which should be re-raised without
Expand All @@ -158,9 +154,9 @@ def logwrap(
:rtype: _log_wrap_shared.BaseLogWrap
"""
if isinstance(log, logging.Logger):
log, func = log, None
func = None
else:
log, func = _log_wrap_shared.logger, log
log, func = _log_wrap_shared.logger, log # type: logging.Logger, typing.Callable

wrapper = LogWrap(
log=log,
Expand Down
18 changes: 18 additions & 0 deletions logwrap/_log_wrap3.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import logging
import typing
from . import _log_wrap_shared

class LogWrap(_log_wrap_shared.BaseLogWrap): ...

def logwrap(
log: typing.Union[logging.Logger, typing.Callable]=...,
log_level: int=...,
exc_level: int=...,
max_indent: int=...,
spec: typing.Optional[typing.Callable]=...,
blacklisted_names: typing.Optional[typing.List[str]]=...,
blacklisted_exceptions: typing.Optional[typing.List[Exception]]=...,
log_call_args: bool=...,
log_call_args_on_exc: bool=...,
log_result_obj: bool=...
) -> typing.Union[LogWrap, typing.Callable]: ...
Loading