diff --git a/.circleci/config.yml b/.circleci/config.yml index db921cba4..06e0ad81b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,7 +52,7 @@ jobs: name: run tests command: | . venv/bin/activate - pep8 --ignore=E402 webcompat/ tests/ config/secrets.py.example + pycodestyle --ignore=E402,W504 webcompat/ tests/ config/secrets.py.example npm run lint npm run build nosetests diff --git a/config/__init__.py b/config/__init__.py index bf0e8979a..d5bf9a7e8 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -17,8 +17,8 @@ import requests -from environment import * # nopep8 -from secrets import * # nopep8 +from environment import * # noqa +from secrets import * # noqa MILESTONE_ERROR = """It failed with {msg}! We will read from data/milestones.json. @@ -29,7 +29,7 @@ in config/secrets.py and try again. Good luck! """ MILESTONE_UNMATCHING = """A milestone is missing or has been added: {names}""" -MILESTONE_UNMATCHING_ERROR = """Check the milestones names on your Github repository and try again. +MILESTONE_UNMATCHING_ERROR = """Check the milestones names on your Github repository and try again. This error was probably caused by a typo. Your milestones.json was erased and a backup copy was created at {path}. """ diff --git a/config/requirements.txt b/config/requirements.txt index aa2420c4e..f99cd2b0f 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -7,8 +7,8 @@ Flask-WTF==0.14.2 GitHub-Flask==3.2.0 mock==2.0.0 nose==1.3.7 -pep8==1.7.1 Pillow==5.4.1 +pycodestyle==2.5.0 requests==2.21.0 ua-parser==0.8 WTForms==2.2.1 diff --git a/docs/pr-coding-guidelines.md b/docs/pr-coding-guidelines.md index 00ba02bc8..0a7a6943d 100644 --- a/docs/pr-coding-guidelines.md +++ b/docs/pr-coding-guidelines.md @@ -91,19 +91,19 @@ For product and design contributions, check out the [Design Repo](https://github Try to take care to follow existing conventions. Some of these are defined in an [.editorconfig](https://github.com/webcompat/webcompat.com/blob/master/.editorconfig) file. You can download the plugin for your editor here http://editorconfig.org/#download. ### Python -As we are still very early in the project, we do not yet have that many conventions for naming, routes, APIs. If in doubt, ask us or open an issue. All Python code should pass [pep8](http://pep8.readthedocs.org/en/1.4.6/intro.html). +As we are still very early in the project, we do not yet have that many conventions for naming, routes, APIs. If in doubt, ask us or open an issue. All Python code should pass [pycodestyle](http://pycodestyle.pycqa.org/en/latest/intro.html). -You can check this by installing the pep8 module. +You can check this by installing the pycodestyle module. This is usually installed through the requirements file in the project. If you wish to install it yourself on your computer, you can do. - sudo pip install pep8 + pip install --user pycodestyle Once at the root of the project you can run it with - pep8 --show-source --show-pep8 . + pycodestyle --ignore=E402,W504 webcompat/ tests/ config/secrets.py.example That will show you the list of errors and their explanations. Another tool, we have used for checking consistency of the code is `flake8` + `hacking`. [Hacking](https://github.com/openstack-dev/hacking) is a set of [OpenStack guidelines](http://docs.openstack.org/developer/hacking/) which is used by the community for the stability of their projects. You will see that there's nothing really hard about it. - sudo pip install hacking + pip install --user hacking will install the relevant flake8 and hacking modules. In the same fashion, if you do diff --git a/tests/unit/test_api_urls.py b/tests/unit/test_api_urls.py index 187586e49..b2f50596c 100644 --- a/tests/unit/test_api_urls.py +++ b/tests/unit/test_api_urls.py @@ -22,7 +22,7 @@ 'rv:31.0) Gecko/20100101 Firefox/31.0'), 'HTTP_ACCEPT': 'application/json'} -STATUSES = {u'sitewait': {'color': '', 'state': 'open', 'id': 5, 'order': 5}, u'worksforme': {'color': '', 'state': 'closed', 'id': 11, 'order': 7}, u'non-compat': {'color': '', 'state': 'closed', 'id': 12, 'order': 5}, u'needsdiagnosis': {'color': '', 'state': 'open', 'id': 2, 'order': 2}, u'contactready': {'color': '', 'state': 'open', 'id': 4, 'order': 4}, u'wontfix': {'color': '', 'state': 'closed', 'id': 6, 'order': 6}, u'needscontact': {'color': '', 'state': 'open', 'id': 3, 'order': 3}, u'invalid': {'color': '', 'state': 'closed', 'id': 8, 'order': 4}, u'needstriage': {'color': '', 'state': 'open', 'id': 1, 'order': 1}, u'duplicate': {'color': '', 'state': 'closed', 'id': 10, 'order': 1}, u'fixed': {'color': '', 'state': 'closed', 'id': 9, 'order': 2}, u'incomplete': {'color': '', 'state': 'closed', 'id': 7, 'order': 3}} # nopep8 +STATUSES = {u'sitewait': {'color': '', 'state': 'open', 'id': 5, 'order': 5}, u'worksforme': {'color': '', 'state': 'closed', 'id': 11, 'order': 7}, u'non-compat': {'color': '', 'state': 'closed', 'id': 12, 'order': 5}, u'needsdiagnosis': {'color': '', 'state': 'open', 'id': 2, 'order': 2}, u'contactready': {'color': '', 'state': 'open', 'id': 4, 'order': 4}, u'wontfix': {'color': '', 'state': 'closed', 'id': 6, 'order': 6}, u'needscontact': {'color': '', 'state': 'open', 'id': 3, 'order': 3}, u'invalid': {'color': '', 'state': 'closed', 'id': 8, 'order': 4}, u'needstriage': {'color': '', 'state': 'open', 'id': 1, 'order': 1}, u'duplicate': {'color': '', 'state': 'closed', 'id': 10, 'order': 1}, u'fixed': {'color': '', 'state': 'closed', 'id': 9, 'order': 2}, u'incomplete': {'color': '', 'state': 'closed', 'id': 7, 'order': 3}} # noqa def mock_api_response(response_config={}): @@ -63,7 +63,7 @@ def test_api_issues_out_of_range(self): with patch('webcompat.helpers.proxy_request') as github_data: github_data.return_value = mock_api_response({ 'status_code': 404, - 'content': '[{"message":"Not Found","documentation_url":"https://developer.github.com/v3"}]' # nopep8 + 'content': '[{"message":"Not Found","documentation_url":"https://developer.github.com/v3"}]' # noqa }) rv = self.app.get('/api/issues/1', environ_base=headers) json_body = json.loads(rv.data) @@ -110,7 +110,7 @@ def test_api_comments_link_header_auth(self): 'status_code': 200, 'content': '[]', 'headers': { - 'Link': '; rel="next", ; rel="last"', # nopep8 + 'Link': '; rel="next", ; rel="last"', # noqa }, }), mock_api_response({'status_code': 200, 'content': '[]'}) diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 4cea502ee..3184ab076 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -53,5 +53,6 @@ def test_update_status_config(self): actual = update_status_config(milestones_json) self.assertEqual(actual, expected) + if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test_dashboard.py b/tests/unit/test_dashboard.py index 184400a5f..bae033f6d 100644 --- a/tests/unit/test_dashboard.py +++ b/tests/unit/test_dashboard.py @@ -21,7 +21,7 @@ # Add webcompat module to import path sys.path.append(os.path.realpath(os.pardir)) -import webcompat # nopep8 +import webcompat # noqa # Any request that depends on parsing HTTP Headers (basically anything # on the index route, will need to include the following: environ_base=headers @@ -87,5 +87,6 @@ def test_browser_labels(self): labels = ['status-foo', 'blah', 'browser-', 'browser-firefox'] self.assertListEqual(['firefox'], browser_labels(labels)) + if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test_form.py b/tests/unit/test_form.py index 881a5e152..4ceeb73ed 100644 --- a/tests/unit/test_form.py +++ b/tests/unit/test_form.py @@ -12,7 +12,7 @@ from webcompat import form from webcompat import helpers -FIREFOX_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:48.0) Gecko/20100101 Firefox/48.0' # nopep8 +FIREFOX_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:48.0) Gecko/20100101 Firefox/48.0' # noqa class TestForm(unittest.TestCase): @@ -144,7 +144,7 @@ def test_get_metadata(self): metadata_keys = ['browser', 'ua_header', 'reported_with', 'extra_labels'] actual = form.get_metadata(metadata_keys, form_object) - expected = u'\n\n\n\n' # nopep8 + expected = u'\n\n\n\n' # noqa self.assertEqual(actual, expected) def test_get_metadata_browser_as_extra(self): @@ -158,7 +158,7 @@ def test_get_metadata_browser_as_extra(self): metadata_keys = ['browser', 'ua_header', 'reported_with', 'extra_labels'] actual = form.get_metadata(metadata_keys, form_object) - expected = u'\n\n\n\n' # nopep8 + expected = u'\n\n\n\n' # noqa self.assertEqual(actual, expected) def test_normalize_metadata(self): @@ -181,18 +181,18 @@ def test_build_formdata(self): # even if the data are empty form_object = {'foo': 'bar'} actual = form.build_formdata(form_object) - expected = {'body': u'\n\n\n\n**URL**: None\n\n**Browser / Version**: None\n**Operating System**: None\n**Tested Another Browser**: Unknown\n\n**Problem type**: Unknown\n**Description**: None\n**Steps to Reproduce**:\nNone\n\n\n\n_From [webcompat.com](https://webcompat.com/) with \u2764\ufe0f_', 'title': 'None - unknown'} # nopep8 + expected = {'body': u'\n\n\n\n**URL**: None\n\n**Browser / Version**: None\n**Operating System**: None\n**Tested Another Browser**: Unknown\n\n**Problem type**: Unknown\n**Description**: None\n**Steps to Reproduce**:\nNone\n\n\n\n_From [webcompat.com](https://webcompat.com/) with \u2764\ufe0f_', 'title': 'None - unknown'} # noqa self.assertIs(type(actual), dict) self.assertEqual(actual, expected) # testing for double URL Schemes. form_object = {'url': 'http://https://example.com/'} actual = form.build_formdata(form_object) - expected = {'body': u'\n\n\n\n**URL**: https://example.com/\n\n**Browser / Version**: None\n**Operating System**: None\n**Tested Another Browser**: Unknown\n\n**Problem type**: Unknown\n**Description**: None\n**Steps to Reproduce**:\nNone\n\n\n\n_From [webcompat.com](https://webcompat.com/) with \u2764\ufe0f_', 'title': 'example.com - unknown'} # nopep8 + expected = {'body': u'\n\n\n\n**URL**: https://example.com/\n\n**Browser / Version**: None\n**Operating System**: None\n**Tested Another Browser**: Unknown\n\n**Problem type**: Unknown\n**Description**: None\n**Steps to Reproduce**:\nNone\n\n\n\n_From [webcompat.com](https://webcompat.com/) with \u2764\ufe0f_', 'title': 'example.com - unknown'} # noqa self.assertEqual(actual, expected) # testing with unicode strings. form_object = {'url': u'愛'} actual = form.build_formdata(form_object) - expected = {'body': u'\n\n\n\n**URL**: http://\u611b\n\n**Browser / Version**: None\n**Operating System**: None\n**Tested Another Browser**: Unknown\n\n**Problem type**: Unknown\n**Description**: None\n**Steps to Reproduce**:\nNone\n\n\n\n_From [webcompat.com](https://webcompat.com/) with \u2764\ufe0f_', 'title': u'\u611b - unknown'} # nopep8 + expected = {'body': u'\n\n\n\n**URL**: http://\u611b\n\n**Browser / Version**: None\n**Operating System**: None\n**Tested Another Browser**: Unknown\n\n**Problem type**: Unknown\n**Description**: None\n**Steps to Reproduce**:\nNone\n\n\n\n_From [webcompat.com](https://webcompat.com/) with \u2764\ufe0f_', 'title': u'\u611b - unknown'} # noqa self.assertEqual(actual, expected) def test_get_details(self): @@ -209,26 +209,26 @@ def test_build_details(self): # Test for receiving JSON object as a string actual_json_arg = form.build_details(json.dumps( {'a': 'b', 'c': False})) - expected_json_arg = '
\nBrowser Configuration\n
    \n
  • a: b
  • c: false
  • \n
\n\n
' # nopep8 + expected_json_arg = '
\nBrowser Configuration\n
    \n
  • a: b
  • c: false
  • \n
\n\n
' # noqa self.assertEqual(actual_json_arg, expected_json_arg) # Test for receiving a JSON value which is not an object actual_json_arg = form.build_details('null') - expected_json_arg = '
\nBrowser Configuration\n
    \n
  • None
  • \n
\n\n
' # nopep8 + expected_json_arg = '
\nBrowser Configuration\n
    \n
  • None
  • \n
\n\n
' # noqa self.assertEqual(actual_json_arg, expected_json_arg) # Test for receiving a string actual_string_arg = form.build_details(u'cool') - expected_string_arg = '
\nBrowser Configuration\n
    \n
  • cool
  • \n
\n\n
' # nopep8 + expected_string_arg = '
\nBrowser Configuration\n
    \n
  • cool
  • \n
\n\n
' # noqa self.assertEqual(actual_string_arg, expected_string_arg) def test_build_details_with_console_logs(self): """Expected HTML is returned for a json object with console logs.""" actual_json_arg = form.build_details(json.dumps( {'a': 'b', 'c': False, 'consoleLog': ['console.log(hi)']})) - expected_json_arg = '
\nBrowser Configuration\n
    \n
  • a: b
  • c: false
  • \n
\n

Console Messages:

\n
\n[u\'console.log(hi)\']\n
\n
' # nopep8 + expected_json_arg = '
\nBrowser Configuration\n
    \n
  • a: b
  • c: false
  • \n
\n

Console Messages:

\n
\n[u\'console.log(hi)\']\n
\n
' # noqa self.assertEqual(actual_json_arg, expected_json_arg) actual_empty_log_arg = form.build_details(json.dumps( {'a': 'b', 'c': False, 'consoleLog': ''})) - expected_empty_log_arg = '
\nBrowser Configuration\n
    \n
  • a: b
  • c: false
  • \n
\n\n
' # nopep8 + expected_empty_log_arg = '
\nBrowser Configuration\n
    \n
  • a: b
  • c: false
  • \n
\n\n
' # noqa self.assertEqual(actual_empty_log_arg, expected_empty_log_arg) def test_get_console_section(self): diff --git a/tests/unit/test_helpers.py b/tests/unit/test_helpers.py index 52765dea6..229159c5b 100644 --- a/tests/unit/test_helpers.py +++ b/tests/unit/test_helpers.py @@ -29,24 +29,24 @@ from webcompat.helpers import sanitize_link -ACCESS_TOKEN_LINK = '; rel="next", ; rel="last", ; rel="first", ; rel="prev"' # nopep8 -GITHUB_ISSUES_LINK_HEADER = '; rel="next", ; rel="last", ; rel="first", ; rel="prev"' # nopep8 -REWRITTEN_ISSUES_LINK_HEADER = '; rel="next", ; rel="last", ; rel="first", ; rel="prev"' # nopep8 -GITHUB_SEARCH_LINK_HEADER = '; rel="next", ; rel="last"' # nopep8 -REWRITTEN_SEARCH_LINK_HEADER = '; rel="next", ; rel="last"' # nopep8 -GITHUB_COMMENTS_LINK_HEADER = '; rel="next", ; rel="last"' # nopep8 -REWRITTEN_COMMENTS_LINK_HEADER = '; rel="next", ; rel="last"' # nopep8 -PARSED_LINKED_HEADERS = [{'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=3', 'rel': 'next'}, {'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=4', 'rel': 'last'}, {'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=1', 'rel': 'first'}, {'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=1', 'rel': 'prev'}] # nopep8 -FIREFOX_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:48.0) Gecko/20100101 Firefox/48.0' # nopep8 -FIREFOX_MOBILE_UA_OLD = 'Mozilla/5.0 (Android; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0' # nopep8 -FIREFOX_MOBILE_UA = 'Mozilla/5.0 (Android 6.0.1; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0' # nopep8 -FIREFOX_TABLET_UA = 'Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0' # nopep8 -SAFARI_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11) AppleWebKit/601.1.39 (KHTML, like Gecko) Version/9.0 Safari/601.1.39' # nopep8 -SAFARI_MOBILE_UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_4 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25' # nopep8 -SAFARI_TABLET_UA = 'Mozilla/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3' # nopep8 -CHROME_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2720.0 Safari/537.36' # nopep8 -CHROME_MOBILE_UA = 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19' # nopep8 -CHROME_TABLET_UA = 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Safari/535.19' # nopep8 +ACCESS_TOKEN_LINK = '; rel="next", ; rel="last", ; rel="first", ; rel="prev"' # noqa +GITHUB_ISSUES_LINK_HEADER = '; rel="next", ; rel="last", ; rel="first", ; rel="prev"' # noqa +REWRITTEN_ISSUES_LINK_HEADER = '; rel="next", ; rel="last", ; rel="first", ; rel="prev"' # noqa +GITHUB_SEARCH_LINK_HEADER = '; rel="next", ; rel="last"' # noqa +REWRITTEN_SEARCH_LINK_HEADER = '; rel="next", ; rel="last"' # noqa +GITHUB_COMMENTS_LINK_HEADER = '; rel="next", ; rel="last"' # noqa +REWRITTEN_COMMENTS_LINK_HEADER = '; rel="next", ; rel="last"' # noqa +PARSED_LINKED_HEADERS = [{'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=3', 'rel': 'next'}, {'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=4', 'rel': 'last'}, {'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=1', 'rel': 'first'}, {'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=1', 'rel': 'prev'}] # noqa +FIREFOX_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:48.0) Gecko/20100101 Firefox/48.0' # noqa +FIREFOX_MOBILE_UA_OLD = 'Mozilla/5.0 (Android; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0' # noqa +FIREFOX_MOBILE_UA = 'Mozilla/5.0 (Android 6.0.1; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0' # noqa +FIREFOX_TABLET_UA = 'Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0' # noqa +SAFARI_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11) AppleWebKit/601.1.39 (KHTML, like Gecko) Version/9.0 Safari/601.1.39' # noqa +SAFARI_MOBILE_UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_4 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25' # noqa +SAFARI_TABLET_UA = 'Mozilla/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3' # noqa +CHROME_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2720.0 Safari/537.36' # noqa +CHROME_MOBILE_UA = 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19' # noqa +CHROME_TABLET_UA = 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Safari/535.19' # noqa class TestHelpers(unittest.TestCase): @@ -316,5 +316,6 @@ def test_json_object(self): # A JSON value, which is not an object self.assertFalse(is_json_object(json.loads('null'))) + if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test_http_caching.py b/tests/unit/test_http_caching.py index 2bbd726ca..d7a03fa39 100644 --- a/tests/unit/test_http_caching.py +++ b/tests/unit/test_http_caching.py @@ -12,7 +12,7 @@ # Add webcompat module to import path sys.path.append(os.path.realpath(os.pardir)) -import webcompat # nopep8 +import webcompat # noqa # Any request that depends on parsing HTTP Headers (basically anything # on the index route, will need to include the following: html_headers=headers diff --git a/tests/unit/test_rendering.py b/tests/unit/test_rendering.py index bf14b717f..4503cdf8f 100644 --- a/tests/unit/test_rendering.py +++ b/tests/unit/test_rendering.py @@ -12,7 +12,7 @@ # Add webcompat module to import path sys.path.append(os.path.realpath(os.pardir)) -import webcompat # nopep8 +import webcompat # noqa # Any request that depends on parsing HTTP Headers (basically anything # on the index route, will need to include the following: environ_base=headers @@ -127,5 +127,6 @@ def test_wellknown_security(self): self.assertEqual(rv.status_code, 200) self.assertTrue(expected in rv.data) + if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test_tools_changelog.py b/tests/unit/test_tools_changelog.py index af17dea36..a52e8177f 100644 --- a/tests/unit/test_tools_changelog.py +++ b/tests/unit/test_tools_changelog.py @@ -60,5 +60,6 @@ def test_normalize_title(self): expected = u'NPM update - Greenkeeper style.' self.assertEqual(actual, expected) + if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test_topsites.py b/tests/unit/test_topsites.py index b65ae399e..ee1c219c8 100644 --- a/tests/unit/test_topsites.py +++ b/tests/unit/test_topsites.py @@ -15,10 +15,10 @@ from tools import topsites -TEST_XML = '''9ffc5e13-175e-4c7e-b33b-0efe3501d1f3ChinaCN671496baidu.com13580007741011.54Success''' # nopep8 -TEST_QUERY_STRING = 'Action=TopSites&Count=100&CountryCode=CN&ResponseGroup=Country&Start=1' # nopep8 -TEST_QUERY_URI = 'https://ats.amazonaws.com/api?Action=TopSites&Count=100&CountryCode=CN&ResponseGroup=Country&Start=1' # nopep8 -TEST_QUERY_AUTH = 'AWS4-HMAC-SHA256 Credential=1234567890ABCDEFGHIJ/20060101/us-west-1/AlexaTopSites/aws4_request, SignedHeaders=host;x-amz-date, Signature=55b760bcae9a2ae93b0d08a85c3e613ec43c7f39f69ef2345896cf7660234f49' # nopep8 +TEST_XML = '''9ffc5e13-175e-4c7e-b33b-0efe3501d1f3ChinaCN671496baidu.com13580007741011.54Success''' # noqa +TEST_QUERY_STRING = 'Action=TopSites&Count=100&CountryCode=CN&ResponseGroup=Country&Start=1' # noqa +TEST_QUERY_URI = 'https://ats.amazonaws.com/api?Action=TopSites&Count=100&CountryCode=CN&ResponseGroup=Country&Start=1' # noqa +TEST_QUERY_AUTH = 'AWS4-HMAC-SHA256 Credential=1234567890ABCDEFGHIJ/20060101/us-west-1/AlexaTopSites/aws4_request, SignedHeaders=host;x-amz-date, Signature=55b760bcae9a2ae93b0d08a85c3e613ec43c7f39f69ef2345896cf7660234f49' # noqa TEST_QUERY_TIMESTAMP = '20060101T000000Z' diff --git a/tests/unit/test_uploads.py b/tests/unit/test_uploads.py index f4a2ff0c9..31b1efa7a 100644 --- a/tests/unit/test_uploads.py +++ b/tests/unit/test_uploads.py @@ -19,7 +19,7 @@ # Add webcompat module to import path sys.path.append(os.path.realpath(os.pardir)) -from webcompat import app # nopep8 +from webcompat import app # noqa def check_rv_format(self, resp): @@ -65,7 +65,7 @@ class TestingFileStorage(FileStorage): :param headers: Multipart headers as a `werkzeug.Headers`. The default is `None`. - taken from https://github.com/srusskih/flask-uploads/blob/master/flaskext/uploads.py#L476 # nopep8 + taken from https://github.com/srusskih/flask-uploads/blob/master/flaskext/uploads.py#L476 # noqa """ def __init__(self, stream=None, filename=None, name=None, @@ -94,7 +94,7 @@ def save(self, dst, buffer_size=16384): class TestUploads(unittest.TestCase): '''Test-only Uploads Class. - Modified from http://prschmid.blogspot.com/2013/05/unit-testing-flask-file-uploads-without.html # nopep8 + Modified from http://prschmid.blogspot.com/2013/05/unit-testing-flask-file-uploads-without.html # noqa ''' def setUp(self): @@ -152,7 +152,7 @@ def files(self): def test_base64_screenshot_uploads(self): '''Test that Base64 screenshots return the expected status codes.''' - BASE64_PNG = u'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQYV2P4DwABAQEAWk1v8QAAAABJRU5ErkJggg==' # nopep8 + BASE64_PNG = u'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQYV2P4DwABAQEAWk1v8QAAAABJRU5ErkJggg==' # noqa BASE64_PNG_GARBAGE = u'data:image/png;base64,garbage!' BASE64_PNG_GARBAGE2 = u'data:image/png;data:image/png;' PILE_OF_POO = u'💩' diff --git a/tests/unit/test_urls.py b/tests/unit/test_urls.py index 46247b04b..de497c65d 100644 --- a/tests/unit/test_urls.py +++ b/tests/unit/test_urls.py @@ -15,7 +15,7 @@ # Add webcompat module to import path sys.path.append(os.path.realpath(os.pardir)) -import webcompat # nopep8 +import webcompat # noqa # Any request that depends on parsing HTTP Headers (basically anything # on the index route, will need to include the following: environ_base=headers @@ -258,7 +258,7 @@ def test_webhooks_route(self): self.assertTrue(content_test) def test_extracted_ga_params_end_up_as_inline_js(self): - """Extract GA params (utm_foo) information of a form request with a POST. + """Extract GA params (utm_foo) information of POST form request. We also test that the nonce in the CSP matches the nonce in the inline style. diff --git a/tests/unit/test_webhook.py b/tests/unit/test_webhook.py index a76c8f1f8..2ff0202d3 100644 --- a/tests/unit/test_webhook.py +++ b/tests/unit/test_webhook.py @@ -288,5 +288,6 @@ def test_new_opened_issue(self): self.assertEqual(response.status_code, 401) self.assertTrue('Bad credentials' in response.content) + if __name__ == '__main__': unittest.main() diff --git a/tools/topsites.py b/tools/topsites.py index 40000b0d4..ef3f4cb1d 100644 --- a/tools/topsites.py +++ b/tools/topsites.py @@ -95,14 +95,14 @@ def query_topsites(country_code, count=1000): dom = parseString(response.content) if response.status_code == 200: - # See https://docs.aws.amazon.com/AlexaTopSites/latest/QUERY_QueryRequests.html # nopep8 + # See https://docs.aws.amazon.com/AlexaTopSites/latest/QUERY_QueryRequests.html # noqa sites = dom.getElementsByTagName('aws:Site') for site in sites: parse_site(site, country_code) session.commit() else: # Get error code and message from response - # See https://docs.aws.amazon.com/AlexaTopSites/latest/Authentication.html # nopep8 + # See https://docs.aws.amazon.com/AlexaTopSites/latest/Authentication.html # noqa message = node_text(dom, 'aws:ErrorCode') error_template = """Send request to {uri} get error message: {message}""" @@ -195,7 +195,7 @@ def build_request(country_code, start_ranking): base=ATS_AWS_BASE_URL, query=canonical_query) - authorization_template = '{algorithm} Credential={access_key}/{scope}, SignedHeaders={signed_headers}, Signature={signature}' # nopep8 + authorization_template = '{algorithm} Credential={access_key}/{scope}, SignedHeaders={signed_headers}, Signature={signature}' # noqa authorization = authorization_template.format( algorithm=ATS_ALGORITHM, access_key=ats_access_key, diff --git a/webcompat/__init__.py b/webcompat/__init__.py index 654050896..9b7a75983 100644 --- a/webcompat/__init__.py +++ b/webcompat/__init__.py @@ -32,8 +32,8 @@ limiter = Limiter(app, key_func=get_remote_address) # import views after we initialize our github object -import webcompat.views # nopep8 -import webhooks # nopep8 +import webcompat.views # noqa +import webhooks # noqa # register blueprints from api.endpoints import api diff --git a/webcompat/db/__init__.py b/webcompat/db/__init__.py index 05ce307fa..478eb38dd 100644 --- a/webcompat/db/__init__.py +++ b/webcompat/db/__init__.py @@ -75,4 +75,5 @@ def __init__(self, url, priority, country_code, ranking): self.country_code = country_code self.ranking = ranking + SiteBase.metadata.create_all(bind=site_engine) diff --git a/webcompat/helpers.py b/webcompat/helpers.py index 27e9e36da..dda7da7e8 100644 --- a/webcompat/helpers.py +++ b/webcompat/helpers.py @@ -414,7 +414,7 @@ def extract_url(issue_body): URL in webcompat.com bugs follow this pattern: **URL**: https://example.com/foobar """ - url_pattern = re.compile('\*\*URL\*\*\: (.+)\n') + url_pattern = re.compile(r'\*\*URL\*\*\: (.+)') url_match = re.search(url_pattern, issue_body) if url_match: url = url_match.group(1).strip() @@ -478,7 +478,7 @@ def add_sec_headers(response): added to all responses. """ if not app.config['LOCALHOST']: - response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains;' # nopep8 + response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains;' # noqa response.headers['X-Content-Type-Options'] = 'nosniff' response.headers['X-XSS-Protection'] = '1; mode=block' response.headers['X-Frame-Options'] = 'DENY' @@ -489,9 +489,9 @@ def get_img_src_policy(): We allow webcompat.com-hosted images on localhost servers for convenience. """ - policy = "img-src 'self' https://www.google-analytics.com https://*.githubusercontent.com data:; " # nopep8 + policy = "img-src 'self' https://www.google-analytics.com https://*.githubusercontent.com data:; " # noqa if app.config['LOCALHOST']: - policy = "img-src 'self' https://webcompat.com https://www.google-analytics.com https://*.githubusercontent.com data:; " # nopep8 + policy = "img-src 'self' https://webcompat.com https://www.google-analytics.com https://*.githubusercontent.com data:; " # noqa return policy @@ -501,19 +501,20 @@ def add_csp(response): This should be used in @app.after_request to ensure the header is added to all responses. """ - response.headers['Content-Security-Policy'] = ( - "default-src 'self'; " + - "object-src 'none'; " + - "connect-src 'self' https://api.github.com; " + - "font-src 'self' https://fonts.gstatic.com; " + - get_img_src_policy() + - "manifest-src 'self'; " + - "script-src 'self' https://www.google-analytics.com https://api.github.com 'nonce-{nonce}'; ".format(nonce=request.nonce) + # nopep8 - "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " + - "base-uri 'self'; " + - "frame-ancestors 'self'; " + + csp_params = [ + "default-src 'self'; ", + "object-src 'none'; ", + "connect-src 'self' https://api.github.com; ", + "font-src 'self' https://fonts.gstatic.com; ", + get_img_src_policy(), + "manifest-src 'self'; ", + "script-src 'self' https://www.google-analytics.com https://api.github.com 'nonce-{nonce}'; ".format(nonce=request.nonce), # noqa + "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; ", + "base-uri 'self'; ", + "frame-ancestors 'self'; ", "report-uri /csp-report" - ) + ] + response.headers['Content-Security-Policy'] = (''.join(csp_params)) def cache_policy(private=True, uri_max_age=86400, must_revalidate=False):