summaryrefslogtreecommitdiff
path: root/src/virtualenv/util/path
diff options
context:
space:
mode:
authorBernát Gábor <bgabor8@bloomberg.net>2020-01-03 13:02:06 +0000
committerBernat Gabor <bgabor8@bloomberg.net>2020-01-10 15:38:37 +0000
commitbca1a13e9ffd2e741e604bcf6ef500f60dd349b8 (patch)
tree23325454f99d7ba1369cff2a31aeb89d52baca2d /src/virtualenv/util/path
parentff6dc73d447a3c6276af64df2eb91e2709e450a3 (diff)
downloadvirtualenv-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__.py12
-rw-r--r--src/virtualenv/util/path/_pathlib/__init__.py40
-rw-r--r--src/virtualenv/util/path/_pathlib/via_os_path.py110
-rw-r--r--src/virtualenv/util/path/_sync.py50
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")