Skip to content
This repository was archived by the owner on Apr 4, 2024. It is now read-only.

Commit 5e98465

Browse files
authored
TypedPath.py (#24 closes #23)
2 parents 8c2ed54 + f86de53 commit 5e98465

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from functools import total_ordering
2+
3+
4+
@total_ordering
5+
class TypedPath:
6+
def __init__(self, absolute_path: str):
7+
self.absolute_path = absolute_path
8+
9+
@property
10+
def name(self) -> str:
11+
if self.absolute_path.endswith("/"):
12+
path = self.absolute_path[:-1]
13+
else:
14+
path = self.absolute_path
15+
last_slash = path.rfind("/")
16+
return path[last_slash + 1 :]
17+
18+
@property
19+
def is_folder(self) -> bool:
20+
return self.absolute_path.endswith("/")
21+
22+
def assert_folder(self) -> None:
23+
if not self.is_folder:
24+
raise AssertionError(
25+
f"Expected {self} to be a folder but it doesn't end with `/`"
26+
)
27+
28+
def parent_folder(self) -> "TypedPath":
29+
if self.absolute_path == "/":
30+
raise ValueError("Path does not have a parent folder")
31+
trimmed_path = self.absolute_path.rstrip("/")
32+
last_idx = trimmed_path.rfind("/")
33+
return TypedPath.of_folder(trimmed_path[: last_idx + 1])
34+
35+
def resolve_file(self, child: str) -> "TypedPath":
36+
self.assert_folder()
37+
if child.startswith("/") or child.endswith("/"):
38+
raise ValueError("Child path is not valid for file resolution")
39+
return self.of_file(f"{self.absolute_path}{child}")
40+
41+
def resolve_folder(self, child: str) -> "TypedPath":
42+
self.assert_folder()
43+
if child.startswith("/"):
44+
raise ValueError("Child path starts with a slash")
45+
return self.of_folder(f"{self.absolute_path}{child}/")
46+
47+
def relativize(self, child: "TypedPath") -> str:
48+
self.assert_folder()
49+
if not child.absolute_path.startswith(self.absolute_path):
50+
raise ValueError(f"Expected {child} to start with {self.absolute_path}")
51+
return child.absolute_path[len(self.absolute_path) :]
52+
53+
def __eq__(self, other: object) -> bool:
54+
if not isinstance(other, TypedPath):
55+
return NotImplemented
56+
return self.absolute_path == other.absolute_path
57+
58+
def __lt__(self, other: "TypedPath") -> bool:
59+
return self.absolute_path < other.absolute_path
60+
61+
@classmethod
62+
def of_folder(cls, path: str) -> "TypedPath":
63+
unix_path = path.replace("\\", "/")
64+
if not unix_path.endswith("/"):
65+
unix_path += "/"
66+
return cls(unix_path)
67+
68+
@classmethod
69+
def of_file(cls, path: str) -> "TypedPath":
70+
unix_path = path.replace("\\", "/")
71+
if unix_path.endswith("/"):
72+
raise ValueError("Expected path to not end with a slash for a file")
73+
return cls(unix_path)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import pytest
2+
from selfie_lib.TypedPath import TypedPath
3+
4+
5+
def test_initialization():
6+
path = TypedPath("/home/user/")
7+
assert path.absolute_path == "/home/user/"
8+
assert path.is_folder
9+
assert path.name == "user"
10+
11+
12+
def test_parent_folder():
13+
path = TypedPath("/home/user/documents/")
14+
parent = path.parent_folder()
15+
assert isinstance(parent, TypedPath)
16+
assert parent.absolute_path == "/home/user/"
17+
18+
19+
def test_resolve_file():
20+
folder = TypedPath("/home/user/")
21+
file = folder.resolve_file("document.txt")
22+
assert file.absolute_path == "/home/user/document.txt"
23+
assert not file.is_folder
24+
assert file.name == "document.txt"
25+
26+
27+
def test_resolve_folder():
28+
folder = TypedPath("/home/user/")
29+
subfolder = folder.resolve_folder("documents")
30+
assert subfolder.absolute_path == "/home/user/documents/"
31+
assert subfolder.is_folder
32+
assert subfolder.name == "documents"
33+
34+
35+
def test_relativize():
36+
folder = TypedPath("/home/user/")
37+
file = TypedPath("/home/user/document.txt")
38+
relative_path = folder.relativize(file)
39+
assert relative_path == "document.txt"
40+
41+
42+
def test_of_folder_class_method():
43+
folder = TypedPath.of_folder("/home/user/documents")
44+
assert folder.absolute_path == "/home/user/documents/"
45+
assert folder.is_folder
46+
47+
48+
def test_of_file_class_method():
49+
file = TypedPath.of_file("/home/user/document.txt")
50+
assert file.absolute_path == "/home/user/document.txt"
51+
assert not file.is_folder
52+
53+
54+
def test_assert_folder_failure():
55+
with pytest.raises(AssertionError):
56+
file = TypedPath("/home/user/document.txt")
57+
file.assert_folder()
58+
59+
60+
def test_parent_folder_failure():
61+
with pytest.raises(ValueError):
62+
path = TypedPath("/")
63+
path.parent_folder()
64+
65+
66+
def test_equality():
67+
path1 = TypedPath("/home/user/")
68+
path2 = TypedPath("/home/user/")
69+
assert path1 == path2
70+
71+
72+
def test_inequality():
73+
path1 = TypedPath("/home/user/")
74+
path2 = TypedPath("/home/another_user/")
75+
assert path1 != path2
76+
77+
78+
def test_ordering():
79+
path1 = TypedPath("/home/a/")
80+
path2 = TypedPath("/home/b/")
81+
assert path1 < path2
82+
assert path2 > path1
83+
84+
85+
def test_relativize_error():
86+
parent = TypedPath("/home/user/")
87+
child = TypedPath("/home/another_user/document.txt")
88+
with pytest.raises(ValueError):
89+
parent.relativize(child)

0 commit comments

Comments
 (0)