Merge pull request #37 from pydot/bug/36

Adjusted quoting to DOT lang specification
This commit is contained in:
Kamil Sambor 2015-03-15 21:26:32 +01:00
commit 4ab4b6a0f5
2 changed files with 42 additions and 18 deletions

View File

@ -198,15 +198,13 @@ class frozendict(dict):
return "frozendict(%s)" % dict.__repr__(self) return "frozendict(%s)" % dict.__repr__(self)
dot_keywords = ['graph', 'subgraph', 'digraph', 'node', 'edge', 'strict'] # cases when no qoutes needed, from http://www.graphviz.org/doc/info/lang.html
dot_keywords = ('graph', 'subgraph', 'digraph', 'node', 'edge', 'strict')
id_re_alpha_nums = re.compile('^[_a-zA-Z][a-zA-Z0-9_,]*$', re.UNICODE) id_alpha_num = re.compile(r'^[_a-zA-Z\200-\377][_a-zA-Z0-9\200-\377]*$',
id_re_alpha_nums_with_ports = re.compile( re.UNICODE)
'^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$', re.UNICODE) id_num = re.compile(r'^[-]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)$', re.UNICODE)
id_re_num = re.compile('^[0-9,]+$', re.UNICODE) id_html = re.compile(r'^<.*>$', re.DOTALL | re.UNICODE)
id_re_with_port = re.compile('^([^:]*):([^:]*)$', re.UNICODE) id_quoted = re.compile(r'^".*"$', re.DOTALL | re.UNICODE)
id_re_dbl_quoted = re.compile('^\".*\"$', re.S | re.UNICODE)
id_re_html = re.compile('^<.*>$', re.S | re.UNICODE)
def needs_quotes(s): def needs_quotes(s):
@ -228,19 +226,18 @@ def needs_quotes(s):
if s in dot_keywords: if s in dot_keywords:
return False return False
chars = [ord(c) for c in s if ord(c) > 0x7f or ord(c) == 0]
if chars and not id_re_dbl_quoted.match(s) and not id_re_html.match(s):
return True
for test_re in [ for test_re in [
id_re_alpha_nums, id_re_num, id_re_dbl_quoted, id_alpha_num,
id_re_html, id_re_alpha_nums_with_ports]: id_num,
id_html,
id_quoted,
]:
if test_re.match(s): if test_re.match(s):
return False return False
m = id_re_with_port.match(s) chars = [ord(c) for c in s if ord(c) > 0x7f or ord(c) == 0]
if m: if chars and not id_quoted.match(s) and not id_html.match(s):
return needs_quotes(m.group(1)) or needs_quotes(m.group(2)) return True
return True return True

View File

@ -285,6 +285,33 @@ class TestGraphAPI(unittest.TestCase):
data = g.create(format='jpe') data = g.create(format='jpe')
self.assertEqual(len(data) > 0, True) self.assertEqual(len(data) > 0, True)
class TestQuoting(unittest.TestCase):
def test_quote_cases(self):
checks = (
('A:', '"A:"'),
(':B', '":B"'),
('A:B', '"A:B"'),
('1A', '"1A"'),
('A', 'A'),
('11', '11'),
('_xyz', '_xyz'),
('.11', '.11'),
('-.09', '-.09'),
('1.8', '1.8'),
('', '""'),
('"1abc"', '"1abc"'),
('@', '"@"'),
('ÿ', 'ÿ'),
('$GUID__/ffb73e1c-7495-40b3-9618-9e5462fc89c7',
'"$GUID__/ffb73e1c-7495-40b3-9618-9e5462fc89c7"')
)
for input, expected in checks:
self.assertEqual(pydot.quote_if_necessary(input), expected)
if __name__ == '__main__': if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(TestGraphAPI) suite = unittest.TestLoader().loadTestsFromTestCase(TestGraphAPI)
unittest.TextTestRunner(verbosity=2).run(suite) unittest.TextTestRunner(verbosity=2).run(suite)