mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Initial python sdk
This commit is contained in:
201
src/lib-python/.gitignore
vendored
Normal file
201
src/lib-python/.gitignore
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
build/*
|
||||
dist/*
|
||||
Releases/*
|
||||
*.zip
|
||||
*.tar.gz
|
||||
test/app_version.py
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Abstra
|
||||
# Abstra is an AI-powered process automation framework.
|
||||
# Ignore directories containing user credentials, local state, and settings.
|
||||
# Learn more at https://abstra.io/docs
|
||||
.abstra/
|
||||
|
||||
# Visual Studio Code
|
||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||
# you could uncomment the following to ignore the entire vscode folder
|
||||
# .vscode/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Cursor
|
||||
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
||||
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
||||
# refer to https://docs.cursor.com/context/ignore-files
|
||||
.cursorignore
|
||||
.cursorindexingignore
|
||||
24
src/lib-python/Cargo.toml
Normal file
24
src/lib-python/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "velopack-python"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
keywords.workspace = true
|
||||
categories.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "velopack"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
velopack = { path = "../lib-rust" }
|
||||
pyo3 = { version = "0.25.0", features = ["extension-module", "macros"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
semver = { workspace = true }
|
||||
|
||||
181
src/lib-python/build.rs
Normal file
181
src/lib-python/build.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 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())
|
||||
});
|
||||
|
||||
let python_version = convert_to_python_version(&version);
|
||||
|
||||
// Set environment variables for PyO3
|
||||
println!("cargo:rustc-env=PYTHON_VERSION={}", python_version);
|
||||
|
||||
// Try setting the package version for PyO3 to pick up
|
||||
println!("cargo:metadata=version={}", python_version);
|
||||
|
||||
// Also set it as a cfg value
|
||||
println!("cargo:rustc-cfg=version=\"{}\"", python_version);
|
||||
|
||||
println!("cargo:rerun-if-changed=../../Cargo.toml");
|
||||
}
|
||||
|
||||
fn get_workspace_version() -> Option<String> {
|
||||
// Navigate up to workspace root and read Cargo.toml
|
||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").ok()?;
|
||||
let workspace_toml = Path::new(&manifest_dir)
|
||||
.parent()? // src
|
||||
.parent()? // velopack root
|
||||
.join("Cargo.toml");
|
||||
|
||||
if !workspace_toml.exists() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let content = fs::read_to_string(&workspace_toml).ok()?;
|
||||
|
||||
// Simple parsing to extract version from [workspace.package] section
|
||||
let mut in_workspace_package = false;
|
||||
for (_line_num, line) in content.lines().enumerate() {
|
||||
let trimmed = line.trim();
|
||||
|
||||
if trimmed == "[workspace.package]" {
|
||||
in_workspace_package = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if trimmed.starts_with('[') && trimmed != "[workspace.package]" {
|
||||
if in_workspace_package {
|
||||
}
|
||||
in_workspace_package = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if in_workspace_package && trimmed.starts_with("version") {
|
||||
if let Some(equals_pos) = trimmed.find('=') {
|
||||
let version_part = &trimmed[equals_pos + 1..].trim();
|
||||
// Remove quotes
|
||||
let version = version_part.trim_matches('"').trim_matches('\'');
|
||||
return Some(version.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn convert_to_python_version(rust_version: &str) -> String {
|
||||
// Handle git-based versions like "0.0.1213-g57cf68d" - drop git ref, keep base
|
||||
if let Some(git_pos) = rust_version.find("-g") {
|
||||
let base = &rust_version[..git_pos];
|
||||
return ensure_xyz_format(base);
|
||||
}
|
||||
|
||||
// Handle local development versions like "0.0.0-local" - drop local suffix
|
||||
if rust_version.ends_with("-local") {
|
||||
let base = rust_version.trim_end_matches("-local");
|
||||
return ensure_xyz_format(base);
|
||||
}
|
||||
|
||||
// Handle Rust pre-release patterns and convert to Python equivalents
|
||||
if rust_version.contains("-alpha") {
|
||||
let base = rust_version.split("-alpha").next().unwrap();
|
||||
let alpha_num = extract_prerelease_number(rust_version, "-alpha");
|
||||
return format!("{}a{}", ensure_xyz_format(base), alpha_num);
|
||||
}
|
||||
|
||||
if rust_version.contains("-beta") {
|
||||
let base = rust_version.split("-beta").next().unwrap();
|
||||
let beta_num = extract_prerelease_number(rust_version, "-beta");
|
||||
return format!("{}b{}", ensure_xyz_format(base), beta_num);
|
||||
}
|
||||
|
||||
if rust_version.contains("-rc") {
|
||||
let base = rust_version.split("-rc").next().unwrap();
|
||||
let rc_num = extract_prerelease_number(rust_version, "-rc");
|
||||
return format!("{}rc{}", ensure_xyz_format(base), rc_num);
|
||||
}
|
||||
|
||||
// For any other dash-separated version, just take the base
|
||||
if rust_version.contains('-') {
|
||||
let base = rust_version.split('-').next().unwrap();
|
||||
return ensure_xyz_format(base);
|
||||
}
|
||||
|
||||
ensure_xyz_format(rust_version)
|
||||
}
|
||||
|
||||
fn extract_prerelease_number(version: &str, pattern: &str) -> String {
|
||||
if let Some(pos) = version.find(pattern) {
|
||||
let after_pattern = &version[pos + pattern.len()..];
|
||||
if after_pattern.starts_with('.') {
|
||||
after_pattern.trim_start_matches('.').split('-').next().unwrap_or("0").to_string()
|
||||
} else if after_pattern.is_empty() {
|
||||
"0".to_string()
|
||||
} else {
|
||||
after_pattern.split('-').next().unwrap_or("0").to_string()
|
||||
}
|
||||
} else {
|
||||
"0".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_xyz_format(version: &str) -> String {
|
||||
let parts: Vec<&str> = version.split('.').collect();
|
||||
let result = match parts.len() {
|
||||
1 => format!("{}.0.0", parts[0]),
|
||||
2 => format!("{}.{}.0", parts[0], parts[1]),
|
||||
_ => format!("{}.{}.{}", parts[0], parts[1], parts[2]),
|
||||
};
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_version_conversion() {
|
||||
// Git versions - drop git ref
|
||||
assert_eq!(convert_to_python_version("0.0.1213-g57cf68d"), "0.0.1213");
|
||||
assert_eq!(convert_to_python_version("1.2-g57cf68d"), "1.2.0");
|
||||
|
||||
// Local versions - drop local suffix
|
||||
assert_eq!(convert_to_python_version("0.0.0-local"), "0.0.0");
|
||||
assert_eq!(convert_to_python_version("1.2.3-local"), "1.2.3");
|
||||
|
||||
// Pre-release versions - convert to Python format
|
||||
assert_eq!(convert_to_python_version("1.0.0-alpha.1"), "1.0.0a1");
|
||||
assert_eq!(convert_to_python_version("1.0.0-alpha"), "1.0.0a0");
|
||||
assert_eq!(convert_to_python_version("1.0.0-beta.2"), "1.0.0b2");
|
||||
assert_eq!(convert_to_python_version("1.0.0-rc.1"), "1.0.0rc1");
|
||||
|
||||
// Standard versions - ensure x.y.z format
|
||||
assert_eq!(convert_to_python_version("1.0.0"), "1.0.0");
|
||||
assert_eq!(convert_to_python_version("1.2"), "1.2.0");
|
||||
assert_eq!(convert_to_python_version("1"), "1.0.0");
|
||||
|
||||
// Other dash-separated versions - take base only
|
||||
assert_eq!(convert_to_python_version("1.0.0-something-else"), "1.0.0");
|
||||
}
|
||||
}
|
||||
17
src/lib-python/pyproject.toml
Normal file
17
src/lib-python/pyproject.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[project]
|
||||
dynamic = ["version", "description", "authors", "license"]
|
||||
name = "velopack"
|
||||
requires-python = ">=3.8"
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=1.0,<2.0"]
|
||||
build-backend = "maturin"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"maturin>=1.8.6",
|
||||
"pyinstaller>=6.13.0",
|
||||
]
|
||||
test = [
|
||||
"pyinstaller>=6.13.0",
|
||||
]
|
||||
178
src/lib-python/src/app.rs
Normal file
178
src/lib-python/src/app.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyFunction;
|
||||
|
||||
use velopack::VelopackApp as VelopackAppRust;
|
||||
|
||||
/// Python wrapper for VelopackApp with builder pattern
|
||||
#[pyclass(name = "App")]
|
||||
pub struct VelopackAppWrapper {
|
||||
// We'll store the callbacks as Python objects
|
||||
install_hook: Option<Py<PyFunction>>,
|
||||
update_hook: Option<Py<PyFunction>>,
|
||||
obsolete_hook: Option<Py<PyFunction>>,
|
||||
uninstall_hook: Option<Py<PyFunction>>,
|
||||
firstrun_hook: Option<Py<PyFunction>>,
|
||||
restarted_hook: Option<Py<PyFunction>>,
|
||||
auto_apply: bool,
|
||||
args: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl VelopackAppWrapper {
|
||||
/// Create a new VelopackApp builder
|
||||
#[new]
|
||||
pub fn new() -> Self {
|
||||
VelopackAppWrapper {
|
||||
install_hook: None,
|
||||
update_hook: None,
|
||||
obsolete_hook: None,
|
||||
uninstall_hook: None,
|
||||
firstrun_hook: None,
|
||||
restarted_hook: None,
|
||||
auto_apply: true,
|
||||
args: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Override the command line arguments used by VelopackApp
|
||||
pub fn set_args(mut slf: PyRefMut<Self>, args: Vec<String>) -> PyRefMut<Self> {
|
||||
slf.args = Some(args);
|
||||
slf
|
||||
}
|
||||
|
||||
/// Set whether to automatically apply downloaded updates on startup
|
||||
pub fn set_auto_apply_on_startup(mut slf: PyRefMut<Self>, apply: bool) -> PyRefMut<Self> {
|
||||
slf.auto_apply = apply;
|
||||
slf
|
||||
}
|
||||
|
||||
/// This hook is triggered when the application is started for the first time after installation
|
||||
pub fn on_first_run(mut slf: PyRefMut<Self>, callback: Py<PyFunction>) -> PyRefMut<Self> {
|
||||
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<Self>, callback: Py<PyFunction>) -> PyRefMut<Self> {
|
||||
slf.restarted_hook = Some(callback);
|
||||
slf
|
||||
}
|
||||
|
||||
/// Fast callback hook for after installation (Windows only)
|
||||
pub fn on_after_install_fast_callback(mut slf: PyRefMut<Self>, callback: Py<PyFunction>) -> PyRefMut<Self> {
|
||||
slf.install_hook = Some(callback);
|
||||
slf
|
||||
}
|
||||
|
||||
/// Fast callback hook for after update (Windows only)
|
||||
pub fn on_after_update_fast_callback(mut slf: PyRefMut<Self>, callback: Py<PyFunction>) -> PyRefMut<Self> {
|
||||
slf.update_hook = Some(callback);
|
||||
slf
|
||||
}
|
||||
|
||||
/// Fast callback hook for before update (Windows only)
|
||||
pub fn on_before_update_fast_callback(mut slf: PyRefMut<Self>, callback: Py<PyFunction>) -> PyRefMut<Self> {
|
||||
slf.obsolete_hook = Some(callback);
|
||||
slf
|
||||
}
|
||||
|
||||
/// Fast callback hook for before uninstall (Windows only)
|
||||
pub fn on_before_uninstall_fast_callback(mut slf: PyRefMut<Self>, callback: Py<PyFunction>) -> PyRefMut<Self> {
|
||||
slf.uninstall_hook = Some(callback);
|
||||
slf
|
||||
}
|
||||
|
||||
/// Runs the Velopack startup logic
|
||||
pub fn run(&mut self, _py: Python) -> PyResult<()> {
|
||||
// Create the Rust VelopackApp with our stored configuration
|
||||
let mut app = VelopackAppRust::build()
|
||||
.set_auto_apply_on_startup(self.auto_apply);
|
||||
|
||||
// Set args if provided
|
||||
if let Some(ref args) = self.args {
|
||||
app = app.set_args(args.clone());
|
||||
}
|
||||
|
||||
// Set up hooks - we need to convert Python callbacks to Rust closures
|
||||
if let Some(ref hook) = self.firstrun_hook {
|
||||
let hook_clone = hook;
|
||||
app = app.on_first_run(move |version| {
|
||||
Python::with_gil(|py| {
|
||||
let version_str = version.to_string();
|
||||
if let Err(e) = hook_clone.call1(py, (version_str,)) {
|
||||
eprintln!("Error calling first_run hook: {:?}", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(ref hook) = self.restarted_hook {
|
||||
let hook_clone = hook;
|
||||
app = app.on_restarted(move |version| {
|
||||
Python::with_gil(|py| {
|
||||
let version_str = version.to_string();
|
||||
if let Err(e) = hook_clone.call1(py, (version_str,)) {
|
||||
eprintln!("Error calling restarted hook: {:?}", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if let Some(ref hook) = self.install_hook {
|
||||
let hook_clone = hook;
|
||||
app = app.on_after_install_fast_callback(move |version| {
|
||||
Python::with_gil(|py| {
|
||||
let version_str = version.to_string();
|
||||
if let Err(e) = hook_clone.call1(py, (version_str,)) {
|
||||
eprintln!("Error calling install hook: {:?}", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(ref hook) = self.update_hook {
|
||||
let hook_clone = hook;
|
||||
app = app.on_after_update_fast_callback(move |version| {
|
||||
Python::with_gil(|py| {
|
||||
let version_str = version.to_string();
|
||||
if let Err(e) = hook_clone.call1(py, (version_str,)) {
|
||||
eprintln!("Error calling update hook: {:?}", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(ref hook) = self.obsolete_hook {
|
||||
let hook_clone = hook;
|
||||
app = app.on_before_update_fast_callback(move |version| {
|
||||
Python::with_gil(|py| {
|
||||
let version_str = version.to_string();
|
||||
if let Err(e) = hook_clone.call1(py, (version_str,)) {
|
||||
eprintln!("Error calling obsolete hook: {:?}", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(ref hook) = self.uninstall_hook {
|
||||
let hook_clone = hook;
|
||||
app = app.on_before_uninstall_fast_callback(move |version| {
|
||||
Python::with_gil(|py| {
|
||||
let version_str = version.to_string();
|
||||
if let Err(e) = hook_clone.call1(py, (version_str,)) {
|
||||
eprintln!("Error calling uninstall hook: {:?}", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// do not Release the GIL before calling the potentially blocking run method
|
||||
app.run();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
20
src/lib-python/src/exceptions.rs
Normal file
20
src/lib-python/src/exceptions.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::exceptions::PyException;
|
||||
|
||||
#[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()
|
||||
}
|
||||
}
|
||||
31
src/lib-python/src/lib.rs
Normal file
31
src/lib-python/src/lib.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyModule;
|
||||
|
||||
mod exceptions;
|
||||
pub use exceptions::VelopackError;
|
||||
|
||||
mod app;
|
||||
pub use app::VelopackAppWrapper;
|
||||
|
||||
mod manager;
|
||||
pub use manager::UpdateManagerWrapper;
|
||||
|
||||
|
||||
#[pymodule]
|
||||
fn velopack(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<VelopackError>()?;
|
||||
|
||||
|
||||
m.add_class::<VelopackAppWrapper>()?;
|
||||
m.add_class::<UpdateManagerWrapper>()?;
|
||||
|
||||
// add __version__ attribute
|
||||
m.add("__version__", env!("CARGO_PKG_VERSION"))?;
|
||||
|
||||
|
||||
|
||||
// add __author__ attribute
|
||||
m.add("__author__", env!("CARGO_PKG_AUTHORS"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
93
src/lib-python/src/manager.rs
Normal file
93
src/lib-python/src/manager.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
|
||||
use velopack::{UpdateCheck, UpdateInfo, UpdateManager as VelopackUpdateManagerRust};
|
||||
use velopack::sources::AutoSource;
|
||||
|
||||
use crate::exceptions::VelopackError;
|
||||
|
||||
#[pyclass(name = "UpdateManager")]
|
||||
pub struct UpdateManagerWrapper {
|
||||
inner: VelopackUpdateManagerRust,
|
||||
updates: UpdateInfo,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl UpdateManagerWrapper {
|
||||
// for new, just take in a string, which is the source
|
||||
#[new]
|
||||
pub fn new(source: String) -> PyResult<Self> {
|
||||
let source = AutoSource::new(&source);
|
||||
// set myinner to a new VelopackUpdateManager with the source
|
||||
let inner = VelopackUpdateManagerRust::new(source, None, None)
|
||||
.map_err(|e| PyErr::new::<VelopackError, _>(format!("Failed to create UpdateManager: {}", e)))?;
|
||||
Ok(UpdateManagerWrapper {
|
||||
inner,
|
||||
updates: UpdateInfo::default(),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// check_for_updates return a bool indicating if updates are available
|
||||
/// This method checks for updates and returns true if updates are available, false otherwise.
|
||||
pub fn check_for_updates(&mut self) -> PyResult<bool> {
|
||||
match self.inner.check_for_updates() {
|
||||
Ok(UpdateCheck::UpdateAvailable(updates)) => {
|
||||
self.updates = updates;
|
||||
Ok(true)
|
||||
}
|
||||
Ok(_) => {
|
||||
self.updates = UpdateInfo::default();
|
||||
Ok(false)
|
||||
}
|
||||
Err(e) => Err(PyErr::new::<VelopackError, _>(format!("Failed to check for updates: {}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyo3(signature = (progress_callback = None))]
|
||||
pub fn download_updates(&mut self, progress_callback: Option<PyObject>) -> PyResult<()> {
|
||||
if let Some(callback) = progress_callback {
|
||||
// Create a channel for progress updates
|
||||
let (sender, receiver) = mpsc::channel::<i16>();
|
||||
|
||||
// Spawn a thread to handle progress updates
|
||||
let progress_thread = thread::spawn(move || {
|
||||
Python::with_gil(|py| {
|
||||
while let Ok(progress) = receiver.recv() {
|
||||
if let Err(e) = callback.call1(py, (progress,)) {
|
||||
// Log error but continue - don't break the download
|
||||
eprintln!("Progress callback error: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Call download with the sender
|
||||
let result = self.inner.download_updates(&self.updates, Some(sender))
|
||||
.map_err(|e| PyErr::new::<VelopackError, _>(format!("Failed to download updates: {}", e)));
|
||||
|
||||
// Wait for the progress thread to finish
|
||||
let _ = progress_thread.join();
|
||||
|
||||
result.map(|_| ())
|
||||
} else {
|
||||
// No progress callback provided
|
||||
self.inner.download_updates(&self.updates, None)
|
||||
.map_err(|e| PyErr::new::<VelopackError, _>(format!("Failed to download updates: {}", e)))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_updates_and_restart(&mut self) -> PyResult<()> {
|
||||
self.inner.apply_updates_and_restart(&self.updates)
|
||||
.map_err(|e| PyErr::new::<VelopackError, _>(format!("Failed to apply updates and restart: {}", e)))
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
15
src/lib-python/test/app.py
Normal file
15
src/lib-python/test/app.py
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
import velopack
|
||||
|
||||
import app_version
|
||||
|
||||
version = app_version.version
|
||||
|
||||
if __name__ == "__main__":
|
||||
velopack.App().run()
|
||||
um = velopack.UpdateManager("http://localhost:8080")
|
||||
if um.check_for_updates():
|
||||
um.download_updates()
|
||||
um.apply_updates_and_restart()
|
||||
with open("version_result.txt", "w") as f:
|
||||
f.write(f"{version}")
|
||||
108
src/lib-python/test/run_test.py
Normal file
108
src/lib-python/test/run_test.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import atexit
|
||||
import functools
|
||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import threading
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
httpd = None
|
||||
|
||||
def log(msg):
|
||||
print(f"[LOG] {msg}")
|
||||
|
||||
|
||||
# Register a cleanup function to ensure the HTTP server is stopped on exit
|
||||
def cleanup():
|
||||
global httpd
|
||||
if httpd:
|
||||
log("Stopping HTTP server...")
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
log("HTTP server stopped.")
|
||||
shutil.rmtree("output", ignore_errors=True)
|
||||
shutil.rmtree("dist", ignore_errors=True)
|
||||
shutil.rmtree("build", ignore_errors=True)
|
||||
shutil.rmtree("Releases", ignore_errors=True)
|
||||
|
||||
atexit.register(cleanup)
|
||||
|
||||
def _run_cmd(args):
|
||||
log(f"Running command: {' '.join(args)}")
|
||||
result = subprocess.run(args, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"Command failed: {' '.join(args)}\n{result.stdout}\n{result.stderr}")
|
||||
return result.stdout.strip()
|
||||
|
||||
def write_app_version(version):
|
||||
log(f"Writing app version: {version}")
|
||||
with open("app_version.py", "w") as f:
|
||||
f.write(f'version = "{version}"\n')
|
||||
log("App version written successfully")
|
||||
|
||||
def read_app_version(path):
|
||||
log(f"Reading app version from: {path}")
|
||||
with open(path, "r") as f:
|
||||
version = f.read()
|
||||
log(f"App version read successfully: {version}")
|
||||
return version
|
||||
|
||||
def extract_full_path(zip_file, target_dir):
|
||||
log(f"Extracting {zip_file} to {target_dir}")
|
||||
with zipfile.ZipFile(zip_file, 'r') as zip_ref:
|
||||
zip_ref.extractall(target_dir)
|
||||
log("Extraction completed")
|
||||
|
||||
# check if we are running on Windows
|
||||
if platform.system() != "Windows":
|
||||
raise RuntimeError("This script is intended to run on Windows only, for now")
|
||||
|
||||
# check if we are running from the test dir
|
||||
if Path(__file__).parent.name != "test":
|
||||
raise RuntimeError("This script must be run from the 'test' directory")
|
||||
|
||||
# check for vpk cli
|
||||
_run_cmd(["vpk", "-h"])
|
||||
|
||||
write_app_version("1.0.0")
|
||||
|
||||
_run_cmd(["uv", "run", "pyinstaller", "app.spec", "-y"])
|
||||
|
||||
# make app version
|
||||
_run_cmd(["vpk", "pack", "--packId", "test-app", "--packVersion", "1.0.0", "--packDir", "dist/app/", "--mainExe", "app.exe"])
|
||||
|
||||
extract_full_path("Releases/test-app-win-Portable.zip", "output")
|
||||
|
||||
log("Starting HTTP server thread serving ./Releases …")
|
||||
handler = functools.partial(SimpleHTTPRequestHandler, directory=str(Path("Releases").resolve()))
|
||||
httpd = HTTPServer(("localhost", 8080), handler)
|
||||
server_thread = threading.Thread(target=httpd.serve_forever, daemon=True)
|
||||
server_thread.start()
|
||||
log("HTTP server is now running at http://localhost:8000")
|
||||
# Give it a moment to start
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
# check if the app version is correct
|
||||
_run_cmd(["output/test-app.exe"])
|
||||
|
||||
current_version = read_app_version("output/current/version_result.txt")
|
||||
if current_version.strip() != "1.0.0":
|
||||
raise RuntimeError(f"Version mismatch: expected '1.0.0', got '{current_version.strip()}'")
|
||||
|
||||
log("App version is correct: 1.0.0")
|
||||
log("Trying to create update package...")
|
||||
write_app_version("1.0.1")
|
||||
_run_cmd(["uv", "run", "pyinstaller", "app.spec", "-y"])
|
||||
_run_cmd(["vpk", "pack", "--packId", "test-app", "--packVersion", "1.0.1", "--packDir", "dist/app/", "--mainExe", "app.exe"])
|
||||
# check if the app version is correct
|
||||
_run_cmd(["output/test-app.exe"])
|
||||
new_version = read_app_version("output/current/version_result.txt")
|
||||
if new_version.strip() != "1.0.1":
|
||||
raise RuntimeError(f"Version mismatch: expected '1.0.1' after update, got '{new_version.strip()}'")
|
||||
|
||||
log("Update package created successfully and version is now 1.0.1")
|
||||
log("Test completed successfully")
|
||||
264
src/lib-python/uv.lock
generated
Normal file
264
src/lib-python/uv.lock
generated
Normal file
@@ -0,0 +1,264 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
requires-python = ">=3.8"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.9'",
|
||||
"python_full_version < '3.9'",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "altgraph"
|
||||
version = "0.17.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/de/a8/7145824cf0b9e3c28046520480f207df47e927df83aa9555fb47f8505922/altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406", size = 48418, upload-time = "2023-09-25T09:04:52.164Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/3f/3bc3f1d83f6e4a7fcb834d3720544ca597590425be5ba9db032b2bf322a2/altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff", size = 21212, upload-time = "2023-09-25T09:04:50.691Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "8.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version < '3.9'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "zipp", version = "3.20.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304, upload-time = "2024-09-11T14:56:08.937Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514, upload-time = "2024-09-11T14:56:07.019Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "8.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.9'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "zipp", version = "3.22.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macholib"
|
||||
version = "1.16.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "altgraph" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/ee/af1a3842bdd5902ce133bd246eb7ffd4375c38642aeb5dc0ae3a0329dfa2/macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30", size = 59309, upload-time = "2023-09-25T09:10:16.155Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/5d/c059c180c84f7962db0aeae7c3b9303ed1d73d76f2bfbc32bc231c8be314/macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c", size = 38094, upload-time = "2023-09-25T09:10:14.188Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maturin"
|
||||
version = "1.8.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/34/bc/c7df50a359c3a31490785c77d1ddd5fc83cc8cc07a4eddd289dbae53545a/maturin-1.8.6.tar.gz", hash = "sha256:0e0dc2e0bfaa2e1bd238e0236cf8a2b7e2250ccaa29c1aa8d0e61fa664b0289d", size = 203320, upload-time = "2025-05-13T13:56:11.033Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/f1/e493add40aebdab88ac1aefb41b9c84ac288fb00025bc96b9213ee02c958/maturin-1.8.6-py3-none-linux_armv6l.whl", hash = "sha256:1bf4c743dd2b24448e82b8c96251597818956ddf848e1d16b59356512c7e58d8", size = 7831195, upload-time = "2025-05-13T13:55:42.634Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/1c/588afdb7bf79c4f15e33e9af6d7f3b12ec662bc63a22919e3bf39afa2a1e/maturin-1.8.6-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4ea89cf76048bc760e12b36b608fc3f5ef4f7359c0895e9afe737be34041d948", size = 15276471, upload-time = "2025-05-13T13:55:46.196Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/0b/4ce97f7f3a42068fbb42ba47d8b072e098c060fc1f64d8523e5588c57543/maturin-1.8.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4dd2e2f005ca63ac7ef0dddf2d65324ee480277a11544dcc4e7e436af68034dd", size = 7966129, upload-time = "2025-05-13T13:55:48.633Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/bf/4eae9d12920c580baf9d47ee63fec3ae0233e90a3aa8987bd7909cdc36a0/maturin-1.8.6-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:b0637604774e2c50ab48a0e9023fe2f071837ecbc817c04ec28e1cfcc25224c2", size = 7835935, upload-time = "2025-05-13T13:55:51.235Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/aa/8090f8b3f5f7ec46bc95deb0f5b29bf52c98156ef594f2e65d20bf94cea1/maturin-1.8.6-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:bec5948c6475954c8089b17fae349966258756bb2ca05e54099e476a08070795", size = 8282553, upload-time = "2025-05-13T13:55:53.266Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/50/4348da6cc16c006dab4e6cd479cf00dc0afa80db289a115a314df9909ee6/maturin-1.8.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:62a65f70ebaadd6eb6083f5548413744f2ef12400445778e08d41d4facf15bbe", size = 7618339, upload-time = "2025-05-13T13:55:55.706Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/77/16458e29487d068c8cdb7f06a4403393568a10b44993fe2ec9c3b29fdccd/maturin-1.8.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:5c0ff7ad43883920032b63c94c76dcdd5758710d7b72b68db69e7826c40534ac", size = 7686748, upload-time = "2025-05-13T13:55:57.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/c1/a52f3c1171c053810606c7c7fae5ce4637446ef9df44f281862d2bef3750/maturin-1.8.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:ca30fdb158a24cf312f3e53072a6e987182c103fa613efea2d28b5f52707d04a", size = 9767394, upload-time = "2025-05-13T13:56:00.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/ab/abae74f36a0f200384eda985ebeb9ee5dcbb19bfe1558c3335ef6f297094/maturin-1.8.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1e786ec9b5f7315c7e3fcc62b0715f9d99ffe477b06d0d62655a71e6a51a67b", size = 10995574, upload-time = "2025-05-13T13:56:03.101Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/1c/c478578a62c1e34b5b0641a474de78cb56d6c4aad0ba88f90dfa9f2a15f7/maturin-1.8.6-py3-none-win32.whl", hash = "sha256:dade5edfaf508439ff6bbc7be4f207e04c0999c47d9ef7e1bae16258e76b1518", size = 7027171, upload-time = "2025-05-13T13:56:05.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/89/2c57d29f25e06696cb3c5b3770ba0b40dfe87f91a879ecbcdc92e071a26b/maturin-1.8.6-py3-none-win_amd64.whl", hash = "sha256:6bc9281b90cd37e2a7985f2e5d6e3d35a1d64cf6e4d04ce5ed25603d162995b9", size = 7947998, upload-time = "2025-05-13T13:56:07.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/f5/3ee1c6aa4e277323bef38ea0ec07262a9b88711d1a29cb5bb08ce3807a6f/maturin-1.8.6-py3-none-win_arm64.whl", hash = "sha256:24f66624db69b895b134a8f1592efdf04cd223c9b3b65243ad32080477936d14", size = 6684974, upload-time = "2025-05-13T13:56:09.415Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pefile"
|
||||
version = "2023.2.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/78/c5/3b3c62223f72e2360737fd2a57c30e5b2adecd85e70276879609a7403334/pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", size = 74854, upload-time = "2023-02-07T12:23:55.958Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791, upload-time = "2023-02-07T12:28:36.678Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyinstaller"
|
||||
version = "6.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "altgraph" },
|
||||
{ name = "importlib-metadata", version = "8.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
|
||||
{ name = "importlib-metadata", version = "8.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" },
|
||||
{ name = "macholib", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pefile", marker = "sys_platform == 'win32'" },
|
||||
{ name = "pyinstaller-hooks-contrib" },
|
||||
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
|
||||
{ name = "setuptools", version = "75.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
|
||||
{ name = "setuptools", version = "80.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a8/b1/2949fe6d3874e961898ca5cfc1bf2cf13bdeea488b302e74a745bc28c8ba/pyinstaller-6.13.0.tar.gz", hash = "sha256:38911feec2c5e215e5159a7e66fdb12400168bd116143b54a8a7a37f08733456", size = 4276427, upload-time = "2025-04-15T23:25:31.646Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/02/d1a347d35b1b627da1e148159e617576555619ac3bb8bbd5fed661fc7bb5/pyinstaller-6.13.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:aa404f0b02cd57948098055e76ee190b8e65ccf7a2a3f048e5000f668317069f", size = 1001923, upload-time = "2025-04-15T23:24:17.646Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/80/6da39f7aeac65c9ca5afad0fac37887d75fdfd480178a7077c9d30b0704c/pyinstaller-6.13.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:92efcf2f09e78f07b568c5cb7ed48c9940f5dad627af4b49bede6320fab2a06e", size = 718135, upload-time = "2025-04-15T23:24:22.385Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/2c/d21d31f780a489609e7bf6385c0f7635238dc98b37cba8645b53322b7450/pyinstaller-6.13.0-py3-none-manylinux2014_i686.whl", hash = "sha256:9f82f113c463f012faa0e323d952ca30a6f922685d9636e754bd3a256c7ed200", size = 728543, upload-time = "2025-04-15T23:24:27.02Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/20/e6ca87bbed6c0163533195707f820f05e10b8da1223fc6972cfe3c3c50c7/pyinstaller-6.13.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:db0e7945ebe276f604eb7c36e536479556ab32853412095e19172a5ec8fca1c5", size = 726868, upload-time = "2025-04-15T23:24:31.779Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/d5/53b19285f8817ab6c4b07c570208d62606bab0e5a049d50c93710a1d9dc6/pyinstaller-6.13.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:92fe7337c5aa08d42b38d7a79614492cb571489f2cb0a8f91dc9ef9ccbe01ed3", size = 725037, upload-time = "2025-04-15T23:24:36.244Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/5b/08e0b305ba71e6d7cb247e27d714da7536895b0283132d74d249bf662366/pyinstaller-6.13.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:bc09795f5954135dd4486c1535650958c8218acb954f43860e4b05fb515a21c0", size = 721027, upload-time = "2025-04-15T23:24:40.16Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/9c/d8d0a7120103471be8dbe1c5419542aa794b9b9ec2ef628b542f9e6f9ef0/pyinstaller-6.13.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:589937548d34978c568cfdc39f31cf386f45202bc27fdb8facb989c79dfb4c02", size = 723443, upload-time = "2025-04-15T23:24:50.63Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/c7/8a9d81569dda2352068ecc6ee779d5feff6729569dd1b4ffd1236ecd38fe/pyinstaller-6.13.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:b7260832f7501ba1d2ce1834d4cddc0f2b94315282bc89c59333433715015447", size = 719915, upload-time = "2025-04-15T23:24:54.917Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/e6/cccadb02b90198c7ed4ffb8bc34d420efb72b996f47cbd4738067a602d65/pyinstaller-6.13.0-py3-none-win32.whl", hash = "sha256:80c568848529635aa7ca46d8d525f68486d53e03f68b7bb5eba2c88d742e302c", size = 1294997, upload-time = "2025-04-15T23:25:01.391Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/06/15cbe0e25d1e73d5b981fa41ff0bb02b15e924e30b8c61256f4a28c4c837/pyinstaller-6.13.0-py3-none-win_amd64.whl", hash = "sha256:8d4296236b85aae570379488c2da833b28828b17c57c2cc21fccd7e3811fe372", size = 1352714, upload-time = "2025-04-15T23:25:08.061Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/ef/74379298d46e7caa6aa7ceccc865106d3d4b15ac487ffdda2a35bfb6fe79/pyinstaller-6.13.0-py3-none-win_arm64.whl", hash = "sha256:d9f21d56ca2443aa6a1e255e7ad285c76453893a454105abe1b4d45e92bb9a20", size = 1293589, upload-time = "2025-04-15T23:25:14.523Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyinstaller-hooks-contrib"
|
||||
version = "2025.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "importlib-metadata", version = "8.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
|
||||
{ name = "importlib-metadata", version = "8.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" },
|
||||
{ name = "packaging" },
|
||||
{ name = "setuptools", version = "75.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
|
||||
{ name = "setuptools", version = "80.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e3/94/dfc5c7903306211798f990e6794c2eb7b8685ac487b26979e9255790419c/pyinstaller_hooks_contrib-2025.4.tar.gz", hash = "sha256:5ce1afd1997b03e70f546207031cfdf2782030aabacc102190677059e2856446", size = 162628, upload-time = "2025-05-03T20:15:55.983Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/e1/ed48c7074145898e5c5b0072e87be975c5bd6a1d0f08c27a1daa7064fca0/pyinstaller_hooks_contrib-2025.4-py3-none-any.whl", hash = "sha256:6c2d73269b4c484eb40051fc1acee0beb113c2cfb3b37437b8394faae6f0d072", size = 434451, upload-time = "2025-05-03T20:15:54.579Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pywin32-ctypes"
|
||||
version = "0.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "75.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version < '3.9'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5c/01/771ea46cce201dd42cff043a5eea929d1c030fb3d1c2ee2729d02ca7814c/setuptools-75.3.2.tar.gz", hash = "sha256:3c1383e1038b68556a382c1e8ded8887cd20141b0eb5708a6c8d277de49364f5", size = 1354489, upload-time = "2025-03-12T00:02:19.004Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/15/65/3f0dba35760d902849d39d38c0a72767794b1963227b69a587f8a336d08c/setuptools-75.3.2-py3-none-any.whl", hash = "sha256:90ab613b6583fc02d5369cbca13ea26ea0e182d1df2d943ee9cbe81d4c61add9", size = 1251198, upload-time = "2025-03-12T00:02:17.554Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "80.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.9'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.2.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "velopack"
|
||||
source = { editable = "." }
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "maturin" },
|
||||
{ name = "pyinstaller" },
|
||||
]
|
||||
test = [
|
||||
{ name = "pyinstaller" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "maturin", specifier = ">=1.8.6" },
|
||||
{ name = "pyinstaller", specifier = ">=6.13.0" },
|
||||
]
|
||||
test = [{ name = "pyinstaller", specifier = ">=6.13.0" }]
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.20.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version < '3.9'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/54/bf/5c0000c44ebc80123ecbdddba1f5dcd94a5ada602a9c225d84b5aaa55e86/zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29", size = 24199, upload-time = "2024-09-13T13:44:16.101Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/8b/5ba542fa83c90e09eac972fc9baca7a88e7e7ca4b221a89251954019308b/zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", size = 9200, upload-time = "2024-09-13T13:44:14.38Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.22.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.9'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/12/b6/7b3d16792fdf94f146bed92be90b4eb4563569eca91513c8609aebf0c167/zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5", size = 25257, upload-time = "2025-05-26T14:46:32.217Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/da/f64669af4cae46f17b90798a827519ce3737d31dbafad65d391e49643dc4/zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343", size = 9796, upload-time = "2025-05-26T14:46:30.775Z" },
|
||||
]
|
||||
Reference in New Issue
Block a user