Files
unchecked-io/src/lib.rs

112 lines
3.8 KiB
Rust
Raw Normal View History

2025-11-17 14:23:03 -07:00
// --- Declare our new modules ---
mod config;
mod parser;
// --- External Crates ---
use pyo3::prelude::*;
use pyo3::exceptions::PyValueError;
use tokio;
2025-11-18 11:52:28 -07:00
use pyo3::types::{PyModule, PyAny};
use pyo3::Bound;
use pyo3_arrow::PyRecordBatch;
// Required for global allocator (mimalloc)
2025-11-18 12:40:08 -07:00
#[cfg(not(target_env = "msvc"))]
use mimalloc;
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
2025-11-17 14:23:03 -07:00
// --- Internal Crates ---
use crate::config::{load_and_validate_config, ConnectorConfig};
// NOTE: run_profiler_logic added, requires definition in parser.rs
use crate::parser::{run_db_logic, run_profiler_logic};
2025-11-17 14:23:03 -07:00
// NEW: Import the tracing libraries we need for the initialization
2025-11-18 20:51:03 -07:00
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
// --- THE PYTHON-CALLABLE ENTRY POINT (Load Data) ---
2025-11-18 12:15:01 -07:00
2025-11-17 14:23:03 -07:00
#[pyfunction]
2025-11-18 12:40:08 -07:00
#[pyo3(signature = (config_path, blast_radius=312500))]
2025-11-17 14:23:03 -07:00
#[allow(unsafe_code)]
#[allow(unsafe_op_in_unsafe_fn)]
#[allow(rust_2024_compatibility)]
fn load_data_from_config<'py>(
py: Python<'py>,
config_path: String,
2025-11-18 11:52:28 -07:00
blast_radius: i64,
) -> PyResult<Bound<'py, PyAny>> {
2025-11-17 14:23:03 -07:00
let config: ConnectorConfig = match load_and_validate_config(&config_path) {
Ok(c) => c,
Err(e) => return Err(PyValueError::new_err(format!("Configuration Error: {:?}", e))),
};
println!("--- UncheckedIO: Schema Accepted ---");
println!("Database: {}", config.connection_string);
2025-11-18 12:15:01 -07:00
println!("Columns (in order): {:?}", config.schema.iter().map(|c| &c.column_name).collect::<Vec<_>>());
2025-11-17 14:23:03 -07:00
let record_batch = py.allow_threads(|| {
2025-11-17 14:23:03 -07:00
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
2025-11-18 12:15:01 -07:00
run_db_logic(config, blast_radius).await
2025-11-17 14:23:03 -07:00
})
}).map_err(|e| PyValueError::new_err(format!("Database/Runtime Error: {:?}", e)))?;
let py_record_batch = PyRecordBatch::new(record_batch);
py_record_batch.into_pyarrow(py)
2025-11-17 14:23:03 -07:00
}
// --- NEW PYTHON-CALLABLE FUNCTION FOR PANDAS CONVERSION (FIXED SIGNATURE) ---
#[pyfunction]
#[pyo3(signature = (arrow_table))] // FIX: Removed 'py' from the signature macro
fn to_pandas_dataframe<'py>(py: Python<'py>, arrow_table: Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
// This calls the 'to_pandas' method on the PyArrow object.
arrow_table.call_method0("to_pandas")
}
2025-11-17 14:23:03 -07:00
// --- NEW PYTHON-CALLABLE ENTRY POINT FOR SCHEMA PROFILING ---
#[pyfunction]
#[pyo3(signature = (config_path))]
fn profile_data(config_path: String) -> PyResult<String> {
// Note: The profiler uses a small, current_thread tokio runtime since it's sequential I/O.
let output = std::thread::spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
run_profiler_logic(&config_path).await
})
}).join().unwrap().map_err(|e| PyValueError::new_err(format!("Profiling Error: {:?}", e)))?;
2025-11-18 12:15:01 -07:00
Ok(output)
}
// --- PYTHON MODULE EXPORT ---
2025-11-17 14:23:03 -07:00
#[pymodule]
fn unchecked_io(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
// --- START NEW CODE ---
// This tries to initialize the "Tracy" layer.
// The '.try_init()' ensures that if you import the library twice (e.g. in a notebook reload),
// it doesn't crash by trying to initialize the global logger a second time.
2025-11-18 20:51:03 -07:00
// Fix: Use 'default()' instead of 'new()' to avoid argument mismatch errors
2025-11-18 20:51:03 -07:00
let _ = tracing_subscriber::registry()
.with(tracing_tracy::TracyLayer::default())
.try_init();
// --- END NEW CODE ---
2025-11-18 20:51:03 -07:00
2025-11-17 14:23:03 -07:00
m.add_function(wrap_pyfunction!(load_data_from_config, m)?)?;
m.add_function(wrap_pyfunction!(to_pandas_dataframe, m)?)?;
m.add_function(wrap_pyfunction!(profile_data, m)?)?;
2025-11-17 14:23:03 -07:00
Ok(())
}