Commit e9e6b324 by Arun Babu Neelicattu

editable: use writable script dir for system env

This change ensures that, When using system environment, poetry falls
back to `userbase` if default location is not writable.
parent 4c81bcc6
...@@ -13,6 +13,7 @@ from poetry.core.semver.version import Version ...@@ -13,6 +13,7 @@ from poetry.core.semver.version import Version
from poetry.utils._compat import WINDOWS from poetry.utils._compat import WINDOWS
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils._compat import decode from poetry.utils._compat import decode
from poetry.utils.helpers import is_dir_writable
SCRIPT_TEMPLATE = """\ SCRIPT_TEMPLATE = """\
...@@ -128,7 +129,17 @@ class EditableBuilder(Builder): ...@@ -128,7 +129,17 @@ class EditableBuilder(Builder):
def _add_scripts(self): def _add_scripts(self):
added = [] added = []
entry_points = self.convert_entry_points() entry_points = self.convert_entry_points()
scripts_path = Path(self._env.paths["scripts"])
for scripts_path in self._env.script_dirs:
if is_dir_writable(scripts_path):
break
else:
self._io.error_line(
" - Failed to find a suitable script installation directory for {}".format(
self._poetry.file.parent
)
)
return []
scripts = entry_points.get("console_scripts", []) scripts = entry_points.get("console_scripts", [])
for script in scripts: for script in scripts:
...@@ -146,7 +157,7 @@ class EditableBuilder(Builder): ...@@ -146,7 +157,7 @@ class EditableBuilder(Builder):
f.write( f.write(
decode( decode(
SCRIPT_TEMPLATE.format( SCRIPT_TEMPLATE.format(
python=self._env._bin("python"), python=self._env.python,
module=module, module=module,
callable_holder=callable_holder, callable_holder=callable_holder,
callable_=callable_, callable_=callable_,
...@@ -160,9 +171,7 @@ class EditableBuilder(Builder): ...@@ -160,9 +171,7 @@ class EditableBuilder(Builder):
if WINDOWS: if WINDOWS:
cmd_script = script_file.with_suffix(".cmd") cmd_script = script_file.with_suffix(".cmd")
cmd = WINDOWS_CMD_TEMPLATE.format( cmd = WINDOWS_CMD_TEMPLATE.format(python=self._env.python, script=name)
python=self._env._bin("python"), script=name
)
self._debug( self._debug(
" - Adding the <c2>{}</c2> script wrapper to <b>{}</b>".format( " - Adding the <c2>{}</c2> script wrapper to <b>{}</b>".format(
cmd_script.name, scripts_path cmd_script.name, scripts_path
......
...@@ -7,7 +7,6 @@ import re ...@@ -7,7 +7,6 @@ import re
import shutil import shutil
import sys import sys
import sysconfig import sysconfig
import tempfile
import textwrap import textwrap
from contextlib import contextmanager from contextlib import contextmanager
...@@ -40,6 +39,7 @@ from poetry.utils._compat import decode ...@@ -40,6 +39,7 @@ from poetry.utils._compat import decode
from poetry.utils._compat import encode from poetry.utils._compat import encode
from poetry.utils._compat import list_to_shell_command from poetry.utils._compat import list_to_shell_command
from poetry.utils._compat import subprocess from poetry.utils._compat import subprocess
from poetry.utils.helpers import is_dir_writable
from poetry.utils.helpers import paths_csv from poetry.utils.helpers import paths_csv
...@@ -170,14 +170,9 @@ class SitePackages: ...@@ -170,14 +170,9 @@ class SitePackages:
self._writable_candidates = [] self._writable_candidates = []
for candidate in self._candidates: for candidate in self._candidates:
try: if not is_dir_writable(candidate):
if not candidate.exists(): continue
continue self._writable_candidates.append(candidate)
with tempfile.TemporaryFile(dir=str(candidate)):
self._writable_candidates.append(candidate)
except (IOError, OSError):
pass
return self._writable_candidates return self._writable_candidates
...@@ -892,6 +887,7 @@ class Env(object): ...@@ -892,6 +887,7 @@ class Env(object):
self._supported_tags = None self._supported_tags = None
self._purelib = None self._purelib = None
self._platlib = None self._platlib = None
self._script_dirs = None
@property @property
def path(self): # type: () -> Path def path(self): # type: () -> Path
...@@ -961,6 +957,11 @@ class Env(object): ...@@ -961,6 +957,11 @@ class Env(object):
return Path(self.paths["usersite"]) return Path(self.paths["usersite"])
@property @property
def userbase(self): # type: () -> Optional[Path]
if "userbase" in self.paths:
return Path(self.paths["userbase"])
@property
def purelib(self): # type: () -> Path def purelib(self): # type: () -> Path
if self._purelib is None: if self._purelib is None:
self._purelib = Path(self.paths["purelib"]) self._purelib = Path(self.paths["purelib"])
...@@ -1106,6 +1107,18 @@ class Env(object): ...@@ -1106,6 +1107,18 @@ class Env(object):
def is_venv(self): # type: () -> bool def is_venv(self): # type: () -> bool
raise NotImplementedError() raise NotImplementedError()
@property
def script_dirs(self): # type: () -> List[Path]
if self._script_dirs is None:
self._script_dirs = (
[Path(self.paths["scripts"])]
if "scripts" in self.paths
else self._bin_dir
)
if self.userbase:
self._script_dirs.append(self.userbase / self._script_dirs[0].name)
return self._script_dirs
def _bin(self, bin): # type: (str) -> str def _bin(self, bin): # type: (str) -> str
""" """
Return path to the given executable. Return path to the given executable.
...@@ -1142,6 +1155,10 @@ class SystemEnv(Env): ...@@ -1142,6 +1155,10 @@ class SystemEnv(Env):
""" """
@property @property
def python(self): # type: () -> str
return sys.executable
@property
def sys_path(self): # type: () -> List[str] def sys_path(self): # type: () -> List[str]
return sys.path return sys.path
...@@ -1181,6 +1198,7 @@ class SystemEnv(Env): ...@@ -1181,6 +1198,7 @@ class SystemEnv(Env):
if site.check_enableusersite() and hasattr(obj, "install_usersite"): if site.check_enableusersite() and hasattr(obj, "install_usersite"):
paths["usersite"] = getattr(obj, "install_usersite") paths["usersite"] = getattr(obj, "install_usersite")
paths["userbase"] = getattr(obj, "install_userbase")
return paths return paths
...@@ -1316,7 +1334,7 @@ class VirtualEnv(Env): ...@@ -1316,7 +1334,7 @@ class VirtualEnv(Env):
def is_sane(self): def is_sane(self):
# A virtualenv is considered sane if both "python" and "pip" exist. # A virtualenv is considered sane if both "python" and "pip" exist.
return os.path.exists(self._bin("python")) and os.path.exists(self._bin("pip")) return os.path.exists(self.python) and os.path.exists(self._bin("pip"))
def _run(self, cmd, **kwargs): def _run(self, cmd, **kwargs):
with self.temp_environ(): with self.temp_environ():
......
...@@ -118,3 +118,18 @@ def get_package_version_display_string( ...@@ -118,3 +118,18 @@ def get_package_version_display_string(
def paths_csv(paths): # type: (List[Path]) -> str def paths_csv(paths): # type: (List[Path]) -> str
return ", ".join('"{}"'.format(str(c)) for c in paths) return ", ".join('"{}"'.format(str(c)) for c in paths)
def is_dir_writable(path, create=False): # type: (Path, bool) -> bool
try:
if not path.exists():
if not create:
return False
path.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryFile(dir=str(path)):
pass
except (IOError, OSError):
return False
else:
return True
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment