Repository: peterbe/premailer

Issue:
Add cache support for _load_external_url
It would be handy if one could use `allow_network` to fetch styles and also rely on a cache for speedy results

It might be simple enough to just expose the session creation used for requests.  Then something like [CacheControl](https://2.python-requests.org/en/master/community/recommended/#cachecontrol) could be dropped in as a simple fix or something more advanced like a custom cache wrapper if needed.

Discussion:
thenewguy: @peterbe Ah can't confirm the test - it says "First-time contributors need a maintainer to approve running workflows".  Please approve
thenewguy: @peterbe thanks - failure seems to be a configuration error on the runner. any ideas? tox said passed but then job failed
peterbe: Failing linting?
thenewguy: > Failing linting?

Whoops yep - didn't realize that lint only ran on py39

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 85c1be6..debbd61 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,6 +4,10 @@ premailer Changes
 Peter's note: Unfortunately, ``premailer`` didn't use to keep a change log. But it's
 never too late to start, so let's start here and now.
 
+Unreleased
+----------
+* New option ``session=None`` to provide the session used for making http requests.
+
 3.9.0
 -----
 
diff --git a/README.rst b/README.rst
index 0dad963..1b6b8cc 100644
--- a/README.rst
+++ b/README.rst
@@ -137,6 +137,7 @@ The ``transform`` shortcut function transforms the given HTML using the defaults
     allow_network=True # allow network access to fetch linked css files
     allow_insecure_ssl=False # Don't allow unverified SSL certificates for external links
     allow_loading_external_files=False # Allow loading any non-HTTP external file URL
+    session=None # Session used for http requests - supply your own for caching or to provide authentication
 
 For more advanced options, check out the code of the ``Premailer`` class
 and all its options in its constructor.
diff --git a/premailer/premailer.py b/premailer/premailer.py
index b9d1390..66907ca 100644
--- a/premailer/premailer.py
+++ b/premailer/premailer.py
@@ -140,6 +140,7 @@ class Premailer(object):
         allow_network=True,
         allow_insecure_ssl=False,
         allow_loading_external_files=False,
+        session=None,
     ):
         self.html = html
         self.base_url = base_url
@@ -185,6 +186,7 @@ class Premailer(object):
         self.allow_network = allow_network
         self.allow_insecure_ssl = allow_insecure_ssl
         self.allow_loading_external_files = allow_loading_external_files
+        self.session = session or requests
 
         if cssutils_logging_handler:
             cssutils.log.addHandler(cssutils_logging_handler)
@@ -568,7 +570,7 @@ class Premailer(object):
             return out
 
     def _load_external_url(self, url):
-        response = requests.get(url, verify=not self.allow_insecure_ssl)
+        response = self.session.get(url, verify=not self.allow_insecure_ssl)
         response.raise_for_status()
         return response.text
 


Test patch:
diff --git a/premailer/tests/test_premailer.py b/premailer/tests/test_premailer.py
index 9c0c409..b4e4180 100644
--- a/premailer/tests/test_premailer.py
+++ b/premailer/tests/test_premailer.py
@@ -2084,6 +2084,17 @@ ent:"" !important;display:block !important}
         mocked_requests.get.assert_called_once_with(faux_uri, verify=True)
         eq_(faux_response, r)
 
+    def test_load_external_url_with_custom_session(self):
+        mocked_session = mock.MagicMock()
+        faux_response = "This is not a response"
+        faux_uri = "https://example.com/site.css"
+        mocked_session.get.return_value = MockResponse(faux_response)
+        p = premailer.premailer.Premailer("<p>A paragraph</p>", session=mocked_session)
+        r = p._load_external_url(faux_uri)
+
+        mocked_session.get.assert_called_once_with(faux_uri, verify=True)
+        eq_(faux_response, r)
+
     @mock.patch("premailer.premailer.requests")
     def test_load_external_url_no_insecure_ssl(self, mocked_requests):
         "Test premailer.premailer.Premailer._load_external_url"