Repository: JanDeDobbeleer/oh-my-posh
Pull request #4724 (2024-02-28 13:37:20)

feat(python): Make the feature from commit-7da84d4 of PR-4400 optional
### Prerequisites

- [x] I have read and understood the [contributing guide][CONTRIBUTING.md].
- [x] The commit message follows the [conventional commits][cc] guidelines.
- [x] Tests for the changes have been added (for bug fixes / features).
- [x] Docs have been added/updated (for bug fixes / features).

### Description

While it is undisputed that "venv" or ".venv" is commonly used as the name for virtual environments in most cases, it doesn't directly imply the desire for displaying the parent directory name as the virtual environment name. Some projects may have more than one virtual environment, such as ".venv", ".venv-test", ".venv-whatever", especially when testing across different Python and dependency versions. In such cases, if a user attempts to switch between these environments, they might find that when switching to ".venv", it displays the project name instead of the expected virtual environment name, causing confusion.

Additionally, considering Conda environments, where the default environment name is "base," it seems that pull-4400 hasn't taken into account the scenario with Conda. Assuming we display the parent directory even when the Conda "base" environment is activated, how do we distinguish whether the current environment is activated under Conda's "base" or ".venv"?

Moreover, when upgrading from older versions, users might find that their previously correct configurations and accustomed display methods have changed, leading sensitive users (like myself) to immediately think of configuration compatibility issues caused by the upgrade.

Therefore, considering all these factors, making this feature an optional Property configuration option with default settings disabled seems to be the best approach for ensuring maximum backward compatibility for our ❤️oh-my-posh❤️ while also catering to specific needs.

### How

Add a `use_parent_folder_4venv` property for python segment:
 - update code in 'src/segments/python.go' and 'src/segments/python_test.go';
 - update doc description in 'website/docs/segments/python.mdx'

check the commited source code for details.

JanDeDobbeleer: @rockyoung I'm reviewing this later today! Probably will adjust a few things, but thanks!
rockyoung: @JanDeDobbeleer looks like you've been quite busy lately 😂, if possible, could leave a msg indicating what adjustments are needed, and i can make the changes and submit another PR afterward.

Patch:
diff --git a/src/segments/python.go b/src/segments/python.go
index ba33a2ca..49cace56 100644
--- a/src/segments/python.go
+++ b/src/segments/python.go
@@ -21,6 +21,7 @@ const (
 	// FetchVirtualEnv fetches the virtual env
 	FetchVirtualEnv      properties.Property = "fetch_virtual_env"
 	UsePythonVersionFile properties.Property = "use_python_version_file"
+	FolderNameFallback   properties.Property = "folder_name_fallback"
 )
 
 func (p *Python) Template() string {
@@ -74,6 +75,7 @@ func (p *Python) loadContext() {
 		"CONDA_DEFAULT_ENV",
 	}
 
+	folderNameFallback := p.language.props.GetBool(FolderNameFallback, true)
 	defaultVenvNames := []string{
 		".venv",
 		"venv",
@@ -87,8 +89,7 @@ func (p *Python) loadContext() {
 		}
 
 		name := platform.Base(p.language.env, venv)
-
-		if slices.Contains(defaultVenvNames, name) {
+		if folderNameFallback && slices.Contains(defaultVenvNames, name) {
 			venv = strings.TrimSuffix(venv, name)
 			name = platform.Base(p.language.env, venv)
 		}
diff --git a/src/segments/spotify_darwin.go b/src/segments/spotify_darwin.go
index 61ba5c05..d468ceef 100644
--- a/src/segments/spotify_darwin.go
+++ b/src/segments/spotify_darwin.go
@@ -3,24 +3,28 @@
 package segments
 
 func (s *Spotify) Enabled() bool {
-	var err error
 	// Check if running
 	running := s.runAppleScriptCommand("application \"Spotify\" is running")
 	if running == "false" || running == "" {
 		s.Status = stopped
 		return false
 	}
+
 	s.Status = s.runAppleScriptCommand("tell application \"Spotify\" to player state as string")
-	if err != nil {
+
+	if len(s.Status) == 0 {
 		s.Status = stopped
 		return false
 	}
+
 	if s.Status == stopped {
 		return false
 	}
+
 	s.Artist = s.runAppleScriptCommand("tell application \"Spotify\" to artist of current track as string")
 	s.Track = s.runAppleScriptCommand("tell application \"Spotify\" to name of current track as string")
 	s.resolveIcon()
+
 	return true
 }
 
diff --git a/website/docs/segments/python.mdx b/website/docs/segments/python.mdx
index 6179b81f..3e5a706f 100644
--- a/website/docs/segments/python.mdx
+++ b/website/docs/segments/python.mdx
@@ -9,8 +9,6 @@ sidebar_label: Python
 Display the currently active python version and virtualenv.
 Supports conda, virtualenv and pyenv (if python points to pyenv shim).
 
-If your virtual environment is a directory named `venv` or `.venv`, the virtual environment's name will be the parent directory.
-
 ## Sample Configuration
 
 import Config from "@site/src/components/Config.js";
@@ -38,8 +36,9 @@ import Config from "@site/src/components/Config.js";
 | `display_mode`         | `string`   | <ul><li>`always`: the segment is always displayed</li><li>`files`: the segment is only displayed when one of the following files is present:<ul><li>`*.py`</li><li>`*.ipynb`</li><li>`pyproject.toml`</li><li>`venv.bak`</li><li>`venv`</li><li>`.venv`</li></ul></li><li>`environment`: the segment is only displayed when a virtual env is present (**default**)</li><li>`context`: the segment is only displayed when either `environment` or `files` is active</li></ul> |
 | `version_url_template` | `string`   | a go [text/template][go-text-template] [template][templates] that creates the URL of the version info / release notes                                                                                                                                                                                                                                                                                                                                                        |
 | `extensions`           | `[]string` | allows to override the default list of file extensions to validate                                                                                                                                                                                                                                                                                                                                                                                                           |
-| `folders`              | `[]string` | allows to override the list of folder names to validate                                                                                                                                                                                                                                                                                                                                                                                                                     |
+| `folders`              | `[]string` | allows to override the list of folder names to validate                                                                                                                                                                                                                                                                                                                                                                                                                      |
 | `cache_version`        | `boolean`  | cache the executable's version or not - defaults to `false`                                                                                                                                                                                                                                                                                                                                                                                                                  |
+| `folder_name_fallback` | `boolean`  | instead of `venv` or `.venv` (case sensitive), use the parent folder name as the virtual environment's name or not - defaults to `true`                                                                                                                                                                                                                                                                                                                                      |
 
 ## Template ([info][templates])
 


Test patch:
diff --git a/src/segments/python_test.go b/src/segments/python_test.go
index c5794d0e..63754473 100644
--- a/src/segments/python_test.go
+++ b/src/segments/python_test.go
@@ -150,14 +150,36 @@ func TestPythonPythonInContext(t *testing.T) {
 
 func TestPythonVirtualEnvIgnoreDefaultVenvNames(t *testing.T) {
 	cases := []struct {
-		VirtualEnvName string
+		Expected           string
+		FolderNameFallback bool
+		VirtualEnvName     string
 	}{
-		{VirtualEnvName: "/path/to/folder/.venv"},
-		{VirtualEnvName: "/path/to/folder/venv"},
+		{
+			Expected:           "folder",
+			FolderNameFallback: true,
+			VirtualEnvName:     "/path/to/folder/.venv",
+		},
+		{
+			Expected:           "folder",
+			FolderNameFallback: true,
+			VirtualEnvName:     "/path/to/folder/venv",
+		},
+		{
+			Expected:           ".venv",
+			FolderNameFallback: false,
+			VirtualEnvName:     "/path/to/folder/.venv",
+		},
+		{
+			Expected:           "venv",
+			FolderNameFallback: false,
+			VirtualEnvName:     "/path/to/folder/venv",
+		},
 	}
 
 	for _, tc := range cases {
-		env := new(mock.MockedEnvironment)
+		params := &mockedLanguageParams{}
+		env, props := getMockedLanguageEnv(params)
+
 		env.On("GOOS").Return("")
 		env.On("PathSeparator").Return("/")
 		env.On("CommandPath", mock2.Anything).Return("")
@@ -167,9 +189,12 @@ func TestPythonVirtualEnvIgnoreDefaultVenvNames(t *testing.T) {
 		env.On("Getenv", "CONDA_DEFAULT_ENV").Return("")
 		env.On("Getenv", "PYENV_VERSION").Return("")
 		env.On("HasParentFilePath", ".python-version").Return(&platform.FileInfo{}, errors.New("no match at root level"))
+
+		props[FolderNameFallback] = tc.FolderNameFallback
+
 		python := &Python{}
-		python.Init(properties.Map{}, env)
+		python.Init(props, env)
 		python.loadContext()
-		assert.Equal(t, "folder", python.Venv)
+		assert.Equal(t, tc.Expected, python.Venv)
 	}
 }