3636 msg = "Could not import cachetools. Did you run 'pip install django-storages[s3]'?"
3737 raise ImproperlyConfigured (msg ) from e
3838
39+ try :
40+ from functools import cached_property
41+ except ImportError : # python_version<='3.7'
42+ try :
43+ from backports .cached_property import cached_property
44+ except (ImportError , ModuleNotFoundError ) as e :
45+ msg = "Could not import backports.cached_property. Did you run 'pip install django-storages[s3]'?" # noqa: E501
46+ raise ImproperlyConfigured (msg ) from e
47+
3948try :
4049 import boto3 .session
4150 import botocore
@@ -379,10 +388,6 @@ def __init__(self, **settings):
379388 else :
380389 self .cloudfront_signer = None
381390
382- self ._ttl_cache = cachetools .cached (
383- cache = cachetools .TTLCache (maxsize = 2 , ttl = 3600 ), lock = threading .Lock ()
384- )
385-
386391 def get_cloudfront_signer (self , key_id , key ):
387392 cache_key = f"{ key_id } :{ key } "
388393 if cache_key not in self .__class__ ._signers :
@@ -451,6 +456,7 @@ def get_default_settings(self):
451456 "use_threads" : setting ("AWS_S3_USE_THREADS" , True ),
452457 "transfer_config" : setting ("AWS_S3_TRANSFER_CONFIG" , None ),
453458 "client_config" : setting ("AWS_S3_CLIENT_CONFIG" , None ),
459+ "client_ttl" : setting ("AWS_S3_CLIENT_TTL" , 3600 ),
454460 }
455461
456462 def __getstate__ (self ):
@@ -465,23 +471,27 @@ def __setstate__(self, state):
465471 def connection (self ):
466472 """
467473 Get the (cached) thread-safe boto3 s3 resource.
468-
469- This function has a 1 hour time to live cache for the boto3 resource.
470- We want to avoid storing a resource for too long to avoid their memory leak
471- ref https://github.com/boto/boto3/issues/1670.
472474 """
473475 return self ._ttl_cache (self ._create_connection )()
474476
475477 @property
476478 def unsigned_connection (self ):
477479 """
478480 Get the (cached) thread-safe boto3 s3 resource (unsigned).
481+ """
482+ return self ._ttl_cache (self ._create_connection )(unsigned = True )
479483
480- This function has a 1 hour time to live cache for the boto3 resource.
484+ @cached_property
485+ def _ttl_cache (self ):
486+ """
487+ This time-to-live cache is used to periodically recreate boto3 clients.
481488 We want to avoid storing a resource for too long to avoid their memory leak
482489 ref https://github.com/boto/boto3/issues/1670.
483490 """
484- return self ._ttl_cache (self ._create_connection )(unsigned = True )
491+ return cachetools .cached (
492+ cache = cachetools .TTLCache (maxsize = 2 , ttl = self .client_ttl ),
493+ lock = threading .Lock (),
494+ )
485495
486496 def _create_connection (self , * , unsigned = False ):
487497 """
0 commit comments