about summary refs log tree commit diff stats
path: root/crates/yt_dlp/src/logging.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--crates/yt_dlp/src/logging.rs148
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))
 }