Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
python-poetry
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
python-poetry
Commits
54870697
Commit
54870697
authored
Mar 21, 2020
by
Ryan Butler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
remove_venv will empty folder contents if it can't delete folder itself
parent
288d9973
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
104 additions
and
44 deletions
+104
-44
poetry/utils/env.py
+36
-13
tests/console/commands/env/test_use.py
+6
-8
tests/utils/test_env.py
+62
-23
No files found.
poetry/utils/env.py
View file @
54870697
...
@@ -15,6 +15,7 @@ from typing import Dict
...
@@ -15,6 +15,7 @@ from typing import Dict
from
typing
import
List
from
typing
import
List
from
typing
import
Optional
from
typing
import
Optional
from
typing
import
Tuple
from
typing
import
Tuple
from
typing
import
Union
import
tomlkit
import
tomlkit
...
@@ -403,7 +404,7 @@ class EnvManager(object):
...
@@ -403,7 +404,7 @@ class EnvManager(object):
if
venv
.
path
.
name
==
python
:
if
venv
.
path
.
name
==
python
:
# Exact virtualenv name
# Exact virtualenv name
if
not
envs_file
.
exists
():
if
not
envs_file
.
exists
():
self
.
remove_venv
(
str
(
venv
.
path
)
)
self
.
remove_venv
(
venv
.
path
)
return
venv
return
venv
...
@@ -413,7 +414,7 @@ class EnvManager(object):
...
@@ -413,7 +414,7 @@ class EnvManager(object):
current_env
=
envs
.
get
(
base_env_name
)
current_env
=
envs
.
get
(
base_env_name
)
if
not
current_env
:
if
not
current_env
:
self
.
remove_venv
(
str
(
venv
.
path
)
)
self
.
remove_venv
(
venv
.
path
)
return
venv
return
venv
...
@@ -421,7 +422,7 @@ class EnvManager(object):
...
@@ -421,7 +422,7 @@ class EnvManager(object):
del
envs
[
base_env_name
]
del
envs
[
base_env_name
]
envs_file
.
write
(
envs
)
envs_file
.
write
(
envs
)
self
.
remove_venv
(
str
(
venv
.
path
)
)
self
.
remove_venv
(
venv
.
path
)
return
venv
return
venv
...
@@ -475,7 +476,7 @@ class EnvManager(object):
...
@@ -475,7 +476,7 @@ class EnvManager(object):
del
envs
[
base_env_name
]
del
envs
[
base_env_name
]
envs_file
.
write
(
envs
)
envs_file
.
write
(
envs
)
self
.
remove_venv
(
str
(
venv
)
)
self
.
remove_venv
(
venv
)
return
VirtualEnv
(
venv
)
return
VirtualEnv
(
venv
)
...
@@ -621,7 +622,7 @@ class EnvManager(object):
...
@@ -621,7 +622,7 @@ class EnvManager(object):
"Creating virtualenv <c1>{}</> in {}"
.
format
(
name
,
str
(
venv_path
))
"Creating virtualenv <c1>{}</> in {}"
.
format
(
name
,
str
(
venv_path
))
)
)
self
.
build_venv
(
str
(
venv
)
,
executable
=
executable
)
self
.
build_venv
(
venv
,
executable
=
executable
)
else
:
else
:
if
force
:
if
force
:
if
not
env
.
is_sane
():
if
not
env
.
is_sane
():
...
@@ -633,8 +634,8 @@ class EnvManager(object):
...
@@ -633,8 +634,8 @@ class EnvManager(object):
io
.
write_line
(
io
.
write_line
(
"Recreating virtualenv <c1>{}</> in {}"
.
format
(
name
,
str
(
venv
))
"Recreating virtualenv <c1>{}</> in {}"
.
format
(
name
,
str
(
venv
))
)
)
self
.
remove_venv
(
str
(
venv
)
)
self
.
remove_venv
(
venv
)
self
.
build_venv
(
str
(
venv
)
,
executable
=
executable
)
self
.
build_venv
(
venv
,
executable
=
executable
)
elif
io
.
is_very_verbose
():
elif
io
.
is_very_verbose
():
io
.
write_line
(
"Virtualenv <c1>{}</> already exists."
.
format
(
name
))
io
.
write_line
(
"Virtualenv <c1>{}</> already exists."
.
format
(
name
))
...
@@ -657,7 +658,9 @@ class EnvManager(object):
...
@@ -657,7 +658,9 @@ class EnvManager(object):
return
VirtualEnv
(
venv
)
return
VirtualEnv
(
venv
)
@classmethod
@classmethod
def
build_venv
(
cls
,
path
,
executable
=
None
):
def
build_venv
(
cls
,
path
,
executable
=
None
):
# type: (Union[Path,str], Optional[str]) -> ()
if
executable
is
not
None
:
if
executable
is
not
None
:
# Create virtualenv by using an external executable
# Create virtualenv by using an external executable
try
:
try
:
...
@@ -682,21 +685,41 @@ class EnvManager(object):
...
@@ -682,21 +685,41 @@ class EnvManager(object):
use_symlinks
=
True
use_symlinks
=
True
builder
=
EnvBuilder
(
with_pip
=
True
,
symlinks
=
use_symlinks
)
builder
=
EnvBuilder
(
with_pip
=
True
,
symlinks
=
use_symlinks
)
builder
.
create
(
path
)
builder
.
create
(
str
(
path
)
)
except
ImportError
:
except
ImportError
:
try
:
try
:
# We fallback on virtualenv for Python 2.7
# We fallback on virtualenv for Python 2.7
from
virtualenv
import
create_environment
from
virtualenv
import
create_environment
create_environment
(
path
)
create_environment
(
str
(
path
)
)
except
ImportError
:
except
ImportError
:
# since virtualenv>20 we have to use cli_run
# since virtualenv>20 we have to use cli_run
from
virtualenv
import
cli_run
from
virtualenv
import
cli_run
cli_run
([
path
])
cli_run
([
str
(
path
)
])
def
remove_venv
(
self
,
path
):
# type: (str) -> None
@classmethod
shutil
.
rmtree
(
path
)
def
remove_venv
(
cls
,
path
):
# type: (Union[Path,str]) -> None
if
isinstance
(
path
,
str
):
path
=
Path
(
path
)
assert
path
.
is_dir
()
try
:
shutil
.
rmtree
(
str
(
path
))
return
except
OSError
as
e
:
# Continue only if e.errno == 16
if
e
.
errno
!=
16
:
# ERRNO 16: Device or resource busy
raise
e
# Delete all files and folders but the toplevel one. This is because sometimes
# the venv folder is mounted by the OS, such as in a docker volume. In such
# cases, an attempt to delete the folder itself will result in an `OSError`.
# See https://github.com/python-poetry/poetry/pull/2064
for
file_path
in
path
.
iterdir
():
if
file_path
.
is_file
()
or
file_path
.
is_symlink
():
file_path
.
unlink
()
elif
file_path
.
is_dir
():
shutil
.
rmtree
(
str
(
file_path
))
def
get_base_prefix
(
self
):
# type: () -> Path
def
get_base_prefix
(
self
):
# type: () -> Path
if
hasattr
(
sys
,
"real_prefix"
):
if
hasattr
(
sys
,
"real_prefix"
):
...
...
tests/console/commands/env/test_use.py
View file @
54870697
import
os
import
os
import
shutil
import
sys
import
sys
from
typing
import
Optional
from
typing
import
Union
import
tomlkit
import
tomlkit
from
cleo.testers
import
CommandTester
from
cleo.testers
import
CommandTester
...
@@ -16,12 +18,8 @@ from poetry.utils.toml_file import TomlFile
...
@@ -16,12 +18,8 @@ from poetry.utils.toml_file import TomlFile
CWD
=
Path
(
__file__
)
.
parent
.
parent
/
"fixtures"
/
"simple_project"
CWD
=
Path
(
__file__
)
.
parent
.
parent
/
"fixtures"
/
"simple_project"
def
build_venv
(
path
,
executable
=
None
):
def
build_venv
(
path
,
executable
=
None
):
# type: (Union[Path,str], Optional[str]) -> ()
os
.
mkdir
(
path
)
os
.
mkdir
(
str
(
path
))
def
remove_venv
(
path
):
shutil
.
rmtree
(
path
)
def
check_output_wrapper
(
version
=
Version
.
parse
(
"3.7.1"
)):
def
check_output_wrapper
(
version
=
Version
.
parse
(
"3.7.1"
)):
...
@@ -62,7 +60,7 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(app, tmp_dir, m
...
@@ -62,7 +60,7 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(app, tmp_dir, m
)
)
m
.
assert_called_with
(
m
.
assert_called_with
(
os
.
path
.
join
(
tmp_dir
,
"{}-py3.7"
.
format
(
venv_name
)
),
executable
=
"python3.7"
Path
(
tmp_dir
)
/
"{}-py3.7"
.
format
(
venv_name
),
executable
=
"python3.7"
)
)
envs_file
=
TomlFile
(
Path
(
tmp_dir
)
/
"envs.toml"
)
envs_file
=
TomlFile
(
Path
(
tmp_dir
)
/
"envs.toml"
)
...
...
tests/utils/test_env.py
View file @
54870697
...
@@ -2,6 +2,9 @@ import os
...
@@ -2,6 +2,9 @@ import os
import
shutil
import
shutil
import
sys
import
sys
from
typing
import
Optional
from
typing
import
Union
import
pytest
import
pytest
import
tomlkit
import
tomlkit
...
@@ -83,12 +86,8 @@ def test_env_get_in_project_venv(manager, poetry):
...
@@ -83,12 +86,8 @@ def test_env_get_in_project_venv(manager, poetry):
shutil
.
rmtree
(
str
(
venv
.
path
))
shutil
.
rmtree
(
str
(
venv
.
path
))
def
build_venv
(
path
,
executable
=
None
):
def
build_venv
(
path
,
executable
=
None
):
# type: (Union[Path,str], Optional[str]) -> ()
os
.
mkdir
(
path
)
os
.
mkdir
(
str
(
path
))
def
remove_venv
(
path
):
shutil
.
rmtree
(
path
)
def
check_output_wrapper
(
version
=
Version
.
parse
(
"3.7.1"
)):
def
check_output_wrapper
(
version
=
Version
.
parse
(
"3.7.1"
)):
...
@@ -125,7 +124,7 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(
...
@@ -125,7 +124,7 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(
venv_name
=
EnvManager
.
generate_env_name
(
"simple-project"
,
str
(
poetry
.
file
.
parent
))
venv_name
=
EnvManager
.
generate_env_name
(
"simple-project"
,
str
(
poetry
.
file
.
parent
))
m
.
assert_called_with
(
m
.
assert_called_with
(
os
.
path
.
join
(
tmp_dir
,
"{}-py3.7"
.
format
(
venv_name
)
),
executable
=
"python3.7"
Path
(
tmp_dir
)
/
"{}-py3.7"
.
format
(
venv_name
),
executable
=
"python3.7"
)
)
envs_file
=
TomlFile
(
Path
(
tmp_dir
)
/
"envs.toml"
)
envs_file
=
TomlFile
(
Path
(
tmp_dir
)
/
"envs.toml"
)
...
@@ -243,7 +242,7 @@ def test_activate_activates_different_virtualenv_with_envs_file(
...
@@ -243,7 +242,7 @@ def test_activate_activates_different_virtualenv_with_envs_file(
env
=
manager
.
activate
(
"python3.6"
,
NullIO
())
env
=
manager
.
activate
(
"python3.6"
,
NullIO
())
m
.
assert_called_with
(
m
.
assert_called_with
(
os
.
path
.
join
(
tmp_dir
,
"{}-py3.6"
.
format
(
venv_name
)
),
executable
=
"python3.6"
Path
(
tmp_dir
)
/
"{}-py3.6"
.
format
(
venv_name
),
executable
=
"python3.6"
)
)
assert
envs_file
.
exists
()
assert
envs_file
.
exists
()
...
@@ -289,17 +288,15 @@ def test_activate_activates_recreates_for_different_patch(
...
@@ -289,17 +288,15 @@ def test_activate_activates_recreates_for_different_patch(
"poetry.utils.env.EnvManager.build_venv"
,
side_effect
=
build_venv
"poetry.utils.env.EnvManager.build_venv"
,
side_effect
=
build_venv
)
)
remove_venv_m
=
mocker
.
patch
(
remove_venv_m
=
mocker
.
patch
(
"poetry.utils.env.EnvManager.remove_venv"
,
side_effect
=
remove_venv
"poetry.utils.env.EnvManager.remove_venv"
,
side_effect
=
EnvManager
.
remove_venv
)
)
env
=
manager
.
activate
(
"python3.7"
,
NullIO
())
env
=
manager
.
activate
(
"python3.7"
,
NullIO
())
build_venv_m
.
assert_called_with
(
build_venv_m
.
assert_called_with
(
os
.
path
.
join
(
tmp_dir
,
"{}-py3.7"
.
format
(
venv_name
)),
executable
=
"python3.7"
Path
(
tmp_dir
)
/
"{}-py3.7"
.
format
(
venv_name
),
executable
=
"python3.7"
)
remove_venv_m
.
assert_called_with
(
os
.
path
.
join
(
tmp_dir
,
"{}-py3.7"
.
format
(
venv_name
))
)
)
remove_venv_m
.
assert_called_with
(
Path
(
tmp_dir
)
/
"{}-py3.7"
.
format
(
venv_name
))
assert
envs_file
.
exists
()
assert
envs_file
.
exists
()
envs
=
envs_file
.
read
()
envs
=
envs_file
.
read
()
...
@@ -340,7 +337,7 @@ def test_activate_does_not_recreate_when_switching_minor(
...
@@ -340,7 +337,7 @@ def test_activate_does_not_recreate_when_switching_minor(
"poetry.utils.env.EnvManager.build_venv"
,
side_effect
=
build_venv
"poetry.utils.env.EnvManager.build_venv"
,
side_effect
=
build_venv
)
)
remove_venv_m
=
mocker
.
patch
(
remove_venv_m
=
mocker
.
patch
(
"poetry.utils.env.EnvManager.remove_venv"
,
side_effect
=
remove_venv
"poetry.utils.env.EnvManager.remove_venv"
,
side_effect
=
EnvManager
.
remove_venv
)
)
env
=
manager
.
activate
(
"python3.6"
,
NullIO
())
env
=
manager
.
activate
(
"python3.6"
,
NullIO
())
...
@@ -535,6 +532,54 @@ def test_remove_also_deactivates(tmp_dir, manager, poetry, config, mocker):
...
@@ -535,6 +532,54 @@ def test_remove_also_deactivates(tmp_dir, manager, poetry, config, mocker):
assert
venv_name
not
in
envs
assert
venv_name
not
in
envs
def
test_remove_keeps_dir_if_not_deleteable
(
tmp_dir
,
manager
,
poetry
,
config
,
mocker
):
# Ensure we empty rather than delete folder if its is an active mount point.
# See https://github.com/python-poetry/poetry/pull/2064
config
.
merge
({
"virtualenvs"
:
{
"path"
:
str
(
tmp_dir
)}})
venv_name
=
manager
.
generate_env_name
(
"simple-project"
,
str
(
poetry
.
file
.
parent
))
venv_path
=
Path
(
tmp_dir
)
/
"{}-py3.6"
.
format
(
venv_name
)
venv_path
.
mkdir
()
folder1_path
=
venv_path
/
"folder1"
folder1_path
.
mkdir
()
file1_path
=
folder1_path
/
"file1"
file1_path
.
touch
(
exist_ok
=
False
)
file2_path
=
venv_path
/
"file2"
file2_path
.
touch
(
exist_ok
=
False
)
mocker
.
patch
(
"poetry.utils._compat.subprocess.check_output"
,
side_effect
=
check_output_wrapper
(
Version
.
parse
(
"3.6.6"
)),
)
original_rmtree
=
shutil
.
rmtree
def
err_on_rm_venv_only
(
path
,
*
args
,
**
kwargs
):
print
(
path
)
if
path
==
str
(
venv_path
):
raise
OSError
(
16
,
"Test error"
)
# ERRNO 16: Device or resource busy
else
:
original_rmtree
(
path
)
m
=
mocker
.
patch
(
"shutil.rmtree"
,
side_effect
=
err_on_rm_venv_only
)
venv
=
manager
.
remove
(
"{}-py3.6"
.
format
(
venv_name
))
m
.
assert_any_call
(
str
(
venv_path
))
assert
venv_path
==
venv
.
path
assert
venv_path
.
exists
()
assert
not
folder1_path
.
exists
()
assert
not
file1_path
.
exists
()
assert
not
file2_path
.
exists
()
m
.
side_effect
=
original_rmtree
# Avoid teardown using `err_on_rm_venv_only`
def
test_env_has_symlinks_on_nix
(
tmp_dir
,
tmp_venv
):
def
test_env_has_symlinks_on_nix
(
tmp_dir
,
tmp_venv
):
venv_available
=
False
venv_available
=
False
try
:
try
:
...
@@ -584,7 +629,7 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_
...
@@ -584,7 +629,7 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_
manager
.
create_venv
(
NullIO
())
manager
.
create_venv
(
NullIO
())
m
.
assert_called_with
(
m
.
assert_called_with
(
str
(
Path
(
"/foo/virtualenvs/{}-py3.7"
.
format
(
venv_name
)
)),
executable
=
"python3"
Path
(
"/foo/virtualenvs/{}-py3.7"
.
format
(
venv_name
)),
executable
=
"python3"
)
)
...
@@ -608,7 +653,7 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific
...
@@ -608,7 +653,7 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific
manager
.
create_venv
(
NullIO
())
manager
.
create_venv
(
NullIO
())
m
.
assert_called_with
(
m
.
assert_called_with
(
str
(
Path
(
"/foo/virtualenvs/{}-py3.8"
.
format
(
venv_name
)
)),
executable
=
"python3.8"
Path
(
"/foo/virtualenvs/{}-py3.8"
.
format
(
venv_name
)),
executable
=
"python3.8"
)
)
...
@@ -691,12 +736,10 @@ def test_create_venv_uses_patch_version_to_detect_compatibility(
...
@@ -691,12 +736,10 @@ def test_create_venv_uses_patch_version_to_detect_compatibility(
assert
not
check_output
.
called
assert
not
check_output
.
called
m
.
assert_called_with
(
m
.
assert_called_with
(
str
(
Path
(
Path
(
"/foo/virtualenvs/{}-py{}.{}"
.
format
(
"/foo/virtualenvs/{}-py{}.{}"
.
format
(
venv_name
,
version
.
major
,
version
.
minor
venv_name
,
version
.
major
,
version
.
minor
)
)
)
),
),
executable
=
None
,
executable
=
None
,
)
)
...
@@ -730,12 +773,10 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable(
...
@@ -730,12 +773,10 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable(
assert
check_output
.
called
assert
check_output
.
called
m
.
assert_called_with
(
m
.
assert_called_with
(
str
(
Path
(
Path
(
"/foo/virtualenvs/{}-py{}.{}"
.
format
(
"/foo/virtualenvs/{}-py{}.{}"
.
format
(
venv_name
,
version
.
major
,
version
.
minor
-
1
venv_name
,
version
.
major
,
version
.
minor
-
1
)
)
)
),
),
executable
=
"python{}.{}"
.
format
(
version
.
major
,
version
.
minor
-
1
),
executable
=
"python{}.{}"
.
format
(
version
.
major
,
version
.
minor
-
1
),
)
)
...
@@ -768,9 +809,7 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir(
...
@@ -768,9 +809,7 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir(
manager
.
activate
(
"python3.7"
,
NullIO
())
manager
.
activate
(
"python3.7"
,
NullIO
())
m
.
assert_called_with
(
m
.
assert_called_with
(
poetry
.
file
.
parent
/
".venv"
,
executable
=
"python3.7"
)
os
.
path
.
join
(
str
(
poetry
.
file
.
parent
),
".venv"
),
executable
=
"python3.7"
)
envs_file
=
TomlFile
(
Path
(
tmp_dir
)
/
"virtualenvs"
/
"envs.toml"
)
envs_file
=
TomlFile
(
Path
(
tmp_dir
)
/
"virtualenvs"
/
"envs.toml"
)
assert
not
envs_file
.
exists
()
assert
not
envs_file
.
exists
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment