diff --git a/logwrap/__init__.pxd b/logwrap/__init__.pxd index 802ae15..aa09ac5 100644 --- a/logwrap/__init__.pxd +++ b/logwrap/__init__.pxd @@ -1 +1 @@ -cdef tuple __all__ +cpdef tuple __all__ diff --git a/logwrap/__init__.pyx b/logwrap/__init__.pyx index e57c6c6..765d677 100644 --- a/logwrap/__init__.pyx +++ b/logwrap/__init__.pyx @@ -1,10 +1,9 @@ -from .repr_utils cimport PrettyFormat, PrettyRepr, PrettyStr -from .repr_utils import pretty_repr, pretty_str +from .repr_utils cimport PrettyFormat, PrettyRepr, PrettyStr, pretty_repr, pretty_str from .log_wrap cimport LogWrap from .log_wrap import logwrap, BoundParameter, bind_args_kwargs -cdef tuple __all__ = ( +cpdef tuple __all__ = ( "LogWrap", "logwrap", "PrettyFormat", diff --git a/logwrap/class_decorator.pxd b/logwrap/class_decorator.pxd index 318b0d5..76e3f77 100644 --- a/logwrap/class_decorator.pxd +++ b/logwrap/class_decorator.pxd @@ -16,5 +16,6 @@ cdef class BaseDecorator: - cdef readonly object _func - cdef dict __dict__ + cdef: + readonly object _func + dict __dict__ diff --git a/logwrap/log_wrap.pxd b/logwrap/log_wrap.pxd index a9157b7..4fa5029 100644 --- a/logwrap/log_wrap.pxd +++ b/logwrap/log_wrap.pxd @@ -20,39 +20,34 @@ import typing from logwrap cimport class_decorator -cdef unsigned int indent +cdef: + unsigned int indent -cdef str pretty_repr( - src: typing.Any, - unsigned int indent=?, - bint no_indent_start=?, - unsigned int max_indent=?, - unsigned int indent_step=? -) + class LogWrap(class_decorator.BaseDecorator): + """Base class for LogWrap implementation.""" + cdef: + public unsigned int log_level + public unsigned int exc_level + public unsigned int max_indent -cdef class LogWrap(class_decorator.BaseDecorator): - """Base class for LogWrap implementation.""" + public bint log_call_args + public bint log_call_args_on_exc + public bint log_traceback + public bint log_result_obj - cdef public unsigned int log_level - cdef public unsigned int exc_level - cdef public unsigned int max_indent + readonly object _spec + readonly object _logger - cdef public bint log_call_args - cdef public bint log_call_args_on_exc - cdef public bint log_traceback - cdef public bint log_result_obj + list __blacklisted_names + list __blacklisted_exceptions - cdef readonly object _spec - cdef readonly object _logger + cpdef object pre_process_param(self, object arg) + cpdef str post_process_param(self, object arg, str arg_repr) - cdef list __blacklisted_names - cdef list __blacklisted_exceptions - - cdef str _get_func_args_repr( - self, sig: inspect.Signature, tuple args, dict kwargs - ) - cdef void _make_done_record(self, str func_name, result: typing.Any) - cdef void _make_calling_record(self, str name, str arguments, str method=?) - cdef void _make_exc_record(self, str name, str arguments) + cdef: + str _get_func_args_repr(self, sig: inspect.Signature, tuple args, dict kwargs) + void _make_done_record(self, str func_name, result: typing.Any) + void _make_calling_record(self, str name, str arguments, str method=?) + void _make_exc_record(self, str name, str arguments) diff --git a/logwrap/log_wrap.pyx b/logwrap/log_wrap.pyx index e82591f..4016e70 100644 --- a/logwrap/log_wrap.pyx +++ b/logwrap/log_wrap.pyx @@ -26,7 +26,7 @@ from logwrap cimport repr_utils from logwrap cimport class_decorator -cdef tuple __all__ = ("LogWrap", "logwrap", "BoundParameter", "bind_args_kwargs") +cpdef tuple __all__ = ("LogWrap", "logwrap", "BoundParameter", "bind_args_kwargs") logger = logging.getLogger("logwrap") # type: logging.Logger @@ -36,19 +36,6 @@ fmt = "\n{spc:<{indent}}{{key!r}}={{val}},{{annotation}}".format(spc="", indent= comment = "\n{spc:<{indent}}# {{kind!s}}:".format(spc="", indent=indent).format -cdef str pretty_repr( - src: typing.Any, - unsigned int indent=0, - bint no_indent_start=False, - unsigned int max_indent=20, - unsigned int indent_step=4 -): - """Make human readable repr of object.""" - return repr_utils.PrettyRepr(max_indent=max_indent, indent_step=indent_step)( - src=src, indent=indent, no_indent_start=no_indent_start - ) - - class BoundParameter: """Parameter-like object store BOUND with value parameter.""" @@ -286,9 +273,7 @@ cdef class LogWrap(class_decorator.BaseDecorator): "log_result_obj={self.log_result_obj}, )".format(cls=self.__class__.__name__, self=self, spec=self._spec) ) - def pre_process_param( - self, arg: BoundParameter - ) -> typing.Union[BoundParameter, typing.Tuple[BoundParameter, typing.Any], None]: + cpdef object pre_process_param(self, object arg: BoundParameter): """Process parameter for the future logging. :param arg: bound parameter @@ -302,7 +287,7 @@ cdef class LogWrap(class_decorator.BaseDecorator): """ return arg - def post_process_param(self, arg: BoundParameter, str arg_repr: str) -> str: + cpdef str post_process_param(self, object arg: BoundParameter, str arg_repr): """Process parameter for the future logging. :param arg: bound parameter @@ -318,123 +303,125 @@ cdef class LogWrap(class_decorator.BaseDecorator): """ return arg_repr - cdef str _get_func_args_repr(self, sig: inspect.Signature, tuple args, dict kwargs): - """Internal helper for reducing complexity of decorator code. - - :param sig: function signature - :type sig: inspect.Signature - :param args: not keyworded arguments - :type args: typing.Tuple - :param kwargs: keyworded arguments - :type kwargs: typing.Dict[str, typing.Any] - :return: repr over function arguments - :rtype: str - - .. versionchanged:: 3.3.0 Use pre- and post- processing of params during execution - """ - if not (self.log_call_args or self.log_call_args_on_exc): - return "" - - cdef str param_str = "" - cdef str val - cdef str annotation - - last_kind = None - for param in bind_args_kwargs(sig, *args, **kwargs): - if param.name in self.blacklisted_names: - continue - - preprocessed = self.pre_process_param(param) - if preprocessed is None: - continue - - if isinstance(preprocessed, (tuple, list)): - param, value = preprocessed - else: - value = param.value - - if param.empty is value: - if param.VAR_POSITIONAL == param.kind: - value = () - elif param.VAR_KEYWORD == param.kind: - value = {} - - val = pretty_repr(src=value, indent=indent + 4, no_indent_start=True, max_indent=self.max_indent) - - val = self.post_process_param(param, val) - - if last_kind != param.kind: - param_str += comment(kind=param.kind) - last_kind = param.kind - - if param.empty is param.annotation: - annotation = "" - else: - annotation = " # type: {param.annotation!s}".format(param=param) - - param_str += fmt(key=param.name, annotation=annotation, val=val) - if param_str: - param_str += "\n" - return param_str - - cdef void _make_done_record(self, str func_name, result: typing.Any): - """Construct success record. - - :type func_name: str - :type result: typing.Any - """ - cdef str msg = "Done: {name!r}".format(name=func_name) - - if self.log_result_obj: - msg += " with result:\n{result}".format( - result=pretty_repr( - result, - indent=0, - no_indent_start=False, - max_indent=self.max_indent, - indent_step=4 - + cdef: + str _get_func_args_repr(self, sig: inspect.Signature, tuple args, dict kwargs): + """Internal helper for reducing complexity of decorator code. + + :param sig: function signature + :type sig: inspect.Signature + :param args: not keyworded arguments + :type args: typing.Tuple + :param kwargs: keyworded arguments + :type kwargs: typing.Dict[str, typing.Any] + :return: repr over function arguments + :rtype: str + + .. versionchanged:: 3.3.0 Use pre- and post- processing of params during execution + """ + if not (self.log_call_args or self.log_call_args_on_exc): + return "" + + cdef: + str param_str = "" + str val + str annotation + + last_kind = None + for param in bind_args_kwargs(sig, *args, **kwargs): + if param.name in self.blacklisted_names: + continue + + preprocessed = self.pre_process_param(param) + if preprocessed is None: + continue + + if isinstance(preprocessed, (tuple, list)): + param, value = preprocessed + else: + value = param.value + + if param.empty is value: + if param.VAR_POSITIONAL == param.kind: + value = () + elif param.VAR_KEYWORD == param.kind: + value = {} + + val = repr_utils.pretty_repr(src=value, indent=indent + 4, no_indent_start=True, max_indent=self.max_indent) + + val = self.post_process_param(param, val) + + if last_kind != param.kind: + param_str += comment(kind=param.kind) + last_kind = param.kind + + if param.empty is param.annotation: + annotation = "" + else: + annotation = " # type: {param.annotation!s}".format(param=param) + + param_str += fmt(key=param.name, annotation=annotation, val=val) + if param_str: + param_str += "\n" + return param_str + + void _make_done_record(self, str func_name, result: typing.Any): + """Construct success record. + + :type func_name: str + :type result: typing.Any + """ + cdef str msg = "Done: {name!r}".format(name=func_name) + + if self.log_result_obj: + msg += " with result:\n{result}".format( + result=repr_utils.pretty_repr( + result, + indent=0, + no_indent_start=False, + max_indent=self.max_indent, + indent_step=4 + + ) ) + self._logger.log(level=self.log_level, msg=msg) # type: ignore + + void _make_calling_record(self, str name, str arguments, str method="Calling"): + """Make log record before execution. + + :type name: str + :type arguments: str + :type method: str + """ + self._logger.log( # type: ignore + level=self.log_level, + msg="{method}: \n{name!r}({arguments})".format( + method=method, name=name, arguments=arguments if self.log_call_args else "" + ), ) - self._logger.log(level=self.log_level, msg=msg) # type: ignore - - cdef void _make_calling_record(self, str name, str arguments, str method="Calling"): - """Make log record before execution. - :type name: str - :type arguments: str - :type method: str - """ - self._logger.log( # type: ignore - level=self.log_level, - msg="{method}: \n{name!r}({arguments})".format( - method=method, name=name, arguments=arguments if self.log_call_args else "" - ), - ) - - cdef void _make_exc_record(self, str name, str arguments): - """Make log record if exception raised. - - :type name: str - :type arguments: str - """ - exc_info = sys.exc_info() - stack = traceback.extract_stack() - tb = traceback.extract_tb(exc_info[2]) - full_tb = stack[:2] + tb # cut decorator and build full traceback - exc_line = traceback.format_exception_only(*exc_info[:2]) - # Make standard traceback string - cdef str tb_text = "Traceback (most recent call last):\n" + "".join(traceback.format_list(full_tb)) + "".join(exc_line) - - self._logger.log( # type: ignore - level=self.exc_level, - msg="Failed: \n{name!r}({arguments})\n{tb_text}".format( - name=name, - arguments=arguments if self.log_call_args_on_exc else "", - tb_text=tb_text if self.log_traceback else "", - ), - exc_info=False, - ) + void _make_exc_record(self, str name, str arguments): + """Make log record if exception raised. + + :type name: str + :type arguments: str + """ + exc_info = sys.exc_info() + stack = traceback.extract_stack() + tb = traceback.extract_tb(exc_info[2]) + full_tb = stack[:2] + tb # cut decorator and build full traceback + exc_line = traceback.format_exception_only(*exc_info[:2]) + # Make standard traceback string + cdef str tb_text = "Traceback (most recent call last):\n" + "".join(traceback.format_list(full_tb)) + "".join(exc_line) + + self._logger.log( # type: ignore + level=self.exc_level, + msg="Failed: \n{name!r}({arguments})\n{tb_text}".format( + name=name, + arguments=arguments if self.log_call_args_on_exc else "", + tb_text=tb_text if self.log_traceback else "", + ), + exc_info=False, + ) def _get_function_wrapper(self, func: typing.Callable) -> typing.Callable: """Here should be constructed and returned real decorator. diff --git a/logwrap/repr_utils.pxd b/logwrap/repr_utils.pxd index fefd06d..2a9d649 100644 --- a/logwrap/repr_utils.pxd +++ b/logwrap/repr_utils.pxd @@ -22,18 +22,41 @@ import types import typing -cdef class PrettyFormat: - cdef readonly unsigned int max_indent - cdef readonly unsigned int indent_step - cdef readonly str _magic_method_name - - cdef int next_indent(self, unsigned int indent, unsigned int multiplier=?) - cdef str _repr_callable(self, src: typing.Union[types.FunctionType, types.MethodType], unsigned int indent=?) - cdef str _repr_simple(self, src: typing.Any, unsigned int indent=?, bint no_indent_start=?) - cdef str _repr_iterable_item(self, bint nl, str obj_type, str prefix, unsigned int indent, str result, str suffix) - -cdef class PrettyRepr(PrettyFormat): - cdef str _strings_repr(self, unsigned int indent, val: typing.Union[bytes, str]) - -cdef class PrettyStr(PrettyFormat): - cdef str _strings_str(self, unsigned int indent, val: typing.Union[bytes, str]) +cdef: + class PrettyFormat: + cdef: + readonly unsigned int max_indent + readonly unsigned int indent_step + readonly str _magic_method_name + + cpdef int next_indent(self, unsigned int indent, unsigned int multiplier=?) + + cdef: + str _repr_callable(self, src: typing.Union[types.FunctionType, types.MethodType], unsigned int indent=?) + str _repr_simple(self, src: typing.Any, unsigned int indent=?, bint no_indent_start=?) + str _repr_iterable_item(self, bint nl, str obj_type, str prefix, unsigned int indent, str result, str suffix) + + cpdef str process_element(self, src: typing.Any, unsigned int indent=?, bint no_indent_start=?) + + class PrettyRepr(PrettyFormat): + cdef str _strings_repr(self, unsigned int indent, val: typing.Union[bytes, str]) + + class PrettyStr(PrettyFormat): + cdef str _strings_str(self, unsigned int indent, val: typing.Union[bytes, str]) + + +cpdef str pretty_repr( + src: typing.Any, + unsigned int indent=?, + bint no_indent_start=?, + unsigned int max_indent=?, + unsigned int indent_step=? +) + +cpdef str pretty_str( + src: typing.Any, + unsigned int indent=?, + bint no_indent_start=?, + unsigned int max_indent=?, + unsigned int indent_step=? +) diff --git a/logwrap/repr_utils.pyx b/logwrap/repr_utils.pyx index d3ff439..46d9a10 100644 --- a/logwrap/repr_utils.pyx +++ b/logwrap/repr_utils.pyx @@ -23,17 +23,18 @@ import types import typing -cdef tuple __all__ = ("PrettyFormat", "PrettyRepr", "PrettyStr", "pretty_repr", "pretty_str") +cpdef tuple __all__ = ("PrettyFormat", "PrettyRepr", "PrettyStr", "pretty_repr", "pretty_str") -cdef bint _known_callable(item: typing.Any): - """Check for possibility to parse callable.""" - return isinstance(item, (types.FunctionType, types.MethodType)) +cdef: + bint _known_callable(item: typing.Any): + """Check for possibility to parse callable.""" + return isinstance(item, (types.FunctionType, types.MethodType)) -cdef bint _simple(item: typing.Any): - """Check for nested iterations: True, if not.""" - return not isinstance(item, (list, set, tuple, dict, frozenset)) + bint _simple(item: typing.Any): + """Check for nested iterations: True, if not.""" + return not isinstance(item, (list, set, tuple, dict, frozenset)) class ReprParameter: @@ -117,7 +118,7 @@ def _prepare_repr(func: typing.Union[types.FunctionType, types.MethodType]) -> t :type func: typing.Union[types.FunctionType, types.MethodType] :return: repr of callable parameter from signature :rtype: typing.Iterator[ReprParameter]""" - ismethod = isinstance(func, types.MethodType) + cdef bint ismethod = isinstance(func, types.MethodType) if not ismethod: real_func = func else: @@ -155,7 +156,7 @@ cdef class PrettyFormat: self.max_indent = max_indent self.indent_step = indent_step - cdef int next_indent(self, unsigned int indent, unsigned int multiplier=1): + cpdef int next_indent(self, unsigned int indent, unsigned int multiplier=1): """Next indentation value. :param indent: current indentation value @@ -167,31 +168,51 @@ cdef class PrettyFormat: """ return indent + multiplier * self.indent_step - cdef str _repr_callable(self, src: typing.Union[types.FunctionType, types.MethodType], unsigned int indent=0): - """Repr callable object (function or method). - - :param src: Callable to process - :type src: typing.Union[types.FunctionType, types.MethodType] - :param indent: start indentation - :type indent: int - :return: Repr of function or method with signature. - :rtype: str - """ - raise NotImplementedError() - - cdef str _repr_simple(self, src: typing.Any, unsigned int indent=0, bint no_indent_start=False): - """Repr object without iteration. - - :param src: Source object - :type src: typing.Any - :param indent: start indentation - :type indent: int - :param no_indent_start: ignore indent - :type no_indent_start: bool - :return: simple repr() over object - :rtype: str - """ - raise NotImplementedError() + cdef: + str _repr_callable(self, src: typing.Union[types.FunctionType, types.MethodType], unsigned int indent=0): + """Repr callable object (function or method). + + :param src: Callable to process + :type src: typing.Union[types.FunctionType, types.MethodType] + :param indent: start indentation + :type indent: int + :return: Repr of function or method with signature. + :rtype: str + """ + raise NotImplementedError() + + str _repr_simple(self, src: typing.Any, unsigned int indent=0, bint no_indent_start=False): + """Repr object without iteration. + + :param src: Source object + :type src: typing.Any + :param indent: start indentation + :type indent: int + :param no_indent_start: ignore indent + :type no_indent_start: bool + :return: simple repr() over object + :rtype: str + """ + raise NotImplementedError() + + str _repr_iterable_item(self, bint nl, str obj_type, str prefix, unsigned int indent, str result, str suffix): + """Repr iterable item. + + :param nl: newline before item + :type nl: bool + :param obj_type: Object type + :type obj_type: str + :param prefix: prefix + :type prefix: str + :param indent: start indentation + :type indent: int + :param result: result of pre-formatting + :type result: str + :param suffix: suffix + :type suffix: str + :rtype: str + """ + raise NotImplementedError() def _repr_dict_items(self, dict src, unsigned int indent=0) -> typing.Iterator[str]: # type """Repr dict items. @@ -204,25 +225,6 @@ cdef class PrettyFormat: """ raise NotImplementedError() - cdef str _repr_iterable_item(self, bint nl, str obj_type, str prefix, unsigned int indent, str result, str suffix): - """Repr iterable item. - - :param nl: newline before item - :type nl: bool - :param obj_type: Object type - :type obj_type: str - :param prefix: prefix - :type prefix: str - :param indent: start indentation - :type indent: int - :param result: result of pre-formatting - :type result: str - :param suffix: suffix - :type suffix: str - :rtype: str - """ - raise NotImplementedError() - def _repr_iterable_items(self, src: typing.Iterable, unsigned int indent=0) -> typing.Iterator[str]: """Repr iterable items (not designed for dicts). @@ -236,7 +238,7 @@ cdef class PrettyFormat: for elem in src: yield "\n" + self.process_element(src=elem, indent=self.next_indent(indent)) + "," - def process_element(self, src: typing.Any, unsigned int indent=0, bint no_indent_start=False) -> str: + cpdef str process_element(self, src: typing.Any, unsigned int indent=0, bint no_indent_start=False): """Make human readable representation of object. :param src: object to process @@ -248,9 +250,10 @@ cdef class PrettyFormat: :return: formatted string :rtype: str """ - cdef str prefix - cdef str suffix - cdef str result + cdef: + str prefix + str suffix + str result if hasattr(src, self._magic_method_name): result = getattr(src, self._magic_method_name)(self, indent=indent, no_indent_start=no_indent_start) @@ -309,35 +312,105 @@ cdef class PrettyRepr(PrettyFormat): def __cinit__(self, unsigned int max_indent=20, unsigned int indent_step=4): self._magic_method_name = "__pretty_repr__" - cdef str _strings_repr(self, unsigned int indent, val: typing.Union[bytes, str]): - """Custom repr for strings and binary strings.""" - cdef str prefix + cdef: + str _strings_repr(self, unsigned int indent, val: typing.Union[bytes, str]): + """Custom repr for strings and binary strings.""" + cdef str prefix - if isinstance(val, bytes): - val = val.decode(encoding="utf-8", errors="backslashreplace") - prefix = "b" - else: - prefix = "u" - return "{spc:<{indent}}{prefix}'''{string}'''".format(spc="", indent=indent, prefix=prefix, string=val) + if isinstance(val, bytes): + val = val.decode(encoding="utf-8", errors="backslashreplace") + prefix = "b" + else: + prefix = "u" + return "{spc:<{indent}}{prefix}'''{string}'''".format(spc="", indent=indent, prefix=prefix, string=val) + + str _repr_simple(self, src: typing.Any, unsigned int indent=0, bint no_indent_start=False): + """Repr object without iteration. + + :param src: Source object + :type src: typing.Any + :param indent: start indentation + :type indent: int + :param no_indent_start: ignore indent + :type no_indent_start: bool + :return: simple repr() over object, except strings (add prefix) and set (uniform py2/py3) + :rtype: str + """ + indent = 0 if no_indent_start else indent + if isinstance(src, set): + return "{spc:<{indent}}{val}".format(spc="", indent=indent, val="set(" + " ,".join(map(repr, src)) + ")") + if isinstance(src, (bytes, str)): + return self._strings_repr(indent=indent, val=src) + return "{spc:<{indent}}{val!r}".format(spc="", indent=indent, val=src) + + str _repr_callable(self, src: typing.Union[types.FunctionType, types.MethodType], unsigned int indent=0): + """Repr callable object (function or method). + + :param src: Callable to process + :type src: typing.Union[types.FunctionType, types.MethodType] + :param indent: start indentation + :type indent: int + :return: Repr of function or method with signature. + :rtype: str + """ + cdef: + str param_str = "" + str annotation + + for param in _prepare_repr(src): + param_str += "\n{spc:<{indent}}{param.name}".format(spc="", indent=self.next_indent(indent), param=param) + if param.annotation is not param.empty: + param_str += ": {param.annotation}".format(param=param) + if param.value is not param.empty: + param_str += "={val}".format( + val=self.process_element(src=param.value, indent=indent, no_indent_start=True) + ) + param_str += "," + + if param_str: + param_str += "\n" + " " * indent + + sig = inspect.signature(src) + if sig.return_annotation is inspect.Parameter.empty: + annotation = "" + else: + annotation = " -> {sig.return_annotation!r}".format(sig=sig) - cdef str _repr_simple(self, src: typing.Any, unsigned int indent=0, bint no_indent_start=False): - """Repr object without iteration. + return "\n{spc:<{indent}}<{obj!r} with interface ({args}){annotation}>".format( + spc="", indent=indent, obj=src, args=param_str, annotation=annotation + ) - :param src: Source object - :type src: typing.Any - :param indent: start indentation - :type indent: int - :param no_indent_start: ignore indent - :type no_indent_start: bool - :return: simple repr() over object, except strings (add prefix) and set (uniform py2/py3) - :rtype: str - """ - indent = 0 if no_indent_start else indent - if isinstance(src, set): - return "{spc:<{indent}}{val}".format(spc="", indent=indent, val="set(" + " ,".join(map(repr, src)) + ")") - if isinstance(src, (bytes, str)): - return self._strings_repr(indent=indent, val=src) - return "{spc:<{indent}}{val!r}".format(spc="", indent=indent, val=src) + str _repr_iterable_item(self, bint nl, str obj_type, str prefix, unsigned int indent, str result, str suffix): + """Repr iterable item. + + :param nl: newline before item + :type nl: bool + :param obj_type: Object type + :type obj_type: str + :param prefix: prefix + :type prefix: str + :param indent: start indentation + :type indent: int + :param result: result of pre-formatting + :type result: str + :param suffix: suffix + :type suffix: str + :return: formatted repr of "result" with prefix and suffix to explain type. + :rtype: str + """ + return ( + "{nl}" + "{spc:<{indent}}{obj_type:}({prefix}{result}\n" + "{spc:<{indent}}{suffix})".format( + nl="\n" if nl else "", + spc="", + indent=indent, + obj_type=obj_type, + prefix=prefix, + result=result, + suffix=suffix, + ) + ) def _repr_dict_items(self, dict src, unsigned int indent=0) -> typing.Iterator[str]: """Repr dict items. @@ -360,74 +433,6 @@ cdef class PrettyRepr(PrettyFormat): val=self.process_element(val, indent=self.next_indent(indent, multiplier=2), no_indent_start=True), ) - cdef str _repr_callable(self, src: typing.Union[types.FunctionType, types.MethodType], unsigned int indent=0): - """Repr callable object (function or method). - - :param src: Callable to process - :type src: typing.Union[types.FunctionType, types.MethodType] - :param indent: start indentation - :type indent: int - :return: Repr of function or method with signature. - :rtype: str - """ - cdef str param_str = "" - cdef str annotation - - for param in _prepare_repr(src): - param_str += "\n{spc:<{indent}}{param.name}".format(spc="", indent=self.next_indent(indent), param=param) - if param.annotation is not param.empty: - param_str += ": {param.annotation}".format(param=param) - if param.value is not param.empty: - param_str += "={val}".format( - val=self.process_element(src=param.value, indent=indent, no_indent_start=True) - ) - param_str += "," - - if param_str: - param_str += "\n" + " " * indent - - sig = inspect.signature(src) - if sig.return_annotation is inspect.Parameter.empty: - annotation = "" - else: - annotation = " -> {sig.return_annotation!r}".format(sig=sig) - - return "\n{spc:<{indent}}<{obj!r} with interface ({args}){annotation}>".format( - spc="", indent=indent, obj=src, args=param_str, annotation=annotation - ) - - cdef str _repr_iterable_item(self, bint nl, str obj_type, str prefix, unsigned int indent, str result, str suffix): - """Repr iterable item. - - :param nl: newline before item - :type nl: bool - :param obj_type: Object type - :type obj_type: str - :param prefix: prefix - :type prefix: str - :param indent: start indentation - :type indent: int - :param result: result of pre-formatting - :type result: str - :param suffix: suffix - :type suffix: str - :return: formatted repr of "result" with prefix and suffix to explain type. - :rtype: str - """ - return ( - "{nl}" - "{spc:<{indent}}{obj_type:}({prefix}{result}\n" - "{spc:<{indent}}{suffix})".format( - nl="\n" if nl else "", - spc="", - indent=indent, - obj_type=obj_type, - prefix=prefix, - result=result, - suffix=suffix, - ) - ) - cdef class PrettyStr(PrettyFormat): """Pretty str. @@ -438,30 +443,94 @@ cdef class PrettyStr(PrettyFormat): def __cinit__(self, unsigned int max_indent=20, unsigned int indent_step=4): self._magic_method_name = "__pretty_str__" - cdef str _strings_str(self, unsigned int indent, val: typing.Union[bytes, str]): - """Custom repr for strings and binary strings.""" - if isinstance(val, bytes): - val = val.decode(encoding="utf-8", errors="backslashreplace") - return "{spc:<{indent}}{string}".format(spc="", indent=indent, string=val) + cdef: + str _strings_str(self, unsigned int indent, val: typing.Union[bytes, str]): + """Custom repr for strings and binary strings.""" + if isinstance(val, bytes): + val = val.decode(encoding="utf-8", errors="backslashreplace") + return "{spc:<{indent}}{string}".format(spc="", indent=indent, string=val) + + str _repr_simple(self, src: typing.Any, unsigned int indent=0, bint no_indent_start=False): + """Repr object without iteration. + + :param src: Source object + :type src: typing.Any + :param indent: start indentation + :type indent: int + :param no_indent_start: ignore indent + :type no_indent_start: bool + :return: simple repr() over object, except strings (decode) and set (uniform py2/py3) + :rtype: str + """ + indent = 0 if no_indent_start else indent + if isinstance(src, set): + return "{spc:<{indent}}{val}".format(spc="", indent=indent, val="set(" + " ,".join(map(str, src)) + ")") + if isinstance(src, (bytes, str)): + return self._strings_str(indent=indent, val=src) + return "{spc:<{indent}}{val!s}".format(spc="", indent=indent, val=src) + + str _repr_callable(self, src: typing.Union[types.FunctionType, types.MethodType], unsigned int indent=0): + """Repr callable object (function or method). + + :param src: Callable to process + :type src: typing.Union[types.FunctionType, types.MethodType] + :param indent: start indentation + :type indent: int + :return: Repr of function or method with signature. + :rtype: str + """ + cdef: + str param_str = "" + str annotation + + for param in _prepare_repr(src): + param_str += "\n{spc:<{indent}}{param.name}".format(spc="", indent=self.next_indent(indent), param=param) + if param.annotation is not param.empty: + param_str += ": {param.annotation}".format(param=param) + if param.value is not param.empty: + param_str += "={val}".format( + val=self.process_element(src=param.value, indent=indent, no_indent_start=True) + ) + param_str += "," + + if param_str: + param_str += "\n" + " " * indent + + sig = inspect.signature(src) + if sig.return_annotation is inspect.Parameter.empty: + annotation = "" + else: + annotation = " -> {sig.return_annotation!r}".format(sig=sig) - cdef str _repr_simple(self, src: typing.Any, unsigned int indent=0, bint no_indent_start=False): - """Repr object without iteration. + return "\n{spc:<{indent}}<{obj!s} with interface ({args}){annotation}>".format( + spc="", indent=indent, obj=src, args=param_str, annotation=annotation + ) - :param src: Source object - :type src: typing.Any - :param indent: start indentation - :type indent: int - :param no_indent_start: ignore indent - :type no_indent_start: bool - :return: simple repr() over object, except strings (decode) and set (uniform py2/py3) - :rtype: str - """ - indent = 0 if no_indent_start else indent - if isinstance(src, set): - return "{spc:<{indent}}{val}".format(spc="", indent=indent, val="set(" + " ,".join(map(str, src)) + ")") - if isinstance(src, (bytes, str)): - return self._strings_str(indent=indent, val=src) - return "{spc:<{indent}}{val!s}".format(spc="", indent=indent, val=src) + str _repr_iterable_item(self, bint nl, str obj_type, str prefix, unsigned int indent, str result, str suffix): + """Repr iterable item. + + :param nl: newline before item + :type nl: bool + :param obj_type: Object type + :type obj_type: str + :param prefix: prefix + :type prefix: str + :param indent: start indentation + :type indent: int + :param result: result of pre-formatting + :type result: str + :param suffix: suffix + :type suffix: str + :return: formatted repr of "result" with prefix and suffix to explain type. + :rtype: str + """ + return ( + "{nl}" + "{spc:<{indent}}{prefix}{result}\n" + "{spc:<{indent}}{suffix}".format( + nl="\n" if nl else "", spc="", indent=indent, prefix=prefix, result=result, suffix=suffix + ) + ) def _repr_dict_items(self, dict src, unsigned int indent=0) -> typing.Iterator[str]: """Repr dict items. @@ -483,76 +552,14 @@ cdef class PrettyStr(PrettyFormat): val=self.process_element(val, indent=self.next_indent(indent, multiplier=2), no_indent_start=True), ) - cdef str _repr_callable(self, src: typing.Union[types.FunctionType, types.MethodType], unsigned int indent=0): - """Repr callable object (function or method). - - :param src: Callable to process - :type src: typing.Union[types.FunctionType, types.MethodType] - :param indent: start indentation - :type indent: int - :return: Repr of function or method with signature. - :rtype: str - """ - cdef str param_str = "" - cdef str annotation - - for param in _prepare_repr(src): - param_str += "\n{spc:<{indent}}{param.name}".format(spc="", indent=self.next_indent(indent), param=param) - if param.annotation is not param.empty: - param_str += ": {param.annotation}".format(param=param) - if param.value is not param.empty: - param_str += "={val}".format( - val=self.process_element(src=param.value, indent=indent, no_indent_start=True) - ) - param_str += "," - - if param_str: - param_str += "\n" + " " * indent - - sig = inspect.signature(src) - if sig.return_annotation is inspect.Parameter.empty: - annotation = "" - else: - annotation = " -> {sig.return_annotation!r}".format(sig=sig) - - return "\n{spc:<{indent}}<{obj!s} with interface ({args}){annotation}>".format( - spc="", indent=indent, obj=src, args=param_str, annotation=annotation - ) - - cdef str _repr_iterable_item(self, bint nl, str obj_type, str prefix, unsigned int indent, str result, str suffix): - """Repr iterable item. - - :param nl: newline before item - :type nl: bool - :param obj_type: Object type - :type obj_type: str - :param prefix: prefix - :type prefix: str - :param indent: start indentation - :type indent: int - :param result: result of pre-formatting - :type result: str - :param suffix: suffix - :type suffix: str - :return: formatted repr of "result" with prefix and suffix to explain type. - :rtype: str - """ - return ( - "{nl}" - "{spc:<{indent}}{prefix}{result}\n" - "{spc:<{indent}}{suffix}".format( - nl="\n" if nl else "", spc="", indent=indent, prefix=prefix, result=result, suffix=suffix - ) - ) - -def pretty_repr( +cpdef str pretty_repr( src: typing.Any, unsigned int indent=0, bint no_indent_start=False, unsigned int max_indent=20, unsigned int indent_step=4 -) -> str: +): """Make human readable repr of object. :param src: object to process @@ -573,13 +580,13 @@ def pretty_repr( ) -def pretty_str( +cpdef str pretty_str( src: typing.Any, unsigned int indent=0, bint no_indent_start=False, unsigned int max_indent=20, unsigned int indent_step=4 -) -> str: +): """Make human readable str of object. :param src: object to process