forked from readthedocs/readthedocs.org
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathutils.py
125 lines (103 loc) · 4.07 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import os
import structlog
from django.http import HttpResponse
from django.shortcuts import render
from readthedocs.projects.models import Project
from readthedocs.proxito.exceptions import ProxitoHttp404
from .decorators import map_project_slug, map_subproject_slug
log = structlog.get_logger(__name__) # noqa
def fast_404(request, *args, **kwargs):
"""
A fast error page handler.
This stops us from running RTD logic in our error handling. We already do
this in RTD prod when we fallback to it.
"""
return HttpResponse("Not Found.", status=404)
def proxito_404_page_handler(request, exception=None, template_name='404.html'):
"""
Decide what 404 page return depending if it's an internal NGINX redirect.
We want to return fast when the 404 is used as an internal NGINX redirect to
reach our ``ServeError404`` view. However, if the 404 exception was risen
inside ``ServeError404`` view, we want to render the default Read the Docs
Maze page.
"""
if request.resolver_match and request.resolver_match.url_name != 'proxito_404_handler':
log.debug("Displaying a 'fast 404'")
return fast_404(request, exception, template_name)
project_slug = getattr(exception, "project_slug", None)
log.debug(exception)
project = getattr(exception, "project", None)
log.debug(
"404 page detected a project slug in request.",
project_slug=project_slug,
)
r = render(
request,
template_name,
context={"project": project, "project_slug": project_slug},
)
r.status_code = 404
return r
@map_project_slug
@map_subproject_slug
def _get_project_data_from_request(
request,
project,
subproject,
lang_slug=None,
version_slug=None,
filename='',
):
"""
Get the proper project based on the request and URL.
This is used in a few places and so we break out into a utility function.
"""
# Take the most relevant project so far
current_project = subproject or project
# Handle single-version projects that have URLs like a real project
if current_project.single_version:
if lang_slug and version_slug:
filename = os.path.join(lang_slug, version_slug, filename)
log.warning(
"URL looks like versioned on a single version project. "
"Changing filename to match.",
filename=filename,
)
lang_slug = version_slug = None
# Check to see if we need to serve a translation
if not lang_slug or lang_slug == current_project.language:
final_project = current_project
else:
try:
final_project = current_project.translations.get(language=lang_slug)
except Project.DoesNotExist:
log.debug(
"Raising 404 for language slug",
lang_slug=lang_slug,
project_slug=current_project.slug,
)
raise ProxitoHttp404(
"Did not find translation",
project=current_project,
project_slug=current_project.slug,
subproject_slug=lang_slug,
)
# Handle single version by grabbing the default version
# We might have version_slug when we're serving a PR
if any([
not version_slug and final_project.single_version,
not version_slug and project.urlconf and '$version' not in project.urlconf
]):
version_slug = final_project.get_default_version()
# Automatically add the default language if it isn't defined in urlconf
if not lang_slug and project.urlconf and '$language' not in project.urlconf:
lang_slug = final_project.language
# ``final_project`` is now the actual project we want to serve docs on,
# accounting for:
# * Project
# * Subproject
# * Translations
# Set the project and version slug on the request so we can log it in middleware
request.path_project_slug = final_project.slug
request.path_version_slug = version_slug
return final_project, lang_slug, version_slug, filename