Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows usage of UV, CPython and pypy mirrors #147

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ passthrough = [
"PYAPP_DISTRIBUTION_PYTHON_PATH",
"PYAPP_DISTRIBUTION_SITE_PACKAGES_PATH",
"PYAPP_DISTRIBUTION_SOURCE",
"PYAPP_DISTRIBUTION_SOURCE_{}",
"PYAPP_DISTRIBUTION_VARIANT",
"PYAPP_EXEC_CODE",
"PYAPP_EXEC_MODULE",
Expand Down Expand Up @@ -80,6 +81,7 @@ passthrough = [
"PYAPP_SELF_COMMAND",
"PYAPP_SKIP_INSTALL",
"PYAPP_UPGRADE_VIRTUALENV",
"PYAPP_UV_CUSTOM_SOURCE",
"PYAPP_UV_ENABLED",
"PYAPP_UV_ONLY_BOOTSTRAP",
"PYAPP_UV_VERSION",
Expand Down
32 changes: 30 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,14 +310,36 @@ fn get_python_version() -> String {
DEFAULT_PYTHON_VERSION.to_string()
}

fn get_custom_source(name: &str) -> Option<String> {
let name = name.to_uppercase().replace(".", "_");
let variable_name = format!("PYAPP_DISTRIBUTION_SOURCE_{}", name);
if let Ok(value) = env::var(variable_name) {
if !value.is_empty() {
return Some(value);
}
}
None
}

fn get_distribution_source() -> String {
let selected_python_version = get_python_version();

// Return custom source if specified for this version
if let Some(custom_source) = get_custom_source(&selected_python_version) {
dbg!(
"Using custom source for version {}: {}",
&selected_python_version,
&custom_source
);
return custom_source;
}

// Otherwise, check if there is a global custom source
let distribution_source = env::var("PYAPP_DISTRIBUTION_SOURCE").unwrap_or_default();
if !distribution_source.is_empty() {
return distribution_source;
};

let selected_python_version = get_python_version();

// https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
let selected_platform = match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() {
"windows" => "windows",
Expand Down Expand Up @@ -922,6 +944,11 @@ fn set_uv_only_bootstrap() {
}
}

fn set_uv_custom_source() {
let variable = "PYAPP_UV_CUSTOM_SOURCE";
set_runtime_variable(variable, env::var(variable).unwrap_or_default());
}

fn set_uv_version() {
let variable = "PYAPP_UV_VERSION";
let version = env::var(variable).unwrap_or("any".to_string());
Expand Down Expand Up @@ -1070,6 +1097,7 @@ fn main() {
set_pip_allow_config();
set_uv_enabled();
set_uv_only_bootstrap();
set_uv_custom_source();
set_uv_version();
set_allow_updates();
set_indicator();
Expand Down
6 changes: 6 additions & 0 deletions docs/config/distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ You may explicitly set the `PYAPP_DISTRIBUTION_SOURCE` option which overrides th

Setting this manually may require you to define extra metadata about the distribution that is required for correct [runtime behavior](../runtime.md).

### Version

For greater granularity, you may set the `PYAPP_DISTRIBUTION_SOURCE_<NAME>` option. The source is also the URL to the distribution's archive.

The placeholder `<NAME>` is the uppercased version of the distribution name with periods replaced by underscores e.g. `pypy3.10` would become `PYPY3_10`.

### Format

The following formats are supported for the `PYAPP_DISTRIBUTION_FORMAT` option, with the default chosen based on the ending of the source URL:
Expand Down
6 changes: 6 additions & 0 deletions docs/config/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ You may use a specific `X.Y.Z` version by setting the `PYAPP_UV_VERSION` option.

By default, a version of UV that has already been downloaded by a PyApp application is used. If UV has not yet been downloaded then the latest version is used.

### Source

You may explicitly set the `PYAPP_UV_SOURCE` option in order to download your own UV release archive.

The value must end with the archive's real file extension, which is used to determine the extraction method.

### Only bootstrap

You may set the `PYAPP_UV_ONLY_BOOTSTRAP` option to `true` or `1` to only use UV for virtual environment creation and continue using pip for project installation.
Expand Down
4 changes: 4 additions & 0 deletions docs/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ The following is not intended to be a complete enumeration. Be sure to view the
| [Litestar](https://github.com/litestar-org/litestar-fullstack/blob/dc72eee78173790c3e91b0c095ac9e70ba91bedd/scripts/post-builds.py)
| [Preservation Workbench](https://github.com/Preservation-Workbench/PWCode/blob/e7777806be35bd60ca8c33e677ffd77e38b277d0/build/make.sh)
| [tidal-wave](https://github.com/ebb-earl-co/tidal-wave/blob/6358ede21adb715a053b1e6cc73968933c3bed05/BUILDME.md#pyapp-created-binaries)

## Industry

- [Amadeus](https://amadeus.com) <sup>\[[1](https://github.com/ofek/pyapp/pull/147)|[2](https://github.com/AmadeusITGroup/pyapp)\]</sup>
24 changes: 24 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use anyhow::{Context, Result};
use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};
use directories::ProjectDirs;
use once_cell::sync::OnceCell;
use reqwest::Url;

static PLATFORM_DIRS: OnceCell<ProjectDirs> = OnceCell::new();
static INSTALLATION_DIRECTORY: OnceCell<PathBuf> = OnceCell::new();
Expand Down Expand Up @@ -206,6 +207,29 @@ pub fn uv_as_installer() -> bool {
uv_enabled() && !uv_only_bootstrap()
}

pub fn uv_custom_source() -> String {
env!("PYAPP_UV_CUSTOM_SOURCE").into()
}

pub fn uv_custom_source_artifact_name() -> String {
let custom_source = uv_custom_source();

let parsed =
Url::parse(&custom_source).expect(&format!("unable to parse url: {}", &custom_source));

// Try to find artifact name from URL path
if let Some(segments) = parsed.path_segments() {
if let Some(segment) = segments.last() {
return segment.into();
}
}

panic!(
"unable to determine artifact name from url: {}",
&custom_source
);
}

pub fn is_gui() -> bool {
env!("PYAPP_IS_GUI") == "1"
}
Expand Down
12 changes: 9 additions & 3 deletions src/distribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,13 +488,20 @@ fn ensure_uv_available() -> Result<()> {
.with_context(|| format!("unable to create UV cache {}", &managed_uv_cache.display()))?;

let dir = tempdir().with_context(|| "unable to create temporary directory")?;
let artifact_name = app::uv_artifact_name();

let artifact_name: String = if app::uv_custom_source().is_empty() {
app::uv_artifact_name()
} else {
app::uv_custom_source_artifact_name()
};
let temp_path = dir.path().join(&artifact_name);

let mut f = fs::File::create(&temp_path)
.with_context(|| format!("unable to create temporary file: {}", &temp_path.display()))?;

let url = if uv_version == "any" {
let url = if !app::uv_custom_source().is_empty() {
app::uv_custom_source()
} else if uv_version == "any" {
format!(
"https://github.com/astral-sh/uv/releases/latest/download/{}",
&artifact_name,
Expand All @@ -505,7 +512,6 @@ fn ensure_uv_available() -> Result<()> {
&uv_version, &artifact_name,
)
};

network::download(&url, &mut f, "UV")?;

if artifact_name.ends_with(".zip") {
Expand Down