diff options
Diffstat (limited to '')
-rw-r--r-- | crates/yt_dlp/src/logging.rs | 148 |
1 files changed, 106 insertions, 42 deletions
diff --git a/crates/yt_dlp/src/logging.rs b/crates/yt_dlp/src/logging.rs index e731502..5cb4c1d 100644 --- a/crates/yt_dlp/src/logging.rs +++ b/crates/yt_dlp/src/logging.rs @@ -10,34 +10,66 @@ // This file is taken from: https://github.com/dylanbstorey/pyo3-pylogger/blob/d89e0d6820ebc4f067647e3b74af59dbc4941dd5/src/lib.rs // It is licensed under the Apache 2.0 License, copyright up to 2024 by Dylan Storey -// It was modified by Benedikt Peetz 2024 - -// The pyo3 `pyfunction` proc-macros call unsafe functions internally, which trigger this lint. -#![allow(unsafe_op_in_unsafe_fn)] - -use std::ffi::CString; +// It was modified by Benedikt Peetz 2024, 2025 use log::{Level, MetadataBuilder, Record, logger}; -use pyo3::{ - Bound, PyAny, PyResult, Python, - prelude::{PyAnyMethods, PyListMethods, PyModuleMethods}, - pyfunction, wrap_pyfunction, +use rustpython::vm::{ + PyObjectRef, PyRef, PyResult, VirtualMachine, + builtins::{PyInt, PyList, PyStr}, + convert::ToPyObject, + function::FuncArgs, }; /// Consume a Python `logging.LogRecord` and emit a Rust `Log` instead. -#[allow(clippy::needless_pass_by_value)] -#[pyfunction] -fn host_log(record: Bound<'_, PyAny>, rust_target: &str) -> PyResult<()> { - let level = record.getattr("levelno")?; - let message = record.getattr("getMessage")?.call0()?.to_string(); - let pathname = record.getattr("pathname")?.to_string(); - let lineno = record - .getattr("lineno")? - .to_string() - .parse::<u32>() - .expect("This should always be a u32"); - - let logger_name = record.getattr("name")?.to_string(); +fn host_log(mut input: FuncArgs, vm: &VirtualMachine) -> PyResult<()> { + let record = input.args.remove(0); + let rust_target = { + let base: PyRef<PyStr> = input.args.remove(0).downcast().expect("Should be a string"); + base.as_str().to_owned() + }; + + let level = { + let level: PyRef<PyInt> = record + .get_attr("levelno", vm)? + .downcast() + .expect("Should always be an int"); + level.as_u32_mask() + }; + let message = { + let get_message = record.get_attr("getMessage", vm)?; + let message: PyRef<PyStr> = get_message + .call((), vm)? + .downcast() + .expect("Downcasting works"); + + message.as_str().to_owned() + }; + + let pathname = { + let pathname: PyRef<PyStr> = record + .get_attr("pathname", vm)? + .downcast() + .expect("Is a string"); + + pathname.as_str().to_owned() + }; + + let lineno = { + let lineno: PyRef<PyInt> = record + .get_attr("lineno", vm)? + .downcast() + .expect("Is a number"); + + lineno.as_u32_mask() + }; + + let logger_name = { + let name: PyRef<PyStr> = record + .get_attr("name", vm)? + .downcast() + .expect("Should be a string"); + name.as_str().to_owned() + }; let full_target: Option<String> = if logger_name.trim().is_empty() || logger_name == "root" { None @@ -48,25 +80,25 @@ fn host_log(record: Bound<'_, PyAny>, rust_target: &str) -> PyResult<()> { Some(format!("{rust_target}::{logger_name}")) }; - let target = full_target.as_deref().unwrap_or(rust_target); + let target = full_target.as_deref().unwrap_or(&rust_target); // error - let error_metadata = if level.ge(40u8)? { + let error_metadata = if level >= 40 { MetadataBuilder::new() .target(target) .level(Level::Error) .build() - } else if level.ge(30u8)? { + } else if level >= 30 { MetadataBuilder::new() .target(target) .level(Level::Warn) .build() - } else if level.ge(20u8)? { + } else if level >= 20 { MetadataBuilder::new() .target(target) .level(Level::Info) .build() - } else if level.ge(10u8)? { + } else if level >= 10 { MetadataBuilder::new() .target(target) .level(Level::Debug) @@ -98,13 +130,24 @@ fn host_log(record: Bound<'_, PyAny>, rust_target: &str) -> PyResult<()> { /// # Panics /// Only if internal assertions fail. #[allow(clippy::module_name_repetitions)] -pub fn setup_logging(py: Python<'_>, target: &str) -> PyResult<()> { - let logging = py.import("logging")?; +pub(super) fn setup_logging(vm: &VirtualMachine, target: &str) -> PyResult<PyObjectRef> { + let logging = vm.import("logging", 0)?; - logging.setattr("host_log", wrap_pyfunction!(host_log, &logging)?)?; + let scope = vm.new_scope_with_builtins(); - py.run( - CString::new(format!( + for (key, value) in logging.dict().expect("Should be a dict") { + let key: PyRef<PyStr> = key.downcast().expect("Is a string"); + + scope.globals.set_item(key.as_str(), value, vm)?; + } + scope + .globals + .set_item("host_log", vm.new_function("host_log", host_log).into(), vm)?; + + let local_scope = scope.clone(); + vm.run_code_string( + local_scope, + format!( r#" class HostHandler(Handler): def __init__(self, level=0): @@ -119,15 +162,36 @@ def basicConfig(*pargs, **kwargs): kwargs["handlers"] = [HostHandler()] return oldBasicConfig(*pargs, **kwargs) "# - )) - .expect("This is hardcoded") - .as_c_str(), - Some(&logging.dict()), - None, + ) + .as_str(), + "<embedded logging inintializing code>".to_owned(), )?; - let all = logging.index()?; - all.append("HostHandler")?; - - Ok(()) + let all: PyRef<PyList> = logging + .get_attr("__all__", vm)? + .downcast() + .expect("Is a list"); + all.borrow_vec_mut().push(vm.new_pyobj("HostHandler")); + + // { + // let logging_dict = logging.dict().expect("Exists"); + // + // for (key, val) in scope.globals { + // let key: PyRef<PyStr> = key.downcast().expect("Is a string"); + // + // if !logging_dict.contains_key(key.as_str(), vm) { + // logging_dict.set_item(key.as_str(), val, vm)?; + // } + // } + // + // for (key, val) in scope.locals { + // let key: PyRef<PyStr> = key.downcast().expect("Is a string"); + // + // if !logging_dict.contains_key(key.as_str(), vm) { + // logging_dict.set_item(key.as_str(), val, vm)?; + // } + // } + // } + + Ok(scope.globals.to_pyobject(vm)) } |