diff --git a/doc/whatsnew/fragments/10402.bugfix b/doc/whatsnew/fragments/10402.bugfix new file mode 100644 index 0000000000..a7cea6782e --- /dev/null +++ b/doc/whatsnew/fragments/10402.bugfix @@ -0,0 +1,3 @@ +Fix double underscores erroneously rendering as bold in pyreverse's Mermaid output. + +Closes #10402 diff --git a/pylint/pyreverse/mermaidjs_printer.py b/pylint/pyreverse/mermaidjs_printer.py index 22a8f768b1..0f1ebd04f0 100644 --- a/pylint/pyreverse/mermaidjs_printer.py +++ b/pylint/pyreverse/mermaidjs_printer.py @@ -32,6 +32,11 @@ def _open_graph(self) -> None: self.emit("classDiagram") self._inc_indent() + def _escape_mermaid_text(self, text: str) -> str: + """Escape characters that conflict with Markdown formatting.""" + text = text.replace("__", r"\_\_") # Double underscore → escaped + return text + def emit_node( self, name: str, @@ -48,14 +53,24 @@ def emit_node( nodetype = self.NODES[type_] body = [] if properties.attrs: - body.extend(properties.attrs) + # Escape attribute names to prevent Markdown formatting issues + escaped_attrs = [ + self._escape_mermaid_text(attr) for attr in properties.attrs + ] + body.extend(escaped_attrs) if properties.methods: for func in properties.methods: args = self._get_method_arguments(func) - line = f"{func.name}({', '.join(args)})" + # Escape method name and arguments + escaped_method_name = self._escape_mermaid_text(func.name) + escaped_args = [self._escape_mermaid_text(arg) for arg in args] + line = f"{escaped_method_name}({', '.join(escaped_args)})" line += "*" if func.is_abstract() else "" if func.returns: - line += f" {get_annotation_label(func.returns)}" + # Escape return type annotation + return_type = get_annotation_label(func.returns) + escaped_return_type = self._escape_mermaid_text(return_type) + line += f" {escaped_return_type}" body.append(line) name = name.split(".")[-1] self.emit(f"{nodetype} {name} {{") @@ -80,7 +95,7 @@ def emit_edge( to_node = to_node.split(".")[-1] edge = f"{from_node} {self.ARROWS[type_]} {to_node}" if label: - edge += f" : {label}" + edge += f" : {self._escape_mermaid_text(label)}" self.emit(edge) def _close_graph(self) -> None: diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd index 3b445288a1..378ef5ccaf 100644 --- a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd @@ -1,25 +1,25 @@ classDiagram class P { name : str - __init__(name: str) + \_\_init\_\_(name: str) } class PrivateAttr { - __x - __init__() + \_\_x + \_\_init\_\_() } class ProtectedAttr { _x - __init__() + \_\_init\_\_() } class PublicAttr { x - __init__() + \_\_init\_\_() } class SpecialAttr { - __x__ - __init__() + \_\_x\_\_ + \_\_init\_\_() } - P --* PrivateAttr : __x + P --* PrivateAttr : \_\_x P --* ProtectedAttr : _x P --* PublicAttr : x - P --* SpecialAttr : __x__ + P --* SpecialAttr : \_\_x\_\_ diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd index 6cfcae244f..07439b6c55 100644 --- a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd @@ -1,21 +1,21 @@ classDiagram class P { name : str - __init__(name: str) + \_\_init\_\_(name: str) } class PrivateAttr { - __init__() + \_\_init\_\_() } class ProtectedAttr { - __init__() + \_\_init\_\_() } class PublicAttr { x - __init__() + \_\_init\_\_() } class SpecialAttr { - __x__ - __init__() + \_\_x\_\_ + \_\_init\_\_() } P --* PublicAttr : x - P --* SpecialAttr : __x__ + P --* SpecialAttr : \_\_x\_\_ diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd index 149b0a694a..6b7f39b905 100644 --- a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd @@ -3,7 +3,7 @@ classDiagram name : str } class PrivateAttr { - __x + \_\_x } class ProtectedAttr { _x @@ -13,6 +13,6 @@ classDiagram } class SpecialAttr { } - P --* PrivateAttr : __x + P --* PrivateAttr : \_\_x P --* ProtectedAttr : _x P --* PublicAttr : x