mirror of
https://github.com/Wojtek242/pylg.git
synced 2024-11-23 08:05:25 +01:00
Convert settings.py to settings.yml
This commit is contained in:
parent
da5a114bc1
commit
20e0c3219c
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
dist
|
||||
PyLg.egg-info
|
||||
*.log
|
||||
.coverage
|
||||
|
@ -1,24 +1,8 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
||||
# Copyright (C) 2017 Wojciech Kozlowski <wk@wojciechkozlowski.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# -----------------------------------------------------------------------------
|
||||
"""PyLg: facilitate and automate the process of writing runtime logs.
|
||||
|
||||
from .load_settings import PYLG_ENABLE
|
||||
"""
|
||||
|
||||
if PYLG_ENABLE:
|
||||
from .pylg import TraceFunction, trace
|
||||
else:
|
||||
from .dummy import TraceFunctionDummy as TraceFunction, trace
|
||||
from pylg.pylg import TraceFunction, trace
|
||||
|
||||
assert TraceFunction
|
||||
assert trace
|
||||
|
@ -1,355 +0,0 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
||||
# Copyright (C) 2017 Wojciech Kozlowski <wk@wojciechkozlowski.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import traceback
|
||||
import warnings
|
||||
import inspect
|
||||
import sys
|
||||
import os
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Import all the defaults first.
|
||||
# -----------------------------------------------------------------------------
|
||||
from .settings import *
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The filename of the user settings. It will be set once it can be
|
||||
# determined after loading the module.
|
||||
# -----------------------------------------------------------------------------
|
||||
PYLG_USER_FILE = None
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Attempt to load the module pylg_settings module. If successful, set
|
||||
# PYLG_USER_FILE to the module's path. By attempting an import rather
|
||||
# than checking if a file exists, we can handle the case of the user
|
||||
# having the settings file elsewhere in their path.
|
||||
# -----------------------------------------------------------------------------
|
||||
try:
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# We import the module itself to be able to determine its source
|
||||
# file before we import all the settings.
|
||||
# -------------------------------------------------------------------------
|
||||
import pylg_settings
|
||||
from pylg_settings import *
|
||||
PYLG_USER_FILE = inspect.getsourcefile(pylg_settings)
|
||||
|
||||
except ImportError:
|
||||
# -------------------------------------------------------------------------
|
||||
# The user settings don't exist. We assume the user is happy with
|
||||
# the defaults.
|
||||
# -------------------------------------------------------------------------
|
||||
pass
|
||||
|
||||
except (NameError, SyntaxError):
|
||||
|
||||
warnings.warn("There was a problem importing user settings")
|
||||
|
||||
sys.stderr.write("\n")
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.stderr.write("\n")
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Utility functions for sanity checking user settings. They raise an
|
||||
# ImportError if something is wrong.
|
||||
# -----------------------------------------------------------------------------
|
||||
def pylg_check_bool(value, name):
|
||||
|
||||
if not isinstance(value, bool):
|
||||
|
||||
warning_msg = ("Invalid type for " + name + " in " +
|
||||
PYLG_USER_FILE +
|
||||
" - should be bool, is type " +
|
||||
type(value).__name__)
|
||||
|
||||
warnings.warn(warning_msg)
|
||||
|
||||
raise ImportError
|
||||
|
||||
|
||||
def pylg_check_string(value, name):
|
||||
|
||||
if not isinstance(value, str):
|
||||
|
||||
warning_msg = ("Invalid type for " + name + " in " +
|
||||
PYLG_USER_FILE +
|
||||
" - should be a string, is type " +
|
||||
type(value).__name__)
|
||||
|
||||
warnings.warn(warning_msg)
|
||||
|
||||
raise ImportError
|
||||
|
||||
|
||||
def pylg_check_int(value, name):
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# We check for bool as well as bools are an instance of int, but
|
||||
# we don't want to let that go through.
|
||||
# -------------------------------------------------------------------------
|
||||
if not isinstance(value, int) or isinstance(value, bool):
|
||||
|
||||
warning_msg = ("Invalid type for " + name + " in " +
|
||||
PYLG_USER_FILE +
|
||||
" - should be int, is " +
|
||||
type(value).__name__)
|
||||
|
||||
warnings.warn(warning_msg)
|
||||
|
||||
raise ImportError
|
||||
|
||||
|
||||
def pylg_check_nonneg_int(value, name):
|
||||
|
||||
pylg_check_int(value, name)
|
||||
|
||||
if value < 0:
|
||||
|
||||
warning_msg = ("Invalid value for " + name + " in " +
|
||||
PYLG_USER_FILE +
|
||||
" - should be non-negative, is " +
|
||||
str(value))
|
||||
|
||||
warnings.warn(warning_msg)
|
||||
|
||||
raise ImportError
|
||||
|
||||
|
||||
def pylg_check_pos_int(value, name):
|
||||
|
||||
pylg_check_int(value, name)
|
||||
|
||||
if value <= 0:
|
||||
|
||||
warning_msg = ("Invalid value for " + name + " in " +
|
||||
PYLG_USER_FILE +
|
||||
" - should be positive, is " +
|
||||
str(value))
|
||||
|
||||
warnings.warn(warning_msg)
|
||||
|
||||
raise ImportError
|
||||
|
||||
|
||||
if PYLG_USER_FILE is not None:
|
||||
# -------------------------------------------------------------------------
|
||||
# If PYLG_USER_FILE is set, we have successfully imported user
|
||||
# settings. Nowe, we need to sanity check them. If anything is
|
||||
# wrong we reset the value to its default. At this stage a single
|
||||
# error should not affect any other settings.
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# PYLG_ENABLE - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(PYLG_ENABLE, "PYLG_ENABLE")
|
||||
except ImportError:
|
||||
from .settings import PYLG_ENABLE
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# PYLG_FILE - string
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_string(PYLG_FILE, "PYLG_FILE")
|
||||
except ImportError:
|
||||
from .settings import PYLG_FILE
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# DEFAULT_EXCEPTION_WARNING - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(DEFAULT_EXCEPTION_WARNING, "DEFAULT_EXCEPTION_WARNING")
|
||||
except ImportError:
|
||||
from .settings import DEFAULT_EXCEPTION_WARNING
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# DEFAULT_EXCEPTION_TB_FILE - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(DEFAULT_EXCEPTION_TB_FILE, "DEFAULT_EXCEPTION_TB_FILE")
|
||||
except ImportError:
|
||||
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
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(TRACE_TIME, "TRACE_TIME")
|
||||
except ImportError:
|
||||
from .settings import TRACE_TIME
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# TIME_FORMAT - string
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_string(TIME_FORMAT, "TIME_FORMAT")
|
||||
except ImportError:
|
||||
from .settings import TIME_FORMAT
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# TRACE_FILENAME - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(TRACE_FILENAME, "TRACE_FILENAME")
|
||||
except ImportError:
|
||||
from .settings import TRACE_FILENAME
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# FILENAME_COLUMN_WIDTH - non-negative integer
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_pos_int(FILENAME_COLUMN_WIDTH, "FILENAME_COLUMN_WIDTH")
|
||||
except ImportError:
|
||||
from .settings import FILENAME_COLUMN_WIDTH
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# TRACE_LINENO - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(TRACE_LINENO, "TRACE_LINENO")
|
||||
except ImportError:
|
||||
from .settings import TRACE_LINENO
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# LINENO_WIDTH - non-negative integer
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_nonneg_int(LINENO_WIDTH, "LINENO_WIDTH")
|
||||
except ImportError:
|
||||
from .settings import LINENO_WIDTH
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# TRACE_FUNCTION - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(TRACE_FUNCTION, "TRACE_FUNCTION")
|
||||
except ImportError:
|
||||
from .settings import TRACE_FUNCTION
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# FUNCTION_COLUMN_WIDTH - non-negative integer
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_pos_int(FUNCTION_COLUMN_WIDTH, "FUNCTION_COLUMN_WIDTH")
|
||||
except ImportError:
|
||||
from .settings import FUNCTION_COLUMN_WIDTH
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# CLASS_NAME_RESOLUTION - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(CLASS_NAME_RESOLUTION, "CLASS_NAME_RESOLUTION")
|
||||
except ImportError:
|
||||
from .settings import CLASS_NAME_RESOLUTION
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# TRACE_MESSAGE - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(TRACE_MESSAGE, "TRACE_MESSAGE")
|
||||
except ImportError:
|
||||
from .settings import TRACE_MESSAGE
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MESSAGE_WIDTH - non-negative integer - note 0 denotes unlimited
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_nonneg_int(MESSAGE_WIDTH, "MESSAGE_WIDTH")
|
||||
except ImportError:
|
||||
from .settings import MESSAGE_WIDTH
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MESSAGE_WRAP - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(MESSAGE_WRAP, "MESSAGE_WRAP")
|
||||
except ImportError:
|
||||
from .settings import MESSAGE_WRAP
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MESSAGE_MARK_TRUNCATION - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(MESSAGE_MARK_TRUNCATION, "MESSAGE_MARK_TRUNCATION")
|
||||
except ImportError:
|
||||
from .settings import MESSAGE_MARK_TRUNCATION
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# TRACE_SELF - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(TRACE_SELF, "TRACE_SELF")
|
||||
except ImportError:
|
||||
from .settings import TRACE_SELF
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# COLLAPSE_LISTS - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(COLLAPSE_LISTS, "COLLAPSE_LISTS")
|
||||
except ImportError:
|
||||
from .settings import COLLAPSE_LISTS
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# COLLAPSE_DICTS - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(COLLAPSE_DICTS, "COLLAPSE_DICTS")
|
||||
except ImportError:
|
||||
from .settings import COLLAPSE_DICTS
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# DEFAULT_TRACE_ARGS - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
pylg_check_bool(DEFAULT_TRACE_ARGS, "DEFAULT_TRACE_ARGS")
|
||||
except ImportError:
|
||||
from .settings import DEFAULT_TRACE_ARGS
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# DEFAULT_TRACE_RV - bool
|
||||
# -------------------------------------------------------------------------
|
||||
try:
|
||||
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")
|
541
pylg/pylg.py
541
pylg/pylg.py
@ -1,24 +1,8 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
||||
# Copyright (C) 2017 Wojciech Kozlowski <wk@wojciechkozlowski.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# -----------------------------------------------------------------------------
|
||||
"""PyLg: facilitate and automate the process of writing runtime logs."""
|
||||
|
||||
from __future__ import print_function
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
from typing import Optional
|
||||
import traceback
|
||||
import warnings
|
||||
import textwrap
|
||||
@ -26,103 +10,156 @@ import inspect
|
||||
import sys
|
||||
import os
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Load settings.
|
||||
# -----------------------------------------------------------------------------
|
||||
from .load_settings import *
|
||||
from pylg.settings import _pylg_check_bool
|
||||
import pylg.settings
|
||||
|
||||
|
||||
class ClassNameStack(object):
|
||||
class ClassNameStack:
|
||||
"""Stack for the class names of the currently executing functions.
|
||||
|
||||
The class name of the last traced function that was called will be on top
|
||||
of the stack. It is removed after it finishes executing.
|
||||
|
||||
""" A class to keep a global stack of the class names of the
|
||||
functions that are currently executing. The class name of the
|
||||
last traced function that was called will be on top of the
|
||||
stack. It is removed after it finishes executing.
|
||||
"""
|
||||
|
||||
stack = []
|
||||
|
||||
@staticmethod
|
||||
def insert(classname):
|
||||
if CLASS_NAME_RESOLUTION:
|
||||
ClassNameStack.stack.append(classname)
|
||||
@classmethod
|
||||
def disable(cls) -> None:
|
||||
"""Disable the stack.
|
||||
|
||||
@staticmethod
|
||||
def pop():
|
||||
if CLASS_NAME_RESOLUTION and ClassNameStack.stack:
|
||||
ClassNameStack.stack.pop()
|
||||
This is achieved by rendering all functions no-ops.
|
||||
|
||||
@staticmethod
|
||||
def get():
|
||||
if CLASS_NAME_RESOLUTION and ClassNameStack.stack:
|
||||
return ClassNameStack.stack[-1]
|
||||
else:
|
||||
return None
|
||||
WARNING: This is an irreversible operation.
|
||||
|
||||
"""
|
||||
cls.insert = lambda _classname: None
|
||||
cls.pop = lambda: None
|
||||
cls.get = lambda: None
|
||||
|
||||
@classmethod
|
||||
def insert(cls, classname: str) -> None:
|
||||
"""Insert an entry.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
classname : str
|
||||
The class name to insert.
|
||||
|
||||
"""
|
||||
cls.stack.append(classname)
|
||||
|
||||
@classmethod
|
||||
def pop(cls) -> str:
|
||||
"""str: Return top-most entry and remove it."""
|
||||
return cls.stack.pop() if cls.stack else None
|
||||
|
||||
@classmethod
|
||||
def peek(cls) -> str:
|
||||
"""str: Return the top-most entry without removing it."""
|
||||
return ClassNameStack.stack[-1] if cls.stack else None
|
||||
|
||||
|
||||
class PyLg(object):
|
||||
|
||||
""" Class to handle the log file.
|
||||
"""
|
||||
class PyLg:
|
||||
"""Class to handle the log file."""
|
||||
|
||||
wfile = None
|
||||
filename = PYLG_FILE
|
||||
|
||||
@staticmethod
|
||||
def set_filename(new_filename):
|
||||
@classmethod
|
||||
def configure(cls, user_settings_path: Optional[str] = None) -> None:
|
||||
"""PyLg initialisation.
|
||||
|
||||
""" Change the file name of the log file. The change will be
|
||||
rejected if the log file is already open.
|
||||
Parameters
|
||||
----------
|
||||
user_settings_path : Optional[str]
|
||||
Path to the user settings file.
|
||||
|
||||
:param str new_filename: The new file name for the log file.
|
||||
"""
|
||||
|
||||
if PyLg.wfile is None:
|
||||
PyLg.filename = new_filename
|
||||
else:
|
||||
warnings.warn("PyLg wfile is open - cannot change filename")
|
||||
# ---------------------------------------------------------------------
|
||||
# Load the settings.
|
||||
# ---------------------------------------------------------------------
|
||||
settings = pylg.settings.load(user_settings_path)
|
||||
|
||||
@staticmethod
|
||||
def write(string):
|
||||
# ---------------------------------------------------------------------
|
||||
# Local variables for settings to avoid dictionary access which is only
|
||||
# O(1) on average. Admittedly the size of the settings dict is not
|
||||
# large, but these accesses will be frequent.
|
||||
# ---------------------------------------------------------------------
|
||||
cls.pylg_enable = settings["pylg_enable"]
|
||||
cls.pylg_file = settings["pylg_file"]
|
||||
cls.default_exception_warning = settings["default_exception_warning"]
|
||||
cls.default_exception_tb_file = settings["default_exception_tb_file"]
|
||||
cls.default_exception_tb_stderr = \
|
||||
settings["default_exception_tb_stderr"]
|
||||
cls.default_exception_exit = settings["default_exception_exit"]
|
||||
cls.trace_time = settings["trace_time"]
|
||||
cls.time_format = settings["time_format"]
|
||||
cls.trace_filename = settings["trace_filename"]
|
||||
cls.filename_column_width = settings["filename_column_width"]
|
||||
cls.trace_lineno = settings["trace_lineno"]
|
||||
cls.lineno_width = settings["lineno_width"]
|
||||
cls.trace_function = settings["trace_function"]
|
||||
cls.function_column_width = settings["function_column_width"]
|
||||
cls.class_name_resolution = settings["class_name_resolution"]
|
||||
cls.trace_message = settings["trace_message"]
|
||||
cls.message_width = settings["message_width"]
|
||||
cls.message_wrap = settings["message_wrap"]
|
||||
cls.message_mark_truncation = settings["message_mark_truncation"]
|
||||
cls.trace_self = settings["trace_self"]
|
||||
cls.collapse_lists = settings["collapse_lists"]
|
||||
cls.collapse_dicts = settings["collapse_dicts"]
|
||||
cls.default_trace_args = settings["default_trace_args"]
|
||||
cls.default_trace_rv = settings["default_trace_rv"]
|
||||
cls.default_trace_rv_type = settings["default_trace_rv_type"]
|
||||
|
||||
""" Write to the log file. A new log file is opened and
|
||||
initialised if it has not been opened yet.
|
||||
if not cls.class_name_resolution:
|
||||
ClassNameStack.disable()
|
||||
|
||||
cls.wfile = open(cls.pylg_file, "w")
|
||||
cls.wfile.write(
|
||||
"=== Log initialised at {} ===\n\n".format(datetime.now())
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def write(cls, string: str):
|
||||
"""Write to the log file.
|
||||
|
||||
A new log file is opened and initialised if it has not been opened yet.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
string : str
|
||||
The string to be written to the log file.
|
||||
|
||||
:param str string: The string to be written to the log file.
|
||||
"""
|
||||
|
||||
if PyLg.wfile is None:
|
||||
PyLg.wfile = open(PyLg.filename, "w")
|
||||
PyLg.wfile.write("=== Log initialised at " +
|
||||
str(datetime.now()) + " ===\n\n")
|
||||
cls.wfile.write(string)
|
||||
cls.wfile.flush()
|
||||
|
||||
PyLg.wfile.write(string)
|
||||
PyLg.wfile.flush()
|
||||
@classmethod
|
||||
def close(cls):
|
||||
"""Close the log file."""
|
||||
|
||||
@staticmethod
|
||||
def close():
|
||||
|
||||
""" Close the log file.
|
||||
"""
|
||||
|
||||
if PyLg.wfile is not None:
|
||||
PyLg.wfile.close()
|
||||
PyLg.wfile = None
|
||||
if cls.wfile is not None:
|
||||
cls.wfile.close()
|
||||
cls.wfile = None
|
||||
else:
|
||||
warnings.warn("PyLg wfile is not open - nothing to close")
|
||||
|
||||
|
||||
class TraceFunction(object):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class TraceFunction:
|
||||
"""Decorator to trace entry and exit from functions.
|
||||
|
||||
Used by appending @TraceFunction on top of the definition of the function
|
||||
to trace.
|
||||
|
||||
""" Class that serves as a decorator to trace entry and exit from
|
||||
functions. Used by appending @TraceFunction on top of the
|
||||
definition of the function to trace.
|
||||
"""
|
||||
|
||||
class TraceFunctionStruct(object):
|
||||
|
||||
""" Internal object to handle traced function properties.
|
||||
"""
|
||||
# pylint: disable=too-few-public-methods
|
||||
class TraceFunctionStruct:
|
||||
"""Internal object to handle traced function properties."""
|
||||
|
||||
function = None
|
||||
varnames = None
|
||||
@ -134,19 +171,10 @@ class TraceFunction(object):
|
||||
functionname = None
|
||||
|
||||
def __get__(self, obj, objtype):
|
||||
|
||||
""" Support for instance functions.
|
||||
"""
|
||||
|
||||
"""Support for instance functions."""
|
||||
return partial(self.__call__, obj)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
""" Constructor for TraceFunction. Note that the behaviour is
|
||||
different depending on whether TraceFunction is passed any
|
||||
parameters. For details see __call__ in this class.
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Make sure this decorator is never called with no arguments.
|
||||
# ---------------------------------------------------------------------
|
||||
@ -154,14 +182,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._exception_warning = PyLg.default_exception_warning
|
||||
self._exception_tb_file = PyLg.default_exception_tb_file
|
||||
self._exception_tb_stderr = PyLg.default_exception_tb_stderr
|
||||
self._exception_exit = PyLg.default_exception_exit
|
||||
|
||||
self.trace_args = DEFAULT_TRACE_ARGS
|
||||
self.trace_rv = DEFAULT_TRACE_RV
|
||||
self.trace_rv_type = DEFAULT_TRACE_RV_TYPE
|
||||
self._trace_args = PyLg.default_trace_args
|
||||
self._trace_rv = PyLg.default_trace_rv
|
||||
self._trace_rv_type = PyLg.default_trace_rv_type
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# The function init_function will verify the input.
|
||||
@ -170,6 +198,11 @@ class TraceFunction(object):
|
||||
|
||||
if kwargs:
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# If kwargs is non-empty, args should be empty.
|
||||
# -----------------------------------------------------------------
|
||||
assert not args
|
||||
|
||||
exception_warning_str = 'exception_warning'
|
||||
exception_tb_file_str = 'exception_tb_file'
|
||||
exception_tb_stderr_str = 'exception_tb_stderr'
|
||||
@ -179,70 +212,47 @@ class TraceFunction(object):
|
||||
trace_rv_str = 'trace_rv'
|
||||
trace_rv_type_str = 'trace_rv_type'
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# If kwargs is non-empty, args should be empty.
|
||||
# -----------------------------------------------------------------
|
||||
assert not args
|
||||
kwopts = {}
|
||||
for option, default in [
|
||||
(exception_warning_str, PyLg.default_exception_warning),
|
||||
(exception_tb_file_str, PyLg.default_exception_tb_file),
|
||||
(exception_tb_stderr_str,
|
||||
PyLg.default_exception_tb_stderr),
|
||||
(exception_exit_str, PyLg.default_exception_exit),
|
||||
(trace_args_str, PyLg.default_trace_args),
|
||||
(trace_rv_str, PyLg.default_trace_rv),
|
||||
(trace_rv_type_str, PyLg.default_trace_rv_type),
|
||||
]:
|
||||
|
||||
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
|
||||
kwopts[option] = kwargs.get(option, default)
|
||||
if not _pylg_check_bool(kwopts[option]):
|
||||
raise ValueError(
|
||||
"Invalid type for {} - should be bool, is type {}"
|
||||
.format(option, type(kwopts[option]).__name__)
|
||||
)
|
||||
|
||||
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
|
||||
self._exception_warning = kwopts[exception_warning_str]
|
||||
self._exception_tb_file = kwopts[exception_tb_file_str]
|
||||
self._exception_tb_stderr = kwopts[exception_tb_stderr_str]
|
||||
self._exception_exit = kwopts[exception_exit_str]
|
||||
|
||||
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]
|
||||
pylg_check_bool(self.trace_args, "trace_args")
|
||||
except (KeyError, ImportError):
|
||||
self.trace_args = DEFAULT_TRACE_ARGS
|
||||
|
||||
try:
|
||||
self.trace_rv = kwargs[trace_rv_str]
|
||||
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._trace_args = kwopts[trace_args_str]
|
||||
self._trace_rv = kwopts[trace_rv_str]
|
||||
self._trace_rv_type = kwopts[trace_rv_type_str]
|
||||
|
||||
self.function = None
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""Wrapper that is called when a call to a decorated function is made.
|
||||
|
||||
""" The actual wrapper that is called when a call to a
|
||||
decorated function is made. It also handles extra
|
||||
initialisation when parameters are passed to
|
||||
TraceFunction.
|
||||
It also handles extra initialisation when parameters are passed to
|
||||
TraceFunction.
|
||||
|
||||
:return: The return value of the decorated function.
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# __call__ has to behave differently depending on whether the
|
||||
# decorator has been given any parameters. The reason for this
|
||||
# is as follows:
|
||||
# __call__ has to behave differently depending on whether the decorator
|
||||
# has been given any parameters. The reason for this is as follows:
|
||||
#
|
||||
# @TraceFunction
|
||||
# decorated_function
|
||||
@ -254,25 +264,24 @@ class TraceFunction(object):
|
||||
#
|
||||
# translates to TraceFunction(*args, **kwargs)(decorated_function)
|
||||
#
|
||||
# In both cases, the result should be a callable object which
|
||||
# will be called whenever the decorated function is called. In
|
||||
# the first case, the callable object is an instance of
|
||||
# TraceFunction, in the latter case the return value of
|
||||
# TraceFunction.__call__ is the callable object.
|
||||
# In both cases, the result should be a callable object which will be
|
||||
# called whenever the decorated function is called. In the first case,
|
||||
# the callable object is an instance of TraceFunction, in the latter
|
||||
# case the return value of TraceFunction.__call__ is the callable
|
||||
# object.
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
if self.function is None:
|
||||
# -----------------------------------------------------------------
|
||||
# If the decorator has been passed a parameter, __init__
|
||||
# will not define self.function and __call__ will be
|
||||
# called immediately after __init__ with the decorated
|
||||
# function as the only parameter. Therefore, this __call__
|
||||
# function has to return the callable object that is meant
|
||||
# to be called every time the decorated function is
|
||||
# called. Here, self is returned in order to return the
|
||||
# object as the callable handle for the decorated
|
||||
# function. This if block should be hit only once at most
|
||||
# and only during initialisation.
|
||||
# If the decorator has been passed a parameter, __init__ will not
|
||||
# define self.function and __call__ will be called immediately
|
||||
# after __init__ with the decorated function as the only parameter.
|
||||
# Therefore, this __call__ function has to return the callable
|
||||
# object that is meant to be called every time the decorated
|
||||
# function is called. Here, self is returned in order to return the
|
||||
# object as the callable handle for the decorated function. This if
|
||||
# block should be hit only once at most and only during
|
||||
# initialisation.
|
||||
# -----------------------------------------------------------------
|
||||
self.init_function(*args, **kwargs)
|
||||
return self
|
||||
@ -285,11 +294,13 @@ class TraceFunction(object):
|
||||
|
||||
try:
|
||||
rv = self.function.function(*args, **kwargs)
|
||||
except Exception as e:
|
||||
self.trace_exception(e)
|
||||
except Exception as exc:
|
||||
self.trace_exception(exc)
|
||||
|
||||
if self.exception_exit:
|
||||
if self._exception_exit:
|
||||
warnings.warn("Exit forced by EXCEPTION_EXIT")
|
||||
|
||||
# pylint: disable=protected-access
|
||||
os._exit(1)
|
||||
|
||||
raise
|
||||
@ -300,17 +311,13 @@ class TraceFunction(object):
|
||||
return rv
|
||||
|
||||
def init_function(self, *args, **kwargs):
|
||||
|
||||
""" Function to initialise the TraceFunctionStruct kept by the
|
||||
decorator.
|
||||
"""
|
||||
"""Initialise the TraceFunctionStruct kept by the decorator."""
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# This function should only ever be called with one parameter
|
||||
# - the function to be decorated. These checks are done here,
|
||||
# rather than by the caller, as anything that calls this
|
||||
# function should also have been called with the decorated
|
||||
# function as its only parameter.
|
||||
# This function should only ever be called with one parameter - the
|
||||
# function to be decorated. These checks are done here, rather than by
|
||||
# the caller, as anything that calls this function should also have
|
||||
# been called with the decorated function as its only parameter.
|
||||
# ---------------------------------------------------------------------
|
||||
assert not kwargs
|
||||
assert len(args) == 1
|
||||
@ -329,8 +336,8 @@ class TraceFunction(object):
|
||||
argspec.defaults))
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# init_function is called from either __init__ or __call__ and
|
||||
# we want the frame before that.
|
||||
# init_function is called from either __init__ or __call__ and we want
|
||||
# the frame before that.
|
||||
# ---------------------------------------------------------------------
|
||||
frames_back = 2
|
||||
caller_frame = inspect.stack()[frames_back]
|
||||
@ -341,10 +348,11 @@ class TraceFunction(object):
|
||||
self.function.functionname = self.function.function.__name__
|
||||
|
||||
def trace_entry(self, *args, **kwargs):
|
||||
"""Handle function entry.
|
||||
|
||||
This function collects all the function arguments and constructs a
|
||||
message to pass to trace.
|
||||
|
||||
""" Called on function entry. This function collects all the
|
||||
function arguments and constructs a message to pass to
|
||||
trace.
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
@ -355,23 +363,22 @@ class TraceFunction(object):
|
||||
msg += ": "
|
||||
|
||||
n_args = len(args)
|
||||
if self.trace_args:
|
||||
if self._trace_args:
|
||||
for arg in range(n_args):
|
||||
|
||||
if not TRACE_SELF and \
|
||||
if not PyLg.trace_self and \
|
||||
self.function.varnames[arg] == "self":
|
||||
continue
|
||||
|
||||
msg += (self.function.varnames[arg] + " = " +
|
||||
self.get_value_string(args[arg]) + ", ")
|
||||
msg += "{} = {}, ".format(
|
||||
self.function.varnames[arg],
|
||||
self.get_value_string(args[arg])
|
||||
)
|
||||
|
||||
for name in self.function.varnames[n_args:]:
|
||||
msg += name + " = "
|
||||
if name in kwargs:
|
||||
value = kwargs[name]
|
||||
else:
|
||||
value = self.function.defaults[name]
|
||||
msg += self.get_value_string(value) + ", "
|
||||
msg += "{} = {}, ".format(
|
||||
name, kwargs.get(name, self.function.defaults[name])
|
||||
)
|
||||
|
||||
msg = msg[:-2]
|
||||
|
||||
@ -381,11 +388,10 @@ class TraceFunction(object):
|
||||
trace(msg, function=self.function)
|
||||
|
||||
def trace_exit(self, rv=None):
|
||||
"""Handle function exit.
|
||||
|
||||
""" Called on function exit to log the fact that a function has
|
||||
finished executing.
|
||||
Log the fact that a function has finished executing.
|
||||
|
||||
:param rv: The return value of the traced function.
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
@ -394,22 +400,19 @@ class TraceFunction(object):
|
||||
msg = "<- EXIT "
|
||||
if rv is not None:
|
||||
msg += ": "
|
||||
if self.trace_rv:
|
||||
if self._trace_rv:
|
||||
msg += self.get_value_string(rv)
|
||||
else:
|
||||
msg += "---"
|
||||
|
||||
if self.trace_rv_type:
|
||||
msg += " (type: " + type(rv).__name__ + ")"
|
||||
if self._trace_rv_type:
|
||||
msg += " (type: {})".format(type(rv).__name__)
|
||||
|
||||
trace(msg, function=self.function)
|
||||
return
|
||||
|
||||
def trace_exception(self, exception):
|
||||
"""Called when a function terminated due to an exception.
|
||||
|
||||
""" Called when a function terminated due to an exception.
|
||||
|
||||
:param exception: The raised exception.
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
@ -418,58 +421,55 @@ class TraceFunction(object):
|
||||
core_msg = type(exception).__name__ + " RAISED"
|
||||
msg = "<- EXIT : " + core_msg
|
||||
|
||||
if str(exception) is not "":
|
||||
if str(exception) != "":
|
||||
msg += " - " + str(exception)
|
||||
|
||||
if self.exception_warning:
|
||||
if self._exception_warning:
|
||||
warnings.warn(core_msg, RuntimeWarning)
|
||||
|
||||
if self.exception_tb_file:
|
||||
if self._exception_tb_file:
|
||||
msg += "\n--- EXCEPTION ---\n"
|
||||
msg += traceback.format_exc()
|
||||
msg += "-----------------"
|
||||
|
||||
if self.exception_tb_stderr:
|
||||
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
|
||||
|
||||
def get_value_string(self, value):
|
||||
@staticmethod
|
||||
def get_value_string(value):
|
||||
"""Convert value to a string for the log."""
|
||||
|
||||
""" Convert value to a string for the log.
|
||||
"""
|
||||
if isinstance(value, list) and PyLg.collapse_lists:
|
||||
return "[ len={} ]".format(len(value))
|
||||
|
||||
if isinstance(value, list) and COLLAPSE_LISTS:
|
||||
return self.collapse_list(value)
|
||||
elif isinstance(value, dict) and COLLAPSE_DICTS:
|
||||
return self.collapse_dict(value)
|
||||
else:
|
||||
return str(value)
|
||||
if isinstance(value, dict) and PyLg.collapse_dicts:
|
||||
return "{{ len={} }}".format(len(value))
|
||||
|
||||
def collapse_list(self, ll):
|
||||
return "[ len=" + str(len(ll)) + " ]"
|
||||
|
||||
def collapse_dict(self, dd):
|
||||
return "{ len=" + str(len(dd)) + " }"
|
||||
return "{}".format(value)
|
||||
|
||||
|
||||
def trace(message, function=None):
|
||||
"""Write message to the log file.
|
||||
|
||||
""" Writes message to the log file. It will also log the time,
|
||||
filename, line number and function name.
|
||||
It will also log the time, filename, line number and function name.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
message : str
|
||||
The log message.
|
||||
function : Optional[TraceFunctionStruct]
|
||||
A TraceFunctionStruct object if called from within TraceFunction.
|
||||
|
||||
:param str message: The log message.
|
||||
:param function: A TraceFunctionStruct object if called from within
|
||||
TraceFunction.
|
||||
"""
|
||||
|
||||
if function is None:
|
||||
# ---------------------------------------------------------------------
|
||||
# If there is no function object, we need to work out
|
||||
# where the trace call was made from.
|
||||
# If there is no function object, we need to work out where the trace
|
||||
# call was made from.
|
||||
# ---------------------------------------------------------------------
|
||||
frames_back = 1
|
||||
caller_frame = inspect.stack()[frames_back]
|
||||
@ -484,37 +484,40 @@ def trace(message, function=None):
|
||||
functionname = function.functionname
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# If CLASS_NAME_RESOLUTION is enabled, the top element of the
|
||||
# stack should be the class name of the function from which this
|
||||
# trace call is made. This cannot be policed so the user must make
|
||||
# sure this is the case by ensuring that trace is only called
|
||||
# outside of any function or from within functions that have the
|
||||
# @TraceFunction decorator.
|
||||
# If _CLASS_NAME_RESOLUTION is enabled, the top element of the stack should
|
||||
# be the class name of the function from which this trace call is made.
|
||||
# This cannot be policed so the user must make sure this is the case by
|
||||
# ensuring that trace is only called outside of any function or from within
|
||||
# functions that have the @TraceFunction decorator.
|
||||
# -------------------------------------------------------------------------
|
||||
classname = ClassNameStack.get()
|
||||
if classname is not None and classname != "<module>":
|
||||
functionname = classname + "." + functionname
|
||||
functionname = "{}.{}".format(classname, functionname)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Generate the string based on the settings.
|
||||
# -------------------------------------------------------------------------
|
||||
msg = ""
|
||||
|
||||
if TRACE_TIME:
|
||||
msg += datetime.now().strftime(TIME_FORMAT) + " "
|
||||
if PyLg.trace_time:
|
||||
msg += datetime.now().strftime(PyLg.time_format) + " "
|
||||
|
||||
if TRACE_FILENAME:
|
||||
msg += '{filename:{w}.{w}} '.format(filename=filename,
|
||||
w=FILENAME_COLUMN_WIDTH)
|
||||
if PyLg.trace_filename:
|
||||
msg += "{filename:{w}.{w}} ".format(
|
||||
filename=filename,
|
||||
w=PyLg.filename_column_width
|
||||
)
|
||||
|
||||
if TRACE_LINENO:
|
||||
msg += '{lineno:0{w}}: '.format(lineno=lineno, w=LINENO_WIDTH)
|
||||
if PyLg.trace_lineno:
|
||||
msg += "{lineno:0{w}}: ".format(lineno=lineno, w=PyLg.lineno_width)
|
||||
|
||||
if TRACE_FUNCTION:
|
||||
msg += '{function:{w}.{w}} '.format(function=functionname,
|
||||
w=FUNCTION_COLUMN_WIDTH)
|
||||
if PyLg.trace_function:
|
||||
msg += "{function:{w}.{w}} ".format(
|
||||
function=functionname,
|
||||
w=PyLg.function_column_width
|
||||
)
|
||||
|
||||
if TRACE_MESSAGE:
|
||||
if PyLg.trace_message:
|
||||
|
||||
message = str(message)
|
||||
|
||||
@ -535,20 +538,20 @@ def trace(message, function=None):
|
||||
# -----------------------------------------------------------------
|
||||
# Wrap the text.
|
||||
# -----------------------------------------------------------------
|
||||
wrapped = textwrap.wrap(line, MESSAGE_WIDTH)
|
||||
wrapped = textwrap.wrap(line, PyLg.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 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)
|
||||
msg += "{:{w}}".format("", w=premsglen)
|
||||
|
||||
if MESSAGE_WRAP:
|
||||
if PyLg.message_wrap:
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# The first wrapped line gets special treatment as any
|
||||
@ -557,34 +560,38 @@ def trace(message, function=None):
|
||||
msg += wrapped[0]
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Print the remaining lines. Append whitespace to
|
||||
# align it with the first line.
|
||||
# Print the remaining lines. Append whitespace to align it with
|
||||
# the first line.
|
||||
# -------------------------------------------------------------
|
||||
for wrline in wrapped[1:]:
|
||||
msg += '\n' + '{:{w}}'.format('', w=premsglen) + wrline
|
||||
msg += "\n{:{w}}".format('', w=premsglen) + wrline
|
||||
|
||||
else:
|
||||
# -------------------------------------------------------------
|
||||
# The message is not being wrapped.
|
||||
# -------------------------------------------------------------
|
||||
|
||||
if MESSAGE_MARK_TRUNCATION and wrapped[1:]:
|
||||
if PyLg.message_mark_truncation and wrapped[1:]:
|
||||
# ---------------------------------------------------------
|
||||
# We want to mark truncated lines so we need to
|
||||
# determine if the line is being truncated. If it
|
||||
# is we replace the last character with '\'.
|
||||
# We want to mark truncated lines so we need to determine
|
||||
# if the line is being truncated. If it is we replace the
|
||||
# last character with '\'.
|
||||
# ---------------------------------------------------------
|
||||
|
||||
if MESSAGE_WIDTH > 1:
|
||||
wrapped = textwrap.wrap(wrapped[0], MESSAGE_WIDTH - 1)
|
||||
if PyLg.message_width > 1:
|
||||
wrapped = textwrap.wrap(
|
||||
wrapped[0],
|
||||
PyLg.message_width - 1
|
||||
)
|
||||
assert wrapped
|
||||
|
||||
msg += ('{m:{w}}'.format(m=wrapped[0],
|
||||
w=MESSAGE_WIDTH - 1) +
|
||||
'\\')
|
||||
msg += "{m:{w}}\\".format(
|
||||
m=wrapped[0],
|
||||
w=PyLg.message_width - 1,
|
||||
)
|
||||
|
||||
else:
|
||||
assert MESSAGE_WIDTH == 1
|
||||
assert PyLg.message_width == 1
|
||||
msg += '\\'
|
||||
|
||||
else:
|
||||
|
305
pylg/settings.py
305
pylg/settings.py
@ -1,167 +1,162 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
||||
# Copyright (C) 2017 Wojciech Kozlowski <wk@wojciechkozlowski.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# -----------------------------------------------------------------------------
|
||||
"""Handlers for PyLg settings."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import os
|
||||
import yaml
|
||||
|
||||
_USER_FILE_NAME = "pylg_settings.yml"
|
||||
_PYLG_FILE_NAME = "settings.yml"
|
||||
|
||||
_BOOL_OPTIONS = set([
|
||||
"pylg_enable",
|
||||
"default_exception_warning",
|
||||
"default_exception_tb_file",
|
||||
"default_exception_tb_stderr",
|
||||
"default_exception_exit",
|
||||
"trace_time",
|
||||
"trace_filename",
|
||||
"trace_lineno",
|
||||
"trace_function",
|
||||
"class_name_resolution",
|
||||
"trace_message",
|
||||
"message_wrap",
|
||||
"message_mark_truncation",
|
||||
"trace_self",
|
||||
"collapse_lists",
|
||||
"collapse_dicts",
|
||||
"default_trace_args",
|
||||
"default_trace_rv",
|
||||
"default_trace_rv_type",
|
||||
])
|
||||
|
||||
_STRING_OPTIONS = set([
|
||||
"pylg_file",
|
||||
"time_format",
|
||||
])
|
||||
|
||||
_POS_INT_OPTIONS = set([
|
||||
"filename_column_width",
|
||||
"function_column_width",
|
||||
])
|
||||
|
||||
_NONNEG_OPTIONS = set([
|
||||
"lineno_width",
|
||||
"message_width",
|
||||
])
|
||||
|
||||
|
||||
def load(user_settings_path: Optional[str] = None) -> dict:
|
||||
"""Load PyLg settings from file.
|
||||
|
||||
If a user provides a settings file, it will be used. Otherwise, PyLg's
|
||||
default settings will be read in.
|
||||
|
||||
"""
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Load the default settings.
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
default_settings_path = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), _PYLG_FILE_NAME
|
||||
)
|
||||
|
||||
with open(default_settings_path, 'r') as settings_file:
|
||||
default_settings = yaml.full_load(settings_file)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Load the user settings.
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
user_settings = {}
|
||||
if user_settings_path is not None:
|
||||
with open(user_settings_path, 'r') as settings_file:
|
||||
user_settings = yaml.full_load(settings_file)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Merge the two settings preferring user values over defaults.
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
settings = {**default_settings, **user_settings}
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Verify the input.
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
for option in settings.keys():
|
||||
source_file = (user_settings_path
|
||||
if option in user_settings
|
||||
else default_settings_path)
|
||||
option_type = type(settings[option]).__name__
|
||||
option_value = settings[option]
|
||||
|
||||
if option in _BOOL_OPTIONS:
|
||||
if not _pylg_check_bool(option_value):
|
||||
raise ImportError(
|
||||
"Invalid type for {} in {} - should be bool, is type {}"
|
||||
.format(option, source_file, option_type)
|
||||
)
|
||||
|
||||
elif option in _STRING_OPTIONS:
|
||||
if not _pylg_check_string(option_value):
|
||||
raise ImportError(
|
||||
"Invalid type for {} in {} - should be str, is type {}"
|
||||
.format(option, source_file, option_type)
|
||||
)
|
||||
|
||||
elif option in _POS_INT_OPTIONS:
|
||||
if not _pylg_check_pos_int(option_value):
|
||||
raise ImportError(
|
||||
"Invalid type/value for {} in {} - "
|
||||
"should be positive int, is {}"
|
||||
.format(option, source_file, option_value)
|
||||
)
|
||||
|
||||
elif option in _NONNEG_OPTIONS:
|
||||
if not _pylg_check_nonneg_int(option_value):
|
||||
raise ImportError(
|
||||
"Invalid type/value for {} in {} - "
|
||||
"should be non-negative int, is {}"
|
||||
.format(option, source_file, option_value)
|
||||
)
|
||||
|
||||
else:
|
||||
raise ImportError("Unrecognised option in {}: {}"
|
||||
.format(source_file, option))
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Some final value processing.
|
||||
# -------------------------------------------------------------------------
|
||||
if settings["message_width"] == 0:
|
||||
settings["message_width"] = float("inf")
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable PyLg.
|
||||
# Utility functions for sanity checking user settings.
|
||||
# -----------------------------------------------------------------------------
|
||||
PYLG_ENABLE = True
|
||||
def _pylg_check_bool(value) -> bool:
|
||||
return isinstance(value, bool)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The log file name.
|
||||
# -----------------------------------------------------------------------------
|
||||
PYLG_FILE = 'pylg.log'
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The default value for 'exception_warning'. If True, PyLg will print
|
||||
# a warning about every exception caught to stderr.
|
||||
# -----------------------------------------------------------------------------
|
||||
DEFAULT_EXCEPTION_WARNING = True
|
||||
def _pylg_check_string(value) -> bool:
|
||||
return isinstance(value, str)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The default value for 'exception_tb_file'. If True, PyLg will write
|
||||
# the exception tracebacks to the log file.
|
||||
# -----------------------------------------------------------------------------
|
||||
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
|
||||
def _pylg_check_int(value) -> bool:
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 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
|
||||
# -------------------------------------------------------------------------
|
||||
# We check for bool as well as bools are an instance of int, but we don't
|
||||
# want to let that go through.
|
||||
# -------------------------------------------------------------------------
|
||||
return isinstance(value, int) and not isinstance(value, bool)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable time logging.
|
||||
# -----------------------------------------------------------------------------
|
||||
TRACE_TIME = True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Formatting for the time trace. For a full list of options, see
|
||||
# https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior.
|
||||
# -----------------------------------------------------------------------------
|
||||
TIME_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
|
||||
def _pylg_check_nonneg_int(value):
|
||||
return _pylg_check_int(value) and value >= 0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable file name logging.
|
||||
# -----------------------------------------------------------------------------
|
||||
TRACE_FILENAME = True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The column width for the file name. If a name is too long, it will
|
||||
# be truncated.
|
||||
# -----------------------------------------------------------------------------
|
||||
FILENAME_COLUMN_WIDTH = 20
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable the logging of the line number from which the trace
|
||||
# call was made. For entry and exit messages this logs the line in
|
||||
# which the decorator is placed (which should be directly above the
|
||||
# function itself).
|
||||
# -----------------------------------------------------------------------------
|
||||
TRACE_LINENO = True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The minimum number of digits to use to print the line number. If the
|
||||
# number is too long, more digits will be used.
|
||||
# -----------------------------------------------------------------------------
|
||||
LINENO_WIDTH = 4
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable the logging of the function name from which the trace
|
||||
# call was made. Entry/exit logs refer to the function they enter into
|
||||
# and exit from.
|
||||
# -----------------------------------------------------------------------------
|
||||
TRACE_FUNCTION = True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The column width for the function name. If a name is too long, it
|
||||
# will be truncated.
|
||||
# -----------------------------------------------------------------------------
|
||||
FUNCTION_COLUMN_WIDTH = 32
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable class name resolution. Function names will be printed
|
||||
# with their class names.
|
||||
#
|
||||
# IMPORTANT: If this setting is enabled, the trace function should
|
||||
# ONLY be called from within functions that have the @TraceFunction
|
||||
# decorator OR outside of any function.
|
||||
# -----------------------------------------------------------------------------
|
||||
CLASS_NAME_RESOLUTION = False
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable message logging.
|
||||
# -----------------------------------------------------------------------------
|
||||
TRACE_MESSAGE = True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The column width for the message. A width of zero means unlimited.
|
||||
# -----------------------------------------------------------------------------
|
||||
MESSAGE_WIDTH = 0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# If True, PyLg will wrap the message to fit within the column
|
||||
# width. Otherwise, the message will be truncated.
|
||||
# -----------------------------------------------------------------------------
|
||||
MESSAGE_WRAP = True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# If true, truncated message lines should have the last character
|
||||
# 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
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable logging of the 'self' function argument.
|
||||
# -----------------------------------------------------------------------------
|
||||
TRACE_SELF = False
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# If True lists/dictionaries will be collapsed to '[ len=x ]' and '{
|
||||
# len=x }' respectively, where x denotes the number of elements in the
|
||||
# collection.
|
||||
# -----------------------------------------------------------------------------
|
||||
COLLAPSE_LISTS = False
|
||||
COLLAPSE_DICTS = False
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 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
|
||||
def _pylg_check_pos_int(value):
|
||||
return _pylg_check_int(value) and value > 0
|
||||
|
143
pylg/settings.yml
Normal file
143
pylg/settings.yml
Normal file
@ -0,0 +1,143 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable PyLg.
|
||||
# -----------------------------------------------------------------------------
|
||||
pylg_enable: True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The log file name.
|
||||
# -----------------------------------------------------------------------------
|
||||
pylg_file: 'pylg.log'
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The default value for 'exception_warning'. If True, PyLg will print a warning
|
||||
# about every exception caught to stderr.
|
||||
# -----------------------------------------------------------------------------
|
||||
default_exception_warning: True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The default value for 'exception_tb_file'. If True, PyLg will write the
|
||||
# exception tracebacks to the log file.
|
||||
# -----------------------------------------------------------------------------
|
||||
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.
|
||||
# -----------------------------------------------------------------------------
|
||||
trace_time: True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Formatting for the time trace. For a full list of options, see
|
||||
# https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior.
|
||||
# -----------------------------------------------------------------------------
|
||||
time_format: "%Y-%m-%d %H:%M:%S.%f"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable file name logging.
|
||||
# -----------------------------------------------------------------------------
|
||||
trace_filename: True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The column width for the file name. If a name is too long, it will be
|
||||
# truncated.
|
||||
# -----------------------------------------------------------------------------
|
||||
filename_column_width: 20
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable the logging of the line number from which the trace call was
|
||||
# made. For entry and exit messages this logs the line in which the decorator
|
||||
# is placed (which should be directly above the function itself).
|
||||
# -----------------------------------------------------------------------------
|
||||
trace_lineno: True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The minimum number of digits to use to print the line number. If the number
|
||||
# is too long, more digits will be used.
|
||||
# -----------------------------------------------------------------------------
|
||||
lineno_width: 4
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable the logging of the function name from which the trace call was
|
||||
# made. Entry/exit logs refer to the function they enter into and exit from.
|
||||
# -----------------------------------------------------------------------------
|
||||
trace_function: True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The column width for the function name. If a name is too long, it will be
|
||||
# truncated.
|
||||
# -----------------------------------------------------------------------------
|
||||
function_column_width: 32
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable class name resolution. Function names will be printed with
|
||||
# their class names.
|
||||
#
|
||||
# IMPORTANT: If this setting is enabled, the trace function should ONLY be
|
||||
# called from within functions that have the @TraceFunction decorator OR
|
||||
# outside of any function.
|
||||
# -----------------------------------------------------------------------------
|
||||
class_name_resolution: False
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable message logging.
|
||||
# -----------------------------------------------------------------------------
|
||||
trace_message: True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# The column width for the message. A width of zero means unlimited.
|
||||
# -----------------------------------------------------------------------------
|
||||
message_width: 0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# If True, PyLg will wrap the message to fit within the column width.
|
||||
# Otherwise, the message will be truncated.
|
||||
# -----------------------------------------------------------------------------
|
||||
message_wrap: True
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# If true, truncated message lines should have the last character 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
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Enable/disable logging of the 'self' function argument.
|
||||
# -----------------------------------------------------------------------------
|
||||
trace_self: False
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# If True lists/dictionaries will be collapsed to '[ len=x ]' and '{ len=x }'
|
||||
# respectively, where x denotes the number of elements in the collection.
|
||||
# -----------------------------------------------------------------------------
|
||||
collapse_lists: False
|
||||
collapse_dicts: False
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 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
|
63
pylg/settings_to_yml.py
Normal file
63
pylg/settings_to_yml.py
Normal file
@ -0,0 +1,63 @@
|
||||
"""Convert old-style settings.py to new-style settings.yml."""
|
||||
|
||||
import warnings
|
||||
import sys
|
||||
from os import path
|
||||
|
||||
|
||||
def convert(source, destination):
|
||||
"""Convert source python settings file to destination yml file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
source : str
|
||||
File name of the old-style Python settings file.
|
||||
destination: str
|
||||
File name for the new-style YAML settings file.
|
||||
|
||||
"""
|
||||
with open(source, 'r') as src, open(destination, 'w') as dst:
|
||||
for line in src:
|
||||
if not line.lstrip().startswith('#') and '=' in line:
|
||||
key, val = line.split('=')
|
||||
key = key.strip().lower()
|
||||
val = val.strip()
|
||||
dst.write("{}: {}\n".format(key, val))
|
||||
else:
|
||||
dst.write(line)
|
||||
|
||||
|
||||
def settings_to_yml():
|
||||
"""Convert user settings file from old-style python to new-style YAML."""
|
||||
|
||||
settings_py = "pylg_settings.py"
|
||||
|
||||
root_dir = path.dirname(sys.modules['__main__'].__file__)
|
||||
settings_py_path = path.join(root_dir, settings_py)
|
||||
if path.isfile(settings_py_path):
|
||||
warnings.warn(
|
||||
"Deprecated {} found".format(settings_py),
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
settings_yml = "{}.yml".format(settings_py[:-3])
|
||||
settings_yml_path = path.join(root_dir, settings_yml)
|
||||
if path.isfile(settings_yml_path):
|
||||
warnings.warn(
|
||||
"Could not convert {py} to {yml}, {yml} already exists. "
|
||||
"If this is the converted settings file, delete {py} to get "
|
||||
"rid of this warning."
|
||||
.format(py=settings_py, yml=settings_yml),
|
||||
DeprecationWarning,
|
||||
)
|
||||
return
|
||||
|
||||
warnings.warn(
|
||||
"Converting {} to {}".format(settings_py, settings_yml),
|
||||
DeprecationWarning,
|
||||
)
|
||||
convert(settings_py_path, settings_yml_path)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
settings_to_yml()
|
0
pylg/tests/__init__.py
Normal file
0
pylg/tests/__init__.py
Normal file
5
pylg/tests/test_pylg.py
Normal file
5
pylg/tests/test_pylg.py
Normal file
@ -0,0 +1,5 @@
|
||||
from pylg import TraceFunction, trace
|
||||
import sys
|
||||
|
||||
def test_load():
|
||||
print(sys.modules['__main__'].__file__)
|
571
pylintrc
Normal file
571
pylintrc
Normal file
@ -0,0 +1,571 @@
|
||||
[MASTER]
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code.
|
||||
extension-pkg-whitelist=
|
||||
|
||||
# Add files or directories to the blacklist. They should be base names, not
|
||||
# paths.
|
||||
ignore=CVS
|
||||
|
||||
# Add files or directories matching the regex patterns to the blacklist. The
|
||||
# regex matches against base names, not paths.
|
||||
ignore-patterns=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||
# number of processors available to use.
|
||||
jobs=1
|
||||
|
||||
# Control the amount of potential inferred values when inferring a single
|
||||
# object. This can help the performance when dealing with large functions or
|
||||
# complex, nested conditions.
|
||||
limit-inference-results=100
|
||||
|
||||
# List of plugins (as comma separated values of python modules names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# Specify a configuration file.
|
||||
#rcfile=
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages.
|
||||
suggestion-mode=yes
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
|
||||
confidence=
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once). You can also use "--disable=all" to
|
||||
# disable everything first and then reenable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||
# --disable=W".
|
||||
disable=print-statement,
|
||||
parameter-unpacking,
|
||||
unpacking-in-except,
|
||||
old-raise-syntax,
|
||||
backtick,
|
||||
long-suffix,
|
||||
old-ne-operator,
|
||||
old-octal-literal,
|
||||
import-star-module-level,
|
||||
non-ascii-bytes-literal,
|
||||
raw-checker-failed,
|
||||
bad-inline-option,
|
||||
locally-disabled,
|
||||
file-ignored,
|
||||
suppressed-message,
|
||||
useless-suppression,
|
||||
deprecated-pragma,
|
||||
use-symbolic-message-instead,
|
||||
apply-builtin,
|
||||
basestring-builtin,
|
||||
buffer-builtin,
|
||||
cmp-builtin,
|
||||
coerce-builtin,
|
||||
execfile-builtin,
|
||||
file-builtin,
|
||||
long-builtin,
|
||||
raw_input-builtin,
|
||||
reduce-builtin,
|
||||
standarderror-builtin,
|
||||
unicode-builtin,
|
||||
xrange-builtin,
|
||||
coerce-method,
|
||||
delslice-method,
|
||||
getslice-method,
|
||||
setslice-method,
|
||||
no-absolute-import,
|
||||
old-division,
|
||||
dict-iter-method,
|
||||
dict-view-method,
|
||||
next-method-called,
|
||||
metaclass-assignment,
|
||||
indexing-exception,
|
||||
raising-string,
|
||||
reload-builtin,
|
||||
oct-method,
|
||||
hex-method,
|
||||
nonzero-method,
|
||||
cmp-method,
|
||||
input-builtin,
|
||||
round-builtin,
|
||||
intern-builtin,
|
||||
unichr-builtin,
|
||||
map-builtin-not-iterating,
|
||||
zip-builtin-not-iterating,
|
||||
range-builtin-not-iterating,
|
||||
filter-builtin-not-iterating,
|
||||
using-cmp-argument,
|
||||
eq-without-hash,
|
||||
div-method,
|
||||
idiv-method,
|
||||
rdiv-method,
|
||||
exception-message-attribute,
|
||||
invalid-str-codec,
|
||||
sys-max-int,
|
||||
bad-python3-import,
|
||||
deprecated-string-function,
|
||||
deprecated-str-translate-call,
|
||||
deprecated-itertools-function,
|
||||
deprecated-types-field,
|
||||
next-method-defined,
|
||||
dict-items-not-iterating,
|
||||
dict-keys-not-iterating,
|
||||
dict-values-not-iterating,
|
||||
deprecated-operator-function,
|
||||
deprecated-urllib-function,
|
||||
xreadlines-attribute,
|
||||
deprecated-sys-function,
|
||||
exception-escape,
|
||||
comprehension-escape
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once). See also the "--disable" option for examples.
|
||||
enable=c-extension-no-member
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Python expression which should return a note less than 10 (10 is the highest
|
||||
# note). You have access to the variables errors warning, statement which
|
||||
# respectively contain the number of errors / warnings messages and the total
|
||||
# number of statements analyzed. This is used by the global evaluation report
|
||||
# (RP0004).
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details.
|
||||
#msg-template=
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, json
|
||||
# and msvs (visual studio). You can also give a reporter class, e.g.
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
output-format=text
|
||||
|
||||
# Tells whether to display a full report or only the messages.
|
||||
reports=no
|
||||
|
||||
# Activate the evaluation score.
|
||||
score=yes
|
||||
|
||||
|
||||
[REFACTORING]
|
||||
|
||||
# Maximum number of nested blocks for function / method body
|
||||
max-nested-blocks=5
|
||||
|
||||
# Complete name of functions that never returns. When checking for
|
||||
# inconsistent-return-statements if a never returning function is called then
|
||||
# it will be considered as an explicit return statement and no message will be
|
||||
# printed.
|
||||
never-returning-functions=sys.exit
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=
|
||||
|
||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# Tells whether to warn about missing members when the owner of the attribute
|
||||
# is inferred to be None.
|
||||
ignore-none=yes
|
||||
|
||||
# This flag controls whether pylint should warn about no-member and similar
|
||||
# checks whenever an opaque object is returned when inferring. The inference
|
||||
# can return multiple potential results while evaluating a Python object, but
|
||||
# some branches might not be evaluated, which results in partial inference. In
|
||||
# that case, it might be useful to still emit no-member and other checks for
|
||||
# the rest of the inferred objects.
|
||||
ignore-on-opaque-inference=yes
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis. It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
missing-member-hint=yes
|
||||
|
||||
# The minimum edit distance a name should have in order to be considered a
|
||||
# similar match for a missing member name.
|
||||
missing-member-hint-distance=1
|
||||
|
||||
# The total number of similar names that should be taken in consideration when
|
||||
# showing a hint for a missing member.
|
||||
missing-member-max-choices=1
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Naming style matching correct argument names.
|
||||
argument-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct argument names. Overrides argument-
|
||||
# naming-style.
|
||||
#argument-rgx=
|
||||
|
||||
# Naming style matching correct attribute names.
|
||||
attr-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||
# style.
|
||||
#attr-rgx=
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma.
|
||||
bad-names=foo,
|
||||
bar,
|
||||
baz,
|
||||
toto,
|
||||
tutu,
|
||||
tata
|
||||
|
||||
# Naming style matching correct class attribute names.
|
||||
class-attribute-naming-style=any
|
||||
|
||||
# Regular expression matching correct class attribute names. Overrides class-
|
||||
# attribute-naming-style.
|
||||
#class-attribute-rgx=
|
||||
|
||||
# Naming style matching correct class names.
|
||||
class-naming-style=PascalCase
|
||||
|
||||
# Regular expression matching correct class names. Overrides class-naming-
|
||||
# style.
|
||||
#class-rgx=
|
||||
|
||||
# Naming style matching correct constant names.
|
||||
const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct constant names. Overrides const-naming-
|
||||
# style.
|
||||
#const-rgx=
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
# Naming style matching correct function names.
|
||||
function-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct function names. Overrides function-
|
||||
# naming-style.
|
||||
#function-rgx=
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma.
|
||||
good-names=i,
|
||||
j,
|
||||
k,
|
||||
ex,
|
||||
Run,
|
||||
rv,
|
||||
_
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name.
|
||||
include-naming-hint=no
|
||||
|
||||
# Naming style matching correct inline iteration names.
|
||||
inlinevar-naming-style=any
|
||||
|
||||
# Regular expression matching correct inline iteration names. Overrides
|
||||
# inlinevar-naming-style.
|
||||
#inlinevar-rgx=
|
||||
|
||||
# Naming style matching correct method names.
|
||||
method-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct method names. Overrides method-naming-
|
||||
# style.
|
||||
#method-rgx=
|
||||
|
||||
# Naming style matching correct module names.
|
||||
module-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct module names. Overrides module-naming-
|
||||
# style.
|
||||
#module-rgx=
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=^_
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
# These decorators are taken in consideration only for invalid-name.
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Naming style matching correct variable names.
|
||||
variable-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct variable names. Overrides variable-
|
||||
# naming-style.
|
||||
#variable-rgx=
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=100
|
||||
|
||||
# Maximum number of lines in a module.
|
||||
max-module-lines=1000
|
||||
|
||||
# List of optional constructs for which whitespace checking is disabled. `dict-
|
||||
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
|
||||
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
|
||||
# `empty-line` allows space-only lines.
|
||||
no-space-check=trailing-comma,
|
||||
dict-separator
|
||||
|
||||
# Allow the body of a class to be on the same line as the declaration if body
|
||||
# contains single statement.
|
||||
single-line-class-stmt=no
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Ignore imports when computing similarities.
|
||||
ignore-imports=no
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,
|
||||
XXX,
|
||||
TODO
|
||||
|
||||
|
||||
[STRING]
|
||||
|
||||
# This flag controls whether the implicit-str-concat-in-sequence should
|
||||
# generate a warning on implicit string concatenation in sequences defined over
|
||||
# several lines.
|
||||
check-str-concat-over-line-jumps=no
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Limits count of emitted suggestions for spelling mistakes.
|
||||
max-spelling-suggestions=4
|
||||
|
||||
# Spelling dictionary name. Available dictionaries: en_GB (myspell), en_US
|
||||
# (myspell)..
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to indicated private dictionary in
|
||||
# --spelling-private-dict-file option instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid defining new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# Tells whether unused global variables should be treated as a violation.
|
||||
allow-global-unused-variables=yes
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,
|
||||
_cb
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expected to
|
||||
# not be used).
|
||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||
|
||||
# Argument names that match this expression will be ignored. Default to name
|
||||
# with leading underscore.
|
||||
ignored-argument-names=_.*|^ignored_|^unused_
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# Format style used to check logging format string. `old` means using %
|
||||
# formatting, while `new` is for `{}` formatting.
|
||||
logging-format-style=old
|
||||
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format.
|
||||
logging-modules=logging
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,
|
||||
__new__,
|
||||
setUp
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,
|
||||
_fields,
|
||||
_replace,
|
||||
_source,
|
||||
_make
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=cls
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# Maximum number of arguments for function / method.
|
||||
max-args=5
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Maximum number of boolean expressions in an if statement.
|
||||
max-bool-expr=5
|
||||
|
||||
# Maximum number of branch for function / method body.
|
||||
max-branches=12
|
||||
|
||||
# Maximum number of locals for function / method body.
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
# Maximum number of return / yield for function / method body.
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of statements in function / method body.
|
||||
max-statements=50
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# Allow wildcard imports from modules that define __all__.
|
||||
allow-wildcard-with-all=no
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma.
|
||||
deprecated-modules=optparse,tkinter.tix
|
||||
|
||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||
# not be disabled).
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report RP0402 must not be disabled).
|
||||
import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||
# not be disabled).
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "BaseException, Exception".
|
||||
overgeneral-exceptions=BaseException,
|
||||
Exception
|
34
setup.py
34
setup.py
@ -1,26 +1,44 @@
|
||||
from setuptools import setup, find_packages
|
||||
from codecs import open
|
||||
"""PyLg setup file."""
|
||||
|
||||
from codecs import open as codecs_open
|
||||
from os import path
|
||||
|
||||
pwd = path.abspath(path.dirname(__file__))
|
||||
from setuptools import setup
|
||||
|
||||
with open(path.join(pwd, 'README.rst'), encoding='utf-8') as f:
|
||||
long_description = f.read()
|
||||
PWD = path.abspath(path.dirname(__file__))
|
||||
|
||||
with codecs_open(path.join(PWD, 'README.rst'), encoding='utf-8') as f:
|
||||
LONG_DESCRIPTION = f.read()
|
||||
|
||||
INSTALL_REQUIRES = [
|
||||
'pyyaml',
|
||||
]
|
||||
|
||||
EXTRAS_REQUIRE = {
|
||||
'dev': [
|
||||
'pytest',
|
||||
'pytest-cov',
|
||||
]
|
||||
}
|
||||
|
||||
setup(
|
||||
name='PyLg',
|
||||
version='1.3.3',
|
||||
description='Python module to facilitate and automate the process of writing runtime logs.',
|
||||
long_description=long_description,
|
||||
description=('Python module to facilitate and automate the process of '
|
||||
'writing runtime logs.'),
|
||||
long_description=LONG_DESCRIPTION,
|
||||
url='https://github.com/Wojtek242/pylg',
|
||||
|
||||
author='Wojciech Kozlowski',
|
||||
author_email='wk@wojciechkozlowski.eu',
|
||||
install_requires=INSTALL_REQUIRES,
|
||||
extras_require=EXTRAS_REQUIRE,
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Intended Audience :: Developers',
|
||||
'Topic :: Software Development :: Debuggers',
|
||||
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
|
||||
('License :: OSI Approved :: '
|
||||
'GNU General Public License v3 or later (GPLv3+)'),
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user