summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorAlex <8340441+alexgmin@users.noreply.github.com>2025-03-05 21:41:34 +0100
committerBaptiste Mispelon <bmispelon@gmail.com>2025-03-05 21:49:30 +0100
commitaf929f0734c319447a061e82b0353fc48b792ec4 (patch)
treeff081e0c07a8b3beb16675b7890c3bf6294f5704 /docs
parent64003b45b00577e1d66b3476bd55db3069028826 (diff)
Revert "Added code references to search results. (#1947)"
This reverts commit fa56d9b841e0bd23c5c8871826ba6abe2205fdb4.
Diffstat (limited to 'docs')
-rw-r--r--docs/builder.py76
-rw-r--r--docs/management/commands/update_docs.py47
-rw-r--r--docs/models.py25
-rw-r--r--docs/templates/docs/search_results.html15
-rw-r--r--docs/templatetags/docs.py67
-rw-r--r--docs/tests/test_builder.py63
-rw-r--r--docs/tests/test_templates.py41
-rw-r--r--docs/tests/test_views.py85
-rw-r--r--docs/utils.py35
9 files changed, 34 insertions, 420 deletions
diff --git a/docs/builder.py b/docs/builder.py
deleted file mode 100644
index 4a10146b..00000000
--- a/docs/builder.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from dataclasses import dataclass
-from functools import cached_property
-
-from sphinxcontrib.serializinghtml import JSONHTMLBuilder
-
-IGNORED_DOMAIN_TYPES = {"module"}
-
-
-@dataclass
-class DomainObject:
- """
- A wrapper around sphinx's domain object descriptions
- https://www.sphinx-doc.org/en/master/extdev/domainapi.html#sphinx.domains.Domain.get_objects
- """
-
- name: str
- dispname: str
- type: str
- docname: str
- anchor: str
- priority: int
-
- @property
- def short_name(self) -> str:
- """
- Returns a shortened version of the object's name.
-
- - If the second-to-last part of the name starts with an uppercase letter
- (indicating a class method or class attribute), returns the last two parts.
- - Otherwise, returns only the last part (likely a function, or class).
-
- Examples:
- - "django.db.models.query.QuerySet.select_related" → "QuerySet.select_related"
- - "django.db.models.query.QuerySet" → "QuerySet"
- - "django.template.context_processors.static" → "static"
- """
- parts = self.name.split(".")
-
- if len(parts) < 2:
- return self.name
-
- last, second_last = parts[-1], parts[-2]
- return f"{second_last}.{last}" if second_last[0].isupper() else last
-
-
-class PythonObjectsJSONHTMLBuilder(JSONHTMLBuilder):
- @cached_property
- def domain_objects(self):
- domain = self.env.get_domain("py")
- return [
- DomainObject(*item)
- for item in domain.get_objects()
- if item[2] not in IGNORED_DOMAIN_TYPES # item[2] is 'type'.
- ]
-
- def get_doc_context(self, docname, body, metatags):
- out_dict = super().get_doc_context(docname, body, metatags)
- python_objects = self.get_python_objects(docname)
- out_dict["python_objects"] = python_objects
- out_dict["python_objects_search"] = " ".join(
- # Keeps the code suffix to improve the search results for terms such as
- # "select" for QuerySet.select_related.
- [key.split(".")[-1] for key in python_objects.keys()]
- )
- return out_dict
-
- def get_python_objects(self, docname):
- return {
- obj.short_name: obj.name
- for obj in self.domain_objects
- if obj.docname == docname
- }
-
-
-def setup(app):
- app.add_builder(PythonObjectsJSONHTMLBuilder, override=True)
diff --git a/docs/management/commands/update_docs.py b/docs/management/commands/update_docs.py
index f48ec6e0..73c26181 100644
--- a/docs/management/commands/update_docs.py
+++ b/docs/management/commands/update_docs.py
@@ -4,7 +4,6 @@ app.
"""
import json
-import multiprocessing
import os
import shutil
import subprocess
@@ -18,9 +17,6 @@ from django.conf import settings
from django.core.management import BaseCommand, call_command
from django.db.models import Q
from django.utils.translation import to_locale
-from sphinx.application import Sphinx
-from sphinx.config import Config
-from sphinx.errors import SphinxError
from ...models import DocumentRelease
@@ -209,30 +205,29 @@ class Command(BaseCommand):
if self.verbosity >= 2:
self.stdout.write(f" building {builder} ({source_dir} -> {build_dir})")
- # Retrieve the extensions from the conf.py so we can append to them.
- conf_extensions = Config.read(source_dir.resolve()).extensions
- extensions = [*conf_extensions, "docs.builder"]
try:
- Sphinx(
- srcdir=source_dir,
- confdir=source_dir,
- outdir=build_dir,
- doctreedir=build_dir.joinpath(".doctrees"),
- buildername=builder,
- # Translated docs builds generate a lot of warnings, so send
- # stderr to stdout to be logged (rather than generating an email)
- warning=sys.stdout,
- parallel=multiprocessing.cpu_count(),
- verbosity=self.verbosity,
- confoverrides={
- "language": to_locale(release.lang),
- "extensions": extensions,
- },
- ).build()
- except SphinxError as e:
+ # Translated docs builds generate a lot of warnings, so send
+ # stderr to stdout to be logged (rather than generating an
+ # email)
+ subprocess.check_call(
+ [
+ "sphinx-build",
+ "-b",
+ builder,
+ "-D",
+ "language=%s" % to_locale(release.lang),
+ "-j",
+ "auto",
+ "-Q" if self.verbosity == 0 else "-q",
+ str(source_dir), # Source file directory
+ str(build_dir), # Destination directory
+ ],
+ stderr=sys.stdout,
+ )
+ except subprocess.CalledProcessError:
self.stderr.write(
- "sphinx-build returned an error (release %s, builder %s): %s"
- % (release, builder, str(e))
+ "sphinx-build returned an error (release %s, builder %s)"
+ % (release, builder)
)
return
diff --git a/docs/models.py b/docs/models.py
index 8dfdb375..c8b383d7 100644
--- a/docs/models.py
+++ b/docs/models.py
@@ -2,7 +2,7 @@ import datetime
import html
import json
import operator
-from functools import partial, reduce
+from functools import reduce
from pathlib import Path
from django.conf import settings
@@ -262,12 +262,6 @@ class DocumentQuerySet(models.QuerySet):
query_text, config=models.F("config"), search_type="websearch"
)
search_rank = SearchRank(models.F("search"), search_query)
- search = partial(
- SearchHeadline,
- start_sel=START_SEL,
- stop_sel=STOP_SEL,
- config=models.F("config"),
- )
base_qs = (
self.prefetch_related(
Prefetch(
@@ -280,18 +274,21 @@ class DocumentQuerySet(models.QuerySet):
)
.filter(release_id=release.id)
.annotate(
- headline=search("title", search_query),
- highlight=search(
- KeyTextTransform("body", "metadata"),
+ headline=SearchHeadline(
+ "title",
search_query,
+ start_sel=START_SEL,
+ stop_sel=STOP_SEL,
+ config=models.F("config"),
),
- searched_python_objects=search(
- KeyTextTransform("python_objects_search", "metadata"),
+ highlight=SearchHeadline(
+ KeyTextTransform("body", "metadata"),
search_query,
- highlight_all=True,
+ start_sel=START_SEL,
+ stop_sel=STOP_SEL,
+ config=models.F("config"),
),
breadcrumbs=models.F("metadata__breadcrumbs"),
- python_objects=models.F("metadata__python_objects"),
)
.only(
"path",
diff --git a/docs/templates/docs/search_results.html b/docs/templates/docs/search_results.html
index f731ead7..11daede6 100644
--- a/docs/templates/docs/search_results.html
+++ b/docs/templates/docs/search_results.html
@@ -48,21 +48,6 @@
{% if result.highlight %}
…&nbsp;{{ result.highlight|cut:"¶"|safe }}&nbsp;…
{% endif %}
- {% code_links result.searched_python_objects result.python_objects as result_code_links %}
- {% if result_code_links %}
- <ul class="code-links">
- {% for name, value in result_code_links.items %}
- <li>
- <a href="{% url 'document-detail' lang=result.release.lang version=result.release.version url=result.path host 'docs' %}#{{ value.full_path }}">
- <div>
- <code>{{ name }}</code>
- {% if value.module_path %}<div class="meta">{{ value.module_path }}</div>{% endif %}
- </div>
- </a>
- </li>
- {% endfor %}
- </ul>
- {% endif %}
</dd>
{% endfor %}
</dl>
diff --git a/docs/templatetags/docs.py b/docs/templatetags/docs.py
index 3410ec65..d5ff379a 100644
--- a/docs/templatetags/docs.py
+++ b/docs/templatetags/docs.py
@@ -12,7 +12,7 @@ from pygments.lexers import get_lexer_by_name
from ..forms import DocSearchForm
from ..models import DocumentRelease
from ..search import START_SEL, STOP_SEL
-from ..utils import get_doc_path, get_doc_root, get_module_path
+from ..utils import get_doc_path, get_doc_root
register = template.Library()
@@ -121,68 +121,3 @@ def generate_scroll_to_text_fragment(highlighted_text):
# Due to Python code such as timezone.now(), remove the space after a bracket.
single_spaced = re.sub(r"([(\[])\s", r"\1", single_spaced)
return f"#:~:text={quote(single_spaced)}"
-
-
-@register.simple_tag
-def code_links(searched_python_objects, python_objects):
- """
- Processes a highlighted search result (from a `SearchHeadline` annotation)
- to extract Python object references and map them to their full paths.
-
- Args:
- searched_python_objects (str):
- A string from a `SearchHeadline` queryset annotation, containing
- highlighted Python object names wrapped with `START_SEL` and `STOP_SEL`.
- Example:
- "QuerySet {START_SEL}select_related{STOP_SEL} prefetch_related"
-
- python_objects (dict):
- A dictionary mapping object short names to their full path. This is
- generated from PythonObjectsJSONHTMLBuilder.
- Example:
- {
- "QuerySet": "django.db.models.query.QuerySet",
- "QuerySet.select_related": (
- "django.db.models.query.QuerySet.select_related"
- ),
- "QuerySet.prefetch_related": (
- "django.db.models.query.QuerySet.prefetch_related"
- ),
- }
-
- Returns:
- dict: A sorted dictionary where:
- - Keys are matched Python object short names.
- - Values are dictionaries containing:
- - `"full_path"` (str): The full path of the object.
- - `"module_path"` (str): The module path derived from the object name.
- Example:
- {
- "QuerySet.select_related": {
- "full_path": "django.db.models.query.QuerySet.select_related",
- "module_path": "django.db.models.query",
- }
- }
- """
- if not searched_python_objects or START_SEL not in searched_python_objects:
- return {}
- python_objects_matched_short_names = [
- word.replace(START_SEL, "").replace(STOP_SEL, "")
- for word in searched_python_objects.split(" ")
- if START_SEL in word
- ]
- matched_reference = {}
- # Map "select_related" to "QuerySet.select_related" in code_references.
- reference_map = {key.split(".")[-1]: key for key in python_objects.keys()}
- for short_name in python_objects_matched_short_names:
- if full_path := python_objects.get(short_name):
- matched_reference[short_name] = {
- "full_path": full_path,
- "module_path": get_module_path(short_name, full_path),
- }
- elif name := reference_map.get(short_name):
- matched_reference[name] = {
- "full_path": python_objects[name],
- "module_path": get_module_path(name, python_objects[name]),
- }
- return dict(sorted(matched_reference.items()))
diff --git a/docs/tests/test_builder.py b/docs/tests/test_builder.py
deleted file mode 100644
index 906951b6..00000000
--- a/docs/tests/test_builder.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from unittest.mock import Mock, patch
-
-from django.test import SimpleTestCase
-
-from ..builder import DomainObject, PythonObjectsJSONHTMLBuilder
-
-
-class TestPythonObjectsJSONHTMLBuilder(SimpleTestCase):
- def setUp(self):
- self.app = Mock()
- self.env = Mock()
- self.app.doctreedir = "/tmp"
- self.env.get_domain = Mock()
- self.mock_domain = Mock()
- self.env.get_domain.return_value = self.mock_domain
- self.builder = PythonObjectsJSONHTMLBuilder(self.app, self.env)
-
- def test_domain_objects_excludes_modules(self):
- self.mock_domain.get_objects.return_value = [
- ("module1", "module1", "module", "doc1", "", 0),
- ("ClassA", "ClassA", "class", "doc2", "", 0),
- ("function_b", "function_b", "function", "doc2", "", 0),
- ]
-
- expected_objects = [
- DomainObject("ClassA", "ClassA", "class", "doc2", "", 0),
- DomainObject("function_b", "function_b", "function", "doc2", "", 0),
- ]
- self.assertEqual(self.builder.domain_objects, expected_objects)
-
- def test_get_python_objects(self):
- self.mock_domain.get_objects.return_value = [
- (
- "module1.ClassA.method",
- "module1.ClassA.method",
- "method",
- "doc1",
- "",
- "",
- ),
- ("module1.ClassA", "module1.ClassA", "class", "doc1", "", ""),
- ("module1.function_b", "module1.function_b", "function", "doc1", "", ""),
- ]
- expected_result = {
- "ClassA": "module1.ClassA",
- "ClassA.method": "module1.ClassA.method",
- "function_b": "module1.function_b",
- }
- self.assertEqual(self.builder.get_python_objects("doc1"), expected_result)
-
- @patch("docs.builder.JSONHTMLBuilder.get_doc_context")
- def test_get_doc_context(self, mock_super_get_doc_context):
- mock_super_get_doc_context.return_value = {}
- self.mock_domain.get_objects.return_value = [
- ("module1", "module1", "module", "doc1", "", ""),
- ("module1.ClassA", "module1.ClassA", "class", "doc1", "", ""),
- ("function_b", "function_b", "function", "doc2", "", ""),
- ]
- result = self.builder.get_doc_context("doc1", "", "")
- self.assertIn("python_objects", result)
- self.assertIn("python_objects_search", result)
- self.assertEqual(result["python_objects"], {"ClassA": "module1.ClassA"})
- self.assertEqual(result["python_objects_search"], "ClassA")
diff --git a/docs/tests/test_templates.py b/docs/tests/test_templates.py
index 66f221ee..0728a726 100644
--- a/docs/tests/test_templates.py
+++ b/docs/tests/test_templates.py
@@ -10,11 +10,7 @@ from django.test import RequestFactory, TestCase
from releases.models import Release
from ..models import Document, DocumentRelease
-from ..templatetags.docs import (
- code_links,
- generate_scroll_to_text_fragment,
- get_all_doc_versions,
-)
+from ..templatetags.docs import generate_scroll_to_text_fragment, get_all_doc_versions
class TemplateTagTests(TestCase):
@@ -153,41 +149,6 @@ def band_listing(request):
url_text_fragment,
)
- def test_code_links(self):
- python_objects = {
- "Layer": "django.contrib.gis.gdal.Layer",
- "Migration.initial": "django.db.migrations.Migration.initial",
- "db_for_write": "db_for_write",
- }
- for searched_python_objects, expected in [
- (None, {}),
- ("", {}),
- ("Layer initial db_for_write", {}),
- (
- "Layer initial <mark>db_for</mark>_write",
- {"db_for_write": {"full_path": "db_for_write", "module_path": None}},
- ),
- (
- "<mark>Layer</mark> <mark>initial</mark> <mark>db_for</mark>_write",
- {
- "db_for_write": {"full_path": "db_for_write", "module_path": None},
- "Layer": {
- "full_path": "django.contrib.gis.gdal.Layer",
- "module_path": "django.contrib.gis.gdal",
- },
- "Migration.initial": {
- "full_path": "django.db.migrations.Migration.initial",
- "module_path": "django.db.migrations",
- },
- },
- ),
- ]:
- with self.subTest(searched_python_objects=searched_python_objects):
- self.assertEqual(
- code_links(searched_python_objects, python_objects),
- expected,
- )
-
class TemplateTestCase(TestCase):
def _assertOGTitleEqual(self, doc, expected):
diff --git a/docs/tests/test_views.py b/docs/tests/test_views.py
index 2a20f275..83c33da3 100644
--- a/docs/tests/test_views.py
+++ b/docs/tests/test_views.py
@@ -60,91 +60,6 @@ class SearchFormTestCase(TestCase):
)
self.assertEqual(response.status_code, 200)
- def test_code_links(self):
- queryset_data = {
- "metadata": {
- "body": (
- "QuerySet API Reference QuerySet select_related selects related"
- " things select_for_update selects things for update."
- ),
- "python_objects": {
- "QuerySet": "django.db.models.query.QuerySet",
- "QuerySet.select_related": (
- "django.db.models.query.QuerySet.select_related"
- ),
- "QuerySet.select_for_update": (
- "django.db.models.query.QuerySet.select_for_update"
- ),
- },
- "python_objects_search": ("QuerySet select_related select_for_update"),
- "breadcrumbs": [{"path": "refs", "title": "API Reference"}],
- "parents": "API Reference",
- "slug": "query",
- "title": "QuerySet API Reference",
- "toc": (
- '<ul>\n<li><a class="reference internal" href="#">QuerySet API'
- " Reference</a></li>\n</ul>\n"
- ),
- },
- "path": "refs/query",
- "release": self.doc_release,
- "title": "QuerySet",
- }
- empty_page_data = {
- "metadata": {
- "body": "Empty page",
- "breadcrumbs": [{"path": "refs", "title": "API Reference"}],
- "parents": "API Reference",
- "slug": "empty",
- "title": "Empty page",
- "toc": (
- '<ul>\n<li><a class="reference internal" href="#">Empty page'
- "</a></li>\n</ul>\n"
- ),
- },
- "path": "refs/empty",
- "release": self.doc_release,
- "title": "Empty page",
- }
- Document.objects.bulk_create(
- [Document(**queryset_data), Document(**empty_page_data)]
- )
- Document.objects.search_update()
- base_url = reverse_with_host(
- "document-detail",
- host="docs",
- kwargs={"lang": "en", "version": "5.1", "url": "refs/query"},
- )
- for query, expected_code_links in [
- (
- "queryset",
- f'<ul class="code-links"><li><a href="{base_url}#django.db.models.query'
- '.QuerySet"><div><code>QuerySet</code><div class="meta">django.db.'
- "models.query</div></div></a></li></ul>",
- ),
- (
- "select",
- f'<ul class="code-links"><li><a href="{base_url}#django.db.models.query'
- '.QuerySet.select_for_update"><div><code>QuerySet.select_for_update'
- '</code><div class="meta">django.db.models.query</div></div></a></li>'
- f'<li><a href="{base_url}#django.db.models.query.QuerySet.'
- 'select_related"><div><code>QuerySet.select_related</code><div '
- 'class="meta">django.db.models.query</div></div></a></li></ul>',
- ),
- ]:
- with self.subTest(query=query):
- response = self.client.get(
- f"/en/5.1/search/?q={query}",
- headers={"host": "docs.djangoproject.localhost:8000"},
- )
- self.assertEqual(response.status_code, 200)
- self.assertContains(
- response,
- f"Only 1 result for <em>{query}</em> in version 5.1",
- html=True,
- )
- self.assertContains(response, expected_code_links, html=True)
-
class SitemapTests(TestCase):
fixtures = ["doc_test_fixtures"]
diff --git a/docs/utils.py b/docs/utils.py
index d1c965d2..3adec60d 100644
--- a/docs/utils.py
+++ b/docs/utils.py
@@ -57,38 +57,3 @@ def sanitize_for_trigram(text):
text = unicodedata.normalize("NFKD", text)
text = re.sub(r"[^\w\s]", "", text, flags=re.UNICODE)
return " ".join(text.split())
-
-
-def get_module_path(name, full_path):
- """
- Checks if the `full_path` ends with `.name` and, if so, removes it to return
- the module path. Otherwise, it returns `None`.
-
- Args:
- name (str):
- The short name of the object (e.g., `"QuerySet.select_related"`).
- full_path (str):
- The full path of the object (e.g.,
- `"django.db.models.query.QuerySet.select_related"`).
-
- Returns:
- str or None:
- The module path if `full_path` ends with `.name`, otherwise `None`.
-
- Example:
- >>> get_module_path(
- ... "QuerySet.select_related",
- ... "django.db.models.query.QuerySet.select_related"
- ... )
- 'django.db.models.query'
-
- >>> get_module_path("Model", "django.db.models.Model")
- 'django.db.models'
-
- >>> get_module_path("django", "django")
- None
- """
- name_suffix = f".{name}"
- if full_path.endswith(name_suffix):
- return full_path.removesuffix(name_suffix)
- return None