Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/whatsnew/fragments/10402.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix double underscores rendering as bold in pyreverse Mermaid output.

Closes #10402
26 changes: 22 additions & 4 deletions pylint/pyreverse/mermaidjs_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ def _open_graph(self) -> None:
self.emit("classDiagram")
self._inc_indent()

def _escape_mermaid_text(self, text: str) -> str:
"""Escape text to prevent Mermaid from interpreting it as Markdown
formatting.
"""
# Escape characters that conflict with Markdown formatting
text = text.replace("__", r"\_\_") # Double underscore β†’ escaped
return text

def emit_node(
self,
name: str,
Expand All @@ -48,14 +56,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} {{")
Expand All @@ -80,7 +98,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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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\_\_
Original file line number Diff line number Diff line change
@@ -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\_\_
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ classDiagram
name : str
}
class PrivateAttr {
__x
\_\_x
}
class ProtectedAttr {
_x
Expand All @@ -13,6 +13,6 @@ classDiagram
}
class SpecialAttr {
}
P --* PrivateAttr : __x
P --* PrivateAttr : \_\_x
P --* ProtectedAttr : _x
P --* PublicAttr : x