Skip to content

Commit 2b65d21

Browse files
committed
Add caching (based on #28)
1 parent 87704e2 commit 2b65d21

File tree

2 files changed

+49
-23
lines changed

2 files changed

+49
-23
lines changed

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
REQUIREMENTS = [
2626
"fs>2.0",
2727
"webdavclient3",
28-
"python-dateutil"
28+
"python-dateutil",
29+
"cachetools"
2930
]
3031

3132
setup(

webdavfs/webdavfs.py

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from fs.iotools import line_iterator
2121
from fs.mode import Mode
2222
from fs.path import dirname
23+
from cachetools import TTLCache
2324

2425

2526
log = 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

Comments
 (0)