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

Add the ability to specify venv location from #1222

Closed
wants to merge 3 commits into from
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ __pycache__
.idea
token.txt
dist
rye/tests/.*
2 changes: 2 additions & 0 deletions docs/guide/commands/add.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,6 @@ Added packagename @ file:///path/to/packagename as regular dependency

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with `-h`)
2 changes: 2 additions & 0 deletions docs/guide/commands/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ By default you will find the artifacts in the `dist` folder.

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help
2 changes: 2 additions & 0 deletions docs/guide/commands/fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@ $ rye fetch cpython@3.9.1 --target-path=my-interpreter

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/fmt.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,6 @@ rye fmt src/foo.py

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,6 @@ $ pycowsay "Great Stuff"

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/lint.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,6 @@ rye lint src/foo.py

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ Werkzeug==3.0.1

* `--pyproject`: Use this `pyproject.toml` file

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/lock.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ Done!

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/make-req.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ flask @ git+https://github.com/pallets/flask@4df377cfbf

* `--features <FEATURES>`: Adds a dependency with a specific feature

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/publish.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ $ rye publish dist/example-0.1.0.tar.gz

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/remove.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ Removed flask>=3.0.1

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ python3.9

* `--pyproject`: Use this `pyproject.toml` file

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/show.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ virtual: false

* `--pyproject`: Use this `pyproject.toml` file

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,6 @@ To exit the sub shell run `exit`.

* `-q, --quiet`: Turns off all output

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
2 changes: 2 additions & 0 deletions docs/guide/commands/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,6 @@ stuff/tests/test_batch.py . [100%]

* `-s`, `--no-capture`: Disable stdout/stderr capture for the test runner

* `--venv <VENV>`: Use this virtual environment

* `-h, --help`: Print help (see a summary with '-h')
4 changes: 4 additions & 0 deletions docs/guide/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ of known supported cloud synchronization systems.
For override this behavior you can set the `behavior.venv-mark-sync-ignore` configuration
key to `false`.

Various CI and test tools can be aided in intentionally setting a location for the virtualenv,
as such there is an additional `--venv` option on several commands. For the given invocation
the command will override `.venv` to the specified location.

## Why Does Rye Contain Trojan "Bearfoos"?

Unfortunately Windows likes to complain that Rye contains the trojan "Win32/Bearfoos.A!ml".
Expand Down
6 changes: 3 additions & 3 deletions docs/philosophy.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ Preferably over time the structure of virtualenvs aligns between different Pytho
(eg: Windows vs Linux) and the deeply nested `lib/py-ver/site-packages` structure is flattened
out.

**What needs to be done:**

* Agree on a name for where managed virtualenvs are placed (eg: `.venv` in the workspace root)
For better integration with external CI tools such as tox and nox we did introduce an optional
parameter on many of the commands, `--venv` which allows the main `.venv` to be overridden for
the given command.

#### Dev and Tool Dependencies

Expand Down
25 changes: 19 additions & 6 deletions rye/src/cli/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,11 @@ impl ReqExtras {
self.absolute = true;
}

pub fn apply_to_requirement(&self, req: &mut Requirement) -> Result<(), Error> {
pub fn apply_to_requirement(
&self,
req: &mut Requirement,
venv: Option<&PathBuf>,
) -> Result<(), Error> {
if let Some(ref git) = self.git {
// XXX: today they are all aliases, it might be better to change
// tag to refs/tags/<tag> and branch to refs/heads/<branch> but
Expand Down Expand Up @@ -158,7 +162,7 @@ impl ReqExtras {
// but this not supported by pip-tools,
// and use ${PROJECT_ROOT} will cause error in hatchling, so force absolute path.
let is_hatchling =
PyProject::discover()?.build_backend() == Some(BuildSystem::Hatchling);
PyProject::discover(venv)?.build_backend() == Some(BuildSystem::Hatchling);
let file_url = if self.absolute || is_hatchling {
Url::from_file_path(env::current_dir()?.join(path))
.map_err(|_| anyhow!("unable to interpret '{}' as path", path.display()))?
Expand Down Expand Up @@ -236,6 +240,9 @@ pub struct Args {
/// Attempt to use `keyring` for authentication for index URLs.
#[arg(long, value_enum, default_value_t)]
keyring_provider: KeyringProvider,
/// Use this virtual environment.
#[arg(long, value_name = "VENV")]
venv: Option<PathBuf>,
}

pub fn execute(cmd: Args) -> Result<(), Error> {
Expand All @@ -244,7 +251,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
let python_path = self_venv.join(VENV_BIN).join("python");
let cfg = Config::current();

let mut pyproject_toml = PyProject::discover()?;
let mut pyproject_toml = PyProject::discover(cmd.venv.as_ref())?;
let py_ver = pyproject_toml.venv_python_version()?;
let dep_kind = if cmd.dev {
DependencyKind::Dev
Expand All @@ -267,14 +274,19 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
let mut requirements = Vec::new();
for str_requirement in &cmd.requirements {
let mut requirement = Requirement::from_str(str_requirement)?;
cmd.req_extras.apply_to_requirement(&mut requirement)?;
cmd.req_extras
.apply_to_requirement(&mut requirement, cmd.venv.as_ref())?;
requirements.push(requirement);
}

if !cmd.excluded {
if cfg.use_uv() {
sync(SyncOptions::python_only().pyproject(None))
.context("failed to sync ahead of add")?;
sync(
SyncOptions::python_only()
.pyproject(None)
.with_venv(cmd.venv.clone()),
)
.context("failed to sync ahead of add")?;
resolve_requirements_with_uv(
&pyproject_toml,
&py_ver,
Expand Down Expand Up @@ -326,6 +338,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
cmd.with_sources,
cmd.generate_hashes,
cmd.keyring_provider,
cmd.venv,
)?;
}

Expand Down
24 changes: 17 additions & 7 deletions rye/src/cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use anyhow::{anyhow, bail, Context, Error};
use clap::Parser;
use console::style;

use crate::bootstrap::{fetch, FetchOptions};
use crate::bootstrap::{ensure_self_venv, fetch, FetchOptions};
use crate::config::Config;

use crate::platform::get_toolchain_python_bin;
Expand Down Expand Up @@ -44,11 +44,15 @@ pub struct Args {
/// Turns off all output.
#[arg(short, long, conflicts_with = "verbose")]
quiet: bool,
/// Use this virtual environment.
#[arg(long, value_name = "VENV")]
venv: Option<PathBuf>,
}

pub fn execute(cmd: Args) -> Result<(), Error> {
let output = CommandOutput::from_quiet_and_verbose(cmd.quiet, cmd.verbose);
let project = PyProject::load_or_discover(cmd.pyproject.as_deref())?;
let self_venv = ensure_self_venv(output)?;
let project = PyProject::load_or_discover(cmd.pyproject.as_deref(), cmd.venv.as_ref())?;
let py_ver = project.venv_python_version()?;

let out = match cmd.out {
Expand All @@ -74,6 +78,12 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
return Ok(());
}

let all_virtual = projects.iter().all(|p| p.is_virtual());
if all_virtual {
warn!("skipping build, all projects are virtual");
return Ok(());
}

// Make sure we have a compatible Python version.
let py_ver = fetch(&py_ver.into(), FetchOptions::with_output(output))
.context("failed fetching toolchain ahead of sync")?;
Expand All @@ -83,17 +93,15 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
// Create a virtual environment in which to perform the builds.
let uv = UvBuilder::new()
.with_output(CommandOutput::Quiet)
.ensure_exists()?;
.ensure_exists()?
.with_output(output);
let venv_dir = tempfile::tempdir().context("failed to create temporary directory")?;
let uv_venv = uv
.venv(venv_dir.path(), &py_bin, &py_ver, None)
.context("failed to create build environment")?;
uv_venv.write_marker()?;
uv_venv.bootstrap()?;

// Respect the output level for the actual builds.
let uv = uv.with_output(output);

for project in projects {
// skip over virtual packages on build
if project.is_virtual() {
Expand All @@ -106,7 +114,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
style(project.normalized_name()?).cyan()
);

let mut build_cmd = Command::new(get_venv_python_bin(venv_dir.path()));
let mut build_cmd = Command::new(get_venv_python_bin(&self_venv));
build_cmd
.arg("-mbuild")
.env("NO_COLOR", "1")
Expand All @@ -115,6 +123,8 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
.arg(&*project.root_path());

if use_uv {
// we need to ensure uv is available to use without installing it into self_venv
let uv = UvBuilder::new().with_output(output).ensure_exists()?;
let uv_dir = uv
.uv_bin()
.parent()
Expand Down
5 changes: 4 additions & 1 deletion rye/src/cli/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub struct Args {
/// Turns off all output.
#[arg(short, long, conflicts_with = "verbose")]
quiet: bool,
/// Use this virtual environment.
#[arg(long, value_name = "VENV")]
venv: Option<PathBuf>,
}

pub fn execute(cmd: Args) -> Result<(), Error> {
Expand All @@ -43,7 +46,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
let version: PythonVersionRequest = match cmd.version {
Some(version) => version.parse()?,
None => {
if let Ok(pyproject) = PyProject::discover() {
if let Ok(pyproject) = PyProject::discover(cmd.venv.as_ref()) {
pyproject.venv_python_version()?.into()
} else {
match get_python_version_request_from_pyenv_pin(&std::env::current_dir()?) {
Expand Down
8 changes: 6 additions & 2 deletions rye/src/cli/install.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::Path;
use std::path::{Path, PathBuf};

use anyhow::{Context, Error};
use clap::Parser;
Expand Down Expand Up @@ -39,6 +39,9 @@ pub struct Args {
/// Turns off all output.
#[arg(short, long, conflicts_with = "verbose")]
quiet: bool,
/// Use this virtual environment.
#[arg(long, value_name = "VENV")]
venv: Option<PathBuf>,
}

pub fn execute(mut cmd: Args) -> Result<(), Error> {
Expand All @@ -50,7 +53,8 @@ pub fn execute(mut cmd: Args) -> Result<(), Error> {
// installations here always use absolute paths for local references
// because we do not have a rye workspace to work with.
cmd.req_extras.force_absolute();
cmd.req_extras.apply_to_requirement(&mut requirement)?;
cmd.req_extras
.apply_to_requirement(&mut requirement, cmd.venv.as_ref())?;

for req in cmd.extra_requirement {
extra_requirements.push(handle_requirement(&req, output, false)?);
Expand Down
5 changes: 4 additions & 1 deletion rye/src/cli/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ pub struct Args {
/// Use this pyproject.toml file
#[arg(long, value_name = "PYPROJECT_TOML")]
pub(crate) pyproject: Option<PathBuf>,
/// Use this virtual environment.
#[arg(long, value_name = "VENV")]
pub(crate) venv: Option<PathBuf>,
}

pub fn execute(cmd: Args) -> Result<(), Error> {
let project = PyProject::load_or_discover(cmd.pyproject.as_deref())?;
let project = PyProject::load_or_discover(cmd.pyproject.as_deref(), cmd.venv.as_ref())?;
let python = get_venv_python_bin(&project.venv_path());
if !python.is_file() {
return Ok(());
Expand Down
4 changes: 4 additions & 0 deletions rye/src/cli/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ pub struct Args {
/// Use this pyproject.toml file.
#[arg(long, value_name = "PYPROJECT_TOML")]
pyproject: Option<PathBuf>,
/// Use this virtual environment.
#[arg(long, value_name = "VENV")]
venv: Option<PathBuf>,
}

pub fn execute(cmd: Args) -> Result<(), Error> {
Expand All @@ -66,6 +69,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
reset: cmd.reset,
generate_hashes: cmd.generate_hashes,
universal: cmd.universal,
venv: cmd.venv,
},
pyproject: cmd.pyproject,
keyring_provider: cmd.keyring_provider,
Expand Down
7 changes: 6 additions & 1 deletion rye/src/cli/make_req.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::path::PathBuf;
use std::str::FromStr;

use anyhow::{Context, Error};
Expand All @@ -14,13 +15,17 @@ pub struct Args {
requirements: Vec<String>,
#[command(flatten)]
req_extras: ReqExtras,
/// Use this virtual environment.
#[arg(long, value_name = "VENV")]
venv: Option<PathBuf>,
}

pub fn execute(cmd: Args) -> Result<(), Error> {
for requirement_str in cmd.requirements {
let mut requirement = Requirement::from_str(&requirement_str)
.with_context(|| format!("unable to parse requirement '{}'", requirement_str))?;
cmd.req_extras.apply_to_requirement(&mut requirement)?;
cmd.req_extras
.apply_to_requirement(&mut requirement, cmd.venv.as_ref())?;
echo!("{}", format_requirement(&requirement));
}

Expand Down
Loading