99da1f7ccb8f7198f793684dc1beb01c66064c17

Author
Michael Aquilina <michaelaquilina@gmail.com>
Committer
GitHub <noreply@github.com>
Date

Message

Merge pull request #132 from MichaelAquilina/poetry_pipenv_improved

Improved integration of pipenv and poetry

Diff

This diff is truncated to protect this page.

  1diff --git a/CHANGELOG.rst b/CHANGELOG.rst
  2index 5b82ea2958e12beca62c07a73a744cca394ecdf2..9573f998dfee98ecd055a94372f38e186b056f79 100644
  3--- a/CHANGELOG.rst
  4+++ b/CHANGELOG.rst
  5@@ -1,6 +1,11 @@
  6 Changelog
  7 =========
  8 
  9+3.0.0
 10+-----
 11+* Improve integration support for poetry and pipenv. The ``mkvenv`` and ``rmvenv`` commands work as expected.
 12+* Improve detection of poetry projects by looking for "poetry.lock" rather than "pyproject.toml"
 13+
 14 2.0.1
 15 -----
 16 * Fix issue where poetry would incorrectly try activate in projects with pyproject.toml but no poetry setup (Issue #130) (Thanks @hauntsaninja)
 17diff --git a/README.rst b/README.rst
 18index c04887800917d37247bf9dbac7797dd4be275fbe..2744beae5e36786299fa25168b1db09b09172078 100644
 19--- a/README.rst
 20+++ b/README.rst
 21@@ -27,13 +27,18 @@ Simply call the ``mkvenv`` command in the directory you wish to setup a
 22 virtual environment. A virtual environment specific to that folder will
 23 now activate every time you enter it.
 24 
 25-``zsh-autoswitch-virtualenv`` will try detect python projects and remind
 26-you to create a virtual environment if e.g. setup.py or requirements.txt is
 27-found in the current directory.
 28+``zsh-autoswitch-virtualenv`` will detect python projects and remind
 29+you to create a virtual environment. This mainly occurs if one of the following
 30+is found in current the directory:
 31 
 32-**NOTE: Pipenv and Poetry projects should not use 'mkvenv'.**
 33+* setup.py
 34+* requirements.txt
 35+* Pipfile
 36+* poetry.lock
 37 
 38-**Use 'pipenv install' or 'poetry install' respectively instead**
 39+To create a virtual environment for that project, simply run ``mkvenv``.
 40+This command works as expected for all popular python project types
 41+(virtualenvs, pipenv and poetry).
 42 
 43 See the Commands_ section below for more detail.
 44 
 45@@ -123,7 +128,7 @@ Commands
 46 mkvenv
 47 ''''''
 48 
 49-Setup a new project with virtualenv autoswitching using the ``mkvenv``
 50+Setup a new python project with autoswitching using the ``mkvenv``
 51 helper command.
 52 
 53 ::
 54@@ -137,6 +142,8 @@ helper command.
 55     Installing collected packages: requests
 56     Successfully installed requests-2.11.1
 57 
 58+This command also works as expected with both ``poetry`` and ``pipenv``.
 59+
 60 Optionally, you can specify the python binary to use for this virtual environment
 61 
 62 ::
 63@@ -144,40 +151,8 @@ Optionally, you can specify the python binary to use for this virtual environmen
 64     $ mkvenv --python=/usr/bin/python3
 65 
 66 
 67-It is also possible to inherit the system site packages (which can reduce duplication on the file system
 68-and download/setup time).
 69-
 70-::
 71-
 72-    $ mkvenv --system-site-packages
 73-
 74-In fact, ``mkvenv`` supports any parameters that can be passed to ``python -m virtualenv``.
 75-A comprehensive list of options may be found in the
 76-`virtualenv guide <https://virtualenv.pypa.io/en/latest/reference/#options>`__
 77-
 78-``mkvenv`` will create a virtual environment with the same name as the
 79-current directory, suggest installing ``requirements.txt`` if available
 80-and create the relevant ``.venv`` file for you.
 81-
 82-Next time you switch to that folder, you'll see the following message
 83-
 84-::
 85-
 86-    $ cd my-python-project
 87-    Switching virtualenv: my-python-project  [Python 3.4.3+]
 88-    $
 89-
 90-If you have set the ``AUTOSWITCH_DEFAULTENV`` environment variable,
 91-exiting that directory will switch back to the value set.
 92-
 93-::
 94-
 95-    $ cd ..
 96-    Switching virtualenv: mydefaultenv  [Python 3.4.3+]
 97-    $
 98-
 99-Otherwise, ``deactivate`` will simply be called on the virtualenv to
100-switch back to the global python environment.
101+In fact any parameters passed to mkvenv will be passed to the relevant setup command.
102+The same applies to passing additional parameters to ``pipenv install`` and ``poetry install``.
103 
104 Autoswitching is smart enough to detect that you have traversed to a
105 project subdirectory. So your virtualenv will not be deactivated if you
106@@ -216,6 +191,9 @@ This will delete the virtual environment in ``.venv`` and remove the
107     $ rmvenv
108     No .venv file in the current directory!
109 
110+Similar to ``mkvenv``, the ``rmvenv`` command also works as you would
111+expect with removing ``poetry`` and ``pipenv`` projects.
112+
113 disable_autoswitch_virtualenv
114 '''''''''''''''''''''''''''''
115 
116diff --git a/autoswitch_virtualenv.plugin.zsh b/autoswitch_virtualenv.plugin.zsh
117index 4f5e1f174b29529cf69bf9a3410db152cd7e4756..5742ddfa85e3ea59a92b8f7b9510bf270e2cee08 100644
118--- a/autoswitch_virtualenv.plugin.zsh
119+++ b/autoswitch_virtualenv.plugin.zsh
120@@ -1,4 +1,4 @@
121-export AUTOSWITCH_VERSION="2.0.1"
122+export AUTOSWITCH_VERSION="3.0.0"
123 export AUTOSWITCH_FILE=".venv"
124 
125 RED="\e[31m"
126@@ -47,17 +47,16 @@ function _autoswitch_message() {
127     fi
128 }
129 
130+
131 function _get_venv_type() {
132     local venv_dir="$1"
133     local venv_type="${2:-virtualenv}"
134     if [[ -f "$venv_dir/Pipfile" ]]; then
135         venv_type="pipenv"
136+    elif [[ -f "$venv_dir/poetry.lock" ]]; then
137+        venv_type="poetry"
138     elif [[ -f "$venv_dir/requirements.txt" || -f "$venv_dir/setup.py" ]]; then
139         venv_type="virtualenv"
140-    # TODO: this is not technically correct as lots of tools use pyproject.toml
141-    # however it's probably a good enough approximation for this function
142-    elif [[ -f "$venv_dir/pyproject.toml" ]]; then
143-        venv_type="poetry"
144     fi
145     printf "%s" "$venv_type"
146 }
147@@ -137,6 +136,34 @@ function _check_path()
148 }
149 
150 
151+function _activate_poetry() {
152+    # check if any environments exist before trying to activate
153+    # if env list is empty, then no environment exists that can be activated
154+    if [[ -n "$(poetry env list)" ]]; then
155+        # we need to infer the target virtualenv directory based on poetry's data
156+        # to easiest way is to actually get the location of the python binary and
157+        # infer the location of the virtualenv from there.
158+        if poetry_python=$(poetry run which python); then
159+            if venv_path="$(dirname $(dirname $poetry_python))"; then
160+                _maybeworkon "$venv_path" "poetry"
161+                return 0
162+            fi
163+        fi
164+    fi
165+    return 1
166+}
167+
168+
169+function _activate_pipenv() {
170+    # unfortunately running pipenv each time we are in a pipenv project directory is slow :(
171+    if venv_path="$(PIPENV_IGNORE_VIRTUALENVS=1 pipenv --venv 2>/dev/null)"; then
172+        _maybeworkon "$venv_path" "pipenv"
173+        return 0
174+    fi
175+    return 1
176+}
177+
178+
179 # Automatically switch virtualenv when $AUTOSWITCH_FILE file detected
180 function check_venv()
181 {
182@@ -175,39 +202,29 @@ function check_venv()
183     # check if Pipfile exists rather than invoking pipenv as it is slow
184     local pipfile_path="$(_check_path "$PWD" "Pipfile")"
185     # Same logic applies to poetry
186-    local pyproject_path="$(_check_path "$PWD" "pyproject.toml")"
187+    local poetry_lock="$(_check_path "$PWD" "poetry.lock")"
188 
189     if [[ -n "$pipfile_path" ]] && type "pipenv" > /dev/null; then
190-        # unfortunately running pipenv each time we are in a pipenv project directory is slow :(
191-        if venv_path="$(PIPENV_IGNORE_VIRTUALENVS=1 pipenv --venv 2>/dev/null)"; then
192-            _maybeworkon "$venv_path" "pipenv"
193+        if _activate_pipenv; then
194             return
195         fi
196-    elif [[ -n "$pyproject_path" ]] && type "poetry" > /dev/null; then
197-        # we need to infer the target virtualenv directory based on poetry's data
198-        # to easiest way is to actually get the location of the python binary and
199-        # infer the location of the virtualenv from there.
200-        if poetry_python=$(poetry run which python); then
201-            if venv_path="$(dirname $(dirname $poetry_python))"; then
202-                _maybeworkon "$venv_path" "poetry"
203-                return
204-            fi
205+    elif [[ -n "$poetry_lock" ]] && type "poetry" > /dev/null; then
206+        if _activate_poetry; then
207+            return
208         fi
209     fi
210 
211     local venv_type="$(_get_venv_type "$PWD" "unknown")"
212 
213     # If we still haven't got anywhere, fallback to defaults
214-    if [[ "$venv_type" == "pipenv" ]]; then
215-        printf "Python project detected. "
216-        printf "Run ${PURPLE}pipenv install${NORMAL} to setup autoswitching\n"
217-    elif [[ "$venv_type" == "virtualenv" ]]; then
218-        printf "Python project detected. "
219+    if [[ "$venv_type" != "unknown" ]]; then
220diff --git a/tests/test_check_venv.zunit b/tests/test_check_venv.zunit
221index 4b3b17953e952afa7f9711a433205de18a316950..64ea4cc195ff4d557bde2b640595aabd156db86b 100644
222--- a/tests/test_check_venv.zunit
223+++ b/tests/test_check_venv.zunit
224@@ -36,7 +36,7 @@
225     run check_venv
226 
227     assert $status equals 0
228-    assert "$output" same_as "Python project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
229+    assert "$output" same_as "Python \e[35mvirtualenv\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
230 }
231 
232 @test 'check_venv - Displays message on project detection (setup.py)' {
233@@ -46,7 +46,7 @@
234     run check_venv
235 
236     assert $status equals 0
237-    assert "$output" same_as "Python project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
238+    assert "$output" same_as "Python \e[35mvirtualenv\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
239 }
240 
241 @test 'check_venv - Displays message on project detection (Pipfile)' {
242@@ -56,7 +56,17 @@
243     run check_venv
244 
245     assert $status equals 0
246-    assert "$output" same_as "Python project detected. Run \e[35mpipenv install\e[0m to setup autoswitching"
247+    assert "$output" same_as "Python \e[35mpipenv\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
248+}
249+
250+@test 'check_venv - Displays message on project detection (poetry.lock)' {
251+    PWD="$TARGET"
252+    touch "$TARGET/poetry.lock"
253+
254+    run check_venv
255+
256+    assert $status equals 0
257+    assert "$output" same_as "Python \e[35mpoetry\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
258 }
259 
260 @test 'check_venv - Displays message on project detection (setup.py + requirements.txt)' {
261@@ -67,7 +77,7 @@
262     run check_venv
263 
264     assert $status equals 0
265-    assert "$output" same_as "Python project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
266+    assert "$output" same_as "Python \e[35mvirtualenv\e[0m project detected. Run \e[35mmkvenv\e[0m to setup autoswitching"
267 }
268 
269 @test 'check_venv - Security warning for weak writeable by group permissions' {
270@@ -156,7 +166,7 @@
271 
272 @test 'check_venv - activate if .venv unavailable but poetry available' {
273     PWD="$TARGET/sub-directory"  # Should also work in subdirectories where pyproject.toml is found
274-    touch "$TARGET/pyproject.toml"
275+    touch "$TARGET/poetry.lock"
276 
277     # Mimic "poetry run which python"
278     function poetry {
279diff --git a/tests/test_get_venv_type.zunit b/tests/test_get_venv_type.zunit
280index f1817798740486c763095db36ba48da49ff9864a..f8e7b902fe9b8afc08708f96b1af5e9250ec2051 100644
281--- a/tests/test_get_venv_type.zunit
282+++ b/tests/test_get_venv_type.zunit
283@@ -18,6 +18,14 @@
284     assert "$output" same_as "pipenv"
285 }
286 
287+@test '_get_venv_type poetry' {
288+    touch "$TARGET/poetry.lock"
289+
290+    run _get_venv_type "$TARGET"
291+
292+    assert $state equals 0
293+    assert "$output" same_as "poetry"
294+}
295 
296 @test '_get_venv_type virtualenv (requirements.txt)' {
297     touch "$TARGET/requirements.txt"
298diff --git a/tests/test_mkvenv.zunit b/tests/test_mkvenv.zunit
299index 9b08b8eedcbe20946093e1368ab53cb6cf74cf6d..ae18ab4ebb5a6d1d080f9e5133fb717ccf9b63a4 100644
300--- a/tests/test_mkvenv.zunit
301+++ b/tests/test_mkvenv.zunit
302@@ -10,6 +10,22 @@
303     unset AUTOSWITCH_DEFAULT_REQUIREMENTS
304     TARGET="$(mktemp -d)"
305     cd "$TARGET"
306+
307+    function poetry {
308+        echo "poetry" $@
309+    }
310+
311+    function _activate_poetry {
312+        echo "activating poetry"
313+    }
314+
315+    function pipenv {
316+        echo "pipenv" $@
317+    }
318+
319+    function _activate_pipenv {
320+        echo "activating pipenv"
321+    }
322 }
323 
324 @teardown {
325@@ -42,6 +58,30 @@
326     assert "$output" same_as "myproject"
327 }
328 
329+@test 'mkvenv - (poetry project) runs correct command' {
330+    mkdir myproject
331+    cd myproject
332+    touch "poetry.lock"
333+
334+    run mkvenv
335+
336+    assert $status equals 0
337+    assert "$lines[1]" same_as "poetry install"
338+    assert "$lines[2]" same_as "activating poetry"
339+}
340+
341+@test 'mkvenv - (pipenv project) runs correct command' {
342+    mkdir myproject
343+    cd myproject
344+    touch "Pipfile"
345+
346+    run mkvenv
347+
348+    assert $status equals 0
349+    assert "$lines[1]" same_as "pipenv install --dev"
350+    assert "$lines[2]" same_as "activating pipenv"
351+}
352+
353 @test 'mkvenv - uses default python if set and not specified' {
354     mkdir myproject
355     cd myproject
356@@ -95,10 +135,54 @@
357     run mkvenv
358 
359     assert $status equals 0
360-    assert "$lines[1]" contains "zsh-autoswitch-virtualenv requires virtualenv to be installed!"
361+    assert "$lines[1]" contains "zsh-autoswitch-virtualenv requires 'virtualenv' to install this project!"
362     assert "$lines[2]" is_empty
363     assert "$lines[3]" contains "If this is already installed but you are still seeing this message,"
364     assert "$lines[4]" contains "then make sure the \e[1mvirtualenv\e[0m command is in your PATH.\n"
365     assert "$lines[5]" is_empty
366     assert ${#lines} equals 4
367 }
368+
369+@test 'prints help message and disables plugin if poetry not setup' {
370+    touch "poetry.lock"
371+
372+    # Mock type to fail
373+    function type() {
374+        if [[ "$1" == "poetry" ]]; then
375+            return 1
376+        fi
377+        return 0
378+    }
379+
380+    run mkvenv
381+
382+    assert $status equals 0
383+    assert "$lines[1]" contains "zsh-autoswitch-virtualenv requires 'poetry' to install this project!"
384+    assert "$lines[2]" is_empty
385+    assert "$lines[3]" contains "If this is already installed but you are still seeing this message,"
386+    assert "$lines[4]" contains "then make sure the \e[1mpoetry\e[0m command is in your PATH.\n"
387+    assert "$lines[5]" is_empty
388+    assert ${#lines} equals 4
389+}
390+
391+@test 'prints help message and disables plugin if pipenv not setup' {
392+    touch "Pipfile"
393+
394+    # Mock type to fail
395+    function type() {
396+        if [[ "$1" == "pipenv" ]]; then
397+            return 1
398+        fi
399+        return 0
400+    }
401+
402diff --git a/tests/test_rmvenv.zunit b/tests/test_rmvenv.zunit
403index 0e1b51df1ebc7c34fce96e0212e4824a95a496b8..c7fde66c80f7d479c6d00ed5db0c1e0c74598efc 100644
404--- a/tests/test_rmvenv.zunit
405+++ b/tests/test_rmvenv.zunit
406@@ -7,6 +7,18 @@
407     unset AUTOSWITCH_DEFAULT_REQUIREMENTS
408     TARGET="$(mktemp -d)"
409     cd "$TARGET"
410+
411+    function poetry {
412+        echo 'poetry' $@
413+    }
414+
415+    function pipenv {
416+        echo 'pipenv' $@
417+    }
418+
419+    function deactivate {
420+        echo 'deactivate'
421+    }
422 }
423 
424 @teardown {
425@@ -64,3 +76,24 @@
426     test ! -f .venv
427     test ! -d "$HOME/.virtualenvs/$test_venv"
428 }
429+
430+@test 'rmvenv - uninstalls poetry project' {
431+    touch "poetry.lock"
432+
433+    run rmvenv
434+
435+    assert $state equals 0
436+    assert "$lines[1]" same_as "deactivate"
437+    assert "$lines[2]" same_as "poetry env remove poetry run which python"
438+}
439+
440+
441+@test 'rmvenv - uninstalls pipenv project' {
442+    touch "Pipfile"
443+
444+    run rmvenv
445+
446+    assert $state equals 0
447+    assert "$lines[1]" same_as "deactivate"
448+    assert "$lines[2]" same_as "pipenv --rm"
449+}