diff --git a/Cargo.toml b/Cargo.toml index b3126c1c..a9e3948b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,9 @@ walkdir = "2.5" rayon = "1.6" progress-streams = "1.1" flate2 = { version = "1.0", default-features = false } -pyo3 = { version = "0.25.0", features = ["extension-module", "macros"] } +pyo3 = { version = "0.25.0", features = ["extension-module", "macros", "abi3", "abi3-py37", "anyhow"] } +pyo3-build-config = "0.25.0" +pyo3-log = "0.12.4" # mtzip = "=4.0.2" # ripunzip = "=2.0.1" # zerofrom = "=0.1.5" diff --git a/src/lib-python/Cargo.toml b/src/lib-python/Cargo.toml index 1be98eb4..3223d8e8 100644 --- a/src/lib-python/Cargo.toml +++ b/src/lib-python/Cargo.toml @@ -15,5 +15,10 @@ name = "velopack_python" crate-type = ["cdylib"] [dependencies] +anyhow.workspace = true velopack.workspace = true pyo3.workspace = true +pyo3-log.workspace = true + +[build-dependencies] +pyo3-build-config.workspace = true diff --git a/src/lib-python/build.rs b/src/lib-python/build.rs index 3de0c482..ed23f184 100644 --- a/src/lib-python/build.rs +++ b/src/lib-python/build.rs @@ -3,6 +3,8 @@ use std::fs; use std::path::Path; fn main() { + pyo3_build_config::add_extension_module_link_args(); + // Get the workspace version let version = get_workspace_version().unwrap_or_else(|| env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "0.1.0".to_string())); diff --git a/src/lib-python/src/app.rs b/src/lib-python/src/app.rs index 127dab76..21d77f33 100644 --- a/src/lib-python/src/app.rs +++ b/src/lib-python/src/app.rs @@ -1,5 +1,5 @@ use pyo3::prelude::*; -use pyo3::types::PyFunction; +use pyo3::types::PyCFunction; use velopack::VelopackApp as VelopackAppRust; @@ -7,12 +7,12 @@ use velopack::VelopackApp as VelopackAppRust; #[pyclass(name = "App")] pub struct VelopackAppWrapper { // We'll store the callbacks as Python objects - install_hook: Option>, - update_hook: Option>, - obsolete_hook: Option>, - uninstall_hook: Option>, - firstrun_hook: Option>, - restarted_hook: Option>, + install_hook: Option>, + update_hook: Option>, + obsolete_hook: Option>, + uninstall_hook: Option>, + firstrun_hook: Option>, + restarted_hook: Option>, auto_apply: bool, args: Option>, } @@ -47,37 +47,37 @@ impl VelopackAppWrapper { } /// This hook is triggered when the application is started for the first time after installation - pub fn on_first_run(mut slf: PyRefMut, callback: Py) -> PyRefMut { + pub fn on_first_run(mut slf: PyRefMut, callback: Py) -> PyRefMut { slf.firstrun_hook = Some(callback); slf } /// This hook is triggered when the application is restarted by Velopack after installing updates - pub fn on_restarted(mut slf: PyRefMut, callback: Py) -> PyRefMut { + pub fn on_restarted(mut slf: PyRefMut, callback: Py) -> PyRefMut { slf.restarted_hook = Some(callback); slf } /// Fast callback hook for after installation (Windows only) - pub fn on_after_install_fast_callback(mut slf: PyRefMut, callback: Py) -> PyRefMut { + pub fn on_after_install_fast_callback(mut slf: PyRefMut, callback: Py) -> PyRefMut { slf.install_hook = Some(callback); slf } /// Fast callback hook for after update (Windows only) - pub fn on_after_update_fast_callback(mut slf: PyRefMut, callback: Py) -> PyRefMut { + pub fn on_after_update_fast_callback(mut slf: PyRefMut, callback: Py) -> PyRefMut { slf.update_hook = Some(callback); slf } /// Fast callback hook for before update (Windows only) - pub fn on_before_update_fast_callback(mut slf: PyRefMut, callback: Py) -> PyRefMut { + pub fn on_before_update_fast_callback(mut slf: PyRefMut, callback: Py) -> PyRefMut { slf.obsolete_hook = Some(callback); slf } /// Fast callback hook for before uninstall (Windows only) - pub fn on_before_uninstall_fast_callback(mut slf: PyRefMut, callback: Py) -> PyRefMut { + pub fn on_before_uninstall_fast_callback(mut slf: PyRefMut, callback: Py) -> PyRefMut { slf.uninstall_hook = Some(callback); slf } diff --git a/src/lib-python/src/exceptions.rs b/src/lib-python/src/exceptions.rs deleted file mode 100644 index 6779923e..00000000 --- a/src/lib-python/src/exceptions.rs +++ /dev/null @@ -1,20 +0,0 @@ -use pyo3::exceptions::PyException; -use pyo3::prelude::*; - -#[pyclass(name="VelopackError", extends=PyException, module="velopack.exceptions")] -#[derive(Debug)] -pub struct VelopackError { - pub message: String, -} - -#[pymethods] -impl VelopackError { - #[new] - fn new(message: String) -> Self { - VelopackError { message } - } - - fn __str__(&self) -> String { - self.message.clone() - } -} diff --git a/src/lib-python/src/lib.rs b/src/lib-python/src/lib.rs index d3131830..f9c09264 100644 --- a/src/lib-python/src/lib.rs +++ b/src/lib-python/src/lib.rs @@ -4,9 +4,6 @@ use pyo3::types::PyModule; mod types; use types::*; -mod exceptions; -use exceptions::VelopackError; - mod app; use app::VelopackAppWrapper; @@ -15,8 +12,8 @@ use manager::UpdateManagerWrapper; #[pymodule] fn velopack(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_class::()?; - + pyo3_log::init(); + // auto-generated DTO's m.add_class::()?; m.add_class::()?; diff --git a/src/lib-python/src/manager.rs b/src/lib-python/src/manager.rs index 07ec1e0a..cd3acab6 100644 --- a/src/lib-python/src/manager.rs +++ b/src/lib-python/src/manager.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use pyo3::prelude::*; use std::sync::mpsc; use std::thread; @@ -5,7 +6,6 @@ use std::thread; use velopack::sources::AutoSource; use velopack::{UpdateCheck, UpdateInfo, UpdateManager as VelopackUpdateManagerRust}; -use crate::exceptions::VelopackError; use crate::types::*; #[pyclass(name = "UpdateManager")] @@ -15,23 +15,17 @@ pub struct UpdateManagerWrapper { #[pymethods] impl UpdateManagerWrapper { - // for new, just take in a string, which is the source #[new] #[pyo3(signature = (source, options = None, locator = None))] - pub fn new(source: String, options: Option, locator: Option) -> PyResult { + pub fn new(source: String, options: Option, locator: Option) -> Result { let source = AutoSource::new(&source); // set myinner to a new VelopackUpdateManager with the source - let inner = VelopackUpdateManagerRust::new(source, options.map(Into::into), locator.map(Into::into)) - .map_err(|e| PyErr::new::(format!("Failed to create UpdateManager: {}", e)))?; + let inner = VelopackUpdateManagerRust::new(source, options.map(Into::into), locator.map(Into::into))?; Ok(UpdateManagerWrapper { inner }) } - // check_for_updates return update info indicating if updates are available - /// This method checks for updates and returns update info if updates are available, None otherwise. - pub fn check_for_updates(&mut self) -> PyResult> { - let update_check = - self.inner.check_for_updates().map_err(|e| PyErr::new::(format!("Failed to check for updates: {}", e)))?; - + pub fn check_for_updates(&mut self) -> Result> { + let update_check = self.inner.check_for_updates()?; match update_check { UpdateCheck::UpdateAvailable(updates) => { let py_updates = PyUpdateInfo::from(updates); @@ -43,7 +37,7 @@ impl UpdateManagerWrapper { } #[pyo3(signature = (update_info, progress_callback = None))] - pub fn download_updates(&mut self, update_info: &PyUpdateInfo, progress_callback: Option) -> PyResult<()> { + pub fn download_updates(&mut self, update_info: &PyUpdateInfo, progress_callback: Option) -> Result<()> { // Convert PyUpdateInfo back to rust UpdateInfo let rust_update_info: UpdateInfo = update_info.clone().into(); @@ -65,31 +59,22 @@ impl UpdateManagerWrapper { }); // Call download with the sender - let result = self - .inner - .download_updates(&rust_update_info, Some(sender)) - .map_err(|e| PyErr::new::(format!("Failed to download updates: {}", e))); + let result = self.inner.download_updates(&rust_update_info, Some(sender))?; // Wait for the progress thread to finish let _ = progress_thread.join(); - - result.map(|_| ()) + Ok(result) } else { // No progress callback provided - self.inner - .download_updates(&rust_update_info, None) - .map_err(|e| PyErr::new::(format!("Failed to download updates: {}", e))) - .map(|_| ()) + self.inner.download_updates(&rust_update_info, None)?; + Ok(()) } } - pub fn apply_updates_and_restart(&mut self, update_info: &PyUpdateInfo) -> PyResult<()> { + pub fn apply_updates_and_restart(&mut self, update_info: &PyUpdateInfo) -> Result<()> { // Convert PyUpdateInfo back to rust UpdateInfo let rust_update_info: UpdateInfo = update_info.clone().into(); - - self.inner - .apply_updates_and_restart(&rust_update_info) - .map_err(|e| PyErr::new::(format!("Failed to apply updates and restart: {}", e))) - .map(|_| ()) + self.inner.apply_updates_and_restart(&rust_update_info)?; + Ok(()) } }