"""Graph file parsing."""importsys,reimportsubprocessimportmsgstructre_nonword=re.compile(r'([^0-9a-zA-Z_.]+)')re_plain=re.compile(r'graph [-0-9.]+ [-0-9.]+ [-0-9.]+$',re.MULTILINE)re_digraph=re.compile(r'\b(graph|digraph)\b',re.IGNORECASE)defguess_type(content):# try to see whether it is a directed graph or not,# or already a .plain file# XXX not a perfect heursiticifre_plain.match(content):return'plain'# already a .plain file# look for the word 'graph' or 'digraph' followed by a '{'.bracepos=Nonelastfound=''formatchinre_digraph.finditer(content):position=match.start()ifbraceposisNone:bracepos=content.find('{',position)ifbracepos<0:breakelifposition>bracepos:breaklastfound=match.group()iflastfound.lower()=='digraph':return'dot'iflastfound.lower()=='graph':return'neato'print>>sys.stderr,"Warning: could not guess file type, using 'dot'"return'unknown'defdot2plain_graphviz(content,contenttype,use_codespeak=False):ifcontenttype!='neato':cmdline='dot -Tplain'else:cmdline='neato -Tplain'#print >> sys.stderr, '* running:', cmdlineclose_fds=sys.platform!='win32'p=subprocess.Popen(cmdline,shell=True,close_fds=close_fds,stdin=subprocess.PIPE,stdout=subprocess.PIPE)(child_in,child_out)=(p.stdin,p.stdout)try:importthreadexceptImportError:bkgndwrite(child_in,content)else:thread.start_new_thread(bkgndwrite,(child_in,content))plaincontent=child_out.read()child_out.close()ifnotplaincontent:# 'dot' is likely not installedraisePlainParseError("no result from running 'dot'")returnplaincontentdefdot2plain_codespeak(content,contenttype):importurllibrequest=urllib.urlencode({'dot':content})url='http://codespeak.net/pypy/convertdot.cgi'print>>sys.stderr,'* posting:',urlg=urllib.urlopen(url,data=request)result=[]whileTrue:data=g.read(16384)ifnotdata:breakresult.append(data)g.close()plaincontent=''.join(result)# very simple-minded way to give a somewhat better error messageifplaincontent.startswith('<body'):raiseException("the dot on codespeak has very likely crashed")returnplaincontentdefbkgndwrite(f,data):f.write(data)f.close()classPlainParseError(Exception):passdefsplitline(line,re_word=re.compile(r'[^\s"]\S*|["]["]|["].*?[^\\]["]')):result=[]forwordinre_word.findall(line):ifword.startswith('"'):word=eval(word)result.append(word)returnresultdefparse_plain(graph_id,plaincontent,links={},fixedfont=False):lines=plaincontent.splitlines(True)foriinrange(len(lines)-2,-1,-1):iflines[i].endswith('\\\n'):# line ending in '\'lines[i]=lines[i][:-2]+lines[i+1]dellines[i+1]header=splitline(lines.pop(0))ifheader[0]!='graph':raisePlainParseError("should start with 'graph'")yield(msgstruct.CMSG_START_GRAPH,graph_id)+tuple(header[1:])texts=[]forlineinlines:line=splitline(line)ifline[0]=='node':iflen(line)!=11:raisePlainParseError("bad 'node'")yield(msgstruct.CMSG_ADD_NODE,)+tuple(line[1:])texts.append(line[6])ifline[0]=='edge':yield(msgstruct.CMSG_ADD_EDGE,)+tuple(line[1:])i=4+2*int(line[3])iflen(line)>i+2:texts.append(line[i])ifline[0]=='stop':breakiflinks:# only include the links that really appear in the graphseen={}fortextintexts:forwordinre_nonword.split(text):ifwordandwordinlinksandwordnotinseen:t=links[word]ifisinstance(t,tuple):statusbartext,color=telse:statusbartext=tcolor=NoneifcolorisnotNone:yield(msgstruct.CMSG_ADD_LINK,word,statusbartext,color[0],color[1],color[2])else:yield(msgstruct.CMSG_ADD_LINK,word,statusbartext)seen[word]=Trueiffixedfont:yield(msgstruct.CMSG_FIXED_FONT,)yield(msgstruct.CMSG_STOP_GRAPH,)defparse_dot(graph_id,content,links={},fixedfont=False):contenttype=guess_type(content)ifcontenttype=='plain':plaincontent=contentelse:try:plaincontent=dot2plain_graphviz(content,contenttype)exceptPlainParseError,e:printe# failed, retry via codespeakplaincontent=dot2plain_codespeak(content,contenttype)returnlist(parse_plain(graph_id,plaincontent,links,fixedfont))