working logger in python with stub

was a mess until it wasnt anymore, yay
This commit is contained in:
Christoph J. Scherr 2023-07-08 00:37:07 +02:00
parent 7da4e18456
commit 4fc6ac7e0a
Signed by: PlexSheep
GPG Key ID: 25B4ACF7D88186CC
12 changed files with 152 additions and 9 deletions

View File

@ -0,0 +1 @@
from ._libpt import *

12
python/libpt/__init__.pyi Normal file
View File

@ -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
"""
...

View File

@ -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
"""
...

65
python/libpt/logger.pyi Normal file
View File

@ -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`
"""
...

1
scripts/pytests.sh Executable file
View File

@ -0,0 +1 @@
python -m unittest discover -fs tests/python

View File

@ -35,7 +35,7 @@ use pyo3::prelude::*;
/// ///
/// Always returns `true` if you can execute it. /// Always returns `true` if you can execute it.
#[pyfunction] #[pyfunction]
pub fn libpt_loaded() -> bool { pub fn is_loaded() -> bool {
true true
} }
@ -50,12 +50,13 @@ fn py_logger(py: Python, m: &PyModule) -> PyResult<()> {
Ok(()) Ok(())
} }
////////////////////////////////////////////////////////////////////////////////////////////////////
/// ## Python module: root /// ## Python module: root
/// ///
/// This function is the entry point of [`PyO3`](pyo3). This is where the main module is built. /// This function is the entry point of [`PyO3`](pyo3). This is where the main module is built.
#[pymodule] #[pymodule]
fn libpt(py: Python, m: &PyModule) -> PyResult<()> { fn _libpt(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(libpt_loaded, m)?)?; m.add_function(wrap_pyfunction!(is_loaded, m)?)?;
// load logger module // load logger module
py_logger(py, m)?; py_logger(py, m)?;

View File

@ -15,12 +15,10 @@
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use std::{ use std::{
fmt, fmt,
io::Write,
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
}; };
use env_logger::{ use env_logger::{
fmt::{Formatter, Style},
Env, Target, WriteStyle, Env, Target, WriteStyle,
}; };
use log::{debug, error, info, trace, warn, Level}; use log::{debug, error, info, trace, warn, Level};
@ -29,7 +27,7 @@ use pyo3::prelude::*;
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// The log level used when none is specified /// The log level used when none is specified
pub const DEFAULT_LOG_LEVEL: Level = Level::Info; 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"; pub const LOGGER_ENV_KEY: &'static str = "LIBPT_LOGLEVEL";
//// STATICS /////////////////////////////////////////////////////////////////////////////////////// //// STATICS ///////////////////////////////////////////////////////////////////////////////////////
@ -43,7 +41,8 @@ static INITIALIZED: AtomicBool = AtomicBool::new(false);
/// ///
/// ### Setting a [`Level`](log::Level) /// ### 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` /// - `Trace`
/// - `Debug` /// - `Debug`
@ -54,6 +53,7 @@ static INITIALIZED: AtomicBool = AtomicBool::new(false);
pub struct Logger {} pub struct Logger {}
//// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// //// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
/// ## Main implementation
impl Logger { impl Logger {
/// ## create a `Logger` /// ## create a `Logger`
/// ///
@ -138,6 +138,7 @@ impl Logger {
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
/// ## Implementation of the python interface
#[pymethods] #[pymethods]
impl Logger { impl Logger {
/// ## Python version of [`new()`](Logger::new) /// ## Python version of [`new()`](Logger::new)
@ -151,6 +152,12 @@ impl Logger {
pub fn py_init() { pub fn py_init() {
Self::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) /// ## Python version of [`error()`](Logger::error)
#[pyo3(name = "error")] #[pyo3(name = "error")]
pub fn py_error(&self, printable: String) { pub fn py_error(&self, printable: String) {

View File

@ -5,7 +5,8 @@
// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////// // IMPORTS /////////////////////////////////////////////////////////////////////////////////////////
use libpt; use libpt;
/// ## check if libpt is loaded
#[test] #[test]
fn loaded_libpt() { fn test_libpt_is_loaded() {
assert!(libpt::libpt_loaded()) assert!(libpt::is_loaded())
} }

15
tests/python/lib.py Normal file
View File

@ -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()

View File

@ -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()

View File

@ -30,6 +30,7 @@ mod test_logger_struct {
use regex::Regex; use regex::Regex;
/// ## setup that's needed before testing the logger struct
fn setup() { fn setup() {
// we don't want to log messages during our tests! // we don't want to log messages during our tests!
Logger::init_specialized(false, false, env_logger::Target::Stdout); Logger::init_specialized(false, false, env_logger::Target::Stdout);
@ -45,6 +46,9 @@ mod test_logger_struct {
/// - [`Logger::info`] /// - [`Logger::info`]
/// - [`Logger::warn`] /// - [`Logger::warn`]
/// - [`Logger::error`] /// - [`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] #[test]
fn test_log_basic() { fn test_log_basic() {
std::env::set_var(LOGGER_ENV_KEY, "Trace"); std::env::set_var(LOGGER_ENV_KEY, "Trace");
@ -62,12 +66,15 @@ mod test_logger_struct {
print!("{}", combined); print!("{}", combined);
// too long, so i split into two lines. // 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!( let regex = Regex::new(concat!(
r"(?m)\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z ", 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" r"(TRACE|DEBUG|INFO|WARN|ERROR) +libpt::logger\] MSG"
)) ))
.unwrap(); .unwrap();
// we have 5 log levels, therefore we should have 5 captures
assert_eq!(regex.captures_iter(&combined).count(), 5); assert_eq!(regex.captures_iter(&combined).count(), 5);
} }