From cf0159b110e58bc6504dd88ab295d06eb9126f12 Mon Sep 17 00:00:00 2001 From: Dane Hillard Date: Mon, 22 Jul 2019 22:57:46 -0400 Subject: [PATCH 1/2] Use black code style and lint with pyflakes --- .travis.yml | 13 +++++ pyproject.toml | 3 ++ tests/test_middleware.py | 29 +++++------ tests/test_models.py | 29 ++++++++--- tests/test_resolution.py | 22 ++++---- tests/test_settings.py | 15 ++---- tests/test_urls.py | 4 +- tests/test_views.py | 72 ++++++++++++++++----------- webmention/__init__.py | 2 + webmention/admin.py | 55 ++++++++------------ webmention/checks.py | 10 ++-- webmention/middleware.py | 20 ++++---- webmention/migrations/0001_initial.py | 28 +++++------ webmention/models.py | 10 ++-- webmention/resolution.py | 4 +- webmention/urls.py | 6 +-- webmention/views.py | 13 +++-- 17 files changed, 174 insertions(+), 161 deletions(-) create mode 100644 pyproject.toml diff --git a/.travis.yml b/.travis.yml index a3020fa..c2acb99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,3 +13,16 @@ install: script: - tox + +stages: + - lint + - test + +jobs: + include: + - stage: lint + install: + - pip install black pyflakes + script: + - pyflakes webmention tests + - black --check webmention tests diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7276f76 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[tool.black] +line-length = 120 +target-version = ['py35', 'py36', 'py37', 'py38'] diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 3ec7c87..5a32306 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -19,38 +19,35 @@ def middleware(): def test_process_request_creates_link_header(middleware): request = Mock() - request.scheme = 'http' - request.META = {'HTTP_HOST': 'example.com'} + request.scheme = "http" + request.META = {"HTTP_HOST": "example.com"} response = HttpResponse() response = middleware.process_response(request, response) expected_link_header = '<{scheme}://{host}{path}>; rel="webmention"'.format( - scheme=request.scheme, - host=request.META.get('HTTP_HOST'), - path=reverse('webmention:receive') + scheme=request.scheme, host=request.META.get("HTTP_HOST"), path=reverse("webmention:receive") ) - assert 'Link' in response - assert response['Link'] == expected_link_header + assert "Link" in response + assert response["Link"] == expected_link_header + def test_process_request_appends_link_header(middleware): request = Mock() - request.scheme = 'http' - request.META = {'HTTP_HOST': 'example.com'} + request.scheme = "http" + request.META = {"HTTP_HOST": "example.com"} response = HttpResponse() original_link_header = '; rel="meta"' - response['Link'] = original_link_header + response["Link"] = original_link_header response = middleware.process_response(request, response) new_link_header = '<{scheme}://{host}{path}>; rel="webmention"'.format( - scheme=request.scheme, - host=request.META.get('HTTP_HOST'), - path=reverse('webmention:receive') + scheme=request.scheme, host=request.META.get("HTTP_HOST"), path=reverse("webmention:receive") ) - expected_link_header = ', '.join((original_link_header, new_link_header)) + expected_link_header = ", ".join((original_link_header, new_link_header)) - assert 'Link' in response - assert response['Link'] == expected_link_header + assert "Link" in response + assert response["Link"] == expected_link_header diff --git a/tests/test_models.py b/tests/test_models.py index 4eb3b11..94d0a96 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -7,48 +7,61 @@ @pytest.fixture def test_response_body(): - return 'foo' + return "foo" @pytest.mark.django_db def test_str(test_source, test_target, test_response_body): - webmention = WebMentionResponse.objects.create(source=test_source, response_to=test_target, response_body=test_response_body) + webmention = WebMentionResponse.objects.create( + source=test_source, response_to=test_target, response_body=test_response_body + ) webmention.save() assert str(webmention) == webmention.source + @pytest.mark.django_db def test_source_for_admin(test_source, test_target, test_response_body): - webmention = WebMentionResponse.objects.create(source=test_source, response_to=test_target, response_body=test_response_body) + webmention = WebMentionResponse.objects.create( + source=test_source, response_to=test_target, response_body=test_response_body + ) webmention.save() assert webmention.source_for_admin() == '{href}'.format(href=webmention.source) + @pytest.mark.django_db def test_response_to_for_admin(test_source, test_target, test_response_body): - webmention = WebMentionResponse.objects.create(source=test_source, response_to=test_target, response_body=test_response_body) + webmention = WebMentionResponse.objects.create( + source=test_source, response_to=test_target, response_body=test_response_body + ) webmention.save() assert webmention.response_to_for_admin() == '{href}'.format(href=webmention.response_to) -@patch('webmention.models.WebMentionResponse.save') + +@patch("webmention.models.WebMentionResponse.save") def test_invalidate_when_not_previously_saved(mock_save): webmention = WebMentionResponse() webmention.invalidate() assert not mock_save.called + @pytest.mark.django_db def test_invalidate_when_previously_saved(test_source, test_target, test_response_body): - webmention = WebMentionResponse.objects.create(source=test_source, response_to=test_target, response_body=test_response_body) + webmention = WebMentionResponse.objects.create( + source=test_source, response_to=test_target, response_body=test_response_body + ) webmention.save() webmention.invalidate() assert not webmention.current -@patch('webmention.models.WebMentionResponse.save') + +@patch("webmention.models.WebMentionResponse.save") def test_update_when_previously_invalid(mock_save, test_source, test_target, test_response_body): - webmention = WebMentionResponse.objects.create(source='foo', response_to='bar', response_body='baz', current=False) + webmention = WebMentionResponse.objects.create(source="foo", response_to="bar", response_body="baz", current=False) assert mock_save.call_count == 1 webmention.update(test_source, test_target, test_response_body) diff --git a/tests/test_resolution.py b/tests/test_resolution.py index fb3d8d3..501dcec 100644 --- a/tests/test_resolution.py +++ b/tests/test_resolution.py @@ -7,22 +7,22 @@ except ImportError: from django.urls import Resolver404 -from django.test import TestCase - from webmention.resolution import url_resolves, fetch_and_validate_source, SourceFetchError, TargetNotFoundError -@patch('webmention.resolution.resolve') +@patch("webmention.resolution.resolve") def test_url_resolves_when_resolves(mock_resolve, test_source, test_target): - mock_resolve.return_value = 'foo' + mock_resolve.return_value = "foo" assert url_resolves(test_target) -@patch('webmention.resolution.resolve') + +@patch("webmention.resolution.resolve") def test_url_resolves_when_does_not_resolve(mock_resolve): mock_resolve.side_effect = Resolver404 - assert not url_resolves('http://example.com/page') + assert not url_resolves("http://example.com/page") + -@patch('requests.get') +@patch("requests.get") def test_fetch_and_validate_source_happy_path(mock_get, test_source, test_target): mock_response = Mock() mock_response.status_code = 200 @@ -31,7 +31,8 @@ def test_fetch_and_validate_source_happy_path(mock_get, test_source, test_target assert fetch_and_validate_source(test_source, test_target) == mock_response.content -@patch('requests.get') + +@patch("requests.get") def test_fetch_and_validate_source_when_source_unavailable(mock_get, test_source, test_target): mock_response = Mock() mock_response.status_code = 404 @@ -40,11 +41,12 @@ def test_fetch_and_validate_source_when_source_unavailable(mock_get, test_source with pytest.raises(SourceFetchError): fetch_and_validate_source(test_source, test_target) -@patch('requests.get') + +@patch("requests.get") def test_fetch_and_validate_source_when_source_does_not_contain_target(mock_get, test_source, test_target): mock_response = Mock() mock_response.status_code = 200 - mock_response.content = 'foo' + mock_response.content = "foo" mock_get.return_value = mock_response with pytest.raises(TargetNotFoundError): diff --git a/tests/test_settings.py b/tests/test_settings.py index 9148e7e..be51a2c 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,13 +1,6 @@ -SECRET_KEY = 'key-for-testing' -INSTALLED_APPS = [ - 'webmention', -] +SECRET_KEY = "key-for-testing" +INSTALLED_APPS = ["webmention"] -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': 'tests.sqlite3', - } -} +DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": "tests.sqlite3"}} -ROOT_URLCONF = 'tests.test_urls' +ROOT_URLCONF = "tests.test_urls" diff --git a/tests/test_urls.py b/tests/test_urls.py index 0d0f7ae..216c177 100644 --- a/tests/test_urls.py +++ b/tests/test_urls.py @@ -1,5 +1,3 @@ from django.conf.urls import url, include -urlpatterns = [ - url(r'^webmention', include('webmention.urls', namespace='webmention')), -] +urlpatterns = [url(r"^webmention", include("webmention.urls", namespace="webmention"))] diff --git a/tests/test_views.py b/tests/test_views.py index 4f17ccc..3b01ba9 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -10,27 +10,29 @@ def test_receive_when_source_not_in_post_data(test_target): request = Mock() - request.method = 'POST' - request.POST = {'target': test_target} + request.method = "POST" + request.POST = {"target": test_target} response = receive(request) assert isinstance(response, HttpResponseBadRequest) + def test_receive_when_target_not_in_post_data(test_source): request = Mock() - request.method = 'POST' - request.POST = {'source': test_source} + request.method = "POST" + request.POST = {"source": test_source} response = receive(request) assert isinstance(response, HttpResponseBadRequest) -@patch('webmention.views.url_resolves') + +@patch("webmention.views.url_resolves") def test_receive_when_target_does_not_resolve(mock_url_resolves, test_source, test_target): request = Mock() - request.method = 'POST' - request.POST = {'source': test_source, 'target': test_target} + request.method = "POST" + request.POST = {"source": test_source, "target": test_target} mock_url_resolves.return_value = False response = receive(request) @@ -38,17 +40,18 @@ def test_receive_when_target_does_not_resolve(mock_url_resolves, test_source, te mock_url_resolves.assert_called_once_with(test_target) assert isinstance(response, HttpResponseBadRequest) + @pytest.mark.django_db -@patch('webmention.views.WebMentionResponse.update') -@patch('webmention.views.fetch_and_validate_source') -@patch('webmention.views.url_resolves') +@patch("webmention.views.WebMentionResponse.update") +@patch("webmention.views.fetch_and_validate_source") +@patch("webmention.views.url_resolves") def test_receive_happy_path(mock_url_resolves, mock_fetch_and_validate_source, mock_update, test_source, test_target): request = Mock() - request.method = 'POST' - request.POST = {'source': test_source, 'target': test_target} + request.method = "POST" + request.POST = {"source": test_source, "target": test_target} mock_url_resolves.return_value = True - mock_fetch_and_validate_source.return_value = 'foo' + mock_fetch_and_validate_source.return_value = "foo" response = receive(request) mock_fetch_and_validate_source.assert_called_once_with(test_source, test_target) @@ -56,14 +59,17 @@ def test_receive_happy_path(mock_url_resolves, mock_fetch_and_validate_source, m mock_url_resolves.assert_called_once_with(test_target) assert isinstance(response, HttpResponse) + @pytest.mark.django_db -@patch('webmention.views.WebMentionResponse.invalidate') -@patch('webmention.views.fetch_and_validate_source') -@patch('webmention.views.url_resolves') -def test_receive_when_source_unavailable(mock_url_resolves, mock_fetch_and_validate_source, mock_invalidate, test_source, test_target): +@patch("webmention.views.WebMentionResponse.invalidate") +@patch("webmention.views.fetch_and_validate_source") +@patch("webmention.views.url_resolves") +def test_receive_when_source_unavailable( + mock_url_resolves, mock_fetch_and_validate_source, mock_invalidate, test_source, test_target +): request = Mock() - request.method = 'POST' - request.POST = {'source': test_source, 'target': test_target} + request.method = "POST" + request.POST = {"source": test_source, "target": test_target} mock_url_resolves.return_value = True mock_fetch_and_validate_source.side_effect = SourceFetchError @@ -74,14 +80,17 @@ def test_receive_when_source_unavailable(mock_url_resolves, mock_fetch_and_valid assert mock_invalidate.call_count == 1 assert isinstance(response, HttpResponseBadRequest) + @pytest.mark.django_db -@patch('webmention.views.WebMentionResponse.invalidate') -@patch('webmention.views.fetch_and_validate_source') -@patch('webmention.views.url_resolves') -def test_receive_when_source_does_not_contain_target(mock_url_resolves, mock_fetch_and_validate_source, mock_invalidate, test_source, test_target): +@patch("webmention.views.WebMentionResponse.invalidate") +@patch("webmention.views.fetch_and_validate_source") +@patch("webmention.views.url_resolves") +def test_receive_when_source_does_not_contain_target( + mock_url_resolves, mock_fetch_and_validate_source, mock_invalidate, test_source, test_target +): request = Mock() - request.method = 'POST' - request.POST = {'source': test_source, 'target': test_target} + request.method = "POST" + request.POST = {"source": test_source, "target": test_target} mock_url_resolves.return_value = True mock_fetch_and_validate_source.side_effect = TargetNotFoundError @@ -92,13 +101,16 @@ def test_receive_when_source_does_not_contain_target(mock_url_resolves, mock_fet assert mock_invalidate.call_count == 1 assert isinstance(response, HttpResponseBadRequest) + @pytest.mark.django_db -@patch('webmention.views.fetch_and_validate_source') -@patch('webmention.views.url_resolves') -def test_receive_when_general_exception_occurs(mock_url_resolves, mock_fetch_and_validate_source, test_source, test_target): +@patch("webmention.views.fetch_and_validate_source") +@patch("webmention.views.url_resolves") +def test_receive_when_general_exception_occurs( + mock_url_resolves, mock_fetch_and_validate_source, test_source, test_target +): request = Mock() - request.method = 'POST' - request.POST = {'source': test_source, 'target': test_target} + request.method = "POST" + request.POST = {"source": test_source, "target": test_target} mock_url_resolves.return_value = True mock_fetch_and_validate_source.side_effect = Exception diff --git a/webmention/__init__.py b/webmention/__init__.py index d05faa4..0d70f87 100644 --- a/webmention/__init__.py +++ b/webmention/__init__.py @@ -1 +1,3 @@ from . import checks + +__all__ = ["checks"] diff --git a/webmention/admin.py b/webmention/admin.py index bd4d080..873817a 100644 --- a/webmention/admin.py +++ b/webmention/admin.py @@ -7,49 +7,36 @@ class WebMentionResponseAdmin(admin.ModelAdmin): model = WebMentionResponse fields = [ - ( - 'source_for_admin', - 'response_to_for_admin', - ), - 'response_body', - ( - 'date_created', - 'date_modified', - ), - ( - 'reviewed', - 'current', - ) + ("source_for_admin", "response_to_for_admin"), + "response_body", + ("date_created", "date_modified"), + ("reviewed", "current"), ] readonly_fields = [ - 'response_body', - 'source_for_admin', - 'response_to_for_admin', - 'date_created', - 'date_modified', - 'current', + "response_body", + "source_for_admin", + "response_to_for_admin", + "date_created", + "date_modified", + "current", ] list_display = [ - 'pk', - 'source_for_admin', - 'response_to_for_admin', - 'date_created', - 'date_modified', - 'reviewed', - 'current', + "pk", + "source_for_admin", + "response_to_for_admin", + "date_created", + "date_modified", + "reviewed", + "current", ] - list_editable = [ - 'reviewed', - ] + list_editable = ["reviewed"] - list_filter = [ - 'reviewed', - 'current', - ] + list_filter = ["reviewed", "current"] + + date_hierarchy = "date_modified" - date_hierarchy = 'date_modified' admin.site.register(WebMentionResponse, WebMentionResponseAdmin) diff --git a/webmention/checks.py b/webmention/checks.py index a744a8a..25da27c 100644 --- a/webmention/checks.py +++ b/webmention/checks.py @@ -8,13 +8,13 @@ def new_style_middleware_check(app_configs, **kwargs): errors = [] if django.VERSION[1] >= 10 or django.VERSION[0] > 1: - installed_middlewares = getattr(settings, 'MIDDLEWARE', []) or [] - if 'webmention.middleware.WebMentionMiddleware' in installed_middlewares: + installed_middlewares = getattr(settings, "MIDDLEWARE", []) or [] + if "webmention.middleware.WebMentionMiddleware" in installed_middlewares: errors.append( Error( - 'You are attempting to use an old-style middleware class in the MIDDLEWARE setting', - hint='Either use MIDDLEWARE_CLASSES or use webmention.middleware.webmention_middleware instead', - id='webmention.E001', + "You are attempting to use an old-style middleware class in the MIDDLEWARE setting", + hint="Either use MIDDLEWARE_CLASSES or use webmention.middleware.webmention_middleware instead", + id="webmention.E001", ) ) return errors diff --git a/webmention/middleware.py b/webmention/middleware.py index 97fff88..1fb15a9 100644 --- a/webmention/middleware.py +++ b/webmention/middleware.py @@ -7,17 +7,15 @@ def add_webmention_headers_to_response(request, response): - link_header = '<{scheme}://{host}{path}>; rel="webmention"'.format( - scheme=request.scheme, - host=request.META.get('HTTP_HOST'), - path=reverse('webmention:receive') - ) - if not response.get('Link'): - response['Link'] = link_header - else: - response['Link'] = ', '.join((response['Link'], link_header)) - - return response + link_header = '<{scheme}://{host}{path}>; rel="webmention"'.format( + scheme=request.scheme, host=request.META.get("HTTP_HOST"), path=reverse("webmention:receive") + ) + if not response.get("Link"): + response["Link"] = link_header + else: + response["Link"] = ", ".join((response["Link"], link_header)) + + return response class WebMentionMiddleware(object): diff --git a/webmention/migrations/0001_initial.py b/webmention/migrations/0001_initial.py index a1d5f35..f7e5ad1 100644 --- a/webmention/migrations/0001_initial.py +++ b/webmention/migrations/0001_initial.py @@ -6,25 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='WebMentionResponse', + name="WebMentionResponse", fields=[ - ('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), - ('response_body', models.TextField()), - ('response_to', models.URLField()), - ('source', models.URLField()), - ('reviewed', models.BooleanField(default=False)), - ('current', models.BooleanField(default=True)), - ('date_created', models.DateTimeField(auto_now_add=True)), - ('date_modified', models.DateTimeField(auto_now=True)), + ("id", models.AutoField(verbose_name="ID", auto_created=True, primary_key=True, serialize=False)), + ("response_body", models.TextField()), + ("response_to", models.URLField()), + ("source", models.URLField()), + ("reviewed", models.BooleanField(default=False)), + ("current", models.BooleanField(default=True)), + ("date_created", models.DateTimeField(auto_now_add=True)), + ("date_modified", models.DateTimeField(auto_now=True)), ], - options={ - 'verbose_name': 'webmention', - 'verbose_name_plural': 'webmentions', - }, - ), + options={"verbose_name": "webmention", "verbose_name_plural": "webmentions"}, + ) ] diff --git a/webmention/models.py b/webmention/models.py index f978b5b..84ea727 100644 --- a/webmention/models.py +++ b/webmention/models.py @@ -11,21 +11,23 @@ class WebMentionResponse(models.Model): date_modified = models.DateTimeField(auto_now=True) class Meta: - verbose_name = 'webmention' - verbose_name_plural = 'webmentions' + verbose_name = "webmention" + verbose_name_plural = "webmentions" def __str__(self): return self.source def source_for_admin(self): return '{href}'.format(href=self.source) + source_for_admin.allow_tags = True - source_for_admin.short_description = 'source' + source_for_admin.short_description = "source" def response_to_for_admin(self): return '{href}'.format(href=self.response_to) + response_to_for_admin.allow_tags = True - response_to_for_admin.short_description = 'response to' + response_to_for_admin.short_description = "response to" def invalidate(self): if self.id: diff --git a/webmention/resolution.py b/webmention/resolution.py index 6f18642..87bc418 100644 --- a/webmention/resolution.py +++ b/webmention/resolution.py @@ -22,9 +22,9 @@ def fetch_and_validate_source(source, target): if target in str(response.content): return response.content else: - raise TargetNotFoundError('Source URL did not contain target URL') + raise TargetNotFoundError("Source URL did not contain target URL") else: - raise SourceFetchError('Could not fetch source URL') + raise SourceFetchError("Could not fetch source URL") class SourceFetchError(Exception): diff --git a/webmention/urls.py b/webmention/urls.py index c91cb18..b0483e3 100644 --- a/webmention/urls.py +++ b/webmention/urls.py @@ -3,9 +3,7 @@ from . import views -app_name = 'webmention' +app_name = "webmention" -urlpatterns = [ - url(r'^receive$', views.receive, name='receive'), -] +urlpatterns = [url(r"^receive$", views.receive, name="receive")] diff --git a/webmention/views.py b/webmention/views.py index 03e5ded..8b381b3 100644 --- a/webmention/views.py +++ b/webmention/views.py @@ -9,13 +9,13 @@ @csrf_exempt @require_POST def receive(request): - if 'source' in request.POST and 'target' in request.POST: - source = request.POST.get('source') - target = request.POST.get('target') + if "source" in request.POST and "target" in request.POST: + source = request.POST.get("source") + target = request.POST.get("target") webmention = None if not url_resolves(target): - return HttpResponseBadRequest('Target URL did not resolve to a resource on the server') + return HttpResponseBadRequest("Target URL did not resolve to a resource on the server") try: try: @@ -25,12 +25,11 @@ def receive(request): response_body = fetch_and_validate_source(source, target) webmention.update(source, target, response_body) - return HttpResponse('The webmention was successfully received', status=202) + return HttpResponse("The webmention was successfully received", status=202) except (SourceFetchError, TargetNotFoundError) as e: webmention.invalidate() return HttpResponseBadRequest(str(e)) except Exception as e: return HttpResponseServerError(str(e)) else: - return HttpResponseBadRequest('webmention source and/or target not in request') - + return HttpResponseBadRequest("webmention source and/or target not in request") From db93582c7d63ec8ca7c8e7bb621dc82c8bfcfe61 Mon Sep 17 00:00:00 2001 From: Dane Hillard Date: Mon, 22 Jul 2019 22:59:11 -0400 Subject: [PATCH 2/2] Reorder Pythons so black runs properly --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2acb99..45869fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,9 @@ language: python cache: pip python: - - "3.5" - - "3.6" - "3.7" + - "3.6" + - "3.5" - "3.8-dev" install: