From 98897932a502bf92ed070a77a348a867dc4af276 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 13 Dec 2025 23:36:46 -0600 Subject: [PATCH 1/7] Replace markdown library with mistune markdown.py had parsing errors that were throwing off img srcsets. --- bookwyrm/connectors/openlibrary.py | 6 +++--- bookwyrm/models/fields.py | 4 ++-- bookwyrm/views/status.py | 4 ++-- requirements.txt | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bookwyrm/connectors/openlibrary.py b/bookwyrm/connectors/openlibrary.py index 032a86f580..55e3616fd4 100644 --- a/bookwyrm/connectors/openlibrary.py +++ b/bookwyrm/connectors/openlibrary.py @@ -3,7 +3,7 @@ import re from typing import Any, Optional, Union, Iterator, Iterable -from markdown import markdown +import mistune from bookwyrm import models from bookwyrm.book_search import SearchResult @@ -249,9 +249,9 @@ def ignore_edition(edition_data: JsonDict) -> bool: def get_description(description_blob: Union[JsonDict, str]) -> str: """descriptions can be a string or a dict""" if isinstance(description_blob, dict): - description = markdown(description_blob.get("value", "")) + description = mistune.html(description_blob.get("value", "")) else: - description = markdown(description_blob) + description = mistune.html(description_blob) if ( description.startswith("

") diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index ecc1976db6..787ad1dd9a 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -16,7 +16,7 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ from django.utils.encoding import filepath_to_uri -from markdown import markdown +import mistune from bookwyrm import activitypub from bookwyrm.connectors import get_image @@ -605,7 +605,7 @@ def field_from_activity(self, value, allow_external_connections=True, trigger=No return clean(value) def field_to_activity(self, value): - return markdown(value) if value else value + return mistune.html(value) if value else value class ArrayField(ActivitypubFieldMixin, DjangoArrayField): diff --git a/bookwyrm/views/status.py b/bookwyrm/views/status.py index 2f698f04e0..cfa9736bee 100644 --- a/bookwyrm/views/status.py +++ b/bookwyrm/views/status.py @@ -16,7 +16,7 @@ from django.views import View from django.views.decorators.http import require_POST -from markdown import markdown +import mistune from bookwyrm import forms, models from bookwyrm.models.report import DELETE_ITEM from bookwyrm.utils import regex, sanitizer @@ -344,6 +344,6 @@ def _unwrap(text): def to_markdown(content): """catch links and convert to markdown""" content = format_links(content) - content = markdown(content) + content = mistune.html(content) # sanitize resulting html return sanitizer.clean(content) diff --git a/requirements.txt b/requirements.txt index 42dd578691..c5d500d3d6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ flower==2.0.1 gunicorn==23.0.0 hiredis==2.3.2 libsass==0.23.0 -Markdown==3.6 +mistune==3.1.2 opentelemetry-api==1.24.0 opentelemetry-exporter-otlp-proto-grpc==1.24.0 opentelemetry-instrumentation-celery==0.45b0 From 882228de8ebfa0789290888a96600bfc9374d219 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 20 Dec 2025 22:03:26 -0600 Subject: [PATCH 2/7] Strip newline from mistune output It doesn't hugely matter one way or another, but the existing tests all expect no newline, so why rock the boat. --- bookwyrm/connectors/openlibrary.py | 4 ++-- bookwyrm/models/fields.py | 2 +- bookwyrm/views/status.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bookwyrm/connectors/openlibrary.py b/bookwyrm/connectors/openlibrary.py index 55e3616fd4..fbe0907bb9 100644 --- a/bookwyrm/connectors/openlibrary.py +++ b/bookwyrm/connectors/openlibrary.py @@ -249,9 +249,9 @@ def ignore_edition(edition_data: JsonDict) -> bool: def get_description(description_blob: Union[JsonDict, str]) -> str: """descriptions can be a string or a dict""" if isinstance(description_blob, dict): - description = mistune.html(description_blob.get("value", "")) + description = mistune.html(description_blob.get("value", "")).rstrip() else: - description = mistune.html(description_blob) + description = mistune.html(description_blob).rstrip() if ( description.startswith("

") diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 787ad1dd9a..bac1e028c4 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -605,7 +605,7 @@ def field_from_activity(self, value, allow_external_connections=True, trigger=No return clean(value) def field_to_activity(self, value): - return mistune.html(value) if value else value + return mistune.html(value).rstrip() if value else value class ArrayField(ActivitypubFieldMixin, DjangoArrayField): diff --git a/bookwyrm/views/status.py b/bookwyrm/views/status.py index cfa9736bee..fab7929e27 100644 --- a/bookwyrm/views/status.py +++ b/bookwyrm/views/status.py @@ -344,6 +344,6 @@ def _unwrap(text): def to_markdown(content): """catch links and convert to markdown""" content = format_links(content) - content = mistune.html(content) + content = mistune.html(content).rstrip() # sanitize resulting html return sanitizer.clean(content) From 74693247e8527852a3d9ebb63c823f3c76c03c68 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 20 Dec 2025 22:10:53 -0600 Subject: [PATCH 3/7] Update test to reflect conversion of quote character --- bookwyrm/tests/connectors/test_openlibrary_connector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/tests/connectors/test_openlibrary_connector.py b/bookwyrm/tests/connectors/test_openlibrary_connector.py index 19df39599d..be5a0b92e0 100644 --- a/bookwyrm/tests/connectors/test_openlibrary_connector.py +++ b/bookwyrm/tests/connectors/test_openlibrary_connector.py @@ -329,7 +329,7 @@ def test_create_edition_markdown_from_data(self): result = self.connector.create_edition_from_data(work, self.edition_md_data) self.assertEqual( result.description, - '

\n

"She didn\'t choose her garden" opens this chapbook ' + '

\n

"She didn\'t choose her garden" opens this chapbook ' "exploring Black womanhood, mental and physical health, spirituality, and " "ancestral roots. It is an investigation of how to locate a self amidst " "complex racial history and how to forge an authentic way forward. There's " From 446600e41ac44e92760b003faa48fbd3d122a232 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 17 Feb 2026 22:31:25 -0600 Subject: [PATCH 4/7] Fix formatting violation --- bookwyrm/tests/connectors/test_openlibrary_connector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/tests/connectors/test_openlibrary_connector.py b/bookwyrm/tests/connectors/test_openlibrary_connector.py index be5a0b92e0..b895a7e661 100644 --- a/bookwyrm/tests/connectors/test_openlibrary_connector.py +++ b/bookwyrm/tests/connectors/test_openlibrary_connector.py @@ -329,7 +329,7 @@ def test_create_edition_markdown_from_data(self): result = self.connector.create_edition_from_data(work, self.edition_md_data) self.assertEqual( result.description, - '

\n

"She didn\'t choose her garden" opens this chapbook ' + "

\n

"She didn't choose her garden" opens this chapbook " "exploring Black womanhood, mental and physical health, spirituality, and " "ancestral roots. It is an investigation of how to locate a self amidst " "complex racial history and how to forge an authentic way forward. There's " From 6509a7262682031c43bd06e6061b0ddc5c3f7adf Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 17 Feb 2026 23:24:10 -0600 Subject: [PATCH 5/7] Cast return values to deal with weird method sig from mistune --- bookwyrm/connectors/openlibrary.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bookwyrm/connectors/openlibrary.py b/bookwyrm/connectors/openlibrary.py index fbe0907bb9..d813b7d1c0 100644 --- a/bookwyrm/connectors/openlibrary.py +++ b/bookwyrm/connectors/openlibrary.py @@ -1,7 +1,7 @@ """openlibrary data connector""" import re -from typing import Any, Optional, Union, Iterator, Iterable +from typing import Any, Optional, Union, Iterator, Iterable, cast import mistune @@ -249,9 +249,9 @@ def ignore_edition(edition_data: JsonDict) -> bool: def get_description(description_blob: Union[JsonDict, str]) -> str: """descriptions can be a string or a dict""" if isinstance(description_blob, dict): - description = mistune.html(description_blob.get("value", "")).rstrip() + description = cast(str, mistune.html(description_blob.get("value", ""))).rstrip() else: - description = mistune.html(description_blob).rstrip() + description = cast(str, mistune.html(description_blob)).rstrip() if ( description.startswith("

") From 6b22ce504dfe780bb6e698bb59f0b2da57bdb916 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Wed, 18 Feb 2026 17:17:53 -0600 Subject: [PATCH 6/7] Fix formatting violation --- bookwyrm/connectors/openlibrary.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bookwyrm/connectors/openlibrary.py b/bookwyrm/connectors/openlibrary.py index d813b7d1c0..f17880c94a 100644 --- a/bookwyrm/connectors/openlibrary.py +++ b/bookwyrm/connectors/openlibrary.py @@ -249,7 +249,9 @@ def ignore_edition(edition_data: JsonDict) -> bool: def get_description(description_blob: Union[JsonDict, str]) -> str: """descriptions can be a string or a dict""" if isinstance(description_blob, dict): - description = cast(str, mistune.html(description_blob.get("value", ""))).rstrip() + description = cast( + str, mistune.html(description_blob.get("value", "")) + ).rstrip() else: description = cast(str, mistune.html(description_blob)).rstrip() From cfe744baa3bf076c0311ce0876fb4195a26318a2 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Wed, 18 Feb 2026 17:21:55 -0600 Subject: [PATCH 7/7] Remove Markdown from deps --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1faf1d902c..52c0634e25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,6 @@ main = [ "gunicorn==23.0.0", "hiredis==2.3.2", "libsass==0.23.0", - "Markdown==3.6", "mistune==3.2.0", "opentelemetry-api==1.24.0", "opentelemetry-exporter-otlp-proto-grpc==1.24.0", @@ -61,7 +60,6 @@ dev = [ "pytidylib==0.3.2", "types-bleach==6.1.0.20240331", "types-dataclasses==0.6.6", - "types-Markdown==3.6.0.20240316", "types-Pillow==10.2.0.20240331", "types-python-dateutil==2.9.0.20240316", "types-requests==2.31.0.20240311",