diff --git a/docs/config/README.md b/docs/config/README.md
index d0380e724..57f6f7e8c 100644
--- a/docs/config/README.md
+++ b/docs/config/README.md
@@ -1225,6 +1225,25 @@ The module will be shown if any of the following conditions are met:
| `style` | `"bold yellow"` | The style for the module. |
| `disabled` | `false` | Disables the `python` module. |
+
+This module has some advanced configuration options.
+
+| Variable | Default | Description |
+| --------------- | -------- | ---------------------------------------------------------------------------- |
+| `python_binary` | `python` | Confgures the python binary that Starship executes when getting the version. |
+
+The `python_binary` variable changes the binary that Starship executes to get
+the version of Python, it doesn't change the arguments that are used.
+
+```toml
+# ~/.config/starship.toml
+
+[python]
+python_binary = "python3"
+```
+
+
+
### Example
```toml
@@ -1480,7 +1499,7 @@ will simply show all custom modules in the order they were defined.
If unset, it will fallback to STARSHIP_SHELL and then to "sh" on Linux, and "cmd /C" on Windows.
If `shell` is not given or only contains one element and Starship detects PowerShell will be used,
-the following arguments will automatically be added: `-NoProfile -Command -`.
+the following arguments will automatically be added: `-NoProfile -Command -`.
This behavior can be avoided by explicitly passing arguments to the shell, e.g.
```toml
diff --git a/src/configs/python.rs b/src/configs/python.rs
index bd986e91c..2b9aa4158 100644
--- a/src/configs/python.rs
+++ b/src/configs/python.rs
@@ -9,6 +9,7 @@ pub struct PythonConfig<'a> {
pub version: SegmentConfig<'a>,
pub pyenv_prefix: SegmentConfig<'a>,
pub pyenv_version_name: bool,
+ pub python_binary: &'a str,
pub scan_for_pyfiles: bool,
pub style: Style,
pub disabled: bool,
@@ -21,6 +22,7 @@ impl<'a> RootModuleConfig<'a> for PythonConfig<'a> {
version: SegmentConfig::default(),
pyenv_prefix: SegmentConfig::new("pyenv "),
pyenv_version_name: false,
+ python_binary: "python",
scan_for_pyfiles: true,
style: Color::Yellow.bold(),
disabled: false,
diff --git a/src/modules/python.rs b/src/modules/python.rs
index 9b6b63b7c..63ca2de6a 100644
--- a/src/modules/python.rs
+++ b/src/modules/python.rs
@@ -49,7 +49,7 @@ pub fn module<'a>(context: &'a Context) -> Option> {
module.create_segment("pyenv_prefix", &config.pyenv_prefix);
module.create_segment("version", &SegmentConfig::new(&python_version.trim()));
} else {
- let python_version = get_python_version()?;
+ let python_version = get_python_version(&config.python_binary)?;
let formatted_version = format_python_version(&python_version);
module.create_segment("version", &SegmentConfig::new(&formatted_version));
};
@@ -64,8 +64,8 @@ pub fn module<'a>(context: &'a Context) -> Option> {
Some(module)
}
-fn get_python_version() -> Option {
- match utils::exec_cmd("python", &["--version"]) {
+fn get_python_version(python_binary: &str) -> Option {
+ match utils::exec_cmd(python_binary, &["--version"]) {
Some(output) => {
if output.stdout.is_empty() {
Some(output.stderr)
@@ -98,6 +98,10 @@ fn get_python_virtual_env() -> Option {
#[cfg(test)]
mod tests {
use super::*;
+ use crate::modules::utils::test::render_module;
+ use ansi_term::Color;
+ use std::fs::File;
+ use std::io;
#[test]
fn test_format_python_version() {
@@ -110,4 +114,149 @@ mod tests {
let input = "Python 3.6.10 :: Anaconda, Inc.";
assert_eq!(format_python_version(input), "v3.6.10");
}
+
+ #[test]
+ fn folder_without_python_files() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ let actual = render_module("python", dir.path(), None);
+ let expected = None;
+ assert_eq!(expected, actual);
+
+ dir.close()
+ }
+
+ #[test]
+ fn folder_with_python_version() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join(".python-version"))?.sync_all()?;
+
+ check_python2_renders(&dir, None);
+ check_python3_renders(&dir, None);
+ dir.close()
+ }
+
+ #[test]
+ fn folder_with_requirements_txt() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("requirements.txt"))?.sync_all()?;
+
+ check_python2_renders(&dir, None);
+ check_python3_renders(&dir, None);
+ dir.close()
+ }
+
+ #[test]
+ fn folder_with_pyproject_toml() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("pyproject.toml"))?.sync_all()?;
+
+ check_python2_renders(&dir, None);
+ check_python3_renders(&dir, None);
+ dir.close()
+ }
+
+ #[test]
+ fn folder_with_pipfile() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("Pipfile"))?.sync_all()?;
+
+ check_python2_renders(&dir, None);
+ check_python3_renders(&dir, None);
+ dir.close()
+ }
+
+ #[test]
+ fn folder_with_tox() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("tox.ini"))?.sync_all()?;
+
+ check_python2_renders(&dir, None);
+ check_python3_renders(&dir, None);
+ dir.close()
+ }
+
+ #[test]
+ fn folder_with_setup_py() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("setup.py"))?.sync_all()?;
+
+ check_python2_renders(&dir, None);
+ check_python3_renders(&dir, None);
+ dir.close()
+ }
+
+ #[test]
+ fn folder_with_init_py() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("__init__.py"))?.sync_all()?;
+
+ check_python2_renders(&dir, None);
+ check_python3_renders(&dir, None);
+ dir.close()
+ }
+
+ #[test]
+ fn folder_with_py_file() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("main.py"))?.sync_all()?;
+
+ check_python2_renders(&dir, None);
+ check_python3_renders(&dir, None);
+ dir.close()
+ }
+
+ #[test]
+ fn disabled_scan_for_pyfiles_and_folder_with_ignored_py_file() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("foo.py"))?.sync_all()?;
+
+ let expected = None;
+ let config = toml::toml! {
+ [python]
+ scan_for_pyfiles = false
+ };
+ let actual = render_module("python", dir.path(), Some(config));
+ assert_eq!(expected, actual);
+ dir.close()
+ }
+
+ #[test]
+ fn disabled_scan_for_pyfiles_and_folder_with_setup_py() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("setup.py"))?.sync_all()?;
+
+ let config = toml::toml! {
+ [python]
+ scan_for_pyfiles = false
+ };
+
+ check_python2_renders(&dir, Some(config));
+
+ let config_python3 = toml::toml! {
+ [python]
+ python_binary = "python3"
+ scan_for_pyfiles = false
+ };
+
+ check_python3_renders(&dir, Some(config_python3));
+
+ dir.close()
+ }
+
+ fn check_python2_renders(dir: &tempfile::TempDir, starship_config: Option) {
+ let actual = render_module("python", dir.path(), starship_config);
+ let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐍 v2.7.17")));
+ assert_eq!(expected, actual);
+ }
+
+ fn check_python3_renders(dir: &tempfile::TempDir, starship_config: Option) {
+ let config = Some(starship_config.unwrap_or(toml::toml! {
+ [python]
+ python_binary = "python3"
+ }));
+
+ let actual = render_module("python", dir.path(), config);
+ let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐍 v3.8.0")));
+ assert_eq!(expected, actual);
+ }
}
diff --git a/src/utils.rs b/src/utils.rs
index 7f7e7f4d4..ee7af608d 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -97,6 +97,14 @@ active boot switches: -d:release\n",
stdout: String::from("0.13.5"),
stderr: String::default(),
}),
+ "python --version" => Some(CommandOutput {
+ stdout: String::from("Python 2.7.17"),
+ stderr: String::default(),
+ }),
+ "python3 --version" => Some(CommandOutput {
+ stdout: String::from("Python 3.8.0"),
+ stderr: String::default(),
+ }),
"ruby -v" => Some(CommandOutput {
stdout: String::from("ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]"),
stderr: String::default(),
diff --git a/tests/testsuite/python.rs b/tests/testsuite/python.rs
index 9c1d1a8dc..d799f7f90 100644
--- a/tests/testsuite/python.rs
+++ b/tests/testsuite/python.rs
@@ -1,163 +1,12 @@
use std::fs::File;
use std::io;
-use ansi_term::Color;
+use crate::common;
-use crate::common::{self, TestCommand};
+// TODO - These tests should be moved into the python module when we have sorted out mocking of env
+// vars.
#[test]
-fn show_nothing_on_empty_dir() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
-
- let output = common::render_module("python")
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = "";
- assert_eq!(expected, actual);
- dir.close()
-}
-
-#[test]
-#[ignore]
-fn folder_with_python_version() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join(".python-version"))?.sync_all()?;
-
- let output = common::render_module("python")
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7"));
- assert_eq!(expected, actual);
- dir.close()
-}
-
-#[test]
-#[ignore]
-fn folder_with_requirements_txt() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("requirements.txt"))?.sync_all()?;
-
- let output = common::render_module("python")
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7"));
- assert_eq!(expected, actual);
- dir.close()
-}
-
-#[test]
-#[ignore]
-fn folder_with_pyproject_toml() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("pyproject.toml"))?.sync_all()?;
-
- let output = common::render_module("python")
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7"));
- assert_eq!(expected, actual);
- dir.close()
-}
-
-#[test]
-#[ignore]
-fn folder_with_pipfile() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("Pipfile"))?.sync_all()?;
-
- let output = common::render_module("python")
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7"));
- assert_eq!(expected, actual);
- dir.close()
-}
-
-#[test]
-#[ignore]
-fn folder_with_tox() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("tox.ini"))?.sync_all()?;
-
- let output = common::render_module("python")
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7"));
- assert_eq!(expected, actual);
- dir.close()
-}
-
-#[test]
-#[ignore]
-fn folder_with_setup_py() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("setup.py"))?.sync_all()?;
-
- let output = common::render_module("python")
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7"));
- assert_eq!(expected, actual);
- Ok(())
-}
-
-#[test]
-#[ignore]
-fn folder_with_init_py() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("__init__.py"))?.sync_all()?;
-
- let output = common::render_module("python")
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7"));
- assert_eq!(expected, actual);
- Ok(())
-}
-
-#[test]
-#[ignore]
-fn folder_with_py_file() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("main.py"))?.sync_all()?;
-
- let output = common::render_module("python")
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7"));
- assert_eq!(expected, actual);
- dir.close()
-}
-
-#[test]
-#[ignore]
fn with_virtual_env() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.py"))?.sync_all()?;
@@ -168,13 +17,11 @@ fn with_virtual_env() -> io::Result<()> {
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7 (my_venv)"));
- assert_eq!(expected, actual);
+ assert!(actual.contains("my_venv"));
dir.close()
}
#[test]
-#[ignore]
fn with_active_venv() -> io::Result<()> {
let dir = tempfile::tempdir()?;
@@ -185,48 +32,6 @@ fn with_active_venv() -> io::Result<()> {
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7 (my_venv)"));
- assert_eq!(expected, actual);
+ assert!(actual.contains("my_venv"));
dir.close()
}
-
-#[test]
-fn disabled_scan_for_pyfiles_and_folder_with_ignored_py_file() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("foo.py"))?.sync_all()?;
-
- let output = common::render_module("python")
- .use_config(toml::toml! {
- [python]
- scan_for_pyfiles = false
- })
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = "";
- assert_eq!(expected, actual);
- Ok(())
-}
-
-#[test]
-#[ignore]
-fn disabled_scan_for_pyfiles_and_folder_with_setup_py() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("setup.py"))?.sync_all()?;
-
- let output = common::render_module("python")
- .use_config(toml::toml! {
- [python]
- scan_for_pyfiles = false
- })
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Yellow.bold().paint("🐍 v3.7.7"));
- assert_eq!(expected, actual);
- Ok(())
-}