diff --git a/CHANGELOG.md b/CHANGELOG.md index 722cd92fa0..e334fae65c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ that were not yet released. _Unreleased_ +- Added support for the new `behavior.global-python` flag which turns on global + Python shimming. When enabled then the `python` shim works even outside of + Rye managed projects. Additionally the shim (when run outside of Rye managed + projects) supports a special first parameter `+VERSION` which requests a + specific version of Python (eg: `python +3.8` to request Python 3.8). #336 + - Renamed the config key `default.dependency_operator` to `default.dependency-operator` and `behavior.force_rye_managed` to `behavior.force-rye-managed`. #338 diff --git a/docs/guide/config.md b/docs/guide/config.md index bfe59eb2a9..e499b8553e 100644 --- a/docs/guide/config.md +++ b/docs/guide/config.md @@ -72,6 +72,11 @@ https = "http://127.0.0.1:4000" # When set to true the `managed` flag is always assumed to be true. force-rye-managed = false +# Enables global shims when set to `true`. This means that the installed +# `python` shim will resolve to a Rye managed toolchain even outside of +# virtual environments. +global-python = false + # a array of tables with optional sources. Same format as in pyproject.toml [[sources]] name = "default" diff --git a/docs/guide/installation.md b/docs/guide/installation.md index fdf0ecd52b..497c874a4e 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -94,6 +94,9 @@ interpreter. Note that you might need to restart your login session for this to take effect. +There is a quite a bit to shims and their behavior. Make sure to [read up on shims](shims.md) +to learn more. + ## Updating Rye To update rye to the latest version you can use `rye` itself: diff --git a/docs/guide/shims.md b/docs/guide/shims.md new file mode 100644 index 0000000000..0bef4d17e0 --- /dev/null +++ b/docs/guide/shims.md @@ -0,0 +1,43 @@ +# Shims + +After installation Rye places two shims on your `PATH`: `python` and `python3`. These +shims have specific behavior that changes depending on if they are used within a Rye +managed project or outside. + +Inside a Rye managed project the resolve to the Python interpreter of the virtualenv. +This means that even if you do not enable the virtualenv, you can just run `python` +in a shell, and it will automatically operate in the right environment. + +Outside a Rye managed project it typically resolves to your system Python, though you +can also opt to have it resolve to a Rye managed Python installation for you. This is +done so that it's not disruptive to your existing workflows which might depend on the +System python installation. + +## Global Shims + ++++ 0.9.0 + +To enable global shims, you need to enable the `global-python` flag in +the [`config.toml`](config.md) file: + +```toml +[behavior] +global-python = true +``` + +Afterwards if you run `python` outside of a Rye managed project it will +spawn a Python interpreter that is shipped with Rye. It will honor the +closest `.python-version` file for you. Additionally you can also +explicitly request a specific Python version by adding `+VERSION` after +the `python` command. For instance this runs a script with Python 3.8: + +```bash +python +3.8 my-script.py +``` + +!!! Note + + Selecting a specific Python version this way only works outside of + Rye managed projects. Within Rye managed projects, the version needs + to be explicitly selected via `.python-version` or with the + `requires-python` key in `pyproject.toml`. diff --git a/mkdocs.yml b/mkdocs.yml index 82a56c61b8..4aa1632fea 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,6 +12,7 @@ nav: - Installation: guide/installation.md - Basics: guide/basics.md - Configuration: guide/config.md + - Shims: guide/shims.md - Python Project: guide/pyproject.md - Syncing and Locking: guide/sync.md - Building and Publishing: guide/publish.md diff --git a/rye/src/cli/shim.rs b/rye/src/cli/shim.rs index 7b5e8ba8ec..10c756c1af 100644 --- a/rye/src/cli/shim.rs +++ b/rye/src/cli/shim.rs @@ -1,15 +1,19 @@ use std::convert::Infallible; use std::env; use std::ffi::{OsStr, OsString}; +use std::str::FromStr; -use anyhow::{bail, Context, Error}; +use anyhow::{anyhow, bail, Context, Error}; use same_file::is_same_file; use std::process::Command; use which::which_in_global; use crate::bootstrap::{ensure_self_venv, get_pip_runner}; +use crate::config::Config; use crate::consts::VENV_BIN; -use crate::pyproject::PyProject; +use crate::platform::{get_python_version_request_from_pyenv_pin, get_toolchain_python_bin}; +use crate::pyproject::{latest_available_python_version, PyProject}; +use crate::sources::{PythonVersion, PythonVersionRequest}; use crate::sync::{sync, SyncOptions}; use crate::utils::{exec_spawn, get_venv_python_bin, CommandOutput}; @@ -90,6 +94,15 @@ fn get_shim_target(target: &str, args: &[OsString]) -> Result Result version_request, + None => config.default_toolchain()?, + } + } else { + // if neither requested explicitly, nor global-python is enabled, we fall + // back to the next shadowed target + return find_shadowed_target(target, args); + }; + + let py_ver = match PythonVersion::try_from(version_request.clone()) { + Ok(py_ver) => py_ver, + Err(_) => latest_available_python_version(&version_request) + .ok_or_else(|| anyhow!("Unable to determine target Python version"))?, + }; + let py = get_toolchain_python_bin(&py_ver)?; + if !py.is_file() { + bail!( + "Requested Python version ({}) is not installed. Install with `rye fetch {}`", + py_ver, + py_ver + ); + } + + let mut args = args.to_vec(); + args[0] = py.into(); + if remove1 { + args.remove(1); + } + return Ok(Some(args)); } // if we make it this far, we did not find a shim in the project, look for diff --git a/rye/src/config.rs b/rye/src/config.rs index 7021d88733..30b10ce11a 100644 --- a/rye/src/config.rs +++ b/rye/src/config.rs @@ -130,6 +130,15 @@ impl Config { }) } + /// Allow rye shims to resolve globally installed Pythons. + pub fn global_python(&self) -> bool { + self.doc + .get("behavior") + .and_then(|x| x.get("global-python")) + .and_then(|x| x.as_bool()) + .unwrap_or(false) + } + /// Pretend that all projects are rye managed. pub fn force_rye_managed(&self) -> bool { self.doc