@@ -18,92 +18,104 @@ class MemoryFileSystem(AbstractFileSystem):
18
18
"""
19
19
20
20
store = {} # global
21
- pseudo_dirs = []
21
+ pseudo_dirs = ["" ]
22
22
protocol = "memory"
23
- root_marker = ""
23
+ root_marker = "/"
24
+
25
+ @classmethod
26
+ def _strip_protocol (cls , path ):
27
+ if path .startswith ("memory://" ):
28
+ path = path [len ("memory://" ) :]
29
+ if "::" in path or "://" in path :
30
+ return path .rstrip ("/" )
31
+ path = path .lstrip ("/" ).rstrip ("/" )
32
+ return "/" + path if path else ""
24
33
25
34
def ls (self , path , detail = False , ** kwargs ):
35
+ path = self ._strip_protocol (path )
26
36
if path in self .store :
27
- # there is a key with this exact name, but could also be directory
28
- out = [
37
+ # there is a key with this exact name
38
+ return [
29
39
{
30
40
"name" : path ,
31
41
"size" : self .store [path ].getbuffer ().nbytes ,
32
42
"type" : "file" ,
33
43
"created" : self .store [path ].created ,
34
44
}
35
45
]
36
- else :
37
- out = []
38
- path = path .strip ("/" ).lstrip ("/" )
39
46
paths = set ()
47
+ starter = path + "/"
48
+ out = []
40
49
for p2 in self .store :
41
- has_slash = "/" if p2 .startswith ("/" ) else ""
42
- p = p2 .lstrip ("/" )
43
- if "/" in p :
44
- root = p .rsplit ("/" , 1 )[0 ]
45
- else :
46
- root = ""
47
- if root == path :
48
- out .append (
49
- {
50
- "name" : has_slash + p ,
51
- "size" : self .store [p2 ].getbuffer ().nbytes ,
52
- "type" : "file" ,
53
- "created" : self .store [p2 ].created ,
54
- }
55
- )
56
- elif (
57
- path
58
- and len (path ) < len (p .strip ("/" ))
59
- and all (
60
- (a == b ) for a , b in zip (path .split ("/" ), p .strip ("/" ).split ("/" ))
61
- )
62
- ):
63
- # implicit directory
64
- ppath = "/" .join (p .split ("/" )[: len (path .split ("/" )) + 1 ])
65
- if ppath not in paths :
66
- out .append (
67
- {
68
- "name" : has_slash + ppath + "/" ,
69
- "size" : 0 ,
70
- "type" : "directory" ,
71
- }
72
- )
73
- paths .add (ppath )
74
- elif all (
75
- (a == b )
76
- for a , b in zip (path .split ("/" ), ["" ] + p .strip ("/" ).split ("/" ))
77
- ):
78
- # root directory entry
79
- ppath = p .rstrip ("/" ).split ("/" , 1 )[0 ]
80
- if ppath not in paths :
50
+ if p2 .startswith (starter ):
51
+ if "/" not in p2 [len (starter ) :]:
52
+ # exact child
81
53
out .append (
82
54
{
83
- "name" : has_slash + ppath + "/" ,
84
- "size" : 0 ,
85
- "type" : "directory" ,
55
+ "name" : p2 ,
56
+ "size" : self .store [p2 ].getbuffer ().nbytes ,
57
+ "type" : "file" ,
58
+ "created" : self .store [p2 ].created ,
86
59
}
87
60
)
88
- paths .add (ppath )
61
+ elif len (p2 ) > len (starter ):
62
+ # implied child directory
63
+ ppath = starter + p2 [len (starter ) :].split ("/" , 1 )[0 ]
64
+ if ppath not in paths :
65
+ out = out or []
66
+ out .append (
67
+ {
68
+ "name" : ppath ,
69
+ "size" : 0 ,
70
+ "type" : "directory" ,
71
+ }
72
+ )
73
+ paths .add (ppath )
89
74
for p2 in self .pseudo_dirs :
90
- if self ._parent (p2 ).strip ("/" ) == path and p2 .strip ("/" ) not in paths :
91
- out .append ({"name" : p2 + "/" , "size" : 0 , "type" : "directory" })
75
+ if p2 .startswith (starter ):
76
+ if "/" not in p2 [len (starter ) :]:
77
+ # exact child pdir
78
+ if p2 not in paths :
79
+ out .append ({"name" : p2 , "size" : 0 , "type" : "directory" })
80
+ paths .add (p2 )
81
+ else :
82
+ # directory implied by deeper pdir
83
+ ppath = starter + p2 [len (starter ) :].split ("/" , 1 )[0 ]
84
+ if ppath not in paths :
85
+ out .append ({"name" : ppath , "size" : 0 , "type" : "directory" })
86
+ paths .add (ppath )
87
+ if not out :
88
+ if path in self .pseudo_dirs :
89
+ # empty dir
90
+ return []
91
+ raise FileNotFoundError (path )
92
92
if detail :
93
93
return out
94
94
return sorted ([f ["name" ] for f in out ])
95
95
96
96
def mkdir (self , path , create_parents = True , ** kwargs ):
97
- path = path . rstrip ( "/" )
98
- if create_parents and self ._parent ( path ) :
99
- self . mkdir ( self . _parent ( path ), create_parents , ** kwargs )
100
- if self ._parent (path ) and not self .isdir (self ._parent (path )):
97
+ path = self . _strip_protocol ( path )
98
+ if path in self .store or path in self . pseudo_dirs :
99
+ raise FileExistsError
100
+ if self ._parent (path ). strip ( "/" ) and self .isfile (self ._parent (path )):
101
101
raise NotADirectoryError (self ._parent (path ))
102
+ if create_parents and self ._parent (path ).strip ("/" ):
103
+ try :
104
+ self .mkdir (self ._parent (path ), create_parents , ** kwargs )
105
+ except FileExistsError :
106
+ pass
102
107
if path and path not in self .pseudo_dirs :
103
108
self .pseudo_dirs .append (path )
104
109
110
+ def makedirs (self , path , exist_ok = False ):
111
+ try :
112
+ self .mkdir (path , create_parents = True )
113
+ except FileExistsError :
114
+ if not exist_ok :
115
+ raise
116
+
105
117
def rmdir (self , path ):
106
- path = path . rstrip ( "/" )
118
+ path = self . _strip_protocol ( path )
107
119
if path in self .pseudo_dirs :
108
120
if not self .ls (path ):
109
121
self .pseudo_dirs .remove (path )
@@ -116,6 +128,26 @@ def exists(self, path):
116
128
path = self ._strip_protocol (path )
117
129
return path in self .store or path in self .pseudo_dirs
118
130
131
+ def info (self , path , ** kwargs ):
132
+ path = self ._strip_protocol (path )
133
+ if path in self .pseudo_dirs or any (
134
+ p .startswith (path + "/" ) for p in list (self .store ) + self .pseudo_dirs
135
+ ):
136
+ return {
137
+ "name" : path ,
138
+ "size" : 0 ,
139
+ "type" : "directory" ,
140
+ }
141
+ elif path in self .store :
142
+ return {
143
+ "name" : path ,
144
+ "size" : self .store [path ].getbuffer ().nbytes ,
145
+ "type" : "file" ,
146
+ "created" : self .store [path ].created ,
147
+ }
148
+ else :
149
+ raise FileNotFoundError (path )
150
+
119
151
def _open (
120
152
self ,
121
153
path ,
@@ -125,6 +157,14 @@ def _open(
125
157
cache_options = None ,
126
158
** kwargs ,
127
159
):
160
+ path = self ._strip_protocol (path )
161
+ if path in self .pseudo_dirs :
162
+ raise IsADirectoryError
163
+ parent = path
164
+ while len (parent ) > 1 :
165
+ parent = self ._parent (parent )
166
+ if self .isfile (parent ):
167
+ raise FileExistsError (parent )
128
168
if mode in ["rb" , "ab" , "rb+" ]:
129
169
if path in self .store :
130
170
f = self .store [path ]
@@ -144,6 +184,8 @@ def _open(
144
184
return m
145
185
146
186
def cp_file (self , path1 , path2 , ** kwargs ):
187
+ path1 = self ._strip_protocol (path1 )
188
+ path2 = self ._strip_protocol (path2 )
147
189
if self .isfile (path1 ):
148
190
self .store [path2 ] = MemoryFile (self , path2 , self .store [path1 ].getbuffer ())
149
191
elif self .isdir (path1 ):
@@ -153,18 +195,18 @@ def cp_file(self, path1, path2, **kwargs):
153
195
raise FileNotFoundError
154
196
155
197
def cat_file (self , path , start = None , end = None , ** kwargs ):
198
+ path = self ._strip_protocol (path )
156
199
try :
157
200
return self .store [path ].getvalue ()[start :end ]
158
201
except KeyError :
159
202
raise FileNotFoundError (path )
160
203
161
204
def _rm (self , path ):
162
- if self .isfile (path ):
205
+ path = self ._strip_protocol (path )
206
+ try :
163
207
del self .store [path ]
164
- elif self .isdir (path ):
165
- self .rmdir (path )
166
- else :
167
- raise FileNotFoundError
208
+ except KeyError as e :
209
+ raise FileNotFoundError from e
168
210
169
211
def rm (self , path , recursive = False , maxdepth = None ):
170
212
paths = self .expand_path (path , recursive = recursive , maxdepth = maxdepth )
@@ -175,13 +217,10 @@ def rm(self, path, recursive=False, maxdepth=None):
175
217
# directories first.
176
218
if not self .exists (p ):
177
219
continue
178
- self .rm_file (p )
179
-
180
- def size (self , path ):
181
- """Size in bytes of the file at path"""
182
- if path not in self .store :
183
- raise FileNotFoundError (path )
184
- return self .store [path ].getbuffer ().nbytes
220
+ if self .isfile (p ):
221
+ self .rm_file (p )
222
+ else :
223
+ self .rmdir (p )
185
224
186
225
187
226
class MemoryFile (BytesIO ):
0 commit comments