diff options
| author | Alex <8340441+alexgmin@users.noreply.github.com> | 2025-03-05 21:41:34 +0100 |
|---|---|---|
| committer | Baptiste Mispelon <bmispelon@gmail.com> | 2025-03-05 21:49:30 +0100 |
| commit | af929f0734c319447a061e82b0353fc48b792ec4 (patch) | |
| tree | ff081e0c07a8b3beb16675b7890c3bf6294f5704 /docs | |
| parent | 64003b45b00577e1d66b3476bd55db3069028826 (diff) | |
Revert "Added code references to search results. (#1947)"
This reverts commit fa56d9b841e0bd23c5c8871826ba6abe2205fdb4.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/builder.py | 76 | ||||
| -rw-r--r-- | docs/management/commands/update_docs.py | 47 | ||||
| -rw-r--r-- | docs/models.py | 25 | ||||
| -rw-r--r-- | docs/templates/docs/search_results.html | 15 | ||||
| -rw-r--r-- | docs/templatetags/docs.py | 67 | ||||
| -rw-r--r-- | docs/tests/test_builder.py | 63 | ||||
| -rw-r--r-- | docs/tests/test_templates.py | 41 | ||||
| -rw-r--r-- | docs/tests/test_views.py | 85 | ||||
| -rw-r--r-- | docs/utils.py | 35 |
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 %} … {{ result.highlight|cut:"¶"|safe }} … {% 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 |
