diff --git a/.editorconfig b/.editorconfig
index afdae29..c13093d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -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
diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
deleted file mode 100644
index c35acad..0000000
--- a/.idea/codeStyleSettings.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
diff --git a/.idea/dictionaries/penguinolog.xml b/.idea/dictionaries/penguinolog.xml
deleted file mode 100644
index 25c262a..0000000
--- a/.idea/dictionaries/penguinolog.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- asyncio
- awaitable
- classmethod
- coroutine
- genindex
- logwrap
- modindex
- quickstart
- toctree
- typecheck
- ungrouped
-
-
-
\ No newline at end of file
diff --git a/.pylintrc b/.pylintrc
index 8bf1f73..9e6d02f 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -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*(# )??$
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 93f3aea..eaa652d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -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
diff --git a/logwrap/__init__.py b/logwrap/__init__.py
index 3c3b726..893c397 100644
--- a/logwrap/__init__.py
+++ b/logwrap/__init__.py
@@ -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
diff --git a/logwrap/_class_decorator.py b/logwrap/_class_decorator.py
index c0cfc64..6dadbe8 100644
--- a/logwrap/_class_decorator.py
+++ b/logwrap/_class_decorator.py
@@ -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
@@ -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__(
@@ -40,11 +78,11 @@ 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__()
@@ -52,7 +90,7 @@ def __init__(
@property
def _func(
self
- ): # type: (BaseDecorator) -> typing.Optional[typing.Callable]
+ ): # type: () -> typing.Optional[typing.Callable]
"""Get wrapped function.
:rtype: typing.Optional[typing.Callable]
@@ -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)
@@ -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
diff --git a/logwrap/_class_decorator.pyi b/logwrap/_class_decorator.pyi
new file mode 100644
index 0000000..e5163f5
--- /dev/null
+++ b/logwrap/_class_decorator.pyi
@@ -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: ...
diff --git a/logwrap/_log_wrap2.py b/logwrap/_log_wrap2.py
index 995753a..7387251 100644
--- a/logwrap/_log_wrap2.py
+++ b/logwrap/_log_wrap2.py
@@ -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."""
@@ -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
@@ -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
@@ -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,
diff --git a/logwrap/_log_wrap2.pyi b/logwrap/_log_wrap2.pyi
new file mode 100644
index 0000000..9ac631f
--- /dev/null
+++ b/logwrap/_log_wrap2.pyi
@@ -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]: ...
diff --git a/logwrap/_log_wrap3.py b/logwrap/_log_wrap3.py
index 7833e75..17eebd7 100644
--- a/logwrap/_log_wrap3.py
+++ b/logwrap/_log_wrap3.py
@@ -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."""
@@ -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
@@ -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,
@@ -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
@@ -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,
diff --git a/logwrap/_log_wrap3.pyi b/logwrap/_log_wrap3.pyi
new file mode 100644
index 0000000..9ac631f
--- /dev/null
+++ b/logwrap/_log_wrap3.pyi
@@ -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]: ...
diff --git a/logwrap/_log_wrap_shared.py b/logwrap/_log_wrap_shared.py
index e3a95d3..6e834ec 100644
--- a/logwrap/_log_wrap_shared.py
+++ b/logwrap/_log_wrap_shared.py
@@ -22,14 +22,14 @@
import functools
import inspect # noqa # pylint: disable=unused-import
import logging
-import typing
+import typing # noqa # pylint: disable=unused-import
import logwrap as core
from . import _class_decorator
__all__ = ('BaseLogWrap', )
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) # type: logging.Logger
indent = 4
@@ -40,18 +40,12 @@
comment = "\n{spc:<{indent}}# {{kind!s}}:".format(spc='', indent=indent).format
-# Long arguments types:
-_LoggerArg = typing.Union[logging.Logger, typing.Callable]
-_BlacklistedNamesArg = typing.Optional[typing.Iterable[str]]
-_BlacklistedExceptionsArg = typing.Optional[typing.Iterable[Exception]]
-
-
-def _check_type(expected):
+def _check_type(expected): # type: (type) -> typing.Callable
"""Check type before assign.
:type expected: type
"""
- def deco(func):
+ def deco(func): # type: (typing.Callable) -> typing.Callable
"""Check type before assign."""
# pylint: disable=missing-docstring
# noinspection PyMissingOrEmptyDocstring
@@ -92,13 +86,13 @@ class BaseLogWrap(_class_decorator.BaseDecorator):
def __init__(
self,
- log=logger, # type: _LoggerArg
+ log=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: _BlacklistedNamesArg
- blacklisted_exceptions=None, # type: _BlacklistedExceptionsArg
+ blacklisted_names=None, # type: typing.Optional[typing.Iterable[str]]
+ blacklisted_exceptions=None, # type: typing.Optional[typing.Iterable[Exception]]
log_call_args=True, # type: bool
log_call_args_on_exc=True, # type: bool
log_result_obj=True, # type: bool
@@ -141,18 +135,18 @@ def __init__(
"""
# Typing fix:
if blacklisted_names is None:
- self.__blacklisted_names = []
+ self.__blacklisted_names = [] # type: typing.List[str]
else:
self.__blacklisted_names = list(blacklisted_names)
if blacklisted_exceptions is None:
- self.__blacklisted_exceptions = []
+ self.__blacklisted_exceptions = [] # type: typing.List[Exception]
else:
self.__blacklisted_exceptions = list(blacklisted_exceptions)
if not isinstance(log, logging.Logger):
- func, self.__logger = log, logger
+ func, self.__logger = log, logger # type: typing.Callable, logging.Logger
else:
- func, self.__logger = None, log
+ func, self.__logger = None, log # type: None, logging.Logger
super(BaseLogWrap, self).__init__(func=func)
self.__log_level = log_level
diff --git a/logwrap/_log_wrap_shared.pyi b/logwrap/_log_wrap_shared.pyi
new file mode 100644
index 0000000..e9caca2
--- /dev/null
+++ b/logwrap/_log_wrap_shared.pyi
@@ -0,0 +1,62 @@
+import logging
+import typing
+from . import _class_decorator
+
+logger: logging.Logger
+
+class BaseLogWrap(_class_decorator.BaseDecorator):
+ def __init__(
+ self,
+ 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.Iterable[str]]=...,
+ blacklisted_exceptions: typing.Optional[typing.Iterable[Exception]]=...,
+ log_call_args: bool=...,
+ log_call_args_on_exc: bool=...,
+ log_result_obj: bool=...
+ ) -> None: ...
+
+ @property
+ def log_level(self) -> int: ...
+
+ @log_level.setter
+ def log_level(self, val: int) -> None: ...
+
+ @property
+ def exc_level(self) -> int: ...
+
+ @exc_level.setter
+ def exc_level(self, val: int) -> None: ...
+
+ @property
+ def max_indent(self) -> int: ...
+
+ @max_indent.setter
+ def max_indent(self, val: int) -> None: ...
+
+ @property
+ def blacklisted_names(self) -> typing.List[str]: ...
+
+ @property
+ def blacklisted_exceptions(self) -> typing.List[Exception]: ...
+
+ @property
+ def log_call_args(self) -> bool: ...
+
+ @log_call_args.setter
+ def log_call_args(self, val: bool) -> None: ...
+
+ @property
+ def log_call_args_on_exc(self) -> bool: ...
+
+ @log_call_args_on_exc.setter
+ def log_call_args_on_exc(self, val: bool) -> None: ...
+
+ @property
+ def log_result_obj(self) -> bool: ...
+
+ @log_result_obj.setter
+ def log_result_obj(self, val: bool) -> None: ...
diff --git a/logwrap/_repr_utils.py b/logwrap/_repr_utils.py
index 99ee6e8..17c8207 100644
--- a/logwrap/_repr_utils.py
+++ b/logwrap/_repr_utils.py
@@ -43,25 +43,27 @@
# pylint: enable=ungrouped-imports, no-name-in-module
-def _known_callable(item):
+def _known_callable(item): # type: (typing.Any) -> bool
"""Check for possibility to parse callable."""
return isinstance(item, (types.FunctionType, types.MethodType))
-def _simple(item):
+def _simple(item): # type: (typing.Any) -> bool
"""Check for nested iterations: True, if not."""
return not isinstance(item, (list, set, tuple, dict, frozenset))
# pylint: disable=no-member
-def _prepare_repr(func):
+def _prepare_repr(
+ func # type: typing.Union[types.FunctionType, types.MethodType]
+): # type: (...) -> typing.Iterator[typing.Union[str, typing.Tuple[str, typing.Any]]]
"""Get arguments lists with defaults.
:type func: typing.Union[types.FunctionType, types.MethodType]
- :rtype: typing.Generator[str]
+ :rtype: typing.Iterator[typing.Union[str, typing.Tuple[str, typing.Any]]]
"""
isfunction = isinstance(func, types.FunctionType)
- real_func = func if isfunction else func.__func__
+ real_func = func if isfunction else func.__func__ # type: typing.Callable
parameters = list(signature(real_func).parameters.values())
@@ -141,7 +143,11 @@ def next_indent(self, indent, multiplier=1): # type: (int, int) -> int
"""
return indent + multiplier * self.indent_step
- def _repr_callable(self, src, indent=0):
+ def _repr_callable(
+ self,
+ src, # type: typing.Union[types.FunctionType, types.MethodType]
+ indent=0 # type: int
+ ): # type: (...) -> six.text_type
"""Repr callable object (function or method).
:type src: typing.Union[types.FunctionType, types.MethodType]
@@ -150,7 +156,12 @@ def _repr_callable(self, src, indent=0):
"""
raise NotImplementedError # pragma: no cover
- def _repr_simple(self, src, indent=0, no_indent_start=False):
+ def _repr_simple(
+ self,
+ src, # type: typing.Any
+ indent=0, # type: int
+ no_indent_start=False # type: bool
+ ): # type: (...) -> six.text_type
"""Repr object without iteration.
:type src: typing.Union[
@@ -163,26 +174,30 @@ def _repr_simple(self, src, indent=0, no_indent_start=False):
"""
raise NotImplementedError() # pragma: no cover
- def _repr_dict_items(self, src, indent=0):
+ def _repr_dict_items(
+ self,
+ src, # type: typing.Dict
+ indent=0 # type: int
+ ): # type: (...) -> typing.Iterator[str]
"""Repr dict items.
:param src: object to process
- :type src: dict
+ :type src: typing.Dict
:param indent: start indentation
:type indent: int
- :rtype: typing.Generator[str]
+ :rtype: typing.Iterator[str]
"""
raise NotImplementedError() # pragma: no cover
@staticmethod
def _repr_iterable_item(
- nl,
- obj_type,
- prefix,
- indent,
- result,
- suffix,
- ):
+ nl, # type: bool
+ obj_type, # type: str
+ prefix, # type: str
+ indent, # type: int
+ result, # type: str
+ suffix, # type: str
+ ): # type: (...) -> six.text_type
"""Repr iterable item.
:param nl: newline before item
@@ -201,14 +216,18 @@ def _repr_iterable_item(
"""
raise NotImplementedError() # pragma: no cover
- def _repr_iterable_items(self, src, indent=0):
+ def _repr_iterable_items(
+ self,
+ src, # type: typing.Iterable
+ indent=0 # type: int
+ ): # type: (...) -> typing.Iterator[str]
"""Repr iterable items (not designed for dicts).
:param src: object to process
:type src: typing.Iterable
:param indent: start indentation
:type indent: int
- :rtype: typing.Generator[str]
+ :rtype: typing.Iterator[str]
"""
for elem in src:
yield '\n' + self.process_element(
@@ -217,7 +236,7 @@ def _repr_iterable_items(self, src, indent=0):
) + ','
@property
- def _magic_method_name(self):
+ def _magic_method_name(self): # type: () -> six.text_type
"""Magic method name.
:rtype: str
@@ -331,7 +350,7 @@ class PrettyRepr(PrettyFormat):
__slots__ = ()
@property
- def _magic_method_name(self):
+ def _magic_method_name(self): # type: () -> six.text_type
"""Magic method name.
:rtype: str
@@ -339,7 +358,10 @@ def _magic_method_name(self):
return '__pretty_repr__'
@staticmethod
- def _strings_repr(indent, val):
+ def _strings_repr(
+ indent, # type: int
+ val # type: typing.Union[six.binary_type, six.text_type]
+ ): # type: (...) -> six.text_type
"""Custom repr for strings and binary strings."""
if isinstance(val, six.binary_type):
val = val.decode(
@@ -356,7 +378,12 @@ def _strings_repr(indent, val):
string=val
)
- def _repr_simple(self, src, indent=0, no_indent_start=False):
+ def _repr_simple(
+ self,
+ src, # type: typing.Any
+ indent=0, # type: int
+ no_indent_start=False # type: bool
+ ): # type: (...) -> six.text_type
"""Repr object without iteration.
:type src: typing.Union[
@@ -382,14 +409,18 @@ def _repr_simple(self, src, indent=0, no_indent_start=False):
val=src,
)
- def _repr_dict_items(self, src, indent=0):
+ def _repr_dict_items(
+ self,
+ src, # type: typing.Dict
+ indent=0 # type: int
+ ): # type: (...) -> typing.Iterator[str]
"""Repr dict items.
:param src: object to process
:type src: dict
:param indent: start indentation
:type indent: int
- :rtype: typing.Generator[str]
+ :rtype: typing.Iterator[str]
"""
max_len = max((len(repr(key)) for key in src)) if src else 0
for key, val in src.items():
@@ -405,7 +436,11 @@ def _repr_dict_items(self, src, indent=0):
)
)
- def _repr_callable(self, src, indent=0):
+ def _repr_callable(
+ self,
+ src, # type: typing.Union[types.FunctionType, types.MethodType]
+ indent=0 # type: int
+ ): # type: (...) -> six.text_type
"""Repr callable object (function or method).
:type src: typing.Union[types.FunctionType, types.MethodType]
@@ -444,13 +479,13 @@ def _repr_callable(self, src, indent=0):
@staticmethod
def _repr_iterable_item(
- nl,
- obj_type,
- prefix,
- indent,
- result,
- suffix,
- ):
+ nl, # type: bool
+ obj_type, # type: str
+ prefix, # type: str
+ indent, # type: int
+ result, # type: str
+ suffix, # type: str
+ ): # type: (...) -> six.text_type
"""Repr iterable item.
:param nl: newline before item
@@ -491,7 +526,7 @@ class PrettyStr(PrettyFormat):
__slots__ = ()
@property
- def _magic_method_name(self):
+ def _magic_method_name(self): # type: () -> six.text_type
"""Magic method name.
:rtype: str
@@ -499,7 +534,10 @@ def _magic_method_name(self):
return '__pretty_str__'
@staticmethod
- def _strings_str(indent, val):
+ def _strings_str(
+ indent, # type: int
+ val # type: typing.Union[six.binary_type, six.text_type]
+ ): # type: (...) -> six.text_type
"""Custom repr for strings and binary strings."""
if isinstance(val, six.binary_type):
val = val.decode(
@@ -512,7 +550,12 @@ def _strings_str(indent, val):
string=val
)
- def _repr_simple(self, src, indent=0, no_indent_start=False):
+ def _repr_simple(
+ self,
+ src, # type: typing.Any
+ indent=0, # type: int
+ no_indent_start=False # type: bool
+ ): # type: (...) -> six.text_type
"""Repr object without iteration.
:type src: typing.Union[
@@ -538,7 +581,11 @@ def _repr_simple(self, src, indent=0, no_indent_start=False):
val=src,
)
- def _repr_dict_items(self, src, indent=0):
+ def _repr_dict_items(
+ self,
+ src, # type: typing.Dict
+ indent=0 # type: int
+ ): # type: (...) -> typing.Iterator[str]
"""Repr dict items.
:param src: object to process
@@ -561,7 +608,11 @@ def _repr_dict_items(self, src, indent=0):
)
)
- def _repr_callable(self, src, indent=0):
+ def _repr_callable(
+ self,
+ src, # type: typing.Union[types.FunctionType, types.MethodType]
+ indent=0 # type: int
+ ): # type: (...) -> six.text_type
"""Repr callable object (function or method).
:type src: typing.Union[types.FunctionType, types.MethodType]
@@ -600,13 +651,13 @@ def _repr_callable(self, src, indent=0):
@staticmethod
def _repr_iterable_item(
- nl,
- obj_type,
- prefix,
- indent,
- result,
- suffix,
- ):
+ nl, # type: bool
+ obj_type, # type: str
+ prefix, # type: str
+ indent, # type: int
+ result, # type: str
+ suffix, # type: str
+ ): # type: (...) -> six.text_type
"""Repr iterable item.
:param nl: newline before item
diff --git a/logwrap/_repr_utils.pyi b/logwrap/_repr_utils.pyi
new file mode 100644
index 0000000..5c5fc79
--- /dev/null
+++ b/logwrap/_repr_utils.pyi
@@ -0,0 +1,44 @@
+import typing
+
+import six
+
+class PrettyFormat:
+ def __init__(
+ self,
+ max_indent: int=...,
+ indent_step: int=...,
+ py2_str: bool=...
+ ) -> None: ...
+
+ @property
+ def max_indent(self) -> int: ...
+
+ @property
+ def indent_step(self) -> int: ...
+
+ def next_indent(self, indent: int, multiplier: int=...) -> int: ...
+
+ def process_element(self, src: typing.Any, indent: int=..., no_indent_start: bool=...) -> six.text_type: ...
+
+ def __call__(self, src: typing.Any, indent: int=..., no_indent_start: bool=...) -> typing.Union[six.text_type, str]: ...
+
+class PrettyRepr(PrettyFormat): ...
+class PrettyStr(PrettyFormat): ...
+
+def pretty_repr(
+ src: typing.Any,
+ indent: int=...,
+ no_indent_start: bool=...,
+ max_indent: int=...,
+ indent_step: int=...,
+ py2_str: bool=...
+) -> typing.Union[six.text_type, str]: ...
+
+def pretty_str(
+ src: typing.Any,
+ indent: int=...,
+ no_indent_start: bool=...,
+ max_indent: int=...,
+ indent_step: int=...,
+ py2_str: bool=...
+) -> typing.Union[six.text_type, str]: ...
diff --git a/setup.py b/setup.py
index 80ac0d8..9f1d299 100644
--- a/setup.py
+++ b/setup.py
@@ -22,6 +22,7 @@
import collections
from distutils.command import build_ext
import distutils.errors
+import glob
import os.path
import shutil
import sys
@@ -260,6 +261,12 @@ def get_simple_vars_from_src(src):
],
},
install_requires=required,
+ package_data={
+ 'logwrap': [
+ os.path.basename(filename)
+ for filename in glob.glob(os.path.join('logwrap', '*.pyi'))
+ ],
+ },
)
if PY3 and cythonize is not None:
setup_args['ext_modules'] = ext_modules
diff --git a/tox.ini b/tox.ini
index 3ab8f28..86f807b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -101,6 +101,7 @@ ignore =
show-pep8 = True
show-source = True
count = True
+max-line-length = 120
[testenv:docs]
deps =