mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 16:34:47 +01:00
commit
7d3892d1f0
5 changed files with 139 additions and 2 deletions
13
news/vox-fs.rst
Normal file
13
news/vox-fs.rst
Normal file
|
@ -0,0 +1,13 @@
|
|||
**Added:** None
|
||||
|
||||
**Changed:**
|
||||
|
||||
* ``voxapi.Vox`` now supports ``pathlib.Path`` and ``PathLike`` objects as virtual environment identifiers
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:** None
|
||||
|
||||
**Security:** None
|
|
@ -125,6 +125,30 @@ def test_crud_subdir(xonsh_builtins, tmpdir):
|
|||
|
||||
assert not tmpdir.join('spam', 'eggs').check()
|
||||
|
||||
try:
|
||||
import pathlib
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
@skip_if_on_conda
|
||||
def test_crud_path(xonsh_builtins, tmpdir):
|
||||
"""
|
||||
Creates a virtual environment, gets it, enumerates it, and then deletes it.
|
||||
"""
|
||||
tmp = pathlib.Path(str(tmpdir))
|
||||
|
||||
vox = Vox()
|
||||
vox.create(tmp)
|
||||
assert stat.S_ISDIR(tmpdir.join('lib').stat().mode)
|
||||
|
||||
ve = vox[tmp]
|
||||
assert ve.env == str(tmp)
|
||||
assert os.path.isdir(ve.bin)
|
||||
|
||||
del vox[tmp]
|
||||
|
||||
assert not tmpdir.check()
|
||||
|
||||
|
||||
@skip_if_on_conda
|
||||
def test_crud_subdir(xonsh_builtins, tmpdir):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
__version__ = '0.4.7'
|
||||
|
||||
# amalgamate exclude jupyter_kernel parser_table parser_test_table pyghooks
|
||||
# amalgamate exclude winutils wizard pytest_plugin
|
||||
# amalgamate exclude winutils wizard pytest_plugin fs
|
||||
import os as _os
|
||||
if _os.getenv('XONSH_DEBUG', ''):
|
||||
pass
|
||||
|
|
94
xonsh/fs.py
Normal file
94
xonsh/fs.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
"""
|
||||
Backported functions to implement the PEP 519 (Adding a file system path protocol) API.
|
||||
"""
|
||||
|
||||
import abc
|
||||
import sys
|
||||
import io
|
||||
import pathlib
|
||||
|
||||
try:
|
||||
from os import PathLike, fspath, fsencode, fsdecode
|
||||
except ImportError:
|
||||
class PathLike(abc.ABC):
|
||||
"""Abstract base class for implementing the file system path protocol."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __fspath__(self):
|
||||
"""Return the file system path representation of the object."""
|
||||
raise NotImplementedError
|
||||
|
||||
PathLike.register(pathlib.Path)
|
||||
|
||||
def fspath(path):
|
||||
"""Return the string representation of the path.
|
||||
|
||||
If str or bytes is passed in, it is returned unchanged. If __fspath__()
|
||||
returns something other than str or bytes then TypeError is raised. If
|
||||
this function is given something that is not str, bytes, or os.PathLike
|
||||
then TypeError is raised.
|
||||
"""
|
||||
if isinstance(path, (str, bytes)):
|
||||
return path
|
||||
|
||||
if isinstance(path, pathlib.Path):
|
||||
return str(path)
|
||||
|
||||
# Work from the object's type to match method resolution of other magic
|
||||
# methods.
|
||||
path_type = type(path)
|
||||
try:
|
||||
path = path_type.__fspath__(path)
|
||||
except AttributeError:
|
||||
if hasattr(path_type, '__fspath__'):
|
||||
raise
|
||||
else:
|
||||
if isinstance(path, (str, bytes)):
|
||||
return path
|
||||
else:
|
||||
raise TypeError("expected __fspath__() to return str or bytes, "
|
||||
"not " + type(path).__name__)
|
||||
|
||||
raise TypeError("expected str, bytes or os.PathLike object, not "
|
||||
+ path_type.__name__)
|
||||
|
||||
def _fscodec():
|
||||
encoding = sys.getfilesystemencoding()
|
||||
if encoding == 'mbcs':
|
||||
errors = 'strict'
|
||||
else:
|
||||
errors = 'surrogateescape'
|
||||
|
||||
def fsencode(filename):
|
||||
"""Encode filename (an os.PathLike, bytes, or str) to the filesystem
|
||||
encoding with 'surrogateescape' error handler, return bytes unchanged.
|
||||
On Windows, use 'strict' error handler if the file system encoding is
|
||||
'mbcs' (which is the default encoding).
|
||||
"""
|
||||
filename = fspath(filename) # Does type-checking of `filename`.
|
||||
if isinstance(filename, str):
|
||||
return filename.encode(encoding, errors)
|
||||
else:
|
||||
return filename
|
||||
|
||||
def fsdecode(filename):
|
||||
"""Decode filename (an os.PathLike, bytes, or str) from the filesystem
|
||||
encoding with 'surrogateescape' error handler, return str unchanged. On
|
||||
Windows, use 'strict' error handler if the file system encoding is
|
||||
'mbcs' (which is the default encoding).
|
||||
"""
|
||||
filename = fspath(filename) # Does type-checking of `filename`.
|
||||
if isinstance(filename, bytes):
|
||||
return filename.decode(encoding, errors)
|
||||
else:
|
||||
return filename
|
||||
|
||||
return fsencode, fsdecode
|
||||
|
||||
fsencode, fsdecode = _fscodec()
|
||||
del _fscodec
|
||||
|
||||
def open(file, *pargs, **kwargs):
|
||||
if isinstance(file, PathLike):
|
||||
file = fspath(file)
|
||||
return io.open(file, *pargs, **kwargs)
|
|
@ -16,6 +16,7 @@ import builtins
|
|||
import collections.abc
|
||||
|
||||
from xonsh.platform import ON_POSIX, ON_WINDOWS
|
||||
from xonsh.fs import PathLike, fspath
|
||||
|
||||
# This is because builtins aren't globally created during testing.
|
||||
# FIXME: Is there a better way?
|
||||
|
@ -132,7 +133,10 @@ class Vox(collections.abc.Mapping):
|
|||
"""
|
||||
# NOTE: clear=True is the same as delete then create.
|
||||
# NOTE: upgrade=True is its own method
|
||||
env_path = os.path.join(self.venvdir, name)
|
||||
if isinstance(name, PathLike):
|
||||
env_path = fspath(name)
|
||||
else:
|
||||
env_path = os.path.join(self.venvdir, name)
|
||||
if not self._check_reserved(env_path):
|
||||
raise ValueError("venv can't contain reserved names ({})".format(', '.join(_subdir_names())))
|
||||
venv.create(
|
||||
|
@ -194,6 +198,8 @@ class Vox(collections.abc.Mapping):
|
|||
"""
|
||||
if name is ...:
|
||||
env_paths = [builtins.__xonsh_env__['VIRTUAL_ENV']]
|
||||
elif isinstance(name, PathLike):
|
||||
env_paths = [fspath(name)]
|
||||
else:
|
||||
if not self._check_reserved(name):
|
||||
# Don't allow a venv that could be a venv special dir
|
||||
|
|
Loading…
Add table
Reference in a new issue