Skip to content
This repository was archived by the owner on Nov 19, 2018. It is now read-only.

Commit 64e3a63

Browse files
committed
Adjusted quoting to DOT lang specification
There was some differences between how pydot quotes IDs (names, attrs) and how it is defined in DOT language specification (and how pygraphviz does it). New test with covered cases has been added and new regexpes for checking IDs has been also added. Fixes #36
1 parent a142f0a commit 64e3a63

File tree

2 files changed

+40
-18
lines changed

2 files changed

+40
-18
lines changed

pydot_ng/__init__.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,13 @@ def __repr__(self):
198198
return "frozendict(%s)" % dict.__repr__(self)
199199

200200

201-
dot_keywords = ['graph', 'subgraph', 'digraph', 'node', 'edge', 'strict']
202-
203-
id_re_alpha_nums = re.compile('^[_a-zA-Z][a-zA-Z0-9_,]*$', re.UNICODE)
204-
id_re_alpha_nums_with_ports = re.compile(
205-
'^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$', re.UNICODE)
206-
id_re_num = re.compile('^[0-9,]+$', re.UNICODE)
207-
id_re_with_port = re.compile('^([^:]*):([^:]*)$', re.UNICODE)
208-
id_re_dbl_quoted = re.compile('^\".*\"$', re.S | re.UNICODE)
209-
id_re_html = re.compile('^<.*>$', re.S | re.UNICODE)
201+
# cases when no qoutes needed, from http://www.graphviz.org/doc/info/lang.html
202+
dot_keywords = ('graph', 'subgraph', 'digraph', 'node', 'edge', 'strict')
203+
id_alpha_num = re.compile(r'^[_a-zA-Z\200-\377][_a-zA-Z0-9\200-\377]*$',
204+
re.UNICODE)
205+
id_num = re.compile(r'^[-]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)$', re.UNICODE)
206+
id_html = re.compile(r'^<.*>$', re.DOTALL | re.UNICODE)
207+
id_quoted = re.compile(r'^".*"$', re.DOTALL | re.UNICODE)
210208

211209

212210
def needs_quotes(s):
@@ -228,19 +226,18 @@ def needs_quotes(s):
228226
if s in dot_keywords:
229227
return False
230228

231-
chars = [ord(c) for c in s if ord(c) > 0x7f or ord(c) == 0]
232-
if chars and not id_re_dbl_quoted.match(s) and not id_re_html.match(s):
233-
return True
234-
235229
for test_re in [
236-
id_re_alpha_nums, id_re_num, id_re_dbl_quoted,
237-
id_re_html, id_re_alpha_nums_with_ports]:
230+
id_alpha_num,
231+
id_num,
232+
id_html,
233+
id_quoted,
234+
]:
238235
if test_re.match(s):
239236
return False
240237

241-
m = id_re_with_port.match(s)
242-
if m:
243-
return needs_quotes(m.group(1)) or needs_quotes(m.group(2))
238+
chars = [ord(c) for c in s if ord(c) > 0x7f or ord(c) == 0]
239+
if chars and not id_quoted.match(s) and not id_html.match(s):
240+
return True
244241

245242
return True
246243

test/test_pydot.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,31 @@ def test_quoting(self):
285285
data = g.create(format='jpe')
286286
self.assertEqual(len(data) > 0, True)
287287

288+
289+
class TestQuoting(unittest.TestCase):
290+
291+
def test_quote_cases(self):
292+
checks = (
293+
('A:', '"A:"'),
294+
(':B', '":B"'),
295+
('A:B', '"A:B"'),
296+
('1A', '"1A"'),
297+
('A', 'A'),
298+
('11', '11'),
299+
('_xyz', '_xyz'),
300+
('.11', '.11'),
301+
('-.09', '-.09'),
302+
('1.8', '1.8'),
303+
('', '""'),
304+
('"1abc"', '"1abc"'),
305+
('@', '"@"'),
306+
('ÿ', 'ÿ'),
307+
)
308+
309+
for input, expected in checks:
310+
self.assertEqual(pydot.quote_if_necessary(input), expected)
311+
312+
288313
if __name__ == '__main__':
289314
suite = unittest.TestLoader().loadTestsFromTestCase(TestGraphAPI)
290315
unittest.TextTestRunner(verbosity=2).run(suite)

0 commit comments

Comments
 (0)