@@ -170,6 +170,7 @@ def __init__(
170170 # This way, it holds the same positional param place for PCA,
171171 # when we would eventually want to add this feature to PCA in future.
172172 exclude_scopes = None ,
173+ http_cache = None ,
173174 ):
174175 """Create an instance of application.
175176
@@ -336,6 +337,60 @@ def __init__(
336337 If that is unnecessary or undesirable for your app,
337338 now you can use this parameter to supply an exclusion list of scopes,
338339 such as ``exclude_scopes = ["offline_access"]``.
340+
341+ :param dict http_cache:
342+ MSAL has long been caching tokens in the ``token_cache``.
343+ Recently, MSAL also introduced a concept of ``http_cache``,
344+ by automatically caching some finite amount of non-token http responses,
345+ so that *long-lived*
346+ ``PublicClientApplication`` and ``ConfidentialClientApplication``
347+ would be more performant and responsive in some situations.
348+
349+ This ``http_cache`` parameter accepts any dict-like object.
350+ If not provided, MSAL will use an in-memory dict.
351+
352+ If your app is a command-line app (CLI),
353+ you would want to persist your http_cache across different CLI runs.
354+ The following recipe shows a way to do so::
355+
356+ # Just add the following lines at the beginning of your CLI script
357+ import sys, atexit, pickle
358+ http_cache_filename = sys.argv[0] + ".http_cache"
359+ try:
360+ with open(http_cache_filename, "rb") as f:
361+ persisted_http_cache = pickle.load(f) # Take a snapshot
362+ except (
363+ IOError, # A non-exist http cache file
364+ pickle.UnpicklingError, # A corrupted http cache file
365+ EOFError, # An empty http cache file
366+ AttributeError, ImportError, IndexError, # Other corruption
367+ ):
368+ persisted_http_cache = {} # Recover by starting afresh
369+ atexit.register(lambda: pickle.dump(
370+ # When exit, flush it back to the file.
371+ # It may occasionally overwrite another process's concurrent write,
372+ # but that is fine. Subsequent runs will reach eventual consistency.
373+ persisted_http_cache, open(http_cache_file, "wb")))
374+
375+ # And then you can implement your app as you normally would
376+ app = msal.PublicClientApplication(
377+ "your_client_id",
378+ ...,
379+ http_cache=persisted_http_cache, # Utilize persisted_http_cache
380+ ...,
381+ #token_cache=..., # You may combine the old token_cache trick
382+ # Please refer to token_cache recipe at
383+ # https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache
384+ )
385+ app.acquire_token_interactive(["your", "scope"], ...)
386+
387+ Content inside ``http_cache`` are cheap to obtain.
388+ There is no need to share them among different apps.
389+
390+ Content inside ``http_cache`` will contain no tokens nor
391+ Personally Identifiable Information (PII). Encryption is unnecessary.
392+
393+ New in version 1.16.0.
339394 """
340395 self .client_id = client_id
341396 self .client_credential = client_credential
@@ -370,7 +425,7 @@ def __init__(
370425 self .http_client .mount ("https://" , a )
371426 self .http_client = ThrottledHttpClient (
372427 self .http_client ,
373- {} # Hard code an in-memory cache, for now
428+ {} if http_cache is None else http_cache , # Default to an in-memory dict
374429 )
375430
376431 self .app_name = app_name
0 commit comments