b54524c036a8ad60307889b4f8ca582714d4515b

Author
TheEdgeOfRage <git@theedgeofrage.com>
Committer
TheEdgeOfRage <git@theedgeofrage.com>
Date

Message

Use built-in venvs without install prompts

Diff

This diff is truncated to protect this page.

  1diff --git a/README.rst b/README.rst
  2index c4b89cc9ef27ef175c79ff7fe97ae15691816d05..c801a97b656887bee4262166a1fe0e3766e3ee7d 100644
  3--- a/README.rst
  4+++ b/README.rst
  5@@ -27,18 +27,17 @@ Simply call the ``mkvenv`` command in the directory you wish to setup a
  6 virtual environment. A virtual environment specific to that folder will
  7 now activate every time you enter it.
  8 
  9-``zsh-autoswitch-virtualenv`` will detect python projects and remind
 10-you to create a virtual environment. This mainly occurs if one of the following
 11-is found in current the directory:
 12+``zsh-autoswitch-virtualenv`` detects Python projects when one of the
 13+following files is in the current directory:
 14 
 15 * setup.py
 16 * requirements.txt
 17 * Pipfile
 18 * poetry.lock
 19+* uv.lock
 20 
 21 To create a virtual environment for that project, simply run ``mkvenv``.
 22-This command works as expected for all popular python project types
 23-(virtualenvs, pipenv and poetry).
 24+This command works as expected for virtualenvs, UV, Pipenv, and Poetry projects.
 25 
 26 See the Commands_ section below for more detail.
 27 
 28@@ -71,10 +70,11 @@ projects (or equivalent file for the Version Control you are using).
 29 Installing
 30 ----------
 31 
 32-``autoswitch-virtualenv`` requires `virtualenv <https://pypi.org/project/virtualenv/>`__ to be installed.
 33-You will also need to make sure that ``python`` (without a suffix; both Python 2 and 3 are supported) is available in your ``$PATH``.
 34+``autoswitch-virtualenv`` requires Python 3 with the ``venv`` module available.
 35+Make sure that ``python3`` is available in your ``$PATH``, or configure
 36+``AUTOSWITCH_DEFAULT_PYTHON`` to use a different Python binary.
 37 
 38-Once ``virtualenv`` is installed, add one of the following lines to your ``.zshrc`` file depending on the
 39+Add one of the following lines to your ``.zshrc`` file depending on the
 40 package manager you are using:
 41 
 42 ZPlug_
 43@@ -146,13 +146,8 @@ helper command.
 44     $ cd my-python-project
 45     $ mkvenv
 46     Creating my-python-project virtualenv
 47-    Found a requirements.txt. Install? [y/N]:
 48-    Collecting requests (from -r requirements.txt (line 1))
 49-      Using cached requests-2.11.1-py2.py3-none-any.whl
 50-    Installing collected packages: requests
 51-    Successfully installed requests-2.11.1
 52 
 53-This command also works as expected with both ``poetry`` and ``pipenv``.
 54+This command also works as expected with ``uv``, ``poetry``, and ``pipenv``.
 55 
 56 Optionally, you can specify the python binary to use for this virtual environment
 57 
 58@@ -161,8 +156,9 @@ Optionally, you can specify the python binary to use for this virtual environmen
 59     $ mkvenv --python=/usr/bin/python3
 60 
 61 
 62-In fact any parameters passed to mkvenv will be passed to the relevant setup command.
 63-The same applies to passing additional parameters to ``pipenv install`` and ``poetry install``.
 64+The ``--python`` option selects the Python binary used to run ``python -m venv``.
 65+Use ``--verbose`` to show its output. Other parameters are passed to the relevant
 66+setup command, including ``uv sync``, ``pipenv install``, and ``poetry install``.
 67 
 68 Autoswitching is smart enough to detect that you have traversed to a
 69 project subdirectory. So your virtualenv will not be deactivated if you
 70@@ -284,19 +280,6 @@ changed by setting the value of ``AUTOSWITCH_FILE``. For example:
 71 
 72     export AUTOSWITCH_FILE=".autoswitch"
 73 
 74-**Default requirements file**
 75-
 76-You may specify a default requirements file to use when creating a virtualenv by
 77-setting the value of ``AUTOSWITCH_DEFAULT_REQUIREMENTS``. For example:
 78-
 79-::
 80-
 81-    export AUTOSWITCH_DEFAULT_REQUIREMENTS="$HOME/.requirements.txt"
 82-
 83-If the value is set and the target file exists you will be prompted to install with that file
 84-each time you create a new virtualenv.
 85-
 86-
 87 **Set verbosity when changing environments**
 88 
 89 You can prevent verbose messages from being displayed when moving
 90@@ -320,9 +303,8 @@ then you can set the variable to use a relative path. For example:
 91 
 92 **Customising pip install invocation**
 93 
 94-By default `mkvenv` will install setup.py via pip in `editable (i.e. development) mode
 95-<https://pip.pypa.io/en/stable/cli/pip_install/#editable-installs>`__.
 96-To change this set ``AUTOSWITCH_PIPINSTALL`` to ``FULL``.
 97+By default, ``mkvenv`` installs Pipenv projects in editable mode. To use a full
 98+Pipenv install instead, set ``AUTOSWITCH_PIPINSTALL`` to ``FULL``.
 99 
100 Security Warnings
101 -----------------
102diff --git a/autoswitch_virtualenv.plugin.zsh b/autoswitch_virtualenv.plugin.zsh
103index 86ae44873f793411e688a781954bcf45d1a60f01..359b37015827a379d3f2d24f8fd54f8e1dee52b9 100644
104--- a/autoswitch_virtualenv.plugin.zsh
105+++ b/autoswitch_virtualenv.plugin.zsh
106@@ -233,13 +233,6 @@ function check_venv()
107         fi
108     fi
109 
110-    local venv_type="$(_get_venv_type "$PWD" "unknown")"
111-
112-    # If we still haven't got anywhere, fallback to defaults
113-    if [[ "$venv_type" != "unknown" ]]; then
114-        printf "Python ${AUTOSWITCH_PURPLE}$venv_type${AUTOSWITCH_NORMAL} project detected. "
115-        printf "Run ${AUTOSWITCH_PURPLE}mkvenv${AUTOSWITCH_NORMAL} to setup autoswitching\n"
116-    fi
117     _default_venv
118 }
119 
120@@ -313,7 +306,8 @@ function _missing_error_message() {
121 
122 function randstr()
123 {
124-    ${AUTOSWITCH_DEFAULT_PYTHON:-python3} -c "from __future__ import print_function; import string, random; print(''.join(random.choice(string.ascii_lowercase) for _ in range(4)))"
125+    local python_bin="${1:-${AUTOSWITCH_DEFAULT_PYTHON:-python3}}"
126+    "$python_bin" -c "from __future__ import print_function; import string, random; print(''.join(random.choice(string.ascii_lowercase) for _ in range(4)))"
127 }
128 
129 
130@@ -358,82 +352,70 @@ function mkvenv()
131         _activate_uv
132         return
133     else
134-        if ! type "virtualenv" > /dev/null; then
135-            _missing_error_message virtualenv
136+        local python_bin="${AUTOSWITCH_DEFAULT_PYTHON:-python3}"
137+        local use_default_python=1
138+        local verbose=0
139+        local -a venv_params
140+        local index
141+
142+        for (( index = 1; index <= ${#params}; index++ )); do
143+            if [[ "${params[index]}" == "--python" ]]; then
144+                (( index++ ))
145+                if (( index > ${#params} )); then
146+                    printf "mkvenv: --python requires a value\n"
147+                    return 1
148+                fi
149+                python_bin="${params[index]}"
150+                use_default_python=0
151+            elif [[ "${params[index]}" == --python=* ]]; then
152+                python_bin="${params[index]#--python=}"
153+                use_default_python=0
154+            elif [[ "${params[index]}" == "--verbose" ]]; then
155+                verbose=1
156+            else
157+                venv_params+=("${params[index]}")
158+            fi
159+        done
160+
161+        if [[ -z "$python_bin" ]]; then
162+            printf "mkvenv: --python requires a value\n"
163+            return 1
164+        fi
165+
166+        if ! type "$python_bin" > /dev/null; then
167+            _missing_error_message "$python_bin"
168             return
169         fi
170 
171         if [[ -f "$AUTOSWITCH_FILE" ]]; then
172             printf "$AUTOSWITCH_FILE file already exists. If this is a mistake use the rmvenv command\n"
173         else
174-            local venv_name="$(basename $PWD)-$(randstr)"
175+            local venv_name="$(basename $PWD)-$(randstr "$python_bin")"
176+            local venv_path="$(_virtual_env_dir "$venv_name")"
177 
178             printf "Creating ${AUTOSWITCH_PURPLE}%s${NONE} virtualenv\n" "$venv_name"
179 
180-
181-            if [[ -n "$AUTOSWITCH_DEFAULT_PYTHON" && ${params[(I)--python*]} -eq 0 ]]; then
182+            if (( use_default_python )) && [[ -n "$AUTOSWITCH_DEFAULT_PYTHON" ]]; then
183                 printf "${AUTOSWITCH_PURPLE}"
184                 printf 'Using $AUTOSWITCH_DEFAULT_PYTHON='
185                 printf "$AUTOSWITCH_DEFAULT_PYTHON"
186                 printf "${NONE}\n"
187-                params+="--python=$AUTOSWITCH_DEFAULT_PYTHON"
188             fi
189 
190             /bin/mkdir -p "$VIRTUAL_ENV_DIR"
191 
192-            if [[ ${params[(I)--verbose]} -eq 0 ]]; then
193-                virtualenv $params "$(_virtual_env_dir "$venv_name")"
194+            if (( verbose )); then
195+                "$python_bin" -m venv "${venv_params[@]}" "$venv_path" || return
196             else
197-                virtualenv $params "$(_virtual_env_dir "$venv_name")" > /dev/null
198+                "$python_bin" -m venv "${venv_params[@]}" "$venv_path" > /dev/null || return
199             fi
200 
201             printf "$venv_name\n" > "$AUTOSWITCH_FILE"
202             chmod 600 "$AUTOSWITCH_FILE"
203 
204-            _maybeworkon "$(_virtual_env_dir "$venv_name")" "virtualenv"
205-
206diff --git a/tests/test_check_venv.zunit b/tests/test_check_venv.zunit
207index c0017cc2b2c8b177ee6cc2c2909dd8fc5587658a..045bca38116317be361a0d67198f9590304a4de1 100644
208--- a/tests/test_check_venv.zunit
209+++ b/tests/test_check_venv.zunit
210@@ -33,59 +33,16 @@
211     rm -rf Pipfile
212 }
213 
214-@test 'check_venv - Displays message on project detection (requirements.txt)' {
215+@test 'check_venv - does not display a project detection message' {
216     PWD="$TARGET"
217+    unset AUTOSWITCH_DEFAULTENV
218+    unset VIRTUAL_ENV
219     touch "$TARGET/requirements.txt"
220 
221     run check_venv
222 
223     assert $status equals 0
224-    assert "$output" same_as "Python \e[35mvirtualenv\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
225-}
226-
227-@test 'check_venv - Displays message on project detection (setup.py)' {
228-    PWD="$TARGET"
229-    touch "$TARGET/setup.py"
230-
231-    run check_venv
232-
233-    assert $status equals 0
234-    assert "$output" same_as "Python \e[35mvirtualenv\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
235-}
236-
237-@test 'check_venv - Displays message on project detection (Pipfile)' {
238-    PWD="$TARGET"
239-    touch "$TARGET/Pipfile"
240-
241-    run check_venv
242-
243-    assert $status equals 0
244-    assert "$output" same_as "Python \e[35mpipenv\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
245-}
246-
247-@test 'check_venv - Displays message on project detection (poetry.lock)' {
248-    PWD="$TARGET"
249-    touch "$TARGET/poetry.lock"
250-
251-    function poetry {
252-        echo ""
253-    }
254-
255-    run check_venv
256-
257-    assert $status equals 0
258-    assert "$output" same_as "Python \e[35mpoetry\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
259-}
260-
261-@test 'check_venv - Displays message on project detection (setup.py + requirements.txt)' {
262-    PWD="$TARGET"
263-    touch "$TARGET/setup.py"
264-    touch "$TARGET/requirements.txt"
265-
266-    run check_venv
267-
268-    assert $status equals 0
269-    assert "$output" same_as "Python \e[35mvirtualenv\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
270+    assert "$output" is_empty
271 }
272 
273 @test 'check_venv - Security warning for weak writeable by group permissions' {
274diff --git a/tests/test_install_requirements.zunit b/tests/test_install_requirements.zunit
275deleted file mode 100644
276index e92c13ad423cfe11077ebf405ceb3fe5de9202bf..0000000000000000000000000000000000000000
277--- a/tests/test_install_requirements.zunit
278+++ /dev/null
279@@ -1,145 +0,0 @@
280-#!/usr/bin/env zunit
281-
282-@setup {
283-    export DISABLE_AUTOSWITCH_VENV="1"
284-    load "../autoswitch_virtualenv.plugin.zsh"
285-    unset AUTOSWITCH_DEFAULT_REQUIREMENTS
286-    unset AUTOSWITCH_PIPINSTALL
287-
288-    function read {
289-        eval $1=\"y\"
290-        echo y
291-    }
292-
293-    TARGET="$(mktemp -d)"
294-    cd "$TARGET"
295-}
296-
297-@teardown {
298-    rm -rf "$TARGET"
299-}
300-
301-@test 'install_requirements - does nothing if no files found' {
302-    run install_requirements
303-
304-    assert $status equals 0
305-    assert "$output" is_empty
306-}
307-
308-@test 'install_requirements - does not install with no input' {
309-    echo "django" > requirements.txt
310-    echo "pytest" > dev-requirements.txt
311-
312-    # override default mock to return nothing
313-    function read {
314-        eval $1=\"\"
315-        echo ""
316-    }
317-
318-    # mock pip to make it testable
319-    function pip {
320-        echo "pip $@"
321-        cat "$3"
322-    }
323-
324-    run install_requirements
325-
326-    assert $status equals 0
327-    assert "$lines[1]" same_as "Found a \e[35mdev-requirements.txt\e[0m file. Install? [y/N]: "
328-    assert "$lines[2]" same_as "Found a \e[35mrequirements.txt\e[0m file. Install? [y/N]: "
329-}
330-
331-@test 'install_requirements - installs default requirements' {
332-    AUTOSWITCH_DEFAULT_REQUIREMENTS="$(mktemp -d)/default-requirements.txt"
333-    echo "django" > "$AUTOSWITCH_DEFAULT_REQUIREMENTS"
334-
335-    # mock pip to make it testable
336-    function pip {
337-        echo "pip $@"
338-        cat "$3"
339-    }
340-
341-    run install_requirements
342-
343-    assert $status equals 0
344-    assert "$lines[1]" same_as "Install default requirements? (\e[35m$AUTOSWITCH_DEFAULT_REQUIREMENTS\e[0m) [y/N]: y"
345-    assert "$lines[2]" same_as "pip install -r $AUTOSWITCH_DEFAULT_REQUIREMENTS"
346-    assert "$lines[3]" same_as "django"
347-}
348-
349-@test 'install_requirements - installs *requirements.txt' {
350-    echo "django" > requirements.txt
351-    echo "pytest" > dev-requirements.txt
352-
353-    # mock pip to make it testable
354-    function pip {
355-        echo "pip $@"
356-        cat "$3"
357-    }
358-
359-    run install_requirements
360-
361-    assert $status equals 0
362-    assert "$lines[1]" same_as "Found a \e[35mdev-requirements.txt\e[0m file. Install? [y/N]: y"
363-    assert "$lines[2]" same_as "pip install -r dev-requirements.txt"
364-    assert "$lines[3]" same_as "pytest"
365-    assert "$lines[4]" same_as "Found a \e[35mrequirements.txt\e[0m file. Install? [y/N]: y"
366-    assert "$lines[5]" same_as "pip install -r requirements.txt"
367-    assert "$lines[6]" same_as "django"
368-}
369-
370-@test 'install_requirements - installs setup.py' {
371-    touch "setup.py"
372-    echo "django" > requirements.txt
373-
374-    # mock pip to make it testable
375-    function pip {
376-        echo "pip $@"
377-    }
378-
379diff --git a/tests/test_mkvenv.zunit b/tests/test_mkvenv.zunit
380index 4b66d90ddd475f0759c0926797e967c1bb887e9d..74b278c31c69451f6d48cc9eb40528c9d5b5b07f 100644
381--- a/tests/test_mkvenv.zunit
382+++ b/tests/test_mkvenv.zunit
383@@ -1,16 +1,27 @@
384 #!/usr/bin/env zunit
385 
386 @setup {
387-    function virtualenv {
388-        echo virtualenv $@
389+    function python3 {
390+        echo python3 $@
391+    }
392+
393+    function python_foo {
394+        echo python_foo $@
395     }
396 
397     export DISABLE_AUTOSWITCH_VENV="1"
398     load "../autoswitch_virtualenv.plugin.zsh"
399-    unset AUTOSWITCH_DEFAULT_REQUIREMENTS
400+    unset AUTOSWITCH_DEFAULT_PYTHON
401     TARGET="$(mktemp -d)"
402     cd "$TARGET"
403 
404+    function randstr {
405+        printf test
406+    }
407+
408+    function _maybeworkon {
409+    }
410+
411     function poetry {
412         echo "poetry" $@
413     }
414@@ -57,6 +68,28 @@
415     assert "$output" matches "myproject-[a-z]{4}"
416 }
417 
418+@test 'mkvenv - uses default Python if set' {
419+    mkdir myproject
420+    cd myproject
421+    AUTOSWITCH_DEFAULT_PYTHON="python_foo"
422+
423+    run mkvenv --system-site-packages
424+
425+    assert $status equals 0
426+    assert "$lines[2]" contains 'Using $AUTOSWITCH_DEFAULT_PYTHON=python_foo'
427+    assert "$lines[3]" same_as "python_foo -m venv --system-site-packages $HOME/.virtualenvs/myproject-test"
428+}
429+
430+@test 'mkvenv - uses explicitly specified Python' {
431+    mkdir myproject
432+    cd myproject
433+
434+    run mkvenv --python python_foo --clear
435+
436+    assert $status equals 0
437+    assert "$lines[2]" same_as "python_foo -m venv --clear $HOME/.virtualenvs/myproject-test"
438+}
439+
440 @test 'mkvenv - (poetry project) runs correct command' {
441     mkdir myproject
442     cd myproject
443@@ -81,10 +114,10 @@
444     assert "$lines[2]" same_as "activating pipenv"
445 }
446 
447-@test 'prints help message and disables plugin if virtualenv not setup' {
448+@test 'prints help message if Python is not setup' {
449     # Mock type to fail
450     function type() {
451-        if [[ "$1" == "virtualenv" ]]; then
452+        if [[ "$1" == "python3" ]]; then
453             return 1
454         fi
455         return 0
456@@ -93,10 +126,10 @@
457     run mkvenv
458 
459     assert $status equals 0
460-    assert "$lines[1]" contains "zsh-autoswitch-virtualenv requires 'virtualenv' to install this project!"
461+    assert "$lines[1]" contains "zsh-autoswitch-virtualenv requires 'python3' to install this project!"
462     assert "$lines[2]" is_empty
463     assert "$lines[3]" contains "If this is already installed but you are still seeing this message,"
464-    assert "$lines[4]" contains "then make sure the \e[1mvirtualenv\e[0m command is in your PATH.\n"
465+    assert "$lines[4]" contains "then make sure the \e[1mpython3\e[0m command is in your PATH.\n"
466     assert "$lines[5]" is_empty
467     assert ${#lines} equals 4
468 }
469diff --git a/tests/test_rmvenv.zunit b/tests/test_rmvenv.zunit
470index 53a6a0285123e21b2a61151262d506d717c27fba..c423c795bc430cf8f518c56a0b19f8b5b184f6f7 100644
471--- a/tests/test_rmvenv.zunit
472+++ b/tests/test_rmvenv.zunit
473@@ -4,7 +4,6 @@
474 
475     export DISABLE_AUTOSWITCH_VENV="1"
476     load "../autoswitch_virtualenv.plugin.zsh"
477-    unset AUTOSWITCH_DEFAULT_REQUIREMENTS
478     TARGET="$(mktemp -d)"
479     cd "$TARGET"
480