Release 1.3.0

This commit is contained in:
Wojciech Kozlowski 2017-03-16 23:17:34 +00:00
parent 6395ddd821
commit 93755c6cc3
6 changed files with 205 additions and 72 deletions

View File

@ -1,6 +1,14 @@
Changelog
=========
1.3.0
-----
- Added option to trace return value type.
- Added options for tracing exception tracebacks.
- Made all exception options possible to set individually for functions.
- Added support for multi-line trace messages.
1.2.2
-----

View File

@ -54,10 +54,27 @@ To automatically log function entry and exit use the
Despite the name, this works for both functions and methods.
``@TraceFunction`` can take up to two optional arguments:
``@TraceFunction`` can take up to seven optional arguments:
- ``trace_args`` - if ``True``, input parameters will be logged.
- ``trace_rv`` - if ``True``, the return value will be logged.
- ``exception_warning`` - if ``True``, PyLg will print a warning about
every exception caught to ``stderr``.
- ``exception_tb_file`` - if ``True``, PyLg will write the exception
tracebacks to the log file.
- ``exception_tb_stderr`` - if ``True``, PyLg will print the exception
tracebacks to ``stderr``.
- ``exception_exit`` - if ``True``, PyLg will force the program to
exit (and not just raise SystemExit) whenever an exception
occurs. This will happen even if the exception would be handled at a
later point.
- ``trace_args`` - if ``True``, PyLg will log input parameters.
- ``trace_rv`` - if ``True``, PyLg will log return values.
- ``trace_rv_type`` - if ``True``, PyLg will log return value types.
The default values for these arguments are set in a global settings
file.
@ -70,11 +87,7 @@ These arguments have to specified explicitly by name. Some examples:
def some_fuction():
pass
@TraceFunction(trace_rv = False)
def some_fuction():
pass
@TraceFunction(trace_args = False, trace_rv = False)
@TraceFunction(trace_args = False, exception_tb_stderr = True)
def some_fuction():
pass
@ -99,13 +112,17 @@ template.
- ``PYLG_FILE`` (default = ``'pylg.log'``) - the log file name.
- ``EXCEPTION_WARNING`` (default = ``True``) - if ``True``, PyLg will
print a warning about every exception caught to stderr.
- ``DEFAULT_EXCEPTION_WARNING`` (default = ``True``) - the default
setting for ``exception_warning``.
- ``EXCEPTION_EXIT`` (default = ``False``) - if ``True``, PyLg will
force the program to exit (and not just raise ``SystemExit``)
whenever an exception occurs. This will happen even if the exception
would be handled at a later point.
- ``DEFAULT_EXCEPTION_TB_FILE`` (default = ``True``) - the default
setting for ``exception_tb_file``.
- ``DEFAULT_EXCEPTION_TB_STDERR`` (default = ``False``) - the default
setting for ``exception_tb_stderr``.
- ``DEAULT_EXCEPTION_EXIT`` (default = ``False``) - the default
setting for ``exception_exit``.
- ``TRACE_TIME`` (default = ``TRUE``) - enable/disable time logging.
@ -167,12 +184,13 @@ template.
elements in the dictionary.
- ``DEFAULT_TRACE_ARGS`` (default = ``True``) - the default setting
for ``trace_args`` which determines whether TraceFunction should
trace function parameters on entry.
for ``trace_args``.
- ``DEFAULT_TRACE_RV`` (default = ``True``) - the default setting for
``trace_rv`` which determines whether TraceFunction should trace
function return values on exit.
``trace_rv``.
- ``DEFAULT_TRACE_RV_TYPE`` (default = ``True``) - the default setting
for ``trace_rv_type``.
Under development
-----------------

View File

@ -172,20 +172,37 @@ if PYLG_USER_FILE is not None:
from .settings import PYLG_FILE
# -------------------------------------------------------------------------
# EXCEPTION_WARNING - bool
# DEFAULT_EXCEPTION_WARNING - bool
# -------------------------------------------------------------------------
try:
pylg_check_bool(EXCEPTION_WARNING, "EXCEPTION_WARNING")
pylg_check_bool(DEFAULT_EXCEPTION_WARNING, "DEFAULT_EXCEPTION_WARNING")
except ImportError:
from .settings import EXCEPTION_WARNING
from .settings import DEFAULT_EXCEPTION_WARNING
# -------------------------------------------------------------------------
# EXCEPTION_EXIT - bool
# DEFAULT_EXCEPTION_TB_FILE - bool
# -------------------------------------------------------------------------
try:
pylg_check_bool(EXCEPTION_EXIT, "EXCEPTION_EXIT")
pylg_check_bool(DEFAULT_EXCEPTION_TB_FILE, "DEFAULT_EXCEPTION_TB_FILE")
except ImportError:
from .settings import EXCEPTION_EXIT
from .settings import DEFAULT_EXCEPTION_TB_FILE
# -------------------------------------------------------------------------
# DEFAULT_EXCEPTION_TB_STDERR - bool
# -------------------------------------------------------------------------
try:
pylg_check_bool(DEFAULT_EXCEPTION_TB_STDERR,
"DEFAULT_EXCEPTION_TB_STDERR")
except ImportError:
from .settings import DEFAULT_EXCEPTION_TB_STDERR
# -------------------------------------------------------------------------
# DEFAULT_EXCEPTION_EXIT - bool
# -------------------------------------------------------------------------
try:
pylg_check_bool(DEFAULT_EXCEPTION_EXIT, "DEFAULT_EXCEPTION_EXIT")
except ImportError:
from .settings import DEFAULT_EXCEPTION_EXIT
# -------------------------------------------------------------------------
# TRACE_TIME - bool
@ -330,3 +347,9 @@ if PYLG_USER_FILE is not None:
pylg_check_bool(DEFAULT_TRACE_RV, "DEFAULT_TRACE_RV")
except ImportError:
from .settings import DEFAULT_TRACE_RV
# -----------------------------------------------------------------------------
# Some final value processing.
# -----------------------------------------------------------------------------
if MESSAGE_WIDTH == 0:
MESSAGE_WIDTH = float("inf")

View File

@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------
from __future__ import print_function
from datetime import datetime
from functools import partial
import traceback
@ -153,8 +154,14 @@ class TraceFunction(object):
if args:
self.exception_warning = DEFAULT_EXCEPTION_WARNING
self.exception_tb_file = DEFAULT_EXCEPTION_TB_FILE
self.exception_tb_stderr = DEFAULT_EXCEPTION_TB_STDERR
self.exception_exit = DEFAULT_EXCEPTION_EXIT
self.trace_args = DEFAULT_TRACE_ARGS
self.trace_rv = DEFAULT_TRACE_RV
self.trace_rv_type = DEFAULT_TRACE_RV_TYPE
# -----------------------------------------------------------------
# The function init_function will verify the input.
@ -163,31 +170,63 @@ class TraceFunction(object):
if kwargs:
exception_warning_str = 'exception_warning'
exception_tb_file_str = 'exception_tb_file'
exception_tb_stderr_str = 'exception_tb_stderr'
exception_exit_str = 'exception_exit'
trace_args_str = 'trace_args'
trace_rv_str = 'trace_rv'
trace_rv_type_str = 'trace_rv_type'
# -----------------------------------------------------------------
# If kwargs is non-empty, it should only contain trace_rv,
# trace_args, or both and args should be empty. Assert all
# this.
# If kwargs is non-empty, args should be empty.
# -----------------------------------------------------------------
assert not args
assert (len(kwargs) > 0) and (len(kwargs) <= 2)
if len(kwargs) == 1:
assert (trace_rv_str in kwargs) or (trace_args_str in kwargs)
elif len(kwargs) == 2:
assert (trace_rv_str in kwargs) and (trace_args_str in kwargs)
if trace_args_str in kwargs:
try:
self.exception_warning = kwargs[exception_warning_str]
pylg_check_bool(self.exception_warning, "exception_warning")
except (KeyError, ImportError):
self.exception_warning = DEFAULT_EXCEPTION_WARNING
try:
self.exception_tb_file = kwargs[exception_tb_file_str]
pylg_check_bool(self.exception_tb_file, "exception_tb_file")
except (KeyError, ImportError):
self.exception_tb_file = DEFAULT_EXCEPTION_TB_FILE
try:
self.exception_tb_stderr = kwargs[exception_tb_stderr_str]
pylg_check_bool(self.exception_tb_stderr,
"exception_tb_stderr")
except (KeyError, ImportError):
self.exception_tb_stderr = DEFAULT_EXCEPTION_TB_STDERR
try:
self.exception_exit = kwargs[exception_exit_str]
pylg_check_bool(self.exception_exit, "exception_exit")
except (KeyError, ImportError):
self.exception_exit = DEFAULT_EXCEPTION_EXIT
try:
self.trace_args = kwargs[trace_args_str]
else:
pylg_check_bool(self.trace_args, "trace_args")
except (KeyError, ImportError):
self.trace_args = DEFAULT_TRACE_ARGS
if trace_rv_str in kwargs:
try:
self.trace_rv = kwargs[trace_rv_str]
else:
pylg_check_bool(self.trace_rv, "trace_rv")
except (KeyError, ImportError):
self.trace_rv = DEFAULT_TRACE_RV
try:
self.trace_rv_type = kwargs[trace_rv_type_str]
pylg_check_bool(self.trace_rv_type, "trace_rv_type")
except (KeyError, ImportError):
self.trace_rv_type = DEFAULT_TRACE_RV_TYPE
self.function = None
def __call__(self, *args, **kwargs):
@ -249,8 +288,8 @@ class TraceFunction(object):
except Exception as e:
self.trace_exception(e)
if EXCEPTION_EXIT:
traceback.print_exc(file=sys.stderr)
if self.exception_exit:
warnings.warn("Exit forced by EXCEPTION_EXIT")
os._exit(1)
raise
@ -360,6 +399,9 @@ class TraceFunction(object):
else:
msg += "---"
if self.trace_rv_type:
msg += " (type: " + type(rv).__name__ + ")"
trace(msg, function=self.function)
return
@ -379,9 +421,19 @@ class TraceFunction(object):
if str(exception) is not "":
msg += " - " + str(exception)
if EXCEPTION_WARNING:
if self.exception_warning:
warnings.warn(core_msg, RuntimeWarning)
if self.exception_tb_file:
msg += "\n--- EXCEPTION ---\n"
msg += traceback.format_exc()
msg += "-----------------"
if self.exception_tb_stderr:
print("--- EXCEPTION ---", file=sys.stderr)
traceback.print_exc(file=sys.stderr)
print("-----------------", file=sys.stderr)
trace(msg, function=self.function)
return
@ -466,26 +518,41 @@ def trace(message, function=None):
message = str(message)
if MESSAGE_WIDTH > 0:
# -----------------------------------------------------------------
# ---------------------------------------------------------------------
# Get the length of the trace line so far
# -----------------------------------------------------------------
# ---------------------------------------------------------------------
premsglen = len(msg)
# ---------------------------------------------------------------------
# Split into lines which will be handled separately.
# ---------------------------------------------------------------------
lines = message.splitlines()
if not lines:
lines = [""]
for idx, line in enumerate(lines):
# -----------------------------------------------------------------
# Wrap the text.
# -----------------------------------------------------------------
wrapped = textwrap.wrap(message, MESSAGE_WIDTH)
wrapped = textwrap.wrap(line, MESSAGE_WIDTH)
if not wrapped:
wrapped = [""]
# -----------------------------------------------------------------
# If this is the first line of the whole trace message, it
# gets special treatment as it doesn't need whitespace in
# front of it. Otherwise, align it with the previous line.
# -----------------------------------------------------------------
if idx != 0:
msg += '{:{w}}'.format('', w=premsglen)
if MESSAGE_WRAP:
# -------------------------------------------------------------
# Print the first line. It gets special treatment as
# it doesn't need whitespace in front of it.
# The first wrapped line gets special treatment as any
# whitespace should already be prepended.
# -------------------------------------------------------------
msg += wrapped[0]
@ -493,8 +560,8 @@ def trace(message, function=None):
# Print the remaining lines. Append whitespace to
# align it with the first line.
# -------------------------------------------------------------
for line in wrapped[1:]:
msg += '\n' + '{:{w}}'.format('', w=premsglen) + line
for wrline in wrapped[1:]:
msg += '\n' + '{:{w}}'.format('', w=premsglen) + wrline
else:
# -------------------------------------------------------------
@ -509,8 +576,7 @@ def trace(message, function=None):
# ---------------------------------------------------------
if MESSAGE_WIDTH > 1:
wrapped = textwrap.wrap(wrapped[0],
MESSAGE_WIDTH - 1)
wrapped = textwrap.wrap(wrapped[0], MESSAGE_WIDTH - 1)
assert wrapped
msg += ('{m:{w}}'.format(m=wrapped[0],
@ -528,16 +594,9 @@ def trace(message, function=None):
# ---------------------------------------------------------
msg += wrapped[0]
else:
# -----------------------------------------------------------------
# A MESSAGE_WIDTH of 0 denotes no limit.
# Terminate with a newline.
# -----------------------------------------------------------------
assert MESSAGE_WIDTH == 0
msg += message
# -------------------------------------------------------------------------
# Terminate the log line with a newline.
# -------------------------------------------------------------------------
msg += "\n"
# -------------------------------------------------------------------------

View File

@ -27,17 +27,30 @@ PYLG_ENABLE = True
PYLG_FILE = 'pylg.log'
# -----------------------------------------------------------------------------
# If True, PyLg will print a warning about every exception caught to
# stderr.
# The default value for 'exception_warning'. If True, PyLg will print
# a warning about every exception caught to stderr.
# -----------------------------------------------------------------------------
EXCEPTION_WARNING = True
DEFAULT_EXCEPTION_WARNING = True
# -----------------------------------------------------------------------------
# If True, PyLg will force the program to exit (and not just raise
# SystemExit) whenever an exception occurs. This will happen even if
# the exception would be handled at a later point.
# The default value for 'exception_tb_file'. If True, PyLg will write
# the exception tracebacks to the log file.
# -----------------------------------------------------------------------------
EXCEPTION_EXIT = False
DEFAULT_EXCEPTION_TB_FILE = True
# -----------------------------------------------------------------------------
# The default value for 'exception_tb_file'. If True, PyLg will print
# the exception tracebacks to stderr.
# -----------------------------------------------------------------------------
DEFAULT_EXCEPTION_TB_STDERR = False
# -----------------------------------------------------------------------------
# The default value for 'exception_exit'. If True, PyLg will force the
# program to exit (and not just raise SystemExit) whenever an
# exception occurs. This will happen even if the exception would be
# handled at a later point.
# -----------------------------------------------------------------------------
DEFAULT_EXCEPTION_EXIT = False
# -----------------------------------------------------------------------------
# Enable/disable time logging.
@ -116,7 +129,9 @@ MESSAGE_WRAP = True
# -----------------------------------------------------------------------------
# If true, truncated message lines should have the last character
# replaced with '\'.
# replaced with '\'. Note that this reduces MESSAGE_WIDTH by 1 for
# truncated lines which may truncate words that would've otherwise
# appeared in the message.
# -----------------------------------------------------------------------------
MESSAGE_MARK_TRUNCATION = True
@ -134,9 +149,19 @@ COLLAPSE_LISTS = False
COLLAPSE_DICTS = False
# -----------------------------------------------------------------------------
# The default settings for 'trace_args' and 'trace_rv' which determine
# whether TraceFunction should trace function parameters on entry and
# return values on exit.
# The default setting for 'trace_args'. If True, PyLg will log input
# parameters.
# -----------------------------------------------------------------------------
DEFAULT_TRACE_ARGS = True
# -----------------------------------------------------------------------------
# The default setting for 'trace_rv'. If True, PyLg will log return
# values.
# -----------------------------------------------------------------------------
DEFAULT_TRACE_RV = True
# -----------------------------------------------------------------------------
# The default settings for 'trace_rv_type'. If True, PyLg will log
# return value types.
# -----------------------------------------------------------------------------
DEFAULT_TRACE_RV_TYPE = False

View File

@ -9,7 +9,7 @@ with open(path.join(pwd, 'README.rst'), encoding='utf-8') as f:
setup(
name='PyLg',
version='1.2.2',
version='1.3.0',
description='Python module to facilitate and automate the process of writing runtime logs.',
long_description=long_description,
url='https://gitlab.wojciechkozlowski.eu/wojtek/PyLg',