From 4fc6ac7e0ae7069cc43b42d9f359c43a05e5c2b7 Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Sat, 8 Jul 2023 00:37:07 +0200 Subject: [PATCH] working logger in python with stub was a mess until it wasnt anymore, yay --- python/libpt/__init__.py | 1 + python/libpt/__init__.pyi | 12 ++++++ python/libpt/libpt.pyi | 12 ++++++ python/libpt/logger.pyi | 65 +++++++++++++++++++++++++++++ python/libpt/{py-typed => py.typed} | 0 scripts/pytests.sh | 1 + src/lib.rs | 7 ++-- src/logger/mod.rs | 15 +++++-- tests/lib.rs | 5 ++- tests/python/lib.py | 15 +++++++ tests/python/test_logger.py | 21 ++++++++++ tests/test_logger_struct.rs | 7 ++++ 12 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 python/libpt/__init__.pyi create mode 100644 python/libpt/logger.pyi rename python/libpt/{py-typed => py.typed} (100%) create mode 100755 scripts/pytests.sh create mode 100644 tests/python/lib.py diff --git a/python/libpt/__init__.py b/python/libpt/__init__.py index e69de29..50c3db3 100644 --- a/python/libpt/__init__.py +++ b/python/libpt/__init__.py @@ -0,0 +1 @@ +from ._libpt import * diff --git a/python/libpt/__init__.pyi b/python/libpt/__init__.pyi new file mode 100644 index 0000000..b10b548 --- /dev/null +++ b/python/libpt/__init__.pyi @@ -0,0 +1,12 @@ +""" +# libpt python bindings + +`libpt` is originally implemented in rust, but offers a python module too. +""" +from . import logger + +def is_loaded() -> bool: + """ + returns true if `libpt` has been loaded + """ + ... diff --git a/python/libpt/libpt.pyi b/python/libpt/libpt.pyi index e69de29..b10b548 100644 --- a/python/libpt/libpt.pyi +++ b/python/libpt/libpt.pyi @@ -0,0 +1,12 @@ +""" +# libpt python bindings + +`libpt` is originally implemented in rust, but offers a python module too. +""" +from . import logger + +def is_loaded() -> bool: + """ + returns true if `libpt` has been loaded + """ + ... diff --git a/python/libpt/logger.pyi b/python/libpt/logger.pyi new file mode 100644 index 0000000..4935bb8 --- /dev/null +++ b/python/libpt/logger.pyi @@ -0,0 +1,65 @@ +""" +# A specialized Logger for `libpt` +""" + +""" the default log level """ +DEFAULT_LOG_LEVEL = "INFO" +""" Set the value of this key as envar to set a loglevel """ +LOGGER_ENV_KEY = "LIBPT_LOGLEVEL" + +class Logger: + """ + `libpt` logger + + Call `init` once before usage, else all logging attempts will be ignored. + """ + def __init__(self): + """ + get a new logger + """ + ... + + @staticmethod + def init(): + """ + initialize the logger before the first usage + """ + ... + + @staticmethod + def init_specialized(color: bool): + """ + initialize the logger before the first usage, but with the ability to tweak things a bit + """ + ... + + def error(self, printable: str): + """ + log at level `error` + """ + ... + + def warn(self, printable: str): + """ + log at level `error` + """ + ... + + def info(self, printable: str): + """ + log at level `error` + """ + ... + + def debug(self, printable: str): + """ + log at level `error` + """ + ... + + def trace(self, printable: str): + """ + log at level `error` + """ + ... + diff --git a/python/libpt/py-typed b/python/libpt/py.typed similarity index 100% rename from python/libpt/py-typed rename to python/libpt/py.typed diff --git a/scripts/pytests.sh b/scripts/pytests.sh new file mode 100755 index 0000000..1971c0c --- /dev/null +++ b/scripts/pytests.sh @@ -0,0 +1 @@ +python -m unittest discover -fs tests/python diff --git a/src/lib.rs b/src/lib.rs index 190a9ad..0a418b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ use pyo3::prelude::*; /// /// Always returns `true` if you can execute it. #[pyfunction] -pub fn libpt_loaded() -> bool { +pub fn is_loaded() -> bool { true } @@ -50,12 +50,13 @@ fn py_logger(py: Python, m: &PyModule) -> PyResult<()> { Ok(()) } +//////////////////////////////////////////////////////////////////////////////////////////////////// /// ## Python module: root /// /// This function is the entry point of [`PyO3`](pyo3). This is where the main module is built. #[pymodule] -fn libpt(py: Python, m: &PyModule) -> PyResult<()> { - m.add_function(wrap_pyfunction!(libpt_loaded, m)?)?; +fn _libpt(py: Python, m: &PyModule) -> PyResult<()> { + m.add_function(wrap_pyfunction!(is_loaded, m)?)?; // load logger module py_logger(py, m)?; diff --git a/src/logger/mod.rs b/src/logger/mod.rs index 4c4123d..54f0858 100644 --- a/src/logger/mod.rs +++ b/src/logger/mod.rs @@ -15,12 +15,10 @@ //// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// use std::{ fmt, - io::Write, sync::atomic::{AtomicBool, Ordering}, }; use env_logger::{ - fmt::{Formatter, Style}, Env, Target, WriteStyle, }; use log::{debug, error, info, trace, warn, Level}; @@ -29,7 +27,7 @@ use pyo3::prelude::*; //// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// /// The log level used when none is specified pub const DEFAULT_LOG_LEVEL: Level = Level::Info; -/// Register your level to this ENVAR to override the used level +/// Register your level to this environment variable to override the used level pub const LOGGER_ENV_KEY: &'static str = "LIBPT_LOGLEVEL"; //// STATICS /////////////////////////////////////////////////////////////////////////////////////// @@ -43,7 +41,8 @@ static INITIALIZED: AtomicBool = AtomicBool::new(false); /// /// ### Setting a [`Level`](log::Level) /// -/// To set a [`Level`](log::Level), you need to set the ENVAR `LIBPT_LOGLEVEL` to either of +/// To set a [`Level`](log::Level), you need to set the environment variable `LIBPT_LOGLEVEL` +/// to either of: /// /// - `Trace` /// - `Debug` @@ -54,6 +53,7 @@ static INITIALIZED: AtomicBool = AtomicBool::new(false); pub struct Logger {} //// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// +/// ## Main implementation impl Logger { /// ## create a `Logger` /// @@ -138,6 +138,7 @@ impl Logger { } //////////////////////////////////////////////////////////////////////////////////////////////////// +/// ## Implementation of the python interface #[pymethods] impl Logger { /// ## Python version of [`new()`](Logger::new) @@ -151,6 +152,12 @@ impl Logger { pub fn py_init() { Self::init() } + /// ## Python version of [`init_specialized()`](Logger::init_specialized) + #[pyo3(name = "init_specialized")] + #[staticmethod] + pub fn py_init_specialized(color: bool) { + Self::init_specialized(false, color, Target::Stdout) + } /// ## Python version of [`error()`](Logger::error) #[pyo3(name = "error")] pub fn py_error(&self, printable: String) { diff --git a/tests/lib.rs b/tests/lib.rs index 91cf5df..fe386dc 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -5,7 +5,8 @@ // IMPORTS ///////////////////////////////////////////////////////////////////////////////////////// use libpt; +/// ## check if libpt is loaded #[test] -fn loaded_libpt() { - assert!(libpt::libpt_loaded()) +fn test_libpt_is_loaded() { + assert!(libpt::is_loaded()) } diff --git a/tests/python/lib.py b/tests/python/lib.py new file mode 100644 index 0000000..2ed4dc7 --- /dev/null +++ b/tests/python/lib.py @@ -0,0 +1,15 @@ +""" +tests for the general behaviour of the libraries availability +""" +import unittest +import libpt + +class TestLibptGeneral(unittest.TestCase): + + def test_loaded(self): + assert libpt.is_loaded() + +if __name__ == '__main__': + unittest.main() + + diff --git a/tests/python/test_logger.py b/tests/python/test_logger.py index e69de29..c13c7c7 100644 --- a/tests/python/test_logger.py +++ b/tests/python/test_logger.py @@ -0,0 +1,21 @@ +""" +test the logger +""" +import unittest +from libpt import logger + +class TestLogger(unittest.TestCase): + + def test_basic_logging(self): + logger.Logger.init() + l = logger.Logger() + l.trace("MSG") + l.debug("MSG") + l.info("MSG") + l.warn("MSG") + l.error("MSG") + +if __name__ == '__main__': + unittest.main() + + diff --git a/tests/test_logger_struct.rs b/tests/test_logger_struct.rs index a63b500..fa676b3 100644 --- a/tests/test_logger_struct.rs +++ b/tests/test_logger_struct.rs @@ -30,6 +30,7 @@ mod test_logger_struct { use regex::Regex; + /// ## setup that's needed before testing the logger struct fn setup() { // we don't want to log messages during our tests! Logger::init_specialized(false, false, env_logger::Target::Stdout); @@ -45,6 +46,9 @@ mod test_logger_struct { /// - [`Logger::info`] /// - [`Logger::warn`] /// - [`Logger::error`] + /// + /// After those methods have Successfully been executed, their outputs gets stored in a single + /// [`String`] and a [`Regex`] checks if we have five correctly formatted messages. #[test] fn test_log_basic() { std::env::set_var(LOGGER_ENV_KEY, "Trace"); @@ -62,12 +66,15 @@ mod test_logger_struct { print!("{}", combined); // too long, so i split into two lines. + // this matches the format of the env_logger perfectly, but make sure that color is off, + // else the ANSI escape sequences break this test let regex = Regex::new(concat!( r"(?m)\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z ", r"(TRACE|DEBUG|INFO|WARN|ERROR) +libpt::logger\] MSG" )) .unwrap(); + // we have 5 log levels, therefore we should have 5 captures assert_eq!(regex.captures_iter(&combined).count(), 5); }