Changeset 4398

Show
Ignore:
Timestamp:
10/06/08 12:16:37 (2 months ago)
Author:
cboos
Message:

GraphvizPlugin: use Option, IntOption and BoolOption where relevant.

This also fixes the potential problem where the local, block parameters, for the processor and output format were assigned to the Graphviz component itself (which is a singleton per environment per process).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • graphvizplugin/0.11/graphviz/graphviz.py

    r4397 r4398  
    2626import subprocess 
    2727 
    28 from trac.config import Option 
     28from trac.config import BoolOption, IntOption, Option 
    2929from trac.core import * 
    3030from trac.wiki.api import IWikiMacroProvider 
     
    3232from trac.util import escape 
    3333from trac.util.text import to_unicode 
     34from trac.util.translation import _ 
    3435from trac.wiki.formatter import wiki_to_oneliner 
    3536from trac.web.api import IRequestHandler 
    36  
    37  
    38 _TRUE_VALUES = ('yes', 'true', 'on', 'aye', '1', 1, True) 
    39  
    4037 
    4138 
     
    4744    """ 
    4845    implements(IWikiMacroProvider, IHTMLPreviewRenderer, IRequestHandler) 
    49  
    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 (should match -Gcharset if given). 
    60             """) 
    6146 
    6247    # Available formats and processors, default first (dot/png) 
     
    8469        } 
    8570 
     71    # Note: the following options named "..._option" are those which need 
     72    #       some additional processing, see `load_config()` below. 
     73 
     74 
     75    cache_dir_option = Option("graphviz", "cache_dir", "gvcache", 
     76            """The directory that will be used to cache the generated images 
     77            (note that the directory must exist). 
     78            If not given as an absolute path, the path will be relative to  
     79            the Trac environment's directory. 
     80            """) 
     81 
     82    encoding = Option("graphviz", "encoding", 'utf-8', 
     83            """The encoding which should be used for communicating with 
     84            Graphviz (should match -Gcharset if given). 
     85            """) 
     86 
     87    cmd_path = Option("graphviz", "cmd_path", '', 
     88            r"""Full path to the directory where the graphviz 
     89            programs are located. If not specified, the 
     90            default is /usr/bin on Linux, c:\Program 
     91            Files\ATT\Graphviz\bin on Windows and 
     92            /usr/local/bin on FreeBSD 6. 
     93            """) 
     94 
     95    out_format = Option("graphviz", "out_format", Formats[0], 
     96            """Graph output format. Valid formats are: png, jpg, 
     97            svg, svgz, gif. If not specified, the default is 
     98            png. This setting can be overrided on a per-graph 
     99            basis. 
     100            """) 
     101 
     102    processor = Option("graphviz", "processor", Processors[0], 
     103            """Graphviz default processor. Valid processors 
     104            are: dot, neato, twopi, fdp, circo. If not 
     105            specified, the default is dot. This setting can 
     106            be overrided on a per-graph basis. 
     107 
     108            GraphvizMacro will verify that the default 
     109            processor is installed and will not work if it 
     110            is missing. All other processors are optional. 
     111            If any of the other processors are missing, a 
     112            warning message will be sent to the trac log and 
     113            GraphvizMacro will continue to work. 
     114            """) 
     115 
     116    png_anti_alias = BoolOption("graphviz", "png_antialias", False, 
     117            """If this entry exists in the configuration file, 
     118            then PNG outputs will be antialiased. 
     119            Note that this requires `rsvg` to be installed. 
     120            """) 
     121 
     122    rsvg_path_option = Option("graphviz", "rsvg_path", "", 
     123            """Full path to where the rsvg binary can be found. 
     124            The default is cmd_path + rsvg. 
     125            """) 
     126 
     127    cache_manager = BoolOption("graphviz", "cache_manager", False, 
     128            """If this entry exists and set to true in the configuration file, 
     129            then the cache management logic will be invoked 
     130            and the cache_max_size, cache_min_size, 
     131            cache_max_count and cache_min_count must be 
     132            defined. 
     133            """) 
     134 
     135    cache_max_size = IntOption("graphviz", "cache_max_size", 1024*1024*10, 
     136            """The maximum size in bytes that the cache should 
     137            consume. This is the high watermark for disk space 
     138            used. 
     139            """) 
     140 
     141    cache_min_size = IntOption("graphviz", "cache_min_size", 1024*1024*5, 
     142            """When cleaning out the cache, remove files until 
     143            this size in bytes is used by the cache. This is 
     144            the low watermark for disk space used. 
     145            """) 
     146 
     147    cache_max_count = IntOption("graphviz", "cache_max_count", 2000, 
     148            """The maximum number of files that the cache should 
     149            contain. This is the high watermark for the 
     150            directory entry count. 
     151            """) 
     152 
     153    cache_min_count = IntOption("graphviz", "cache_minax_count", 1500, 
     154            """The maximum number of files that the cache should 
     155            contain. This is the high watermark for the 
     156            directory entry count. 
     157            """) 
     158 
     159    dpi = IntOption('graphviz', 'default_graph_dpi', 96, 
     160            """Default dpi setting for graphviz, used during SVG to PNG  
     161            rasterization. 
     162            """) 
     163 
    86164 
    87165    def __init__(self): 
     
    90168        #self.log.info('formats: %s' % str(Graphviz.Formats)) 
    91169 
     170    # IWikiMacroProvider methods 
    92171 
    93172    def get_macros(self): 
     
    138217        content - The text the user entered for the macro to process. 
    139218        """ 
    140  
    141         self.formatter = formatter 
    142         self.env = formatter.env 
    143219        req = formatter.req 
    144220 
    145221        # check and load the configuration 
    146         trouble, msg = self.load_config() 
    147         if trouble
    148             return msg.getvalue(
     222        errmsg = self.load_config() 
     223        if errmsg
     224            return self.show_err(errmsg
    149225 
    150226        buf = StringIO() 
    151227 
    152228        ## Extract processor and format from name 
    153         l_proc = l_out_format = '' 
     229        processor = out_format = None 
    154230 
    155231        # first try with the RegExp engine 
    156232        try:  
    157233            m = re.match('graphviz\.?([a-z]*)\/?([a-z]*)', name) 
    158             (l_proc, l_out_format) = m.group(1, 2) 
     234            (processor, out_format) = m.group(1, 2) 
    159235 
    160236        # or use the string.split method 
     
    164240                s_sp = d_sp[1].split('/') 
    165241                if len(s_sp) > 1: 
    166                     l_out_format = s_sp[1] 
    167                 l_proc = s_sp[0] 
     242                    out_format = s_sp[1] 
     243                processor = s_sp[0] 
    168244            elif len(s_sp) > 1: 
    169                 l_out_format = s_sp[1] 
     245                out_format = s_sp[1] 
    170246             
    171247        # assign default values, if instance ones are empty 
    172         self.out_format = (self.out_format, l_out_format)[bool(len(l_out_format))] 
    173         self.processor  = (self.processor,  l_proc)      [bool(len(l_proc))] 
    174  
    175  
    176         if self.processor in Graphviz.Processors: 
    177             proc_cmd = self.cmds[self.processor] 
     248        if not out_format: 
     249            out_format = self.out_format 
     250        if not processor: 
     251            processor = self.processor 
     252 
     253        if processor in Graphviz.Processors: 
     254            proc_cmd = self.cmds[processor] 
    178255 
    179256        else: 
    180             self.log.error('render_macro: requested processor (%s) not found.' % self.processor) 
    181             buf.write('<p>Graphviz macro processor error: requested processor (%s) not found.</p>' % self.processor) 
    182             return buf.getvalue() 
     257            self.log.error('render_macro: requested processor (%s) not found.' % 
     258                           processor) 
     259            return self.show_err('requested processor (%s) not found.' %  
     260                                 processor) 
    183261            
    184         if self.out_format not in Graphviz.Formats: 
    185             self.log.error('render_macro: requested format (%s) not found.' % self.out_format) 
    186             buf.write('<p>Graphviz macro processor error: requested format (%s) not valid.</p>' % self.out_format) 
    187             return buf.getvalue() 
    188  
    189         encoded_cmd = (self.processor + unicode(self.processor_options)) \ 
     262        if out_format not in Graphviz.Formats: 
     263            self.log.error('render_macro: requested format (%s) not found.' % 
     264                           out_format) 
     265            return self.show_err( 
     266                    '<p>Graphviz macro processor ' 
     267                    'error: requested format (%s) not valid.</p>' % out_format) 
     268 
     269        encoded_cmd = (processor + unicode(self.processor_options)) \ 
    190270                .encode(self.encoding) 
    191271        encoded_content = content.encode(self.encoding) 
    192272        sha_key  = sha.new(encoded_cmd + encoded_content).hexdigest() 
    193         img_name = '%s.%s.%s' % (sha_key, self.processor, self.out_format) 
     273        img_name = '%s.%s.%s' % (sha_key, processor, out_format) 
    194274        # cache: hash.<dot>.<png> 
    195275        img_path = os.path.join(self.cache_dir, img_name) 
    196         map_name = '%s.%s.map' % (sha_key, self.processor) 
     276        map_name = '%s.%s.map' % (sha_key, processor) 
    197277        # cache: hash.<dot>.map 
    198278        map_path = os.path.join(self.cache_dir, map_name) 
     
    207287            #self.log.debug('render_macro.URL_in_graph: %s' % str(URL_in_graph)) 
    208288            if URL_in_graph: # translate wiki TracLinks in URL 
    209                 content = self.expand_wiki_links(content) 
     289                content = self.expand_wiki_links(formatter, out_format, content) 
    210290 
    211291            # Antialias PNGs with rsvg, if requested 
    212             if self.out_format == 'png' and self.png_anti_alias == True: 
     292            if out_format == 'png' and self.png_anti_alias == True: 
    213293                # 1. SVG output 
    214294                cmd = [proc_cmd] + self.processor_options + \ 
     
    228308            else: # Render other image formats 
    229309                cmd = [proc_cmd] + self.processor_options + \ 
    230                         ['-T%s' % self.out_format, '-o%s' % img_path] 
     310                        ['-T%s' % out_format, '-o%s' % img_path] 
    231311                out, err = self.launch(cmd, encoded_content) 
    232312                if len(out) or len(err): 
     
    235315 
    236316            # Generate a map file for binary formats 
    237             if URL_in_graph and self.out_format in Graphviz.Bitmap_Formats: 
     317            if URL_in_graph and out_format in Graphviz.Bitmap_Formats: 
    238318 
    239319                # Create the map if not in cache 
     
    249329        # Generate HTML output 
    250330        # for SVG(z) 
    251         if self.out_format in Graphviz.Vector_Formats: 
     331        if out_format in Graphviz.Vector_Formats: 
    252332            try: # try to get SVG dimensions 
    253333                f = open(img_path, 'r') 
     
    287367 
    288368 
    289     def expand_wiki_links(self, content): 
     369    def expand_wiki_links(self, formatter, out_format, content): 
    290370        """Expand TracLinks that follow all URL= patterns.""" 
    291         return re.sub(r'URL="(.*?)"', self._expand_wiki_links, content) 
    292  
    293     def _expand_wiki_links(self, match): 
    294         wiki_url = match.groups()[0] # TracLink ([1], source:file/, ...) 
    295         html_url = wiki_to_oneliner(wiki_url, self.env, req=self.formatter.req) 
    296         # <a href="http://someurl">...</a> 
    297  
    298         href     = re.search('href="(.*?)"', html_url)   # http://someurl 
    299         url      = href and href.groups()[0] or html_url 
    300         if self.out_format == 'svg'
    301             format = 'URL="javascript:window.parent.location.href=\'%s\'"' 
    302         else: 
    303             format = 'URL="%s"' 
    304         return format % url 
     371        def expand(match): 
     372            wiki_url = match.groups()[0] # TracLink ([1], source:file/, ...) 
     373            html_url = wiki_to_oneliner(wiki_url, self.env, req=formatter.req) 
     374            # <a href="http://someurl">...</a> 
     375 
     376            href     = re.search('href="(.*?)"', html_url)   # http://someurl 
     377            url      = href and href.groups()[0] or html_url 
     378            if out_format == 'svg': 
     379                format = 'URL="javascript:window.parent.location.href=\'%s\'"' 
     380            else
     381                format = 'URL="%s"' 
     382            return format % url 
     383        return re.sub(r'URL="(.*?)"', expand, content) 
     384 
    305385 
    306386    def load_config(self): 
    307         """Load the graphviz trac.ini configuration into object instance variables.""" 
    308         buf = StringIO() 
    309  
    310         if 'graphviz' not in self.config.sections(): 
    311             msg = 'The [graphviz] section was not found in the trac configuration file.' 
    312             return (True, self.show_err(msg)) 
     387        """Preprocess the graphviz trac.ini configuration.""" 
     388 
     389        # if 'graphviz' not in self.config.sections(): 
     390        # ... so what? the defaults might be good enough 
    313391 
    314392        # check for the cache_dir entry 
    315393        self.cache_dir = self.cache_dir_option 
    316394        if not self.cache_dir: 
    317             msg = 'The [graphviz] section is missing the cache_dir field.' 
    318             return True, self.show_err(msg) 
     395            return _("The [graphviz] section is missing the cache_dir field.") 
    319396 
    320397        if not os.path.isabs(self.cache_dir): 
     
    322399 
    323400        if not os.path.exists(self.cache_dir): 
    324             msg = "The cache_dir '%s' doesn't exist, please create it." % \ 
    325                     self.cache_dir 
    326             return True, self.show_err(msg) 
     401            return _("The cache_dir '%(path)s' doesn't exist, " 
     402                    "please create it.", path=self.cache_dir) 
    327403 
    328404        # Get optional configuration parameters from trac.ini. 
    329405 
    330         # check for the default processor - processor 
    331         self.processor = self.config.get('graphviz', 'processor', Graphviz.Processors[0]) 
    332  
    333         # check for the cmd_path entry and setup the various program command paths 
     406        # check for the cmd_path entry and setup the various command paths 
    334407        cmd_paths = Graphviz.Cmd_Paths.get(sys.platform, []) 
    335408 
    336         cfg_path = self.config.get('graphviz', 'cmd_path') 
    337         if cfg_path
    338             if not os.path.exists(cfg_path): 
    339                 msg = 'The cmd_path is set to "%s" but that path does not exist.' % cfg_path 
    340                 return True, self.show_err(msg
    341             cmd_paths = [cfg_path] 
     409        if self.cmd_path: 
     410            if not os.path.exists(self.cmd_path)
     411                return _("The '[graphviz] cmd_path' configuration entry " 
     412                         "is set to '%(path)s' but that path does not exist.",  
     413                         path=self.cmd_path
     414            cmd_paths = [self.cmd_path] 
    342415 
    343416        if not cmd_paths: 
    344             msg = 'The [graphviz] section is missing the cmd_path field and there is no default for %s.' % sys.platform 
    345             return True, self.show_err(msg) 
    346  
     417            return _("The '[graphviz] cmd_path' configuration entry " 
     418                     "is not set and there is no default for %(platform)s.", 
     419                     platform=sys.platform) 
    347420 
    348421        self.cmds = {} 
    349422        pname = self._find_cmd(self.processor, cmd_paths) 
    350423        if not pname: 
    351             msg = 'The default processor, %s, was not found in %s.' % (self.processor, str(cmd_paths)) 
    352             return True, self.show_err(msg
     424            return _("The default processor '%(proc)s' was not found " 
     425                     "in '%(paths)s'.", proc=self.processor, paths=cmd_paths
    353426 
    354427        for name in Graphviz.Processors: 
     
    356429 
    357430            if not pname: 
    358                 self.log.warn('The %s program was not found. The graphviz/%s macro will be disabled.' % (pname, name)) 
     431                self.log.warn('The %s program was not found. ' 
     432                              'The graphviz/%s macro will be disabled.' % 
     433                              (pname, name)) 
    359434                Graphviz.Processors.remove(name) 
    360435 
    361436            self.cmds[name] = pname 
    362437 
    363         # check for the default output format - out_format 
    364         self.out_format = self.config.get('graphviz', 'out_format',  
    365                                           Graphviz.Formats[0]) 
    366  
    367         # check if png anti aliasing should be done - png_antialias 
    368         self.png_anti_alias = self.config.getbool( 
    369                 'graphviz', 'png_antialias', False) 
    370  
    371438        if self.png_anti_alias: 
    372             self.rsvg_path = self.config.get('graphviz', 'rsvg_path', 
    373                                              self._find_cmd('rsvg', cmd_paths)
    374  
     439            self.rsvg_path = self.rsvg_path_option or \ 
     440                    self._find_cmd('rsvg', cmd_paths
     441             
    375442            if not os.path.exists(self.rsvg_path): 
    376                 err = 'The rsvg program is set to "%s" but that path does not exist.' % self.rsvg_path 
    377                 return True, self.show_err(err
     443                return _("The rsvg program is set to '%(path)s' but that path " 
     444                         "does not exist.", path=self.rsvg_path
    378445 
    379446        # get default graph/node/edge attributes 
     
    390457                            (optkey, name.replace(prefix,''), value)) 
    391458 
    392         # check if we should run the cache manager 
    393         self.cache_manager = self.boolean(self.config.get('graphviz', 'cache_manager', False)) 
    394         if self.cache_manager: 
    395             # use IntOption 
    396             self.cache_max_size  = int(self.config.get('graphviz', 'cache_max_size',  10000000)) 
    397             self.cache_min_size  = int(self.config.get('graphviz', 'cache_min_size',  5000000)) 
    398             self.cache_max_count = int(self.config.get('graphviz', 'cache_max_count', 2000)) 
    399             self.cache_min_count = int(self.config.get('graphviz', 'cache_min_count', 1500)) 
    400  
    401         # is there a graphviz default DPI setting? 
    402         self.dpi = int(self.config.get('graphviz', 'default_graph_dpi', 96)) 
    403  
    404459        # setup mimetypes to support the IHTMLPreviewRenderer interface 
    405460        if 'graphviz' not in MIME_MAP: 
     
    408463            if processor not in MIME_MAP: 
    409464                MIME_MAP[processor] = 'application/graphviz' 
    410  
    411         return False, buf 
    412465 
    413466 
     
    503556                        size = size - entry_list[file][6] 
    504557 
    505     # Extra helper functions 
    506     def boolean(self, value): 
    507         # This code is almost directly from trac.config in the 0.10 line... 
    508         if isinstance(value, basestring): 
    509             value = value.lower() in _TRUE_VALUES 
    510         return bool(value) 
    511  
    512  
    513558    MIME_TYPES = ('application/graphviz') 
    514559 
     
    521566        return 0 
    522567 
    523     def render(self, req, mimetype, content, filename=None, url=None): 
     568    def render(self, context, mimetype, content, filename=None, url=None): 
    524569        ext = filename.split('.')[1] 
    525570        name = ext == 'graphviz' and 'graphviz' or 'graphviz.%s' % ext 
    526571        text = hasattr(content, 'read') and content.read() or content 
    527         return self.render_macro(req, name, text) 
     572        return self.render_macro(context, name, text) 
    528573 
    529574 
     
    536581    def process_request(self, req): 
    537582        # check and load the configuration 
    538         trouble, msg = self.load_config() 
    539         if trouble
    540             return msg.getvalue(
     583        errmsg = self.load_config() 
     584        if errmsg
     585            return self.show_err(errmsg
    541586 
    542587        pieces = [item for item in req.path_info.split('/graphviz') if item] 
  • graphvizplugin/0.11/README.txt

    r1601 r4398  
    113113 
    114114    cache_dir       - The directory that will be used to cache the  
    115                       generated images. 
     115                      generated images. That directory must exist. 
    116116 
    117117    cmd_path        - Full path to the directory where the graphviz