Ticket #3542: graphvizplugin-misc-fixes-r4341.patch

File graphvizplugin-misc-fixes-r4341.patch, 9.7 kB (added by cboos, 3 months ago)

cumulative patch for graphviz 0.11, on top of r4341

  • graphviz/graphviz.py

    old new  
    1616__version__   = '0.7.2' 
    1717 
    1818 
    19 try: 
    20     from cStringIO import StringIO 
    21 except ImportError: 
    22     from StringIO import StringIO 
     19from StringIO import StringIO 
     20import locale 
    2321import sha 
    2422import os 
    2523import sys 
     
    2725import inspect 
    2826import subprocess 
    2927 
     28from trac.config import Option 
    3029from trac.core import * 
    3130from trac.wiki.api import IWikiMacroProvider 
    3231from trac.mimeview.api import IHTMLPreviewRenderer, MIME_MAP 
    3332from trac.util import escape 
     33from trac.util.text import to_unicode 
    3434from trac.wiki.formatter import wiki_to_oneliner 
    3535from trac.web.api import IRequestHandler 
    3636 
     
    4747    """ 
    4848    implements(IWikiMacroProvider, IHTMLPreviewRenderer, IRequestHandler) 
    4949 
     50    cache_dir_option = Option("graphviz", "cache_dir", "gvcache", 
     51            """The directory that will be used to cache the generated images 
     52            (note that the directory must exist). 
     53            If not given as an absolute path, the path will be relative to  
     54            the Trac environment's directory. 
     55            """) 
     56 
     57    encoding = Option("graphviz", "encoding", 'utf-8', 
     58            """The encoding which should be used for communicating with 
     59            Graphviz. 
     60            """) 
     61 
    5062    # Available formats and processors, default first (dot/png) 
    5163    Processors = ['dot', 'neato', 'twopi', 'circo', 'fdp'] 
    5264    Bitmap_Formats = ['png', 'jpg', 'gif'] 
     
    176188            buf.write('<p>Graphviz macro processor error: requested format (%s) not valid.</p>' % self.out_format) 
    177189            return buf.getvalue() 
    178190 
    179         encoding = 'utf-8' 
    180         if type(content) == type(u''): 
    181             content  = content.encode(encoding) 
    182             sha_text = self.processor.encode(encoding) + self.processor_options.encode(encoding) + content 
    183  
    184         else: 
    185             sha_text = self.processor + self.processor_options + content 
    186  
     191        sha_text = self.processor + unicode(self.processor_options) + content 
    187192        sha_key  = sha.new(sha_text).hexdigest() 
    188193        img_name = '%s.%s.%s' % (sha_key, self.processor, self.out_format) # cache: hash.<dot>.<png> 
    189194        img_path = os.path.join(self.cache_dir, img_name) 
     
    200205            #self.log.debug('render_macro.URL_in_graph: %s' % str(URL_in_graph)) 
    201206            if URL_in_graph: # translate wiki TracLinks in URL 
    202207                #self.log.debug('content: %s' % content) 
    203                 content = re.sub(r'URL="(.*?)"', self.expand_wiki_links, unicode(content, 'utf-8')
     208                content = self.expand_wiki_links(content
    204209 
    205  
    206210            # Antialias PNGs with rsvg, if requested 
    207211            if self.out_format == 'png' and self.png_anti_alias == True: 
    208212                # 1. SVG output 
    209                 cmd = [proc_cmd, self.processor_options, '-Tsvg', '-o%s.svg' % img_path] 
     213                cmd = [proc_cmd] + self.processor_options + \ 
     214                        ['-Tsvg', '-o%s.svg' % img_path] 
    210215                #self.log.debug('render_macro: svg output - running command %s' % cmd) 
    211216                out, err = self.launch(cmd, content) 
    212217                if len(out) or len(err): 
     
    222227                    return self.show_err(msg).getvalue() 
    223228             
    224229            else: # Render other image formats 
    225                 cmd = [proc_cmd, self.processor_options, '-T%s' % self.out_format, '-o%s' % img_path] 
     230                cmd = [proc_cmd] + self.processor_options + \ 
     231                        ['-T%s' % self.out_format, '-o%s' % img_path] 
    226232                #self.log.debug('render_macro: render other image formats - running command %s' % cmd) 
    227233                out, err = self.launch(cmd, content) 
    228234                if len(out) or len(err): 
     
    234240 
    235241                # Create the map if not in cache 
    236242                if not os.path.exists(map_path): 
    237                     cmd = [proc_cmd, self.processor_options, '-Tcmap', '-o%s' % map_path] 
     243                    cmd = [proc_cmd] + self.processor_options + \ 
     244                            ['-Tcmap', '-o%s' % map_path] 
    238245                    #self.log.debug('render_macro: create map if not in cache - running command %s' % cmd) 
    239246                    out, err = self.launch(cmd, content) 
    240247                    if len(out) or len(err): 
     
    269276            buf.write('<object data="%s/graphviz/%s" type="image/svg+xml" %s><embed src="%s/graphviz/%s" type="image/svg+xml" %s></embed></object>' % (req.base_url, img_name, dimensions, req.base_url, img_name, dimensions)) 
    270277 
    271278        # for binary formats, add map 
    272         elif URL_in_graph
     279        elif URL_in_graph and os.path.exists(map_path)
    273280            f = open(map_path, 'r') 
    274281            map = f.readlines() 
    275282            f.close() 
     
    284291        return buf.getvalue() 
    285292 
    286293 
    287     def expand_wiki_links(self, match): 
    288         wiki_url = match.groups()[0]                     # TracLink ([1], source:file/, ...) 
     294    def expand_wiki_links(self, content): 
     295        """Expand TracLinks that follow all URL= patterns.""" 
     296        return re.sub(r'URL="(.*?)"', self._expand_wiki_links, content) 
     297 
     298    def _expand_wiki_links(self, match): 
     299        wiki_url = match.groups()[0] # TracLink ([1], source:file/, ...) 
    289300        #self.log.debug('wiki_url: %s' % wiki_url) 
    290301        #self.log.debug('self.env: %s' % str(self.env)) 
    291302        #self.log.debug('self.formatter.req: %s' % str(self.formatter.req)) 
     
    313324            return (True, self.show_err(msg)) 
    314325 
    315326        # check for the cache_dir entry 
    316         self.cache_dir = self.config.get('graphviz', 'cache_dir') 
     327        self.cache_dir = self.cache_dir_option 
    317328        if not self.cache_dir: 
    318329            msg = 'The [graphviz] section is missing the cache_dir field.' 
    319330            return True, self.show_err(msg) 
    320331 
     332        if not os.path.isabs(self.cache_dir): 
     333            self.cache_dir = os.path.join(self.env.path, self.cache_dir) 
     334 
    321335        if not os.path.exists(self.cache_dir): 
    322             msg = 'The cache_dir is set to "%s" but that path does not exist.' % self.cache_dir 
     336            msg = "The cache_dir '%s' doesn't exist, please create it." % \ 
     337                    self.cache_dir 
    323338            return True, self.show_err(msg) 
    324339        #self.log.debug('self.cache_dir: %s' % self.cache_dir) 
    325340 
     
    379394            #self.log.debug('self.rsvg_path: %s' % self.rsvg_path) 
    380395 
    381396        # get default graph/node/edge attributes 
    382         self.processor_options = '' 
    383         default_attributes = [ o for o in self.config.options('graphviz') if o[0].startswith('default_') ] 
    384         if default_attributes: 
    385            graph_attributes   = [ o for o in default_attributes if o[0].startswith('default_graph_') ] 
    386            node_attributes    = [ o for o in default_attributes if o[0].startswith('default_node_') ] 
    387            edge_attributes    = [ o for o in default_attributes if o[0].startswith('default_edge_') ] 
    388            if graph_attributes: 
    389                self.processor_options += " ".join([ "-G" + o[0].replace('default_graph_', '') + "=" + o[1] for o in graph_attributes]) + " " 
    390            if node_attributes: 
    391                self.processor_options += " ".join([ "-N" + o[0].replace('default_node_', '') + "=" + o[1] for o in node_attributes]) + " " 
    392            if edge_attributes: 
    393                self.processor_options += " ".join([ "-E" + o[0].replace('default_edge_', '') + "=" + o[1] for o in edge_attributes]) 
     397        self.processor_options = [] 
     398        defaults = [opt for opt in self.config.options('graphviz')  
     399                    if opt[0].startswith('default_')] 
     400        for name, value in defaults: 
     401            for prefix, optkey in [ 
     402                    ('default_graph_', '-G'),  
     403                    ('default_node_', '-N'), 
     404                    ('default_edge_', '-E')]: 
     405                if name.startswith(prefix): 
     406                    self.processor_options.append("%s%s=%s" %  
     407                            (optkey, name.replace(prefix,''), value)) 
    394408 
    395  
    396409        # check if we should run the cache manager 
    397410        self.cache_manager = self.boolean(self.config.get('graphviz', 'cache_manager', False)) 
    398411        if self.cache_manager: 
     
    421434 
    422435    def launch(self, cmd, input): 
    423436        """Launch a process (cmd), and returns exitcode, stdout + stderr""" 
    424         p = subprocess.Popen(cmd, 
     437        # Note: subprocess.Popen doesn't support unicode options arguments 
     438        # (http://bugs.python.org/issue1759845) so we have to encode them. 
     439        # We use the same encoding as the one sys.argv is supposed to have, see  
     440        # http://mail.python.org/pipermail/python-list/2006-October/410404.html 
     441        cmdline_encoding = locale.getpreferredencoding() 
     442        encoded_cmd = [] 
     443        for arg in cmd: 
     444            if isinstance(arg, unicode): 
     445                arg = arg.encode(cmdline_encoding, 'replace') 
     446            encoded_cmd.append(arg) 
     447        p = subprocess.Popen(encoded_cmd, 
    425448                             stdin=subprocess.PIPE, 
    426449                             stdout=subprocess.PIPE, 
    427450                             stderr=subprocess.PIPE) 
    428451 
    429452        if input: 
    430             p.stdin.write(input
     453            p.stdin.write(input.encode(self.encoding)
    431454        p.stdin.close() 
    432455        out = p.stdout.read() 
    433456        err = p.stderr.read() 
     
    440463        buf.write('<div id="content" class="error"><div class="message"> \n\ 
    441464                   <strong>Graphviz macro processor has detected an error. Please fix the problem before continuing.</strong> \n\ 
    442465                   <pre>%s</pre> \n\ 
    443                    </div></div>' % escape(msg)) 
     466                   </div></div>' % escape(to_unicode(msg))) 
    444467        self.log.error(msg) 
    445468        return buf 
    446469