Skip to content

Commit

Permalink
feat: add serialization support for mashumaro (#15)
Browse files Browse the repository at this point in the history
* Enhancement: Adding a class attribute that allows the Path to be represented as its path's string.

* Enhancement: Adding `__gt__`, '__ge__', '__le__', and '__getitem__' methods to add more coverage with the 'str' interface.

* Change: `posix` is only accepted as keyed argument anymore, not as unkeyed argument.
  • Loading branch information
matfax authored Oct 24, 2019
1 parent def5a2b commit 00c3437
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 18 deletions.
43 changes: 38 additions & 5 deletions mutapath/immutapath.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,23 @@
SerializableType = object

POSIX_ENABLED_DEFAULT = False
STRING_REPR = False


@path_wrapper
class Path(SerializableType):
"""Immutable Path"""
_contained: Union[path.Path, pathlib.PurePath, str] = path.Path("")
__always_posix_format: bool
__string_repr: bool
__mutable: ClassVar[object]

def __init__(self, contained: Union[Path, path.Path, pathlib.PurePath, str] = "",
posix: bool = POSIX_ENABLED_DEFAULT):
def __init__(self, contained: Union[Path, path.Path, pathlib.PurePath, str] = "", *,
posix: bool = POSIX_ENABLED_DEFAULT, string_repr: bool = STRING_REPR):
self.__always_posix_format = posix
self.__string_repr = string_repr
self._set_contained(contained, posix)
super().__init__()

def _set_contained(self, contained: Union[Path, path.Path, pathlib.PurePath, str], posix: Optional[bool] = None):
if contained:
Expand All @@ -62,6 +66,12 @@ def _set_contained(self, contained: Union[Path, path.Path, pathlib.PurePath, str
def __dir__(self) -> Iterable[str]:
return sorted(super(Path, self).__dir__()) + dir(path.Path)

def __getitem__(self, item):
return self._contained.__getitem__(item)

def __getattr__(self, item):
return getattr(self._contained, item)

def __setattr__(self, key, value):
if key == "_contained":
lock = self.__dict__.get("lock", None)
Expand All @@ -74,12 +84,14 @@ def __setattr__(self, key, value):
if isinstance(value, Path):
value = value._contained
self._set_contained(value)
elif key in ["_Path__mutable", "_Path__always_posix_format"]:
elif key in ["_Path__mutable", "_Path__always_posix_format", "_Path__string_repr"]:
super(Path, self).__setattr__(key, value)
else:
raise AttributeError(f"attribute {key} can not be set because mutapath.Path is an immutable class.")

def __repr__(self):
if self.__string_repr:
return self.__str__()
return repr(self._contained)

def __str__(self):
Expand Down Expand Up @@ -121,9 +133,30 @@ def __lt__(self, other):
if isinstance(other, Path):
return self.splitall() < other.splitall()
left = self.posix_string()
right = str(other).replace("\\\\", "\\").replace("\\", "/")
right = Path.posix_string(str(other))
return left < right

def __le__(self, other):
if isinstance(other, Path):
return self.splitall() <= other.splitall()
left = self.posix_string()
right = Path.posix_string(str(other))
return left <= right

def __gt__(self, other):
if isinstance(other, Path):
return self.splitall() > other.splitall()
left = self.posix_string()
right = Path.posix_string(str(other))
return left > right

def __ge__(self, other):
if isinstance(other, Path):
return self.splitall() >= other.splitall()
left = self.posix_string()
right = Path.posix_string(str(other))
return left >= right

def __add__(self, other) -> str:
return str(self.clone(self._contained.__add__(Path(other)._contained)))

Expand Down Expand Up @@ -152,7 +185,7 @@ def __fspath__(self):
def __invert__(self):
"""Create a cloned :class:`~mutapath.MutaPath` from this immutable Path."""
from mutapath import MutaPath
return MutaPath(self._contained, self.posix_enabled)
return MutaPath(self._contained, posix=self.posix_enabled)

def _serialize(self) -> str:
return str(self._contained)
Expand Down
8 changes: 4 additions & 4 deletions mutapath/mutapath.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@

import mutapath
from mutapath.decorator import mutable_path_wrapper
from mutapath.immutapath import POSIX_ENABLED_DEFAULT
from mutapath.immutapath import POSIX_ENABLED_DEFAULT, STRING_REPR


@mutable_path_wrapper
class MutaPath(mutapath.Path):
"""Mutable Path"""

def __init__(self, contained: Union[MutaPath, mutapath.Path, path.Path, pathlib.PurePath, str] = "",
posix: Optional[bool] = POSIX_ENABLED_DEFAULT):
def __init__(self, contained: Union[MutaPath, mutapath.Path, path.Path, pathlib.PurePath, str] = "", *,
posix: Optional[bool] = POSIX_ENABLED_DEFAULT, string_repr: bool = STRING_REPR):
if isinstance(contained, MutaPath):
contained = contained._contained
super(MutaPath, self).__init__(contained, posix)
super(MutaPath, self).__init__(contained, posix=posix, string_repr=string_repr)

def __eq__(self, other):
return super(MutaPath, self).__eq__(other)
Expand Down
57 changes: 49 additions & 8 deletions tests/test_immutapath.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ def test_repr(self):
actual = Path("\\A\\B", posix=True)
self.assertEqual(repr(actual), expected)

def test_string_repr(self):
expected = "/A/B"
actual = Path("\\A\\B", posix=True, string_repr=True)
self.assertEqual(repr(actual), expected)

def test_str(self):
expected = "/A/B"
actual = Path("\\A\\B", posix=True)
Expand Down Expand Up @@ -226,15 +231,35 @@ def test_hash(self):
actual = hash(Path("/A/B/"))
self.assertEqual(expected, actual)

def test_lt_last(self):
def test_lt_gt_last(self):
lesser = Path("/A/B/")
lesser2 = Path("/A/B")
greater = Path("/A/C")
# lt gt
self.assertFalse(lesser < lesser2)
self.assertFalse(lesser > lesser2)
self.assertLess(lesser, greater)

def test_lt_first(self):
self.assertGreater(greater, lesser)
# le ge
self.assertLessEqual(lesser, lesser2)
self.assertGreaterEqual(lesser, lesser2)
self.assertLessEqual(lesser, greater)
self.assertGreaterEqual(greater, lesser)

def test_lt_gt_le_ge_first(self):
lesser = Path("/A/D")
lesser2 = Path("/A/D/")
greater = Path("/B/C")
# lt gt
self.assertFalse(lesser < lesser2)
self.assertFalse(lesser > lesser2)
self.assertLess(lesser, greater)
self.assertGreater(greater, lesser)
# le ge
self.assertLessEqual(lesser, lesser2)
self.assertGreaterEqual(lesser, lesser2)
self.assertLessEqual(lesser, greater)
self.assertGreaterEqual(greater, lesser)

def test_sort(self):
first = Path("/A/B/C")
Expand All @@ -244,10 +269,26 @@ def test_sort(self):
actual = sorted([third, first, second])
self.assertEqual(expected, actual)

def test_lt_str(self):
lesser = Path("/A/B/")
def test_lt_gt_le_ge_str(self):
path = Path("/A/B/")
greater = "/A/C"
self.assertLess(lesser, greater)
lesser = "/A/A"
equal = "/A/B"
self.assertGreater(path, lesser)
self.assertGreaterEqual(path, lesser)
self.assertLess(path, greater)
self.assertLessEqual(path, greater)
self.assertLessEqual(path, equal)
self.assertLessEqual(path, greater)
self.assertGreaterEqual(path, equal)
self.assertGreaterEqual(path, lesser)

def test_getitem(self):
expected = "A"
actual_root = Path("/A/B/")[1]
actual_name = Path("/B/A/").name[0]
self.assertEqual(expected, actual_root)
self.assertEqual(expected, actual_name)

def test_static_posix_string(self):
expected = "/A/B/C"
Expand All @@ -256,8 +297,8 @@ def test_static_posix_string(self):

def test_posix_string(self):
expected = "/A/B/C"
actual = Path("\\A\\B/C", False).posix_string()
actual2 = Path("/A\\B\\C", True).posix_string()
actual = Path("\\A\\B/C", posix=False).posix_string()
actual2 = Path("/A\\B\\C", posix=True).posix_string()
self.assertEqual(expected, actual)
self.assertEqual(expected, actual2)

Expand Down
2 changes: 1 addition & 1 deletion tests/test_mutapath.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def __init__(self, *args):
super().__init__(*args)

def _gen_start_path(self, posix: bool = False):
return MutaPath(super(TestMutaPath, self)._gen_start_path(posix), posix)
return MutaPath(super(TestMutaPath, self)._gen_start_path(posix), posix=posix)

@file_test_no_asserts
def test_suffix(self, test_file: Path):
Expand Down

0 comments on commit 00c3437

Please sign in to comment.