2020from fs .iotools import line_iterator
2121from fs .mode import Mode
2222from fs .path import dirname
23+ from cachetools import TTLCache
2324
2425
2526log = logging .getLogger (__name__ )
@@ -156,7 +157,8 @@ class WebDAVFS(FS):
156157 'virtual' : False ,
157158 }
158159
159- def __init__ (self , url , login = None , password = None , root = None ):
160+ def __init__ (self , url , login = None , password = None , root = None ,
161+ cache_maxsize = 10000 , cache_ttl = 60 ):
160162 self .url = url
161163 self .root = root
162164 super (WebDAVFS , self ).__init__ ()
@@ -167,6 +169,8 @@ def __init__(self, url, login=None, password=None, root=None):
167169 'webdav_password' : password ,
168170 'root' : self .root
169171 }
172+ self .info_cache = TTLCache (maxsize = cache_maxsize ,
173+ ttl = cache_ttl )
170174 self .client = wc .Client (options )
171175
172176 def _create_resource (self , path ):
@@ -182,7 +186,8 @@ def _create_info_dict(info):
182186 info_dict = {
183187 'basic' : {"is_dir" : False },
184188 'details' : {'type' : int (ResourceType .file )},
185- 'access' : {}
189+ 'access' : {},
190+ 'other' : {}
186191 }
187192
188193 if six .PY2 :
@@ -208,7 +213,7 @@ def decode_datestring(s):
208213 if key in ('modified' , 'created' , 'accessed' ):
209214 info_dict ['details' ][key ] = decode (val ) or None
210215 else :
211- info_dict ['details' ][key ] = decode (val )
216+ info_dict ['details' ][key ] = decode (val )
212217 elif key in access :
213218 info_dict ['access' ][key ] = decode (val )
214219 else :
@@ -230,45 +235,65 @@ def exists(self, path):
230235
231236 def getinfo (self , path , namespaces = None ):
232237 _path = self .validatepath (path )
233- namespaces = namespaces or ()
234238
235239 if _path in '/' :
240+ self .info_cache .clear ()
236241 info_dict = {
237242 "basic" : {
238243 "name" : "" ,
239244 "is_dir" : True
240245 },
241246 "details" : {
242247 "type" : ResourceType .directory
243- }
248+ },
249+ 'access' : {},
250+ "other" : {}
244251 }
245252
246- else :
247- try :
248- info = self .client .info (_path .encode ('utf-8' ))
249- # displayname is optional
253+ try :
254+ namespaces = namespaces or ()
255+ urn = wu .Urn (_path .encode ('utf-8' ))
256+ path = self .client .get_full_path (urn )
257+ if path in self .info_cache :
258+ info = self .info_cache [path ]
259+ else :
260+ response = self .client .execute_request (action = 'info' ,
261+ path = urn .quote ())
262+ info = wc .WebDavXmlUtils .parse_info_response (content = response .content , path = path , hostname = self .client .webdav .hostname )
250263 if info ['name' ] is None :
251264 info ['name' ] = _path .split ("/" )[- 1 ]
252- info_dict = self ._create_info_dict (info )
253- if self .client .is_dir (_path .encode ('utf-8' )):
254- info_dict ['basic' ]['is_dir' ] = True
255- info_dict ['details' ]['type' ] = ResourceType .directory
256- except we .RemoteResourceNotFound as exc :
257- raise errors .ResourceNotFound (path , exc = exc )
265+ if wc .WebDavXmlUtils .parse_is_dir_response (content = response .content , path = path , hostname = self .client .webdav .hostname ):
266+ info ['isdir' ] = True
267+ info ['files' ] = []
268+ for i in wc .WebDavXmlUtils .parse_get_list_info_response (response .content ):
269+ if i ['path' ].rstrip ('/' ) != path .rstrip ('/' ):
270+ if i ['name' ] is None :
271+ i ['name' ] = i ['path' ].split ("/" )[- 1 ]
272+ self .info_cache [i ['path' ]] = i
273+ filename = wu .Urn (i ['path' ], i ['isdir' ]).filename ()
274+ if six .PY2 :
275+ filename = filename .decode ('utf-8' )
276+ filename = filename .rstrip ('/' )
277+ info ['files' ].append (filename )
278+ self .info_cache [path ] = info
279+ info_dict = self ._create_info_dict (info )
280+ if info .get ('isdir' , False ):
281+ info_dict ['basic' ]['is_dir' ] = True
282+ info_dict ['details' ]['type' ] = ResourceType .directory
283+ except we .RemoteResourceNotFound as exc :
284+ raise errors .ResourceNotFound (path , exc = exc )
258285
259286 return Info (info_dict )
260287
261288 def listdir (self , path ):
262- _path = self .validatepath (path )
263-
264- if not self .getinfo (_path ).is_dir :
289+ info = self .getinfo (path )
290+ if not info .is_dir :
265291 raise errors .DirectoryExpected (path )
266292
267- dir_list = self .client .list (_path .encode ('utf-8' ))
268- if six .PY2 :
269- dir_list = map (operator .methodcaller ('decode' , 'utf-8' ), dir_list )
293+ for i in info .raw ['other' ]['files' ]:
294+ yield i
270295
271- return list ( map ( operator . methodcaller ( 'rstrip' , '/' ), dir_list ))
296+ return
272297
273298 def makedir (self , path , permissions = None , recreate = False ):
274299 _path = self .validatepath (path )
0 commit comments