-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract url_fetcher into URLFetchHandler and add state testing
- Loading branch information
Showing
4 changed files
with
82 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import werkzeug | ||
from sentry_sdk import add_breadcrumb | ||
|
||
from .errors import ForbiddenURLFetchError, URLFetcherCalledAfterExitException | ||
|
||
|
||
class URLFetchHandler: | ||
""" | ||
Implements an url_fetcher for WeasyPrint. | ||
Normally WeasyPrint will swallow any url fetch errors and demote them to warning. | ||
This implementation keeps track of thrown errors and throws an exception if any occured. | ||
It's important to note that the `url_fetcher` is stored by HTML and will then be used by the | ||
`.render` method, so the render call has to be inside the with, too. | ||
:raise: werkzeug.exceptions.Forbidden() if a forbidden URL was requested. | ||
:example: | ||
>>> from weasyprint import HTML | ||
>>> | ||
>>> with URLFetchHandler() as url_fetcher | ||
>>> html = HTML(string=html_string, url_fetcher=url_fetcher) | ||
>>> doc = html.render() | ||
""" | ||
|
||
def __init__(self): | ||
self.url_errors = [] | ||
self.closed = False | ||
|
||
def __enter__(self): | ||
return self | ||
|
||
def __exit__(self, exc_type, exc_val, exc_tb): | ||
self.closed = True | ||
if len(self.url_errors) != 0: | ||
raise werkzeug.exceptions.Forbidden() | ||
|
||
def __call__(self, url): | ||
if self.closed: | ||
raise URLFetcherCalledAfterExitException() | ||
|
||
error = ForbiddenURLFetchError(url) | ||
add_breadcrumb(message="Refused to fetch URL (%s)" % url) | ||
self.url_errors.append(error) | ||
raise error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import pytest | ||
import werkzeug | ||
|
||
from pdf_service import URLFetchHandler | ||
from pdf_service.errors import URLFetcherCalledAfterExitException | ||
|
||
|
||
def test_exits_without_throwing_if_fetcher_isnt_called(): | ||
with URLFetchHandler(): | ||
pass | ||
|
||
|
||
def test_throws_if_fetcher_throws(): | ||
with pytest.raises(werkzeug.exceptions.Forbidden): | ||
with URLFetchHandler() as url_fetcher: | ||
url_fetcher("https://example.com/test.png") | ||
|
||
|
||
def test_throws_when_called_after_exit(): | ||
handler = URLFetchHandler() | ||
with handler: | ||
pass | ||
|
||
with pytest.raises(URLFetcherCalledAfterExitException): | ||
handler("https://example.com/test.png") |