Skip to content

Commit

Permalink
Only create a venv before shim resolution for rye managed projects (#212
Browse files Browse the repository at this point in the history
)
  • Loading branch information
mitsuhiko committed May 24, 2023
1 parent 292868d commit b5bbed1
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 23 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ that were not yet released.

_Unreleased_

- The shims are now more resilient. Previously a `pyproject.toml` file
caused in all cases a virtualenv to be created. Now this will only
happen when the `rye.tool.managed` flag is set to `true`. The old
behavior can be forced via the global config. #212

<!-- released start -->

## 0.2.0
Expand Down
4 changes: 4 additions & 0 deletions docs/guide/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ requires-python = ">= 3.8"
# This is the default toolchain that is used
toolchain = "cpython@3.11.1"

[behavior]
# When set to true the `managed` flag is always assumed to be true.
force_rye_managed = false

# a array of tables with optional sources. Same format as in pyproject.toml
[[sources]]
name = "default"
Expand Down
9 changes: 6 additions & 3 deletions docs/guide/pyproject.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ excluded-dependencies = ["cffi"]

## `tool.rye.managed`

This is a purely informational key that tells rye that this project is supposed to be managed
by Rye. This key is not changing any behavior in Rye, but it's a hint to better aid the
user experience.
+++ 0.3.0

This key tells rye that this project is supposed to be managed by Rye. This key
primarily affects some automatic creation of virtualenvs. For instance Rye
will not try to initialize a virtualenv when using shims without this flag. It
can be forced enabled in the global config.

```toml
[tool.rye]
Expand Down
42 changes: 22 additions & 20 deletions rye/src/cli/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::bootstrap::{ensure_self_venv, get_pip_runner};
use crate::consts::VENV_BIN;
use crate::pyproject::PyProject;
use crate::sync::{sync, SyncOptions};
use crate::utils::{exec_spawn, CommandOutput};
use crate::utils::{exec_spawn, get_venv_python_bin, CommandOutput};

fn detect_shim(args: &[OsString]) -> Option<String> {
// Shims are detected if the executable is linked into
Expand Down Expand Up @@ -49,7 +49,7 @@ fn get_pip_shim(
) -> Result<Vec<OsString>, Error> {
let venv = ensure_self_venv(output)?;
let runner = get_pip_runner(&venv);
let python = pyproject.venv_path().join("bin/python");
let python = get_venv_python_bin(&pyproject.venv_path());

// pip likes to emit deprecation warnings
env::set_var("PYTHONWARNINGS", "ignore");
Expand Down Expand Up @@ -83,27 +83,29 @@ fn find_shadowed_target(target: &str, args: &[OsString]) -> Result<Option<Vec<Os

/// Figures out where a shim should point to.
fn get_shim_target(target: &str, args: &[OsString]) -> Result<Option<Vec<OsString>>, Error> {
let pyproject = match PyProject::discover() {
Ok(project) => project,
Err(_) => return find_shadowed_target(target, args),
};

// make sure we have the minimal virtualenv.
sync(SyncOptions::python_only()).context("sync ahead of shim resolution failed")?;

let mut args = args.to_vec();
let folder = pyproject.venv_path().join(VENV_BIN);
if let Some(m) = which_in_global(target, Some(folder))?.next() {
args[0] = m.into();
return Ok(Some(args));
}
// if we can find a project, we always look for a local virtualenv first for shims.
if let Ok(pyproject) = PyProject::discover() {
// However we only allow automatic synching, if we are rye managed.
if pyproject.rye_managed() {
sync(SyncOptions::python_only()).context("sync ahead of shim resolution failed")?;
}

let mut args = args.to_vec();
let folder = pyproject.venv_path().join(VENV_BIN);
if let Some(m) = which_in_global(target, Some(folder))?.next() {
args[0] = m.into();
return Ok(Some(args));
}

// secret pip shims
if target == "pip" || target == "pip3" {
return Ok(Some(get_pip_shim(&pyproject, args, CommandOutput::Normal)?));
// secret pip shims
if target == "pip" || target == "pip3" {
return Ok(Some(get_pip_shim(&pyproject, args, CommandOutput::Normal)?));
}
}

Ok(None)
// if we make it this far, we did not find a shim in the project, look for
// a global one instead.
find_shadowed_target(target, args)
}

fn spawn_shim(args: Vec<OsString>) -> Result<Infallible, Error> {
Expand Down
9 changes: 9 additions & 0 deletions rye/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ impl Config {
.context("failed to get default toolchain")
}

/// Pretend that all projects are rye managed.
pub fn force_rye_managed(&self) -> bool {
self.doc
.get("behavior")
.and_then(|x| x.get("force_rye_managed"))
.and_then(|x| x.as_bool())
.unwrap_or(false)
}

/// Returns the list of default sources.
pub fn sources(&self) -> Result<Vec<SourceRef>, Error> {
let mut rv = Vec::new();
Expand Down
24 changes: 24 additions & 0 deletions rye/src/pyproject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,11 @@ impl Workspace {
pub fn sources(&self) -> Result<Vec<SourceRef>, Error> {
get_sources(&self.doc)
}

/// Is this workspace rye managed?
pub fn rye_managed(&self) -> bool {
is_rye_managed(&self.doc)
}
}

/// Helps working with pyproject.toml files
Expand Down Expand Up @@ -769,6 +774,14 @@ impl PyProject {
}
}

/// Is this project rye managed?
pub fn rye_managed(&self) -> bool {
match self.workspace {
Some(ref workspace) => workspace.rye_managed(),
None => is_rye_managed(&self.doc),
}
}

/// Save back changes
pub fn save(&self) -> Result<(), Error> {
fs::write(self.toml_path(), self.doc.to_string()).with_context(|| {
Expand Down Expand Up @@ -964,6 +977,17 @@ fn get_sources(doc: &Document) -> Result<Vec<SourceRef>, Error> {
Ok(rv)
}

fn is_rye_managed(doc: &Document) -> bool {
if Config::current().force_rye_managed() {
return true;
}
doc.get("tool")
.and_then(|x| x.get("rye"))
.and_then(|x| x.get("managed"))
.and_then(|x| x.as_bool())
.unwrap_or(false)
}

/// Represents expanded sources.
#[derive(Debug, Clone, Serialize)]
pub struct ExpandedSources {
Expand Down

0 comments on commit b5bbed1

Please sign in to comment.