Skip to content

Commit

Permalink
fix: returned generators and iterators yield wrapped Paths now
Browse files Browse the repository at this point in the history
  • Loading branch information
matfax committed Oct 15, 2019
1 parent b95f234 commit 4a9cf29
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 40 deletions.
20 changes: 12 additions & 8 deletions mutapath/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,21 @@ def __is_def(member):
return inspect.isroutine(member)


def _convert_path(result):
if isinstance(result, path.Path):
return mutapath.Path(result)
return result


def __path_func(orig_func):
def wrap_decorator(*args, **kwargs):
result = orig_func(*args, **kwargs)
if isinstance(result, path.Path):
return mutapath.Path(result)
return result
return _convert_path(result)

return wrap_decorator


def path_wrap(cls):
for name, method in inspect.getmembers(path.Path, __is_def):
if not name.startswith("__"):
setattr(path.Path, name, __path_func(method))
for name, method in inspect.getmembers(cls, __is_def):
if name not in __EXCLUDE_FROM_WRAPPING:
setattr(cls, name, __path_func(method))
Expand All @@ -48,8 +49,11 @@ def mutation_decorator(self, *args, **kwargs):
orig_func = getattr(path.Path, method_name)
if isinstance(self, Path):
result = orig_func(self._contained, *args, **kwargs)
if isinstance(result, mutapath.Path):
self._contained = result._contained
if isinstance(result, path.Path):
self._contained = result
return self
elif isinstance(result, mutapath.Path):
self._contained = getattr(result, "_contained")
return self
return result
else:
Expand Down
29 changes: 23 additions & 6 deletions mutapath/immutapath.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import pathlib
from contextlib import contextmanager
from dataclasses import dataclass
from typing import Union, Iterable, ClassVar, Callable
from types import GeneratorType
from typing import Union, Iterable, ClassVar, Callable, List
from xml.dom.minicompat import StringTypes

import path

import mutapath
from mutapath.decorator import path_wrap
from mutapath.decorator import path_wrap, _convert_path
from mutapath.exceptions import PathException


Expand All @@ -36,8 +38,23 @@ def __post_init__(self):
def __dir__(self) -> Iterable[str]:
return sorted(super(Path, self).__dir__()) + dir(path.Path)

@staticmethod
def __wrap_attribute(orig_func):
def __wrap_decorator(*args, **kwargs):
result = orig_func(*args, **kwargs)
if isinstance(result, List) and not isinstance(result, StringTypes):
return list(map(_convert_path, result))
if isinstance(result, Iterable) and not isinstance(result, StringTypes):
return iter(map(_convert_path, result))
if isinstance(result, GeneratorType):
return map(_convert_path, result)
return _convert_path(result)

return __wrap_decorator

def __getattr__(self, item):
return getattr(self._contained, item)
attr = getattr(self._contained, item)
return Path.__wrap_attribute(attr)

def __setattr__(self, key, value):
if key == "_contained":
Expand Down Expand Up @@ -152,7 +169,7 @@ def getcwd(cls) -> Path:

@path.multimethod
def joinpath(self, first, *others) -> Path:
contained_others = map(str, list(others))
contained_others = map(str, others)
joined = path.Path.joinpath(self._contained, str(first), *contained_others)
return Path(joined)

Expand Down Expand Up @@ -315,12 +332,12 @@ def _op_context(self, name: str, op: Callable):
except FileExistsError as e:
raise PathException(
f"{name.capitalize()} to {current_file.normpath()} failed because the file already exists. "
f"Falling back to original value {self._contained}.") from e
f"Falling back to original value {self._contained}.") from e
else:
if not current_file.exists():
raise PathException(
f"{name.capitalize()} to {current_file.normpath()} failed because can not be found. "
f"Falling back to original value {self._contained}.")
f"Falling back to original value {self._contained}.")

self._contained = current_file

Expand Down
50 changes: 32 additions & 18 deletions tests/test_immutapath.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,107 +11,119 @@ class TestPath(unittest.TestCase):
def test_with_name_posix(self):
expected = Path("/A/B/other")
actual = Path("/A/B/test1.txt").with_name("other")
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_with_name_win(self):
if os.name == 'nt':
expected = Path("C:/B/other")
actual = Path("C:/B/test1.txt").with_name("other")
self.assertEqual(expected.abspath(), actual.abspath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_with_base_posix(self):
expected = Path("/home/joe/folder/sub")
actual = Path("/home/doe/folder/sub").with_base("/home/joe")
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_with_base_length_posix(self):
expected = Path("/home/joe/doe/folder/sub")
actual = Path("/home/doe/folder/sub").with_base("/home/joe", 1)
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_with_base_win(self):
if os.name == 'nt':
expected = Path("C:/Users/joe/folder/sub")
actual = Path("C:/Users/doe/folder/sub").with_base("C:/Users/joe")
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_with_base_length_win(self):
if os.name == 'nt':
expected = Path("C:/Users/joe/doe/folder/sub").abspath()
actual = Path("C:/Users/doe/folder/sub").abspath().with_base("C:/Users/joe", 1)
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_with_base_fail(self):
def too_long():
with self.assertRaises(ValueError):
Path("/A/B/other.txt").with_base("/A/B/C")

self.assertRaises(ValueError, too_long)

def test_with_stem(self):
expected = Path("/A/B/other.txt")
actual = Path("/A/B/test1.txt").with_stem("other")
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_with_parent(self):
other = Path("/A/D/other.txt")
expected = Path("D/other.txt")
actual = Path("/A/B/other.txt").with_parent(other.dirname.name)
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_static_joinpath(self):
expected = Path("/A/B/C/D/other.txt")
actual = Path.joinpath("/A/B", "C/", Path("D"), MutaPath("other.txt"))
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_joinpath(self):
expected = Path("/A/B/C/D/other.txt")
actual = Path("/A/B").joinpath("C", Path("D"), MutaPath("other.txt"))
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_pathlib_path(self):
expected = Path("/A/B/other.txt")
actual = Path(pathlib.Path("/A/B")).joinpath(pathlib.PurePosixPath("other.txt"))
self.assertEqual(expected.normpath(), actual.normpath())
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_block_setter(self):
some = Path("/A/B/other.txt")

def set_name():
with self.assertRaises(AttributeError):
some.name = "try"

self.assertRaises(AttributeError, set_name)

def test_eq(self):
some = Path("/A/B/other.txt")
other = path.Path("/A/B/other.txt")
third = pathlib.Path("/A/B/other.txt")
self.assertEqual(some, other)
self.assertEqual(some, third)
self.assertIsInstance(some, Path)

def test_add(self):
expected = Path("/A/B/other.txt")
actual = Path("/A/") + "/B/" + "/other.txt"
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_radd(self):
expected = Path("/A/B/other.txt")
actual = "/A/" + Path("/B/") + "/other.txt"
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_div(self):
expected = Path("/A/B/other.txt")
actual = Path("/A/") / "B/other.txt"
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_rdiv(self):
expected = Path("/A/B/other.txt")
actual = "/A/" / Path("B") / "other.txt"
self.assertEqual(expected, actual)
self.assertIsInstance(actual, Path)

def test_capsulation(self):
excpected = Path("/A/B")
actual = Path(Path(excpected))
self.assertEqual(excpected, actual)
self.assertIsInstance(actual, Path)

def test_repr(self):
excpected = Path("/A/B")
Expand All @@ -127,12 +139,14 @@ def test_home(self):
actual = Path("/A/B/C").relpath("/A").home
self.assertEqual(excpected, actual)
self.assertEqual(excpected.abspath(), actual.abspath())
self.assertIsInstance(actual, Path)

def test_home_root(self):
excpected = Path(".")
actual = Path("/").home
self.assertEqual(excpected, actual)
self.assertEqual(excpected.abspath(), actual.abspath())
self.assertIsInstance(actual, Path)

def test_hash(self):
expected = hash(Path("/A") / "B")
Expand Down

0 comments on commit 4a9cf29

Please sign in to comment.