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 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 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. 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. - ``exception_warning`` - if ``True``, PyLg will print a warning about
- ``trace_rv`` - if ``True``, the return value will be logged. 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 The default values for these arguments are set in a global settings
file. file.
@ -70,11 +87,7 @@ These arguments have to specified explicitly by name. Some examples:
def some_fuction(): def some_fuction():
pass pass
@TraceFunction(trace_rv = False) @TraceFunction(trace_args = False, exception_tb_stderr = True)
def some_fuction():
pass
@TraceFunction(trace_args = False, trace_rv = False)
def some_fuction(): def some_fuction():
pass pass
@ -99,13 +112,17 @@ template.
- ``PYLG_FILE`` (default = ``'pylg.log'``) - the log file name. - ``PYLG_FILE`` (default = ``'pylg.log'``) - the log file name.
- ``EXCEPTION_WARNING`` (default = ``True``) - if ``True``, PyLg will - ``DEFAULT_EXCEPTION_WARNING`` (default = ``True``) - the default
print a warning about every exception caught to stderr. setting for ``exception_warning``.
- ``EXCEPTION_EXIT`` (default = ``False``) - if ``True``, PyLg will - ``DEFAULT_EXCEPTION_TB_FILE`` (default = ``True``) - the default
force the program to exit (and not just raise ``SystemExit``) setting for ``exception_tb_file``.
whenever an exception occurs. This will happen even if the exception
would be handled at a later point. - ``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. - ``TRACE_TIME`` (default = ``TRUE``) - enable/disable time logging.
@ -167,12 +184,13 @@ template.
elements in the dictionary. elements in the dictionary.
- ``DEFAULT_TRACE_ARGS`` (default = ``True``) - the default setting - ``DEFAULT_TRACE_ARGS`` (default = ``True``) - the default setting
for ``trace_args`` which determines whether TraceFunction should for ``trace_args``.
trace function parameters on entry.
- ``DEFAULT_TRACE_RV`` (default = ``True``) - the default setting for - ``DEFAULT_TRACE_RV`` (default = ``True``) - the default setting for
``trace_rv`` which determines whether TraceFunction should trace ``trace_rv``.
function return values on exit.
- ``DEFAULT_TRACE_RV_TYPE`` (default = ``True``) - the default setting
for ``trace_rv_type``.
Under development Under development
----------------- -----------------

View File

@ -172,20 +172,37 @@ if PYLG_USER_FILE is not None:
from .settings import PYLG_FILE from .settings import PYLG_FILE
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# EXCEPTION_WARNING - bool # DEFAULT_EXCEPTION_WARNING - bool
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
try: try:
pylg_check_bool(EXCEPTION_WARNING, "EXCEPTION_WARNING") pylg_check_bool(DEFAULT_EXCEPTION_WARNING, "DEFAULT_EXCEPTION_WARNING")
except ImportError: except ImportError:
from .settings import EXCEPTION_WARNING from .settings import DEFAULT_EXCEPTION_WARNING
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# EXCEPTION_EXIT - bool # DEFAULT_EXCEPTION_TB_FILE - bool
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
try: try:
pylg_check_bool(EXCEPTION_EXIT, "EXCEPTION_EXIT") pylg_check_bool(DEFAULT_EXCEPTION_TB_FILE, "DEFAULT_EXCEPTION_TB_FILE")
except ImportError: 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 # TRACE_TIME - bool
@ -330,3 +347,9 @@ if PYLG_USER_FILE is not None:
pylg_check_bool(DEFAULT_TRACE_RV, "DEFAULT_TRACE_RV") pylg_check_bool(DEFAULT_TRACE_RV, "DEFAULT_TRACE_RV")
except ImportError: except ImportError:
from .settings import DEFAULT_TRACE_RV 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
from __future__ import print_function
from datetime import datetime from datetime import datetime
from functools import partial from functools import partial
import traceback import traceback
@ -153,8 +154,14 @@ class TraceFunction(object):
if args: 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_args = DEFAULT_TRACE_ARGS
self.trace_rv = DEFAULT_TRACE_RV self.trace_rv = DEFAULT_TRACE_RV
self.trace_rv_type = DEFAULT_TRACE_RV_TYPE
# ----------------------------------------------------------------- # -----------------------------------------------------------------
# The function init_function will verify the input. # The function init_function will verify the input.
@ -163,31 +170,63 @@ class TraceFunction(object):
if kwargs: 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_args_str = 'trace_args'
trace_rv_str = 'trace_rv' trace_rv_str = 'trace_rv'
trace_rv_type_str = 'trace_rv_type'
# ----------------------------------------------------------------- # -----------------------------------------------------------------
# If kwargs is non-empty, it should only contain trace_rv, # If kwargs is non-empty, args should be empty.
# trace_args, or both and args should be empty. Assert all
# this.
# ----------------------------------------------------------------- # -----------------------------------------------------------------
assert not args 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] 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 self.trace_args = DEFAULT_TRACE_ARGS
if trace_rv_str in kwargs: try:
self.trace_rv = kwargs[trace_rv_str] 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 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 self.function = None
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
@ -249,8 +288,8 @@ class TraceFunction(object):
except Exception as e: except Exception as e:
self.trace_exception(e) self.trace_exception(e)
if EXCEPTION_EXIT: if self.exception_exit:
traceback.print_exc(file=sys.stderr) warnings.warn("Exit forced by EXCEPTION_EXIT")
os._exit(1) os._exit(1)
raise raise
@ -360,6 +399,9 @@ class TraceFunction(object):
else: else:
msg += "---" msg += "---"
if self.trace_rv_type:
msg += " (type: " + type(rv).__name__ + ")"
trace(msg, function=self.function) trace(msg, function=self.function)
return return
@ -379,9 +421,19 @@ class TraceFunction(object):
if str(exception) is not "": if str(exception) is not "":
msg += " - " + str(exception) msg += " - " + str(exception)
if EXCEPTION_WARNING: if self.exception_warning:
warnings.warn(core_msg, RuntimeWarning) 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) trace(msg, function=self.function)
return return
@ -466,26 +518,41 @@ def trace(message, function=None):
message = str(message) message = str(message)
if MESSAGE_WIDTH > 0: # ---------------------------------------------------------------------
# -----------------------------------------------------------------
# Get the length of the trace line so far # Get the length of the trace line so far
# ----------------------------------------------------------------- # ---------------------------------------------------------------------
premsglen = len(msg) 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. # Wrap the text.
# ----------------------------------------------------------------- # -----------------------------------------------------------------
wrapped = textwrap.wrap(message, MESSAGE_WIDTH) wrapped = textwrap.wrap(line, MESSAGE_WIDTH)
if not wrapped: if not wrapped:
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: if MESSAGE_WRAP:
# ------------------------------------------------------------- # -------------------------------------------------------------
# Print the first line. It gets special treatment as # The first wrapped line gets special treatment as any
# it doesn't need whitespace in front of it. # whitespace should already be prepended.
# ------------------------------------------------------------- # -------------------------------------------------------------
msg += wrapped[0] msg += wrapped[0]
@ -493,8 +560,8 @@ def trace(message, function=None):
# Print the remaining lines. Append whitespace to # Print the remaining lines. Append whitespace to
# align it with the first line. # align it with the first line.
# ------------------------------------------------------------- # -------------------------------------------------------------
for line in wrapped[1:]: for wrline in wrapped[1:]:
msg += '\n' + '{:{w}}'.format('', w=premsglen) + line msg += '\n' + '{:{w}}'.format('', w=premsglen) + wrline
else: else:
# ------------------------------------------------------------- # -------------------------------------------------------------
@ -509,8 +576,7 @@ def trace(message, function=None):
# --------------------------------------------------------- # ---------------------------------------------------------
if MESSAGE_WIDTH > 1: if MESSAGE_WIDTH > 1:
wrapped = textwrap.wrap(wrapped[0], wrapped = textwrap.wrap(wrapped[0], MESSAGE_WIDTH - 1)
MESSAGE_WIDTH - 1)
assert wrapped assert wrapped
msg += ('{m:{w}}'.format(m=wrapped[0], msg += ('{m:{w}}'.format(m=wrapped[0],
@ -528,16 +594,9 @@ def trace(message, function=None):
# --------------------------------------------------------- # ---------------------------------------------------------
msg += wrapped[0] 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" msg += "\n"
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------

View File

@ -27,17 +27,30 @@ PYLG_ENABLE = True
PYLG_FILE = 'pylg.log' PYLG_FILE = 'pylg.log'
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# If True, PyLg will print a warning about every exception caught to # The default value for 'exception_warning'. If True, PyLg will print
# stderr. # 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 # The default value for 'exception_tb_file'. If True, PyLg will write
# SystemExit) whenever an exception occurs. This will happen even if # the exception tracebacks to the log file.
# the exception would be handled at a later point.
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
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. # Enable/disable time logging.
@ -116,7 +129,9 @@ MESSAGE_WRAP = True
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# If true, truncated message lines should have the last character # 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 MESSAGE_MARK_TRUNCATION = True
@ -134,9 +149,19 @@ COLLAPSE_LISTS = False
COLLAPSE_DICTS = False COLLAPSE_DICTS = False
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# The default settings for 'trace_args' and 'trace_rv' which determine # The default setting for 'trace_args'. If True, PyLg will log input
# whether TraceFunction should trace function parameters on entry and # parameters.
# return values on exit.
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
DEFAULT_TRACE_ARGS = True DEFAULT_TRACE_ARGS = True
# -----------------------------------------------------------------------------
# The default setting for 'trace_rv'. If True, PyLg will log return
# values.
# -----------------------------------------------------------------------------
DEFAULT_TRACE_RV = True 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( setup(
name='PyLg', name='PyLg',
version='1.2.2', version='1.3.0',
description='Python module to facilitate and automate the process of writing runtime logs.', description='Python module to facilitate and automate the process of writing runtime logs.',
long_description=long_description, long_description=long_description,
url='https://gitlab.wojciechkozlowski.eu/wojtek/PyLg', url='https://gitlab.wojciechkozlowski.eu/wojtek/PyLg',