1+ # coding: utf-8
12from __future__ import unicode_literals
23
34import errno
4- import io
55import json
66import os
77import re
88import shutil
99import traceback
1010
11- from .compat import compat_getenv
11+ from .compat import (
12+ compat_getenv ,
13+ compat_open as open ,
14+ compat_os_makedirs ,
15+ )
1216from .utils import (
1317 error_to_compat_str ,
18+ escape_rfc3986 ,
1419 expand_path ,
1520 is_outdated_version ,
16- try_get ,
21+ traverse_obj ,
1722 write_json_file ,
1823)
1924from .version import __version__
@@ -28,23 +33,35 @@ class Cache(object):
2833 def __init__ (self , ydl ):
2934 self ._ydl = ydl
3035
36+ def _write_debug (self , * args , ** kwargs ):
37+ self ._ydl .write_debug (* args , ** kwargs )
38+
39+ def _report_warning (self , * args , ** kwargs ):
40+ self ._ydl .report_warning (* args , ** kwargs )
41+
42+ def _to_screen (self , * args , ** kwargs ):
43+ self ._ydl .to_screen (* args , ** kwargs )
44+
45+ def _get_param (self , k , default = None ):
46+ return self ._ydl .params .get (k , default )
47+
3148 def _get_root_dir (self ):
32- res = self ._ydl . params . get ('cachedir' )
49+ res = self ._get_param ('cachedir' )
3350 if res is None :
3451 cache_root = compat_getenv ('XDG_CACHE_HOME' , '~/.cache' )
3552 res = os .path .join (cache_root , self ._YTDL_DIR )
3653 return expand_path (res )
3754
3855 def _get_cache_fn (self , section , key , dtype ):
39- assert re .match (r'^[a-zA-Z0-9_ .-]+$' , section ), \
56+ assert re .match (r'^[\w .-]+$' , section ), \
4057 'invalid section %r' % section
41- assert re . match ( r'^[a-zA-Z0-9_.-]+$' , key ), 'invalid key %r' % key
58+ key = escape_rfc3986 ( key , safe = '' ). replace ( '%' , ',' ) # encode non-ascii characters
4259 return os .path .join (
4360 self ._get_root_dir (), section , '%s.%s' % (key , dtype ))
4461
4562 @property
4663 def enabled (self ):
47- return self ._ydl . params . get ('cachedir' ) is not False
64+ return self ._get_param ('cachedir' ) is not False
4865
4966 def store (self , section , key , data , dtype = 'json' ):
5067 assert dtype in ('json' ,)
@@ -54,61 +71,75 @@ def store(self, section, key, data, dtype='json'):
5471
5572 fn = self ._get_cache_fn (section , key , dtype )
5673 try :
57- try :
58- os .makedirs (os .path .dirname (fn ))
59- except OSError as ose :
60- if ose .errno != errno .EEXIST :
61- raise
74+ compat_os_makedirs (os .path .dirname (fn ), exist_ok = True )
75+ self ._write_debug ('Saving {section}.{key} to cache' .format (section = section , key = key ))
6276 write_json_file ({self ._VERSION_KEY : __version__ , 'data' : data }, fn )
6377 except Exception :
6478 tb = traceback .format_exc ()
65- self ._ydl .report_warning (
66- 'Writing cache to %r failed: %s' % (fn , tb ))
79+ self ._report_warning ('Writing cache to {fn!r} failed: {tb}' .format (fn = fn , tb = tb ))
80+
81+ def clear (self , section , key , dtype = 'json' ):
82+
83+ if not self .enabled :
84+ return
85+
86+ fn = self ._get_cache_fn (section , key , dtype )
87+ self ._write_debug ('Clearing {section}.{key} from cache' .format (section = section , key = key ))
88+ try :
89+ os .remove (fn )
90+ except Exception as e :
91+ if getattr (e , 'errno' ) == errno .ENOENT :
92+ # file not found
93+ return
94+ tb = traceback .format_exc ()
95+ self ._report_warning ('Clearing cache from {fn!r} failed: {tb}' .format (fn = fn , tb = tb ))
6796
6897 def _validate (self , data , min_ver ):
69- version = try_get (data , lambda x : x [ self ._VERSION_KEY ] )
98+ version = traverse_obj (data , self ._VERSION_KEY )
7099 if not version : # Backward compatibility
71100 data , version = {'data' : data }, self ._DEFAULT_VERSION
72101 if not is_outdated_version (version , min_ver or '0' , assume_new = False ):
73102 return data ['data' ]
74- self ._ydl .to_screen (
75- 'Discarding old cache from version {version} (needs {min_ver})' .format (** locals ()))
103+ self ._write_debug ('Discarding old cache from version {version} (needs {min_ver})' .format (version = version , min_ver = min_ver ))
76104
77- def load (self , section , key , dtype = 'json' , default = None , min_ver = None ):
105+ def load (self , section , key , dtype = 'json' , default = None , ** kw_min_ver ):
78106 assert dtype in ('json' ,)
107+ min_ver = kw_min_ver .get ('min_ver' )
79108
80109 if not self .enabled :
81110 return default
82111
83112 cache_fn = self ._get_cache_fn (section , key , dtype )
84113 try :
114+ with open (cache_fn , encoding = 'utf-8' ) as cachef :
115+ self ._write_debug ('Loading {section}.{key} from cache' .format (section = section , key = key ), only_once = True )
116+ return self ._validate (json .load (cachef ), min_ver )
117+ except (ValueError , KeyError ):
85118 try :
86- with io .open (cache_fn , 'r' , encoding = 'utf-8' ) as cachef :
87- return self ._validate (json .load (cachef ), min_ver )
88- except ValueError :
89- try :
90- file_size = os .path .getsize (cache_fn )
91- except (OSError , IOError ) as oe :
92- file_size = error_to_compat_str (oe )
93- self ._ydl .report_warning (
94- 'Cache retrieval from %s failed (%s)' % (cache_fn , file_size ))
95- except IOError :
96- pass # No cache available
119+ file_size = 'size: %d' % os .path .getsize (cache_fn )
120+ except (OSError , IOError ) as oe :
121+ file_size = error_to_compat_str (oe )
122+ self ._report_warning ('Cache retrieval from %s failed (%s)' % (cache_fn , file_size ))
123+ except Exception as e :
124+ if getattr (e , 'errno' ) == errno .ENOENT :
125+ # no cache available
126+ return
127+ self ._report_warning ('Cache retrieval from %s failed' % (cache_fn ,))
97128
98129 return default
99130
100131 def remove (self ):
101132 if not self .enabled :
102- self ._ydl . to_screen ('Cache is disabled (Did you combine --no-cache-dir and --rm-cache-dir?)' )
133+ self ._to_screen ('Cache is disabled (Did you combine --no-cache-dir and --rm-cache-dir?)' )
103134 return
104135
105136 cachedir = self ._get_root_dir ()
106137 if not any ((term in cachedir ) for term in ('cache' , 'tmp' )):
107- raise Exception ('Not removing directory %s - this does not look like a cache dir' % cachedir )
138+ raise Exception ('Not removing directory %s - this does not look like a cache dir' % ( cachedir ,) )
108139
109- self ._ydl . to_screen (
110- 'Removing cache dir %s .' % cachedir , skip_eol = True )
140+ self ._to_screen (
141+ 'Removing cache dir %s .' % ( cachedir ,), skip_eol = True , ),
111142 if os .path .exists (cachedir ):
112- self ._ydl . to_screen ('.' , skip_eol = True )
143+ self ._to_screen ('.' , skip_eol = True )
113144 shutil .rmtree (cachedir )
114- self ._ydl . to_screen ('.' )
145+ self ._to_screen ('.' )
0 commit comments