@@ -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 )
3150class 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 )
157156class 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