-Fixed issue 52. Improved handling of BOM-less UTF-8 encoded files.

-Fixed issue 55 regarding unicode handling.
-Fixed issue 50 where an ending colon in a node name was understood as a port separator. Colons as the last character of node names will be left as-is.
-Issue 59 (and duplicate issue 62): Program arguments are mishandled in Dot.create - Patch merged.
-Fixed issue 49, handling of quotes in unicode html-labels.
-Fixed issue 60. Added an additional check in __get_attribute__ to not assume the parent graph is always retrievable.
-Fixed issue 61. Graph names will be adequately quoted when necessary.

git-svn-id: http://pydot.googlecode.com/svn/trunk@27 06aa9b79-7134-0410-ae7e-c1cd3e483e87
This commit is contained in:
Ero Carrera 2012-01-02 23:21:31 +00:00
parent ef97949461
commit 9999c00c26
1 changed files with 42 additions and 16 deletions

View File

@ -141,11 +141,12 @@ class frozendict(dict):
dot_keywords = ['graph', 'subgraph', 'digraph', 'node', 'edge', 'strict']
id_re_alpha_nums = re.compile('^[_a-zA-Z][a-zA-Z0-9_:,]*$')
id_re_num = re.compile('^[0-9,]+$')
id_re_with_port = re.compile('^([^:]*):([^:]*)$')
id_re_dbl_quoted = re.compile('^\".*\"$', re.S)
id_re_html = re.compile('^<.*>$', re.S)
id_re_alpha_nums = re.compile('^[_a-zA-Z][a-zA-Z0-9_,]*$', re.UNICODE)
id_re_alpha_nums_with_ports = re.compile('^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$', re.UNICODE)
id_re_num = re.compile('^[0-9,]+$', re.UNICODE)
id_re_with_port = re.compile('^([^:]*):([^:]*)$', 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 ):
@ -154,18 +155,25 @@ def needs_quotes( s ):
It will check whether the string is solely composed
by the characters allowed in an ID or not.
If the string is one of the reserved keywords it will
need quotes too.
need quotes too but the user will need to add them
manually.
"""
# If the name is a reserved keyword it will need quotes but pydot
# can't tell when it's being used as a keyword or when it's simply
# a name. Hence the user needs to supply the quotes when an element
# would use a reserved keyword as name. This function will return
# false indicating that a keyword string, if provided as-is, won't
# need quotes.
if s in dot_keywords:
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):
if chars and not id_re_dbl_quoted.match(s) and not id_re_html.match(s):
return True
for test in [id_re_alpha_nums, id_re_num, id_re_dbl_quoted, id_re_html]:
if test.match(s):
for test_re in [id_re_alpha_nums, id_re_num, id_re_dbl_quoted, id_re_html, id_re_alpha_nums_with_ports]:
if test_re.match(s):
return False
m = id_re_with_port.match(s)
@ -185,6 +193,9 @@ def quote_if_necessary(s):
if not isinstance( s, basestring ):
return s
if not s:
return s
if needs_quotes(s):
replace = {'"' : r'\"',
"\n" : r'\n',
@ -591,7 +602,11 @@ class Common:
if default_node_name in ('subgraph', 'digraph', 'cluster'):
default_node_name = 'graph'
defaults = self.get_parent_graph().get_node( default_node_name )
g = self.get_parent_graph()
if g is not None:
defaults = g.get_node( default_node_name )
else:
return None
# Multiple defaults could be set by having repeated 'graph [...]'
# 'node [...]', 'edge [...]' statements. In such case, if the
@ -743,7 +758,7 @@ class Node(object, Common):
port = None
if isinstance(name, basestring) and not name.startswith('"'):
idx = name.find(':')
if idx > 0:
if idx > 0 and idx+1 < len(name):
name, port = name[:idx], name[idx:]
if isinstance(name, (long, int)):
@ -1063,7 +1078,7 @@ class Graph(object, Common):
raise Error, 'Invalid type "%s". Accepted graph types are: graph, digraph, subgraph' % graph_type
self.obj_dict['name'] = graph_name
self.obj_dict['name'] = quote_if_necessary(graph_name)
self.obj_dict['type'] = graph_type
self.obj_dict['strict'] = strict
@ -1879,7 +1894,19 @@ class Dot(Graph):
dot_fd = file(path, "w+b")
if format == 'raw':
dot_fd.write(self.to_string())
data = self.to_string()
if isinstance(data, basestring):
if not isinstance(data, unicode):
try:
data = unicode(data, 'utf-8')
except:
pass
try:
data = data.encode('utf-8')
except:
pass
dot_fd.write(data)
else:
dot_fd.write(self.create(prog, format))
dot_fd.close()
@ -1915,8 +1942,7 @@ class Dot(Graph):
prog = self.prog
if isinstance(prog, (list, tuple)):
prog = prog[0]
args = prog[1:]
prog, args = prog[0], prog[1:]
else:
args = []