From 7f617657068e2cf877edfb06a70d435dd5dfbdc0 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Fri, 25 Feb 2022 01:09:58 +0100 Subject: Remove and merge superfluous modules The constants module more or less did what belongs to the settings. --- pydis_site/apps/home/views/home.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'pydis_site/apps/home/views/home.py') diff --git a/pydis_site/apps/home/views/home.py b/pydis_site/apps/home/views/home.py index e28a3a00..dafe32c0 100644 --- a/pydis_site/apps/home/views/home.py +++ b/pydis_site/apps/home/views/home.py @@ -2,6 +2,7 @@ import logging from typing import Dict, List import requests +from django.conf import settings from django.core.handlers.wsgi import WSGIRequest from django.http import HttpResponse from django.shortcuts import render @@ -10,7 +11,6 @@ from django.views import View from pydis_site import settings from pydis_site.apps.home.models import RepositoryMetadata -from pydis_site.constants import GITHUB_TOKEN, TIMEOUT_PERIOD log = logging.getLogger(__name__) @@ -43,8 +43,8 @@ class HomeView(View): # specifically, GitHub will reject any requests from us due to the # invalid header. We can make a limited number of anonymous requests # though, which is useful for testing. - if GITHUB_TOKEN: - self.headers = {"Authorization": f"token {GITHUB_TOKEN}"} + if settings.GITHUB_TOKEN: + self.headers = {"Authorization": f"token {settings.GITHUB_TOKEN}"} else: self.headers = {} @@ -60,7 +60,7 @@ class HomeView(View): api_data: List[dict] = requests.get( self.github_api, headers=self.headers, - timeout=TIMEOUT_PERIOD + timeout=settings.TIMEOUT_PERIOD ).json() except requests.exceptions.Timeout: log.error("Request to fetch GitHub repository metadata for timed out!") -- cgit v1.2.3 From b78d9496867a13ed096b4b1f561a47a25001a901 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Fri, 25 Feb 2022 01:10:45 +0100 Subject: Add a readme to the Django project directory --- pydis_site/README.md | 52 ++++++++++++++++++++++++++++++++++++++ pydis_site/apps/home/views/home.py | 1 - 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 pydis_site/README.md (limited to 'pydis_site/apps/home/views/home.py') diff --git a/pydis_site/README.md b/pydis_site/README.md new file mode 100644 index 00000000..456f7e9e --- /dev/null +++ b/pydis_site/README.md @@ -0,0 +1,52 @@ +# `pydis_site` project directory + +This directory hosts the root of our **Django project**[^1], and is responsible +for all logic powering our website. Let's go over the directories in detail: + +- [`apps`](./apps) contains our **Django apps**. If you want to add your own API + endpoint or new functionality to our homepage, that's the place to go. + + +- [`static`](./static) contains our **static files**, such as CSS, JavaScript, + images, and anything else that isn't either content or Python code. Static + files relevant for a specific application are put into subdirectories named + after the application. + +- [`templates`](./templates) contains our **Django templates**. Like with static + files, templates specific to a single application are stored in a subdirectory + named after that application. We also have two special templates here: + + - `404.html`, which is our error page shown when a site was not found. + + - `500.html`, which is our error page shown in the astronomically rare case + that we encounter an internal server error. + + +We also have a few files in here that are relevant or useful in large parts of +the website: + +- [`context_processors.py`](./context_processors.py), which contains custom + *context processors* that add variables to the Django template context. To + read more, see the [`RequestContext` documentation from + Django](https://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.RequestContext) + +- [`settings.py`](./settings.py), our Django settings file. This mostly just + parses configuration out of your environment variables, so you shouldn't need + to edit it directly unless you want to add new settings. + +- [`urls.py`](./urls.py), which configures our Django URL routing by installing + our apps into the routing tree. + +- [`wsgi.py`](./wsgi.py), which serves as an adapter for + [`gunicorn`](https://github.com/benoitc/gunicorn), + [`uwsgi`](https://github.com/unbit/uwsgi) or other application servers to run + our application in production. Unless you want to test an interaction between + our application and those servers, you probably won't need to touch this. + +Note that for both `static` and `templates`, we are not using the default Django +directory structure which puts these directories in a directory per app (in our +case, this would for example be ``pydis_site/apps/content/static/``). + + +[^1]: See [Django Glossary: project](https://docs.djangoproject.com/en/dev/glossary/#term-project) diff --git a/pydis_site/apps/home/views/home.py b/pydis_site/apps/home/views/home.py index dafe32c0..69e706c5 100644 --- a/pydis_site/apps/home/views/home.py +++ b/pydis_site/apps/home/views/home.py @@ -2,7 +2,6 @@ import logging from typing import Dict, List import requests -from django.conf import settings from django.core.handlers.wsgi import WSGIRequest from django.http import HttpResponse from django.shortcuts import render -- cgit v1.2.3 From c24ccccde65cd8d5601ade63e47f2167ba64b5ee Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Sat, 9 Jul 2022 22:27:11 +0400 Subject: Switch Out requests For httpx The requests library has been replaced by httpx. It's a drop-in replacement, but provides a better interface for certain things, such as client sessions, and sync/async support. Signed-off-by: Hassan Abouelela --- poetry.lock | 120 +++++++++++++++++++-- .../apps/home/tests/test_repodata_helpers.py | 12 +-- pydis_site/apps/home/views/home.py | 6 +- pyproject.toml | 2 +- 4 files changed, 120 insertions(+), 20 deletions(-) (limited to 'pydis_site/apps/home/views/home.py') diff --git a/poetry.lock b/poetry.lock index 3b26c275..fcdc9d84 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,20 @@ +[[package]] +name = "anyio" +version = "3.6.1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16)"] + [[package]] name = "asgiref" version = "3.5.0" @@ -373,6 +390,52 @@ gevent = ["gevent (>=0.13)"] setproctitle = ["setproctitle"] tornado = ["tornado (>=0.2)"] +[[package]] +name = "h11" +version = "0.12.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "httpcore" +version = "0.15.0" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +anyio = ">=3.0.0,<4.0.0" +certifi = "*" +h11 = ">=0.11,<0.13" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "httpx" +version = "0.23.0" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.16.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotlicffi", "brotli"] +cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10,<13)", "pygments (>=2.0.0,<3.0.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + [[package]] name = "identify" version = "2.4.6" @@ -617,21 +680,35 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "requests" -version = "2.27.1" +version = "2.28.1" description = "Python HTTP for Humans." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" [package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] [[package]] name = "sentry-sdk" @@ -677,6 +754,14 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "sniffio" +version = "1.2.0" +description = "Sniff out which async library your code is running under" +category = "main" +optional = false +python-versions = ">=3.5" + [[package]] name = "snowballstemmer" version = "2.2.0" @@ -782,9 +867,13 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "3.9.*" -content-hash = "fc9b20c33c65a289122d710844285ac20d7598e65c7f8237f8903509f5b2dea4" +content-hash = "c94949e29f868689d9c99379dbf4f9479e2ddfe5e6d49e15b57d016210a50379" [metadata.files] +anyio = [ + {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"}, + {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"}, +] asgiref = [ {file = "asgiref-3.5.0-py3-none-any.whl", hash = "sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9"}, {file = "asgiref-3.5.0.tar.gz", hash = "sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0"}, @@ -959,6 +1048,12 @@ gunicorn = [ {file = "gunicorn-20.0.4-py2.py3-none-any.whl", hash = "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"}, {file = "gunicorn-20.0.4.tar.gz", hash = "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"}, ] +h11 = [ + {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, + {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, +] +httpcore = [] +httpx = [] identify = [ {file = "identify-2.4.6-py2.py3-none-any.whl", hash = "sha256:cf06b1639e0dca0c184b1504d8b73448c99a68e004a80524c7923b95f7b6837c"}, {file = "identify-2.4.6.tar.gz", hash = "sha256:233679e3f61a02015d4293dbccf16aa0e4996f868bd114688b8c124f18826706"}, @@ -1149,9 +1244,10 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] -requests = [ - {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, - {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +requests = [] +rfc3986 = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, ] sentry-sdk = [ {file = "sentry-sdk-0.20.3.tar.gz", hash = "sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237"}, @@ -1165,6 +1261,10 @@ smmap = [ {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, ] +sniffio = [ + {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, + {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, +] snowballstemmer = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, diff --git a/pydis_site/apps/home/tests/test_repodata_helpers.py b/pydis_site/apps/home/tests/test_repodata_helpers.py index d43bd28e..4007eded 100644 --- a/pydis_site/apps/home/tests/test_repodata_helpers.py +++ b/pydis_site/apps/home/tests/test_repodata_helpers.py @@ -36,7 +36,7 @@ class TestRepositoryMetadataHelpers(TestCase): """Executed before each test method.""" self.home_view = HomeView() - @mock.patch('requests.get', side_effect=mocked_requests_get) + @mock.patch('httpx.get', side_effect=mocked_requests_get) def test_returns_metadata(self, _: mock.MagicMock): """Test if the _get_repo_data helper actually returns what it should.""" metadata = self.home_view._get_repo_data() @@ -59,7 +59,7 @@ class TestRepositoryMetadataHelpers(TestCase): self.assertIsInstance(metadata[0], RepositoryMetadata) self.assertIsInstance(str(metadata[0]), str) - @mock.patch('requests.get', side_effect=mocked_requests_get) + @mock.patch('httpx.get', side_effect=mocked_requests_get) def test_refresh_stale_metadata(self, _: mock.MagicMock): """Test if the _get_repo_data helper will refresh when the data is stale.""" repo_data = RepositoryMetadata( @@ -75,7 +75,7 @@ class TestRepositoryMetadataHelpers(TestCase): self.assertIsInstance(metadata[0], RepositoryMetadata) - @mock.patch('requests.get', side_effect=mocked_requests_get) + @mock.patch('httpx.get', side_effect=mocked_requests_get) def test_returns_api_data(self, _: mock.MagicMock): """Tests if the _get_api_data helper returns what it should.""" api_data = self.home_view._get_api_data() @@ -86,7 +86,7 @@ class TestRepositoryMetadataHelpers(TestCase): self.assertIn(repo, api_data.keys()) self.assertIn("stargazers_count", api_data[repo]) - @mock.patch('requests.get', side_effect=mocked_requests_get) + @mock.patch('httpx.get', side_effect=mocked_requests_get) def test_mocked_requests_get(self, mock_get: mock.MagicMock): """Tests if our mocked_requests_get is returning what it should.""" success_data = mock_get(HomeView.github_api) @@ -98,7 +98,7 @@ class TestRepositoryMetadataHelpers(TestCase): self.assertIsNotNone(success_data.json_data) self.assertIsNone(fail_data.json_data) - @mock.patch('requests.get') + @mock.patch('httpx.get') def test_falls_back_to_database_on_error(self, mock_get: mock.MagicMock): """Tests that fallback to the database is performed when we get garbage back.""" repo_data = RepositoryMetadata( @@ -117,7 +117,7 @@ class TestRepositoryMetadataHelpers(TestCase): [item] = metadata self.assertEqual(item, repo_data) - @mock.patch('requests.get') + @mock.patch('httpx.get') def test_falls_back_to_database_on_error_without_entries(self, mock_get: mock.MagicMock): """Tests that fallback to the database is performed when we get garbage back.""" mock_get.return_value.json.return_value = ['garbage'] diff --git a/pydis_site/apps/home/views/home.py b/pydis_site/apps/home/views/home.py index 69e706c5..9bb1f8fd 100644 --- a/pydis_site/apps/home/views/home.py +++ b/pydis_site/apps/home/views/home.py @@ -1,7 +1,7 @@ import logging from typing import Dict, List -import requests +import httpx from django.core.handlers.wsgi import WSGIRequest from django.http import HttpResponse from django.shortcuts import render @@ -56,12 +56,12 @@ class HomeView(View): repo_dict = {} try: # Fetch the data from the GitHub API - api_data: List[dict] = requests.get( + api_data: List[dict] = httpx.get( self.github_api, headers=self.headers, timeout=settings.TIMEOUT_PERIOD ).json() - except requests.exceptions.Timeout: + except httpx.TimeoutException: log.error("Request to fetch GitHub repository metadata for timed out!") return repo_dict diff --git a/pyproject.toml b/pyproject.toml index b350836e..01e7eda2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ djangorestframework = "~=3.12.0" psycopg2-binary = "~=2.8.0" django-simple-bulma = "~=2.4" whitenoise = "~=5.0" -requests = "~=2.21" +httpx = "~=0.23.0" pyyaml = "~=5.1" gunicorn = "~=20.0.4" sentry-sdk = "~=0.19" -- cgit v1.2.3