3838 SupportsInt ,
3939 Union ,
4040 Text ,
41+ Tuple ,
4142 )
4243 from .base import _OpendirFactory
4344 from .info import RawInfo
@@ -274,6 +275,10 @@ def remove_entry(self, name):
274275 # type: (Text) -> None
275276 del self ._dir [name ]
276277
278+ def clear (self ):
279+ # type: () -> None
280+ self ._dir .clear ()
281+
277282 def __contains__ (self , name ):
278283 # type: (object) -> bool
279284 return name in self ._dir
@@ -294,6 +299,21 @@ def remove_open_file(self, memory_file):
294299 # type: (_MemoryFile) -> None
295300 self ._open_files .remove (memory_file )
296301
302+ def to_info (self , namespaces = None ):
303+ # type: (Optional[Collection[Text]]) -> Info
304+ namespaces = namespaces or ()
305+ info = {"basic" : {"name" : self .name , "is_dir" : self .is_dir }}
306+ if "details" in namespaces :
307+ info ["details" ] = {
308+ "_write" : ["accessed" , "modified" ],
309+ "type" : int (self .resource_type ),
310+ "size" : self .size ,
311+ "accessed" : self .accessed_time ,
312+ "modified" : self .modified_time ,
313+ "created" : self .created_time ,
314+ }
315+ return Info (info )
316+
297317
298318@six .python_2_unicode_compatible
299319class MemoryFS (FS ):
@@ -368,33 +388,24 @@ def close(self):
368388
369389 def getinfo (self , path , namespaces = None ):
370390 # type: (Text, Optional[Collection[Text]]) -> Info
371- namespaces = namespaces or ()
372391 _path = self .validatepath (path )
373392 dir_entry = self ._get_dir_entry (_path )
374393 if dir_entry is None :
375394 raise errors .ResourceNotFound (path )
376- info = {"basic" : {"name" : dir_entry .name , "is_dir" : dir_entry .is_dir }}
377- if "details" in namespaces :
378- info ["details" ] = {
379- "_write" : ["accessed" , "modified" ],
380- "type" : int (dir_entry .resource_type ),
381- "size" : dir_entry .size ,
382- "accessed" : dir_entry .accessed_time ,
383- "modified" : dir_entry .modified_time ,
384- "created" : dir_entry .created_time ,
385- }
386- return Info (info )
395+ return dir_entry .to_info (namespaces = namespaces )
387396
388397 def listdir (self , path ):
389398 # type: (Text) -> List[Text]
390399 self .check ()
391400 _path = self .validatepath (path )
392401 with self ._lock :
402+ # locate and validate the entry corresponding to the given path
393403 dir_entry = self ._get_dir_entry (_path )
394404 if dir_entry is None :
395405 raise errors .ResourceNotFound (path )
396406 if not dir_entry .is_dir :
397407 raise errors .DirectoryExpected (path )
408+ # return the filenames in the order they were created
398409 return dir_entry .list ()
399410
400411 if typing .TYPE_CHECKING :
@@ -433,6 +444,46 @@ def makedir(
433444 parent_dir .set_entry (dir_name , new_dir )
434445 return self .opendir (path )
435446
447+ def move (self , src_path , dst_path , overwrite = False ):
448+ src_dir , src_name = split (self .validatepath (src_path ))
449+ dst_dir , dst_name = split (self .validatepath (dst_path ))
450+
451+ with self ._lock :
452+ src_dir_entry = self ._get_dir_entry (src_dir )
453+ if src_dir_entry is None or src_name not in src_dir_entry :
454+ raise errors .ResourceNotFound (src_path )
455+ src_entry = src_dir_entry .get_entry (src_name )
456+ if src_entry .is_dir :
457+ raise errors .FileExpected (src_path )
458+
459+ dst_dir_entry = self ._get_dir_entry (dst_dir )
460+ if dst_dir_entry is None :
461+ raise errors .ResourceNotFound (dst_path )
462+ elif not overwrite and dst_name in dst_dir_entry :
463+ raise errors .DestinationExists (dst_path )
464+
465+ dst_dir_entry .set_entry (dst_name , src_entry )
466+ src_dir_entry .remove_entry (src_name )
467+
468+ def movedir (self , src_path , dst_path , create = False ):
469+ src_dir , src_name = split (self .validatepath (src_path ))
470+ dst_dir , dst_name = split (self .validatepath (dst_path ))
471+
472+ with self ._lock :
473+ src_dir_entry = self ._get_dir_entry (src_dir )
474+ if src_dir_entry is None or src_name not in src_dir_entry :
475+ raise errors .ResourceNotFound (src_path )
476+ src_entry = src_dir_entry .get_entry (src_name )
477+ if not src_entry .is_dir :
478+ raise errors .DirectoryExpected (src_path )
479+
480+ dst_dir_entry = self ._get_dir_entry (dst_dir )
481+ if dst_dir_entry is None or (not create and dst_name not in dst_dir_entry ):
482+ raise errors .ResourceNotFound (dst_path )
483+
484+ dst_dir_entry .set_entry (dst_name , src_entry )
485+ src_dir_entry .remove_entry (src_name )
486+
436487 def openbin (self , path , mode = "r" , buffering = - 1 , ** options ):
437488 # type: (Text, Text, int, **Any) -> BinaryIO
438489 _mode = Mode (mode )
@@ -499,12 +550,29 @@ def remove(self, path):
499550
500551 def removedir (self , path ):
501552 # type: (Text) -> None
553+ # make sure we are not removing root
502554 _path = self .validatepath (path )
503-
504555 if _path == "/" :
505556 raise errors .RemoveRootError ()
557+ # make sure the directory is empty
558+ if not self .isempty (path ):
559+ raise errors .DirectoryNotEmpty (path )
560+ # we can now delegate to removetree since we confirmed that
561+ # * path exists (isempty)
562+ # * path is a folder (isempty)
563+ # * path is not root
564+ self .removetree (_path )
565+
566+ def removetree (self , path ):
567+ # type: (Text) -> None
568+ _path = self .validatepath (path )
506569
507570 with self ._lock :
571+
572+ if _path == "/" :
573+ self .root .clear ()
574+ return
575+
508576 dir_path , file_name = split (_path )
509577 parent_dir_entry = self ._get_dir_entry (dir_path )
510578
@@ -515,11 +583,34 @@ def removedir(self, path):
515583 if not dir_dir_entry .is_dir :
516584 raise errors .DirectoryExpected (path )
517585
518- if len (dir_dir_entry ):
519- raise errors .DirectoryNotEmpty (path )
520-
521586 parent_dir_entry .remove_entry (file_name )
522587
588+ def scandir (
589+ self ,
590+ path , # type: Text
591+ namespaces = None , # type: Optional[Collection[Text]]
592+ page = None , # type: Optional[Tuple[int, int]]
593+ ):
594+ # type: (...) -> Iterator[Info]
595+ self .check ()
596+ _path = self .validatepath (path )
597+ with self ._lock :
598+ # locate and validate the entry corresponding to the given path
599+ dir_entry = self ._get_dir_entry (_path )
600+ if dir_entry is None :
601+ raise errors .ResourceNotFound (path )
602+ if not dir_entry .is_dir :
603+ raise errors .DirectoryExpected (path )
604+ # if paging was requested, slice the filenames
605+ filenames = dir_entry .list ()
606+ if page is not None :
607+ start , end = page
608+ filenames = filenames [start :end ]
609+ # yield info with the right namespaces
610+ for name in filenames :
611+ entry = typing .cast (_DirEntry , dir_entry .get_entry (name ))
612+ yield entry .to_info (namespaces = namespaces )
613+
523614 def setinfo (self , path , info ):
524615 # type: (Text, RawInfo) -> None
525616 _path = self .validatepath (path )
0 commit comments