Skip to content

Commit b222614

Browse files
committed
refactor: inherit from NodeWalker
1 parent 6c435c8 commit b222614

File tree

2 files changed

+59
-62
lines changed

2 files changed

+59
-62
lines changed

scripts/make_fs.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ def built_tree(current_path: PurePosixPath, depth: int) -> Generator[PurePosixPa
9494
def create_node(name: str, parent_id: str) -> Node:
9595
owner = "me"
9696
random_key = random_u32int_array(6)
97-
key, iv = random_key[:4], random_key[4:]
98-
meta_mac = (0, 0)
97+
key, iv, meta_mac = random_key[:4], random_key[4:], (0, 0)
9998
type_ = NodeType.FOLDER if name in _FOLDERS else NodeType.FILE
10099
crypto = Crypto.compose(key, iv, meta_mac, type_)
101100
attris = Attributes(name, random.choice(_LABELS), random.choice((True, False)))
@@ -104,7 +103,7 @@ def create_node(name: str, parent_id: str) -> Node:
104103
return Node(
105104
id=random_id(8),
106105
parent_id=parent_id,
107-
owner="me",
106+
owner=owner,
108107
type=type_,
109108
attributes=attris,
110109
created_at=int(time.time()),

src/mega/filesystem.py

Lines changed: 57 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -27,44 +27,49 @@ class NodeLookup(NamedTuple):
2727
was_deleted: bool
2828

2929

30-
@dataclasses.dataclass(slots=True, frozen=True, weakref_slot=True)
30+
def _resolve_paths(walker: NodeWalker, *roots: Node) -> Generator[NodeLookup]:
31+
def walk(node_id: str, current_path: PurePosixPath) -> Generator[tuple[NodeID, PurePosixPath]]:
32+
for node in walker.iterdir(node_id):
33+
node_path = current_path / node.attributes.name
34+
yield node.id, node_path
35+
36+
if node.type is not NodeType.FILE:
37+
yield from walk(node.id, node_path)
38+
39+
for root in roots:
40+
name = root.attributes.name
41+
path = _POSIX_ROOT if root.type is NodeType.ROOT_FOLDER else _POSIX_ROOT / name
42+
43+
yield NodeLookup(root.id, path, False)
44+
deleted = root.type is NodeType.TRASH
45+
for child_id, child_path in walk(root.id, path):
46+
yield NodeLookup(child_id, child_path, deleted)
47+
48+
49+
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True)
3150
class NodeWalker:
32-
nodes: MappingProxyType[NodeID, Node] = dataclasses.field(repr=False)
33-
children: MappingProxyType[NodeID, tuple[NodeID, ...]] = dataclasses.field(repr=False)
51+
_nodes: MappingProxyType[NodeID, Node] = dataclasses.field(repr=False)
52+
_children: MappingProxyType[NodeID, tuple[NodeID, ...]] = dataclasses.field(repr=False)
3453

35-
def ls(self, node_id: NodeID, *, recursive: bool) -> Iterable[NodeID]:
54+
def _ls(self, node_id: NodeID, *, recursive: bool) -> Iterable[NodeID]:
3655
"""Get ID of every child of this node"""
37-
for child_id in self.children.get(node_id, ()):
56+
for child_id in self._children.get(node_id, ()):
3857
yield child_id
3958
if recursive:
40-
yield from self.ls(child_id, recursive=recursive)
59+
yield from self._ls(child_id, recursive=recursive)
4160

4261
def iterdir(self, node_id: NodeID, *, recursive: bool = False) -> Iterable[Node]:
4362
"""Iterate over the children in this node"""
44-
for child_id in self.ls(node_id, recursive=recursive):
45-
yield self.nodes[child_id]
46-
47-
def walk(self, node_id: str, current_path: PurePosixPath) -> Generator[tuple[NodeID, PurePosixPath]]:
48-
for node in self.iterdir(node_id):
49-
node_path = current_path / node.attributes.name
50-
yield node.id, node_path
63+
for child_id in self._ls(node_id, recursive=recursive):
64+
yield self._nodes[child_id]
5165

52-
if node.type is not NodeType.FILE:
53-
yield from self.walk(node.id, node_path)
54-
55-
def resolve_paths(self, *roots: Node) -> Generator[NodeLookup]:
56-
for root in roots:
57-
name = root.attributes.name
58-
path = _POSIX_ROOT if root.type is NodeType.ROOT_FOLDER else _POSIX_ROOT / name
59-
60-
yield NodeLookup(root.id, path, False)
61-
deleted = root.type is NodeType.TRASH
62-
for child_id, child_path in self.walk(root.id, path):
63-
yield NodeLookup(child_id, child_path, deleted)
66+
def listdir(self, node_id: NodeID) -> list[Node]:
67+
"""Get a list of children of this node (non recursive)"""
68+
return list(self.iterdir(node_id))
6469

6570

66-
@dataclasses.dataclass(slots=True, frozen=True, weakref_slot=True)
67-
class SimpleFileSystem(_DictDumper):
71+
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True)
72+
class SimpleFileSystem(NodeWalker, _DictDumper):
6873
"""A simple representation of Mega.nz's file system.
6974
7075
Supports lookups and traversal of nodes by node ID.
@@ -77,8 +82,6 @@ class SimpleFileSystem(_DictDumper):
7782
file_count: int
7883
folder_count: int
7984

80-
_walker: NodeWalker = dataclasses.field(repr=False)
81-
8285
def __len__(self) -> int:
8386
return len(self.nodes)
8487

@@ -95,12 +98,12 @@ def __getitem__(self, node_id: NodeID) -> Node:
9598
@property
9699
def nodes(self) -> MappingProxyType[NodeID, Node]:
97100
"""A mapping of every node"""
98-
return self._walker.nodes
101+
return self._nodes
99102

100103
@property
101104
def children(self) -> MappingProxyType[NodeID, tuple[NodeID, ...]]:
102105
"""A mapping of nodes to their inmediate children"""
103-
return self._walker.children
106+
return self._children
104107

105108
@classmethod
106109
def build(cls, nodes: Sequence[Node]) -> Self:
@@ -127,19 +130,15 @@ def build(cls, nodes: Sequence[Node]) -> Self:
127130
case _:
128131
raise RuntimeError
129132

130-
walker = NodeWalker(
131-
MappingProxyType(nodes_map),
132-
MappingProxyType({node_id: tuple(nodes) for node_id, nodes in children.items()}),
133+
return cls(
134+
root=root,
135+
inbox=inbox,
136+
trash_bin=trash_bin,
137+
file_count=file_count,
138+
folder_count=folder_count,
139+
_nodes=MappingProxyType(nodes_map),
140+
_children=MappingProxyType({node_id: tuple(nodes) for node_id, nodes in children.items()}),
133141
)
134-
return cls(root, inbox, trash_bin, file_count, folder_count, walker)
135-
136-
def iterdir(self, node_id: NodeID, *, recursive: bool = False) -> Iterable[Node]:
137-
"""Iterate over the children in this node"""
138-
yield from self._walker.iterdir(node_id, recursive=recursive)
139-
140-
def listdir(self, node_id: NodeID) -> list[Node]:
141-
"""Get a list of children of this node (non recursive)"""
142-
return list(self.iterdir(node_id))
143142

144143
def dump(self) -> dict[str, Any]:
145144
"""Get a JSONable dict representation of this object"""
@@ -153,7 +152,7 @@ def dump(self) -> dict[str, Any]:
153152
)
154153

155154

156-
@dataclasses.dataclass(slots=True, frozen=True, weakref_slot=True)
155+
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True)
157156
class FileSystem(SimpleFileSystem):
158157
"""Mega.nz's file system.
159158
@@ -180,31 +179,30 @@ def build(cls, nodes: Sequence[Node]) -> Self:
180179
# - 4. Sort their paths
181180
# - 5. Freeze inv paths (list -> tuple)
182181

183-
simple_self = SimpleFileSystem.build(nodes)
182+
self = SimpleFileSystem.build(nodes)
184183

185-
roots = list(filter(None, (simple_self.root, simple_self.inbox, simple_self.trash_bin))) or [nodes[0]]
184+
roots = list(filter(None, (self.root, self.inbox, self.trash_bin))) or [nodes[0]]
186185
paths: dict[NodeID, PurePosixPath] = {}
187186
inv_paths: dict[PurePosixPath, list[NodeID]] = {}
188187
deleted_ids: set[NodeID] = set()
189188

190-
for node_id, path, was_deleted in sorted(
191-
simple_self._walker.resolve_paths(*roots), key=lambda x: str(x[1]).casefold()
192-
):
189+
for node_id, path, was_deleted in sorted(_resolve_paths(self, *roots), key=lambda x: str(x[1]).casefold()):
193190
paths[node_id] = path
194191
inv_paths.setdefault(path, []).append(node_id)
195192
if was_deleted:
196193
deleted_ids.add(node_id)
197194

198195
return cls(
199-
simple_self.root,
200-
simple_self.inbox,
201-
simple_self.trash_bin,
202-
simple_self.file_count,
203-
simple_self.folder_count,
204-
simple_self._walker,
205-
MappingProxyType(paths),
206-
MappingProxyType({path: tuple(nodes) for path, nodes in inv_paths.items()}),
207-
frozenset(deleted_ids),
196+
root=self.root,
197+
inbox=self.inbox,
198+
trash_bin=self.trash_bin,
199+
file_count=self.file_count,
200+
folder_count=self.folder_count,
201+
_nodes=self.nodes,
202+
_children=self.children,
203+
_paths=MappingProxyType(paths),
204+
_inv_paths=MappingProxyType({path: tuple(nodes) for path, nodes in inv_paths.items()}),
205+
_deleted=frozenset(deleted_ids),
208206
)
209207

210208
@property
@@ -246,7 +244,7 @@ def dirmap(self, node_id: str, *, recursive: bool = False) -> dict[NodeID, PureP
246244

247245
pairs = (
248246
(child_id, self.resolve(child_id))
249-
for child_id in self._walker.ls(
247+
for child_id in self._ls(
250248
node_id,
251249
recursive=recursive,
252250
)

0 commit comments

Comments
 (0)