From 8494638e459123ec4167842f58dc20d7e30866a9 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 29 Jan 2018 21:09:28 +0900 Subject: [PATCH] Fix graphviz: workaround for wrong map ID which graphviz generates --- CHANGES | 1 + sphinx/ext/graphviz.py | 19 +++++++++++++------ tests/test_ext_graphviz.py | 26 ++++++++++++++------------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/CHANGES b/CHANGES index c17f7319a..d6417a03f 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,7 @@ Bugs fixed * #4449: apidoc: include "empty" packages that contain modules * #3917: citation labels are tranformed to ellipsis * #4501: graphviz: epub3 validation error caused if graph is not clickable +* #4514: graphviz: workaround for wrong map ID which graphviz generates Testing -------- diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index b60925405..8a3dc6848 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -47,22 +47,29 @@ class ClickableMapDefinition(object): maptag_re = re.compile(' None + def __init__(self, filename, content, dot=''): + # type: (unicode, unicode, unicode) -> None self.id = None self.filename = filename self.content = content.splitlines() self.clickable = [] # type: List[unicode] - self.parse() + self.parse(dot=dot) - def parse(self): - # type: () -> None + def parse(self, dot=None): + # type: (None) -> None matched = self.maptag_re.match(self.content[0]) # type: ignore if not matched: raise GraphvizError('Invalid clickable map file found: %s' % self.filename) self.id = matched.group(1) + if self.id == '%3': + # graphviz generates wrong ID if graph name not specified + # https://gitlab.com/graphviz/graphviz/issues/1327 + hashed = sha1(dot.encode('utf-8')).hexdigest() + self.id = 'grapviz%s' % hashed[-10:] + self.content[0] = self.content[0].replace('%3', self.id) + for line in self.content: if self.href_re.search(line): self.clickable.append(line) @@ -289,7 +296,7 @@ def render_dot_html(self, node, code, options, prefix='graphviz', self.body.append(svgtag) else: with codecs.open(outfn + '.map', 'r', encoding='utf-8') as mapfile: - imgmap = ClickableMapDefinition(outfn + '.map', mapfile.read()) + imgmap = ClickableMapDefinition(outfn + '.map', mapfile.read(), dot=code) if imgmap.clickable: # has a map self.body.append('%s\n' % diff --git a/tests/test_ext_graphviz.py b/tests/test_ext_graphviz.py index 62d3e37ee..fc5325037 100644 --- a/tests/test_ext_graphviz.py +++ b/tests/test_ext_graphviz.py @@ -118,30 +118,32 @@ def test_graphviz_i18n(app, status, warning): def test_graphviz_parse_mapfile(): - # digraph { - # } + # empty graph + code = ('# digraph {\n' + '# }\n') content = ('\n' '') - cmap = ClickableMapDefinition('dummy.map', content) + cmap = ClickableMapDefinition('dummy.map', content, code) assert cmap.filename == 'dummy.map' - assert cmap.id == '%3' + assert cmap.id == 'grapvizb08107169e' assert len(cmap.clickable) == 0 assert cmap.generate_clickable_map() == '' - # digraph { - # foo [href="http://www.google.com/"]; - # foo -> bar; - # } + # normal graph + code = ('digraph {\n' + ' foo [href="http://www.google.com/"];\n' + ' foo -> bar;\n' + '}\n') content = ('\n' '\n' '') - cmap = ClickableMapDefinition('dummy.map', content) + cmap = ClickableMapDefinition('dummy.map', content, code) assert cmap.filename == 'dummy.map' - assert cmap.id == '%3' + assert cmap.id == 'grapviza4ccdd48ce' assert len(cmap.clickable) == 1 - assert cmap.generate_clickable_map() == content + assert cmap.generate_clickable_map() == content.replace('%3', cmap.id) # inheritance-diagram:: sphinx.builders.html content = ( @@ -168,7 +170,7 @@ def test_graphviz_parse_mapfile(): ' alt="" coords="11,3,141,19"/>\n' '' ) - cmap = ClickableMapDefinition('dummy.map', content) + cmap = ClickableMapDefinition('dummy.map', content, 'dummy_code') assert cmap.filename == 'dummy.map' assert cmap.id == 'inheritance66ff5471b9' assert len(cmap.clickable) == 0