e1cd7811989c1fad7b1108432a315d73a05b39bb
Author Vladimir Skesov <vladimir.skesov@scalefinal.com> Committer Vladimir Skesov <vladimir.skesov@scalefinal.com> Date 2026-02-06 14:52 UTC
Message fix: prioritize local .venv directory over parent project markers
When a directory contains a .venv directory (valid virtualenv),
_check_path() now detects it before searching parent directories
for project markers (poetry.lock, uv.lock, Pipfile).
This prevents activating wrong virtualenv from parent directory
when local .venv exists.
Changes:
- Add _is_valid_virtualenv() helper to check virtualenv directory
- Use helper in _check_path() and check_venv() (DRY)
- Add tests for new functionality
Diff 1 diff --git a/autoswitch_virtualenv.plugin.zsh b/autoswitch_virtualenv.plugin.zsh
2 index a6c67d2d2df48303ded6943ddba51d481246e095..d7b13a71d23db25b13abc911066c9a468a3bee46 100644
3 --- a/autoswitch_virtualenv.plugin.zsh
4 +++ b/autoswitch_virtualenv.plugin.zsh
5 @@ -47,6 +47,11 @@ function _autoswitch_message() {
6 }
7
8
9 +function _is_valid_virtualenv() {
10 + [[ -d "$1" ]] && [[ -f "$1/bin/activate" ]]
11 +}
12 +
13 +
14 function _get_venv_type() {
15 local venv_dir="$1"
16 local venv_type="${2:-virtualenv}"
17 @@ -126,6 +131,9 @@ function _check_path()
18 if [[ -f "${check_dir}/${AUTOSWITCH_FILE}" ]]; then
19 printf "${check_dir}/${AUTOSWITCH_FILE}"
20 return
21 + elif _is_valid_virtualenv "${check_dir}/${AUTOSWITCH_FILE}"; then
22 + printf "${check_dir}/${AUTOSWITCH_FILE}"
23 + return
24 elif [[ -f "${check_dir}/poetry.lock" ]]; then
25 printf "${check_dir}/poetry.lock"
26 elif [[ -f "${check_dir}/Pipfile" ]]; then
27 @@ -218,8 +226,7 @@ function check_venv()
28 local switch_to="$(<"$venv_path")"
29 _maybeworkon "$(_virtual_env_dir "$switch_to")" "virtualenv"
30 return
31 - # $venv_path actually is itself a virtualenv
32 - elif [[ -d "$venv_path" ]] && [[ -f "$venv_path/bin/activate" ]]; then
33 + elif _is_valid_virtualenv "$venv_path"; then
34 _maybeworkon "$venv_path" "virtualenv"
35 return
36 fi
37 diff --git a/tests/test_check_path.zunit b/tests/test_check_path.zunit
38 index 34e16a5a2a51d4c7d9347c96c97e342a723a21f8..c13f802ed39ae92e7b1d3890585fe48071a19df5 100644
39 --- a/tests/test_check_path.zunit
40 +++ b/tests/test_check_path.zunit
41 @@ -53,3 +53,33 @@
42 assert $state equals 0
43 assert "$output" is_empty
44 }
45 +
46 +@test '_check_path - finds .venv directory with bin/activate' {
47 + mkdir -p "$TARGET/.venv/bin"
48 + touch "$TARGET/.venv/bin/activate"
49 +
50 + run _check_path "$TARGET"
51 +
52 + assert $state equals 0
53 + assert "$output" same_as "$TARGET/.venv"
54 +}
55 +
56 +@test '_check_path - ignores .venv directory without bin/activate' {
57 + mkdir -p "$TARGET/.venv"
58 +
59 + run _check_path "$TARGET"
60 +
61 + assert $state equals 0
62 + assert "$output" is_empty
63 +}
64 +
65 +@test '_check_path - finds local .venv directory instead of parent marker' {
66 + touch "$TARGET/uv.lock"
67 + mkdir -p "$TARGET/subdir/.venv/bin"
68 + touch "$TARGET/subdir/.venv/bin/activate"
69 +
70 + run _check_path "$TARGET/subdir"
71 +
72 + assert $state equals 0
73 + assert "$output" same_as "$TARGET/subdir/.venv"
74 +}
75 diff --git a/tests/test_is_valid_virtualenv.zunit b/tests/test_is_valid_virtualenv.zunit
76 new file mode 100644
77 index 0000000000000000000000000000000000000000..924946c908a91fbe97af7fad1d467ee1d8df1155
78 --- /dev/null
79 +++ b/tests/test_is_valid_virtualenv.zunit
80 @@ -0,0 +1,43 @@
81 +#!/usr/bin/env zunit
82 +
83 +
84 +@setup {
85 + export DISABLE_AUTOSWITCH_VENV="1"
86 + load "../autoswitch_virtualenv.plugin.zsh"
87 + TARGET="$(mktemp -d)"
88 +}
89 +
90 +@teardown {
91 + rm -rf "$TARGET"
92 +}
93 +
94 +@test '_is_valid_virtualenv - returns true for valid virtualenv' {
95 + mkdir -p "$TARGET/.venv/bin"
96 + touch "$TARGET/.venv/bin/activate"
97 +
98 + run _is_valid_virtualenv "$TARGET/.venv"
99 +
100 + assert $state equals 0
101 +}
102 +
103 +@test '_is_valid_virtualenv - returns false for directory without bin/activate' {
104 + mkdir -p "$TARGET/.venv/bin"
105 +
106 + run _is_valid_virtualenv "$TARGET/.venv"
107 +
108 + assert $state equals 1
109 +}
110 +
111 +@test '_is_valid_virtualenv - returns false for non-existent path' {
112 + run _is_valid_virtualenv "$TARGET/.venv"
113 +
114 + assert $state equals 1
115 +}
116 +
117 +@test '_is_valid_virtualenv - returns false for file' {
118 + touch "$TARGET/.venv"
119 +
120 + run _is_valid_virtualenv "$TARGET/.venv"
121 +
122 + assert $state equals 1
123 +}