diff options
| author | Bernát Gábor <bgabor8@bloomberg.net> | 2020-01-03 13:02:06 +0000 |
|---|---|---|
| committer | Bernat Gabor <bgabor8@bloomberg.net> | 2020-01-10 15:38:37 +0000 |
| commit | bca1a13e9ffd2e741e604bcf6ef500f60dd349b8 (patch) | |
| tree | 23325454f99d7ba1369cff2a31aeb89d52baca2d /src/virtualenv/util/path | |
| parent | ff6dc73d447a3c6276af64df2eb91e2709e450a3 (diff) | |
| download | virtualenv-bca1a13e9ffd2e741e604bcf6ef500f60dd349b8.tar.gz | |
interface compatibility with before rewrite (#1479)
Ensure that what ran with virtualenv 17 will continue running in a post
rewrite world minus the deprecated flags, plus the relocatable feature.
Signed-off-by: Bernat Gabor <bgabor8@bloomberg.net>
Diffstat (limited to 'src/virtualenv/util/path')
| -rw-r--r-- | src/virtualenv/util/path/__init__.py | 12 | ||||
| -rw-r--r-- | src/virtualenv/util/path/_pathlib/__init__.py | 40 | ||||
| -rw-r--r-- | src/virtualenv/util/path/_pathlib/via_os_path.py | 110 | ||||
| -rw-r--r-- | src/virtualenv/util/path/_sync.py | 50 |
4 files changed, 212 insertions, 0 deletions
diff --git a/src/virtualenv/util/path/__init__.py b/src/virtualenv/util/path/__init__.py new file mode 100644 index 0000000..ab5db8e --- /dev/null +++ b/src/virtualenv/util/path/__init__.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import, unicode_literals + +from ._pathlib import Path +from ._sync import copy, ensure_dir, symlink, symlink_or_copy + +__all__ = ( + "ensure_dir", + "symlink_or_copy", + "symlink", + "copy", + "Path", +) diff --git a/src/virtualenv/util/path/_pathlib/__init__.py b/src/virtualenv/util/path/_pathlib/__init__.py new file mode 100644 index 0000000..3b18d3d --- /dev/null +++ b/src/virtualenv/util/path/_pathlib/__init__.py @@ -0,0 +1,40 @@ +from __future__ import absolute_import, unicode_literals + +import sys + +import six + +if six.PY3: + from pathlib import Path + + if sys.version_info[0:2] == (3, 4): + # no read/write text on python3.4 + BuiltinPath = Path + + class Path(type(BuiltinPath())): + def read_text(self, encoding=None, errors=None): + """ + Open the file in text mode, read it, and close the file. + """ + with self.open(mode="r", encoding=encoding, errors=errors) as f: + return f.read() + + def write_text(self, data, encoding=None, errors=None): + """ + Open the file in text mode, write to it, and close the file. + """ + if not isinstance(data, str): + raise TypeError("data must be str, not %s" % data.__class__.__name__) + with self.open(mode="w", encoding=encoding, errors=errors) as f: + return f.write(data) + + +else: + if sys.platform == "win32": + # workaround for https://github.com/mcmtroffaes/pathlib2/issues/56 + from .via_os_path import Path + else: + from pathlib2 import Path + + +__all__ = ("Path",) diff --git a/src/virtualenv/util/path/_pathlib/via_os_path.py b/src/virtualenv/util/path/_pathlib/via_os_path.py new file mode 100644 index 0000000..1dddd7e --- /dev/null +++ b/src/virtualenv/util/path/_pathlib/via_os_path.py @@ -0,0 +1,110 @@ +from __future__ import absolute_import, unicode_literals + +import os +from contextlib import contextmanager + +import six + + +class Path(object): + def __init__(self, path): + self._path = path._path if isinstance(path, Path) else six.ensure_text(path) + + def __repr__(self): + return six.ensure_str("Path({})".format(self._path)) + + def __str__(self): + return six.ensure_str(self._path) + + def __div__(self, other): + return Path(os.path.join(self._path, other._path if isinstance(other, Path) else six.ensure_text(other))) + + def __truediv__(self, other): + return self.__div__(other) + + def __eq__(self, other): + return self._path == (other._path if isinstance(other, Path) else None) + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash(self._path) + + def exists(self): + return os.path.exists(self._path) + + def absolute(self): + return Path(os.path.abspath(self._path)) + + @property + def parent(self): + return Path(os.path.abspath(os.path.join(self._path, os.path.pardir))) + + def resolve(self): + return Path(os.path.realpath(self._path)) + + @property + def name(self): + return os.path.basename(self._path) + + @property + def parts(self): + return self._path.split(os.sep) + + def is_file(self): + return os.path.isfile(self._path) + + def is_dir(self): + return os.path.isdir(self._path) + + def mkdir(self, parents=True, exist_ok=True): + if not self.exists() and exist_ok: + os.makedirs(self._path) + + def read_text(self, encoding="utf-8"): + with open(self._path, "rb") as file_handler: + return file_handler.read().decode(encoding) + + def write_text(self, text, encoding="utf-8"): + with open(self._path, "wb") as file_handler: + file_handler.write(text.encode(encoding)) + + def iterdir(self): + for p in os.listdir(self._path): + yield Path(os.path.join(self._path, p)) + + @property + def suffix(self): + _, ext = os.path.splitext(self.name) + return ext + + @property + def stem(self): + base, _ = os.path.splitext(self.name) + return base + + @contextmanager + def open(self, mode="r"): + with open(self._path, mode) as file_handler: + yield file_handler + + @property + def parents(self): + result = [] + parts = self.parts + for i in range(len(parts)): + result.append(Path(os.sep.join(parts[0 : i + 1]))) + return result + + def unlink(self): + os.remove(self._path) + + def with_name(self, name): + return self.parent / name + + def is_symlink(self): + return os.path.islink(self._path) + + +__all__ = ("Path",) diff --git a/src/virtualenv/util/path/_sync.py b/src/virtualenv/util/path/_sync.py new file mode 100644 index 0000000..c3d6111 --- /dev/null +++ b/src/virtualenv/util/path/_sync.py @@ -0,0 +1,50 @@ +from __future__ import absolute_import, unicode_literals + +import logging +import os +import shutil +from functools import partial + +import six + +HAS_SYMLINK = hasattr(os, "symlink") + + +def ensure_dir(path): + if not path.exists(): + logging.debug("created %s", six.ensure_text(str(path))) + os.makedirs(six.ensure_text(str(path))) + + +def symlink_or_copy(do_copy, src, dst, relative_symlinks_ok=False): + """ + Try symlinking a target, and if that fails, fall back to copying. + """ + if do_copy is False and HAS_SYMLINK is False: # if no symlink, always use copy + do_copy = True + if not do_copy: + try: + if not dst.is_symlink(): # can't link to itself! + if relative_symlinks_ok: + assert src.parent == dst.parent + os.symlink(six.ensure_text(src.name), six.ensure_text(str(dst))) + else: + os.symlink(six.ensure_text(str(src)), six.ensure_text(str(dst))) + except OSError as exception: + logging.warning( + "symlink failed %r, for %s to %s, will try copy", + exception, + six.ensure_text(str(src)), + six.ensure_text(str(dst)), + ) + do_copy = True + if do_copy: + copier = shutil.copy2 if src.is_file() else shutil.copytree + copier(six.ensure_text(str(src)), six.ensure_text(str(dst))) + logging.debug("%s %s to %s", "copy" if do_copy else "symlink", six.ensure_text(str(src)), six.ensure_text(str(dst))) + + +symlink = partial(symlink_or_copy, False) +copy = partial(symlink_or_copy, True) + +__all__ = ("ensure_dir", "symlink", "copy", "symlink_or_copy") |
