diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index da3eb5fc6..1a9c7670b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -28,8 +28,7 @@ Before opening a new issue, make sure you do the following: # code and logs here. # please use the following to format logs when posting them here -import logging import pymodbus -pymodbus.pymodbus_apply_logging_config(logging.DEBUG) +pymodbus.pymodbus_apply_logging_config() ``` diff --git a/API_changes.rst b/API_changes.rst index 29eac1a2d..b442269b5 100644 --- a/API_changes.rst +++ b/API_changes.rst @@ -5,6 +5,9 @@ PyModbus - API changes. ------------- Version 3.2.0 ------------- +- import pymodbus.version -> from pymodbus import __version__, __version_full__ +- pymodbus.pymodbus_apply_logging_config(log_file_name="pymodbus.log") to enable file pymodbus_apply_logging_config +- pymodbus.pymodbus_apply_logging_config have default DEBUG, it not called root settings will be used. - pymodbus/interfaces/IModbusDecoder removed. - pymodbus/interfaces/IModbusFramer removed. - pymodbus/interfaces/IModbusSlaveContext -> pymodbus/datastore/ModbusBaseSlaveContext. diff --git a/doc/conf.py b/doc/conf.py index 2c4733835..1c034aa13 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -11,7 +11,7 @@ from recommonmark.transform import AutoStructify -from pymodbus import __version__ +from pymodbus import __version__ as pymodbus_version parent_dir = os.path.abspath(os.pardir) @@ -26,8 +26,8 @@ project = "PyModbus" copyright = "See license" author = "Open Source volunteers" -version = __version__ -release = __version__ +version = pymodbus_version +release = pymodbus_version language = "en" exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] pygments_style = "sphinx" diff --git a/doc/source/library/pymodbus.rst b/doc/source/library/pymodbus.rst index 215b9b386..9875fd1ed 100644 --- a/doc/source/library/pymodbus.rst +++ b/doc/source/library/pymodbus.rst @@ -87,8 +87,3 @@ Extra functions :members: :undoc-members: :show-inheritance: - -.. automodule:: pymodbus.version - :members: - :undoc-members: - :show-inheritance: diff --git a/examples/server_async.py b/examples/server_async.py index fc802b9b4..7d89542ce 100755 --- a/examples/server_async.py +++ b/examples/server_async.py @@ -32,6 +32,7 @@ import os from examples.helper import get_commandline +from pymodbus import __version__ as pymodbus_version from pymodbus.datastore import ( ModbusSequentialDataBlock, ModbusServerContext, @@ -49,7 +50,6 @@ StartAsyncTlsServer, StartAsyncUdpServer, ) -from pymodbus.version import version _logger = logging.getLogger() @@ -130,7 +130,7 @@ def setup_server(args): "VendorUrl": "https://github.com/pymodbus-dev/pymodbus/", "ProductName": "Pymodbus Server", "ModelName": "Pymodbus Server", - "MajorMinorRevision": version.short(), + "MajorMinorRevision": pymodbus_version, } ) return args diff --git a/examples/v2.5.3/callback_server.py b/examples/v2.5.3/callback_server.py index 294ea49c5..87984dbac 100755 --- a/examples/v2.5.3/callback_server.py +++ b/examples/v2.5.3/callback_server.py @@ -10,6 +10,10 @@ from multiprocessing import Queue from threading import Thread +# --------------------------------------------------------------------------- # +# import the modbus libraries we need +# --------------------------------------------------------------------------- # +from pymodbus import __version__ as pymodbus_version from pymodbus.datastore import ( ModbusServerContext, ModbusSlaveContext, @@ -18,11 +22,6 @@ from pymodbus.device import ModbusDeviceIdentification from pymodbus.server import StartTcpServer -# --------------------------------------------------------------------------- # -# import the modbus libraries we need -# --------------------------------------------------------------------------- # -from pymodbus.version import version - # from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer @@ -137,7 +136,7 @@ def run_callback_server(): "VendorUrl": "https://github.com/pymodbus-dev/pymodbus/", "ProductName": "pymodbus Server", "ModelName": "pymodbus Server", - "MajorMinorRevision": version.short(), + "MajorMinorRevision": pymodbus_version, } ) diff --git a/examples/v2.5.3/custom_datablock.py b/examples/v2.5.3/custom_datablock.py index 6c1c851e1..a472f566a 100755 --- a/examples/v2.5.3/custom_datablock.py +++ b/examples/v2.5.3/custom_datablock.py @@ -7,6 +7,10 @@ """ import logging +# --------------------------------------------------------------------------- # +# import the modbus libraries we need +# --------------------------------------------------------------------------- # +from pymodbus import __version__ as pymodbus_version from pymodbus.datastore import ( ModbusServerContext, ModbusSlaveContext, @@ -15,11 +19,6 @@ from pymodbus.device import ModbusDeviceIdentification from pymodbus.server import StartTcpServer -# --------------------------------------------------------------------------- # -# import the modbus libraries we need -# --------------------------------------------------------------------------- # -from pymodbus.version import version - # from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer @@ -75,7 +74,7 @@ def run_custom_db_server(): "VendorUrl": "https://github.com/pymodbus-dev/pymodbus/", "ProductName": "pymodbus Server", "ModelName": "pymodbus Server", - "MajorMinorRevision": version.short(), + "MajorMinorRevision": pymodbus_version, } ) diff --git a/examples/v2.5.3/custom_synchronous_server.py b/examples/v2.5.3/custom_synchronous_server.py index deed5c5a3..0f2bc62d8 100755 --- a/examples/v2.5.3/custom_synchronous_server.py +++ b/examples/v2.5.3/custom_synchronous_server.py @@ -57,6 +57,7 @@ def decode(self, data): """ import logging +from pymodbus import __version__ as pymodbus_version from pymodbus.datastore import ( ModbusSequentialDataBlock, ModbusServerContext, @@ -64,7 +65,6 @@ def decode(self, data): ) from pymodbus.device import ModbusDeviceIdentification from pymodbus.server import StartTcpServer -from pymodbus.version import version from .custom_message import ( # pylint: disable=relative-beyond-top-level CustomModbusRequest, @@ -112,7 +112,7 @@ def run_server(): "VendorUrl": "https://github.com/pymodbus-dev/pymodbus/", "ProductName": "Pymodbus Server", "ModelName": "Pymodbus Server", - "MajorMinorRevision": version.short(), + "MajorMinorRevision": pymodbus_version, } ) diff --git a/examples/v2.5.3/dbstore_update_server.py b/examples/v2.5.3/dbstore_update_server.py index f045a8911..3db3f0878 100644 --- a/examples/v2.5.3/dbstore_update_server.py +++ b/examples/v2.5.3/dbstore_update_server.py @@ -17,16 +17,15 @@ import logging import random +# --------------------------------------------------------------------------- # +# import the modbus libraries we need +# --------------------------------------------------------------------------- # +from pymodbus import __version__ as pymodbus_version from pymodbus.datastore import ModbusSequentialDataBlock, ModbusServerContext from pymodbus.datastore.database import SqlSlaveContext from pymodbus.device import ModbusDeviceIdentification from pymodbus.server import StartAsyncTcpServer -# --------------------------------------------------------------------------- # -# import the modbus libraries we need -# --------------------------------------------------------------------------- # -from pymodbus.version import version - # from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer @@ -89,7 +88,7 @@ async def run_dbstore_update_server(): "VendorUrl": "https://github.com/pymodbus-dev/pymodbus/", "ProductName": "pymodbus Server", "ModelName": "pymodbus Server", - "MajorMinorRevision": version.short(), + "MajorMinorRevision": pymodbus_version, } ) diff --git a/examples/v2.5.3/deviceinfo_showcase_server.py b/examples/v2.5.3/deviceinfo_showcase_server.py index 794aa825e..10f068e2a 100755 --- a/examples/v2.5.3/deviceinfo_showcase_server.py +++ b/examples/v2.5.3/deviceinfo_showcase_server.py @@ -19,11 +19,6 @@ from pymodbus.device import ModbusDeviceIdentification from pymodbus.server import StartTcpServer -# --------------------------------------------------------------------------- # -# import the various server implementations -# --------------------------------------------------------------------------- # -from pymodbus.version import version - # --------------------------------------------------------------------------- # # configure the service logging @@ -57,7 +52,7 @@ def run_server(): "VendorUrl": "https://github.com/pymodbus-dev/pymodbus/", "ProductName": "Pymodbus Server", "ModelName": "Pymodbus Server", - "MajorMinorRevision": version.short(), + "MajorMinorRevision": pymodbus_version, } ) diff --git a/examples/v2.5.3/tornado_twisted/asynchronous_server.py b/examples/v2.5.3/tornado_twisted/asynchronous_server.py index 7a3471796..780e4214f 100755 --- a/examples/v2.5.3/tornado_twisted/asynchronous_server.py +++ b/examples/v2.5.3/tornado_twisted/asynchronous_server.py @@ -12,6 +12,7 @@ from custom_message import CustomModbusRequest +from pymodbus import __version__ as pymodbus_version from pymodbus.datastore import ( ModbusSequentialDataBlock, ModbusServerContext, @@ -21,8 +22,7 @@ # from pymodbus.server.asynchronous import StartUdpServer # from pymodbus.server.asynchronous import StartSerialServer from pymodbus.device import ModbusDeviceIdentification -from pymodbus.server.asynchronous import StartTcpServer -from pymodbus.version import version +from pymodbus.server import StartTcpServer # from pymodbus.transaction import ( @@ -121,7 +121,7 @@ def run_async_server(): info_name={ "VendorName": "Pymodbus", "ModelName": "Pymodbus Server", - "MajorMinorRevision": version.short(), + "MajorMinorRevision": pymodbus_version, "ProductCode": "PM", "VendorUrl": "https://github.com/pymodbus-dev/pymodbus/", "ProductName": "Pymodbus Server", diff --git a/pymodbus/__init__.py b/pymodbus/__init__.py index 2d61c1290..c8a453346 100644 --- a/pymodbus/__init__.py +++ b/pymodbus/__init__.py @@ -3,20 +3,19 @@ Released under the the BSD license """ -from logging import WARNING +from pymodbus.logging import pymodbus_apply_logging_config +from pymodbus.version import version -import pymodbus.version as __version -from pymodbus.logging import Log +__version__ = version.short() +__version_full__ = str(version) -__version__ = __version.version.short() -__author__ = "Galen Collins, Jan Iversen" -__maintainer__ = "dhoomakethu, janiversen" +# --------------------------------------------------------------------------- # +# Exported symbols +# --------------------------------------------------------------------------- # - -def pymodbus_apply_logging_config(level=WARNING): - """Apply basic logging configuration used by default by Pymodbus maintainers. - - Please call this function to format logging appropriately when opening issues. - """ - Log.apply_logging_config(level) +__all__ = [ + "pymodbus_apply_logging_config", + "__version__", + "__version_full__", +] diff --git a/pymodbus/logging.py b/pymodbus/logging.py index 78d3e86f0..fac314649 100644 --- a/pymodbus/logging.py +++ b/pymodbus/logging.py @@ -2,10 +2,10 @@ Released under the the BSD license """ - import logging from binascii import b2a_hex from logging import NullHandler as __null +from typing import Union from pymodbus.utilities import hexlify_packets @@ -16,30 +16,50 @@ logging.getLogger(__name__).addHandler(__null()) +def pymodbus_apply_logging_config( + level: Union[str, int] = logging.DEBUG, log_file_name: str = None +): + """Apply basic logging configuration used by default by Pymodbus maintainers. + + :param level: (optional) set log level, if not set it is inherited. + :param log_file_name: (optional) log additional to file + + Please call this function to format logging appropriately when opening issues. + """ + Log.apply_logging_config(level, log_file_name) + + class Log: """Class to hide logging complexity. :meta private: """ - CRITICAL = logging.CRITICAL - ERROR = logging.ERROR - WARNING = logging.WARNING - DEBUG = logging.DEBUG - INFO = logging.INFO - - LOG_LEVEL = logging.WARNING + LOG_LEVEL = logging.NOTSET _logger = logging.getLogger(__name__) @classmethod - def apply_logging_config(cls, level=logging.WARNING): + def apply_logging_config(cls, level, log_file_name): """Apply basic logging configuration""" - logging.basicConfig( - format="%(asctime)s %(levelname)-5s %(module)s:%(lineno)s %(message)s", - datefmt="%H:%M:%S", + if level == logging.NOTSET: + level = cls._logger.getEffectiveLevel() + log_stream_handler = logging.StreamHandler() + log_formatter = logging.Formatter( + "%(asctime)s %(levelname)-5s %(module)s:%(lineno)s %(message)s" ) + log_stream_handler.setFormatter(log_formatter) + cls._logger.addHandler(log_stream_handler) + if log_file_name: + log_file_handler = logging.FileHandler(log_file_name) + log_file_handler.setFormatter(log_formatter) + cls._logger.addHandler(log_file_handler) + cls.setLevel(level) + + @classmethod + def setLevel(cls, level): + """Apply basic logging level""" cls._logger.setLevel(level) - cls.LOG_LEVEL = cls._logger.getEffectiveLevel() + cls.LOG_LEVEL = level @classmethod def build_msg(cls, txt, *args): @@ -70,29 +90,39 @@ def build_msg(cls, txt, *args): @classmethod def info(cls, txt, *args): """Log info messagees.""" + if cls.LOG_LEVEL == logging.NOTSET: + cls.LOG_LEVEL = cls._logger.getEffectiveLevel() if logging.INFO >= cls.LOG_LEVEL: cls._logger.info(cls.build_msg(txt, *args)) @classmethod def debug(cls, txt, *args): """Log debug messagees.""" + if cls.LOG_LEVEL == logging.NOTSET: + cls.LOG_LEVEL = cls._logger.getEffectiveLevel() if logging.DEBUG >= cls.LOG_LEVEL: cls._logger.debug(cls.build_msg(txt, *args)) @classmethod def warning(cls, txt, *args): """Log warning messagees.""" + if cls.LOG_LEVEL == logging.NOTSET: + cls.LOG_LEVEL = cls._logger.getEffectiveLevel() if logging.WARNING >= cls.LOG_LEVEL: cls._logger.warning(cls.build_msg(txt, *args)) @classmethod def error(cls, txt, *args): """Log error messagees.""" + if cls.LOG_LEVEL == logging.NOTSET: + cls.LOG_LEVEL = cls._logger.getEffectiveLevel() if logging.ERROR >= cls.LOG_LEVEL: cls._logger.error(cls.build_msg(txt, *args)) @classmethod def critical(cls, txt, *args): """Log critical messagees.""" + if cls.LOG_LEVEL == logging.NOTSET: + cls.LOG_LEVEL = cls._logger.getEffectiveLevel() if logging.CRITICAL >= cls.LOG_LEVEL: cls._logger.critical(cls.build_msg(txt, *args)) diff --git a/pymodbus/repl/client/main.py b/pymodbus/repl/client/main.py index 2f8c342b0..005602411 100644 --- a/pymodbus/repl/client/main.py +++ b/pymodbus/repl/client/main.py @@ -12,6 +12,7 @@ from prompt_toolkit.styles import Style from pygments.lexers.python import PythonLexer +from pymodbus import __version__ as pymodbus_version from pymodbus.exceptions import ParameterException from pymodbus.repl.client.completer import ( CmdCompleter, @@ -25,7 +26,6 @@ ModbusRtuFramer, ModbusSocketFramer, ) -from pymodbus.version import version _logger = logging.getLogger() @@ -38,7 +38,7 @@ | | \___ / Y ( <_> ) /_/ | | | \ ___/| |_> > |__ |____| / ____\____|__ /\____/\____ | /\ |____|_ /\___ > __/|____/ \/ \/ \/ \/ \/ \/|__| - v1.3.0 - {version} + v1.3.0 - {pymodbus_version} ---------------------------------------------------------------------------- """ @@ -238,7 +238,7 @@ def _(event): @click.group("pymodbus-repl") -@click.version_option(str(version), message=TITLE) +@click.version_option(str(pymodbus_version), message=TITLE) @click.option("--verbose", is_flag=True, default=False, help="Verbose logs") @click.option( "--broadcast-support", diff --git a/pymodbus/repl/server/main.py b/pymodbus/repl/server/main.py index d121086ac..2d983112b 100644 --- a/pymodbus/repl/server/main.py +++ b/pymodbus/repl/server/main.py @@ -1,6 +1,7 @@ """Repl server main.""" import asyncio import json +import logging import sys from enum import Enum from pathlib import Path @@ -106,7 +107,7 @@ def server( ), ): """Run server code.""" - log_level = Log.DEBUG if verbose else Log.ERROR + log_level = logging.DEBUG if verbose else logging.ERROR pymodbus_apply_logging_config(log_level) ctx.obj = { diff --git a/pymodbus/server/reactive/main.py b/pymodbus/server/reactive/main.py index c9b7d53bc..322566f43 100644 --- a/pymodbus/server/reactive/main.py +++ b/pymodbus/server/reactive/main.py @@ -10,7 +10,6 @@ import threading import time from enum import Enum -from typing import Union try: @@ -22,6 +21,7 @@ ) sys.exit(1) +from pymodbus import __version__ as pymodbus_version from pymodbus.datastore import ModbusServerContext, ModbusSlaveContext from pymodbus.datastore.store import ( BaseModbusDataBlock, @@ -47,7 +47,6 @@ ModbusSocketFramer, ModbusTlsFramer, ) -from pymodbus.version import version as pymodbus_version SERVER_MAPPER = { @@ -368,7 +367,7 @@ def create_identity( vendor_url="https://github.com/pymodbus-dev/pymodbus/", product_name="Pymodbus Server", model_name="Reactive Server", - version=pymodbus_version.short(), + version=pymodbus_version, ): """Create modbus identity. @@ -397,7 +396,7 @@ def create_identity( def create_context( cls, data_block_settings: dict = {}, - unit: Union[list[int]] | int = [1], + unit: list[int] | int = [1], single: bool = False, randomize: int = 0, change_rate: int = 0, diff --git a/pymodbus/version.py b/pymodbus/version.py index 46e7da867..0043f01e6 100644 --- a/pymodbus/version.py +++ b/pymodbus/version.py @@ -38,10 +38,3 @@ def __str__(self) -> str: version = Version("pymodbus", 3, 1, "x", "") - - -# --------------------------------------------------------------------------- # -# Exported symbols -# --------------------------------------------------------------------------- # - -__all__ = ["version"] diff --git a/test/test_logging.py b/test/test_logging.py index 105b0eeb7..52984262d 100644 --- a/test/test_logging.py +++ b/test/test_logging.py @@ -1,4 +1,6 @@ """Test datastore.""" +import logging + import pytest from pymodbus import pymodbus_apply_logging_config @@ -10,15 +12,24 @@ class TestLogging: def test_log_our_default(self): """Test default logging""" + logging.getLogger().setLevel(logging.WARNING) + Log.setLevel(logging.NOTSET) + Log.info("test") + assert Log.LOG_LEVEL == logging.WARNING + Log.setLevel(logging.NOTSET) + logging.getLogger().setLevel(logging.INFO) + Log.info("test") + assert Log.LOG_LEVEL == logging.INFO + Log.setLevel(logging.NOTSET) pymodbus_apply_logging_config() - assert Log.LOG_LEVEL == Log.WARNING + assert Log.LOG_LEVEL == logging.DEBUG def test_log_set_level(self): """Test default logging""" - pymodbus_apply_logging_config(Log.DEBUG) - assert Log.LOG_LEVEL == Log.DEBUG - pymodbus_apply_logging_config(Log.INFO) - assert Log.LOG_LEVEL == Log.INFO + pymodbus_apply_logging_config(logging.DEBUG) + assert Log.LOG_LEVEL == logging.DEBUG + pymodbus_apply_logging_config(logging.INFO) + assert Log.LOG_LEVEL == logging.INFO def test_log_simple(self): """Test simple string""" diff --git a/test/test_version.py b/test/test_version.py index cd6ae3f8b..17f329334 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -1,6 +1,8 @@ """Test version.""" import unittest +from pymodbus import __version__ as pymodbus_version +from pymodbus import __version_full__ as pymodbus_version_full from pymodbus.version import Version, version @@ -15,10 +17,11 @@ def tearDown(self): def test_version_class(self): """Test version class.""" - version = Version("test", 1, 2, 3, "sometag") - self.assertEqual(version.short(), "1.2.3.sometag") - self.assertEqual(str(version), "[test, version 1.2.3.sometag]") + test_version = Version("test", 1, 2, 3, "sometag") + self.assertEqual(test_version.short(), "1.2.3.sometag") + self.assertEqual(str(test_version), "[test, version 1.2.3.sometag]") + self.assertEqual(test_version.package, "test") - def test_current_version(self): - """Test current version""" + self.assertEqual(pymodbus_version, version.short()) + self.assertEqual(pymodbus_version_full, str(version)) self.assertEqual(version.package, "pymodbus")