1313import stat
1414import sys
1515import tempfile
16+ import typing as t
1617from hashlib import sha1
1718from io import BytesIO
19+ from types import CodeType
20+
21+ if t .TYPE_CHECKING :
22+ import typing_extensions as te
23+ from .environment import Environment
24+
25+ class _MemcachedClient (te .Protocol ):
26+ def get (self , key : str ) -> bytes :
27+ ...
28+
29+ def set (self , key : str , value : bytes , timeout : t .Optional [int ] = None ) -> None :
30+ ...
1831
19- from .utils import open_if_exists
2032
2133bc_version = 5
2234# Magic bytes to identify Jinja bytecode cache files. Contains the
@@ -38,17 +50,17 @@ class Bucket:
3850 cache subclasses don't have to care about cache invalidation.
3951 """
4052
41- def __init__ (self , environment , key , checksum ) :
53+ def __init__ (self , environment : "Environment" , key : str , checksum : str ) -> None :
4254 self .environment = environment
4355 self .key = key
4456 self .checksum = checksum
4557 self .reset ()
4658
47- def reset (self ):
59+ def reset (self ) -> None :
4860 """Resets the bucket (unloads the bytecode)."""
49- self .code = None
61+ self .code : t . Optional [ CodeType ] = None
5062
51- def load_bytecode (self , f ) :
63+ def load_bytecode (self , f : t . BinaryIO ) -> None :
5264 """Loads bytecode from a file or file like object."""
5365 # make sure the magic header is correct
5466 magic = f .read (len (bc_magic ))
@@ -67,20 +79,20 @@ def load_bytecode(self, f):
6779 self .reset ()
6880 return
6981
70- def write_bytecode (self , f ) :
82+ def write_bytecode (self , f : t . BinaryIO ) -> None :
7183 """Dump the bytecode into the file or file like object passed."""
7284 if self .code is None :
7385 raise TypeError ("can't write empty bucket" )
7486 f .write (bc_magic )
7587 pickle .dump (self .checksum , f , 2 )
7688 marshal .dump (self .code , f )
7789
78- def bytecode_from_string (self , string ) :
79- """Load bytecode from a string ."""
90+ def bytecode_from_string (self , string : bytes ) -> None :
91+ """Load bytecode from bytes ."""
8092 self .load_bytecode (BytesIO (string ))
8193
82- def bytecode_to_string (self ):
83- """Return the bytecode as string ."""
94+ def bytecode_to_string (self ) -> bytes :
95+ """Return the bytecode as bytes ."""
8496 out = BytesIO ()
8597 self .write_bytecode (out )
8698 return out .getvalue ()
@@ -115,41 +127,48 @@ def dump_bytecode(self, bucket):
115127 Jinja.
116128 """
117129
118- def load_bytecode (self , bucket ) :
130+ def load_bytecode (self , bucket : Bucket ) -> None :
119131 """Subclasses have to override this method to load bytecode into a
120132 bucket. If they are not able to find code in the cache for the
121133 bucket, it must not do anything.
122134 """
123135 raise NotImplementedError ()
124136
125- def dump_bytecode (self , bucket ) :
137+ def dump_bytecode (self , bucket : Bucket ) -> None :
126138 """Subclasses have to override this method to write the bytecode
127139 from a bucket back to the cache. If it unable to do so it must not
128140 fail silently but raise an exception.
129141 """
130142 raise NotImplementedError ()
131143
132- def clear (self ):
144+ def clear (self ) -> None :
133145 """Clears the cache. This method is not used by Jinja but should be
134146 implemented to allow applications to clear the bytecode cache used
135147 by a particular environment.
136148 """
137149
138- def get_cache_key (self , name , filename = None ):
150+ def get_cache_key (
151+ self , name : str , filename : t .Optional [t .Union [str ]] = None
152+ ) -> str :
139153 """Returns the unique hash key for this template name."""
140154 hash = sha1 (name .encode ("utf-8" ))
155+
141156 if filename is not None :
142- filename = "|" + filename
143- if isinstance (filename , str ):
144- filename = filename .encode ("utf-8" )
145- hash .update (filename )
157+ hash .update (f"|{ filename } " .encode ("utf-8" ))
158+
146159 return hash .hexdigest ()
147160
148- def get_source_checksum (self , source ) :
161+ def get_source_checksum (self , source : str ) -> str :
149162 """Returns a checksum for the source."""
150163 return sha1 (source .encode ("utf-8" )).hexdigest ()
151164
152- def get_bucket (self , environment , name , filename , source ):
165+ def get_bucket (
166+ self ,
167+ environment : "Environment" ,
168+ name : str ,
169+ filename : t .Optional [str ],
170+ source : str ,
171+ ) -> Bucket :
153172 """Return a cache bucket for the given template. All arguments are
154173 mandatory but filename may be `None`.
155174 """
@@ -159,7 +178,7 @@ def get_bucket(self, environment, name, filename, source):
159178 self .load_bytecode (bucket )
160179 return bucket
161180
162- def set_bucket (self , bucket ) :
181+ def set_bucket (self , bucket : Bucket ) -> None :
163182 """Put the bucket into the cache."""
164183 self .dump_bytecode (bucket )
165184
@@ -182,14 +201,16 @@ class FileSystemBytecodeCache(BytecodeCache):
182201 This bytecode cache supports clearing of the cache using the clear method.
183202 """
184203
185- def __init__ (self , directory = None , pattern = "__jinja2_%s.cache" ):
204+ def __init__ (
205+ self , directory : t .Optional [str ] = None , pattern : str = "__jinja2_%s.cache"
206+ ) -> None :
186207 if directory is None :
187208 directory = self ._get_default_cache_dir ()
188209 self .directory = directory
189210 self .pattern = pattern
190211
191- def _get_default_cache_dir (self ):
192- def _unsafe_dir ():
212+ def _get_default_cache_dir (self ) -> str :
213+ def _unsafe_dir () -> t . NoReturn :
193214 raise RuntimeError (
194215 "Cannot determine safe temp directory. You "
195216 "need to explicitly provide one."
@@ -235,25 +256,21 @@ def _unsafe_dir():
235256
236257 return actual_dir
237258
238- def _get_cache_filename (self , bucket ) :
259+ def _get_cache_filename (self , bucket : Bucket ) -> str :
239260 return os .path .join (self .directory , self .pattern % (bucket .key ,))
240261
241- def load_bytecode (self , bucket ):
242- f = open_if_exists (self ._get_cache_filename (bucket ), "rb" )
243- if f is not None :
244- try :
262+ def load_bytecode (self , bucket : Bucket ) -> None :
263+ filename = self ._get_cache_filename (bucket )
264+
265+ if os .path .exists (filename ):
266+ with open (filename , "rb" ) as f :
245267 bucket .load_bytecode (f )
246- finally :
247- f .close ()
248268
249- def dump_bytecode (self , bucket ):
250- f = open (self ._get_cache_filename (bucket ), "wb" )
251- try :
269+ def dump_bytecode (self , bucket : Bucket ) -> None :
270+ with open (self ._get_cache_filename (bucket ), "wb" ) as f :
252271 bucket .write_bytecode (f )
253- finally :
254- f .close ()
255272
256- def clear (self ):
273+ def clear (self ) -> None :
257274 # imported lazily here because google app-engine doesn't support
258275 # write access on the file system and the function does not exist
259276 # normally.
@@ -314,32 +331,34 @@ class MemcachedBytecodeCache(BytecodeCache):
314331
315332 def __init__ (
316333 self ,
317- client ,
318- prefix = "jinja2/bytecode/" ,
319- timeout = None ,
320- ignore_memcache_errors = True ,
334+ client : "_MemcachedClient" ,
335+ prefix : str = "jinja2/bytecode/" ,
336+ timeout : t . Optional [ int ] = None ,
337+ ignore_memcache_errors : bool = True ,
321338 ):
322339 self .client = client
323340 self .prefix = prefix
324341 self .timeout = timeout
325342 self .ignore_memcache_errors = ignore_memcache_errors
326343
327- def load_bytecode (self , bucket ) :
344+ def load_bytecode (self , bucket : Bucket ) -> None :
328345 try :
329346 code = self .client .get (self .prefix + bucket .key )
330347 except Exception :
331348 if not self .ignore_memcache_errors :
332349 raise
333- code = None
334- if code is not None :
350+ else :
335351 bucket .bytecode_from_string (code )
336352
337- def dump_bytecode (self , bucket ) :
338- args = ( self .prefix + bucket .key , bucket . bytecode_to_string ())
339- if self . timeout is not None :
340- args += ( self . timeout ,)
353+ def dump_bytecode (self , bucket : Bucket ) -> None :
354+ key = self .prefix + bucket .key
355+ value = bucket . bytecode_to_string ()
356+
341357 try :
342- self .client .set (* args )
358+ if self .timeout is not None :
359+ self .client .set (key , value , self .timeout )
360+ else :
361+ self .client .set (key , value )
343362 except Exception :
344363 if not self .ignore_memcache_errors :
345364 raise
0 commit comments