feat(python): support pyenv .python-version files

Pyenv will use .python-version files up the file heirarchy when
determining the active Python version or virtual environment to use
based on which folder your shell is in.  This change will read the file,
and if the first line does not look like a Python version, set the Venv
to that string for display.

This feature is gated behind the `use_python_version_file` Property
This commit is contained in:
Brandon Ewing 2022-04-06 16:33:56 -05:00 committed by Jan De Dobbeleer
parent 9ab318d4e0
commit b3b5267605
3 changed files with 38 additions and 1 deletions

View file

@ -26,6 +26,7 @@ Supports conda, virtualenv and pyenv.
- home_enabled: `boolean` - display the segment in the HOME folder or not - defaults to `false`
- fetch_virtual_env: `boolean` - fetch the name of the virtualenv or not - defaults to `true`
- use_python_version_file: `boolean` - Use pyenv `.python-version` files to determine virtual env - defaults to `false`
- display_default: `boolean` - show the name of the virtualenv when it's default (`system`, `base`)
or not - defaults to `true`
- fetch_version: `boolean` - fetch the python version - defaults to `true`

View file

@ -3,6 +3,8 @@ package segments
import (
"oh-my-posh/environment"
"oh-my-posh/properties"
"oh-my-posh/regex"
"strings"
)
type Python struct {
@ -13,7 +15,8 @@ type Python struct {
const (
// FetchVirtualEnv fetches the virtual env
FetchVirtualEnv properties.Property = "fetch_virtual_env"
FetchVirtualEnv properties.Property = "fetch_virtual_env"
UsePythonVersionFile properties.Property = "use_python_version_file"
)
func (p *Python) Template() string {
@ -68,6 +71,15 @@ func (p *Python) loadContext() {
break
}
}
if !p.language.props.GetBool(UsePythonVersionFile, false) {
return
}
if f, err := p.language.env.HasParentFilePath(".python-version"); err == nil {
contents := strings.Split(p.language.env.FileContent(f.Path), "\n")
if contents[0] != "" && regex.MatchString("[0-9]+.[0-9]+.[0-9]", contents[0]) == false && p.canUseVenvName(contents[0]) {
p.Venv = contents[0]
}
}
}
func (p *Python) inContext() bool {

View file

@ -1,6 +1,7 @@
package segments
import (
"errors"
"oh-my-posh/environment"
"oh-my-posh/mock"
"oh-my-posh/properties"
@ -17,6 +18,7 @@ func TestPythonTemplate(t *testing.T) {
Template string
VirtualEnvName string
FetchVersion bool
PyenvLocal string
}{
{Case: "No virtual env present", FetchVersion: true, Expected: "3.8.4", Template: "{{ if .Venv }}{{ .Venv }} {{ end }}{{ .Full }}"},
{Case: "Virtual env present", FetchVersion: true, Expected: "VENV 3.8.4", VirtualEnvName: "VENV", Template: "{{ if .Venv }}{{ .Venv }} {{ end }}{{ .Full }}"},
@ -41,6 +43,20 @@ func TestPythonTemplate(t *testing.T) {
VirtualEnvName: "billy",
Template: "{{ if ne .Venv \"default\" }}{{ .Venv }} {{ end }}{{ .Major }}.{{ .Minor }}",
},
{
Case: "Pyenv show env",
FetchVersion: true,
Expected: "VENV 3.8",
PyenvLocal: "VENV\n",
Template: "{{ if ne .Venv \"default\" }}{{ .Venv }} {{ end }}{{ .Major }}.{{ .Minor }}",
},
{
Case: "Pyenv skip version",
FetchVersion: true,
Expected: "3.8",
PyenvLocal: "3.8.7\n",
Template: "{{ if ne .Venv \"default\" }}{{ .Venv }} {{ end }}{{ .Major }}.{{ .Minor }}",
},
}
for _, tc := range cases {
@ -48,6 +64,12 @@ func TestPythonTemplate(t *testing.T) {
env.On("HasCommand", "python").Return(true)
env.On("RunCommand", "python", []string{"--version"}).Return("Python 3.8.4", nil)
env.On("HasFiles", "*.py").Return(true)
env.On("HasParentFilePath", ".python-version").Return(&environment.FileInfo{
ParentFolder: "/usr/home/project",
Path: "/usr/home/project/.python-version",
IsDir: false,
}, nil)
env.On("FileContent", "/usr/home/project/.python-version").Return(tc.PyenvLocal)
env.On("Getenv", "VIRTUAL_ENV").Return(tc.VirtualEnvName)
env.On("Getenv", "CONDA_ENV_PATH").Return(tc.VirtualEnvName)
env.On("Getenv", "CONDA_DEFAULT_ENV").Return(tc.VirtualEnvName)
@ -57,6 +79,7 @@ func TestPythonTemplate(t *testing.T) {
env.On("Home").Return("/usr/home")
props := properties.Map{
properties.FetchVersion: tc.FetchVersion,
UsePythonVersionFile: true,
DisplayMode: DisplayModeAlways,
}
env.On("TemplateCache").Return(&environment.TemplateCache{
@ -85,6 +108,7 @@ func TestPythonPythonInContext(t *testing.T) {
env.On("Getenv", "CONDA_ENV_PATH").Return("")
env.On("Getenv", "CONDA_DEFAULT_ENV").Return("")
env.On("Getenv", "PYENV_VERSION").Return("")
env.On("HasParentFilePath", ".python-version").Return(&environment.FileInfo{}, errors.New("no match at root level"))
python := &Python{}
python.Init(properties.Map{}, env)
python.loadContext()