Changeset 4398
- Timestamp:
- 10/06/08 12:16:37 (2 months ago)
- Files:
-
- graphvizplugin/0.11/graphviz/graphviz.py (modified) (19 diffs)
- graphvizplugin/0.11/README.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
graphvizplugin/0.11/graphviz/graphviz.py
r4397 r4398 26 26 import subprocess 27 27 28 from trac.config import Option28 from trac.config import BoolOption, IntOption, Option 29 29 from trac.core import * 30 30 from trac.wiki.api import IWikiMacroProvider … … 32 32 from trac.util import escape 33 33 from trac.util.text import to_unicode 34 from trac.util.translation import _ 34 35 from trac.wiki.formatter import wiki_to_oneliner 35 36 from trac.web.api import IRequestHandler 36 37 38 _TRUE_VALUES = ('yes', 'true', 'on', 'aye', '1', 1, True)39 40 37 41 38 … … 47 44 """ 48 45 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 images52 (note that the directory must exist).53 If not given as an absolute path, the path will be relative to54 the Trac environment's directory.55 """)56 57 encoding = Option("graphviz", "encoding", 'utf-8',58 """The encoding which should be used for communicating with59 Graphviz (should match -Gcharset if given).60 """)61 46 62 47 # Available formats and processors, default first (dot/png) … … 84 69 } 85 70 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 86 164 87 165 def __init__(self): … … 90 168 #self.log.info('formats: %s' % str(Graphviz.Formats)) 91 169 170 # IWikiMacroProvider methods 92 171 93 172 def get_macros(self): … … 138 217 content - The text the user entered for the macro to process. 139 218 """ 140 141 self.formatter = formatter142 self.env = formatter.env143 219 req = formatter.req 144 220 145 221 # 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) 149 225 150 226 buf = StringIO() 151 227 152 228 ## Extract processor and format from name 153 l_proc = l_out_format = ''229 processor = out_format = None 154 230 155 231 # first try with the RegExp engine 156 232 try: 157 233 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) 159 235 160 236 # or use the string.split method … … 164 240 s_sp = d_sp[1].split('/') 165 241 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] 168 244 elif len(s_sp) > 1: 169 l_out_format = s_sp[1]245 out_format = s_sp[1] 170 246 171 247 # 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] 178 255 179 256 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) 183 261 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)) \ 190 270 .encode(self.encoding) 191 271 encoded_content = content.encode(self.encoding) 192 272 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) 194 274 # cache: hash.<dot>.<png> 195 275 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) 197 277 # cache: hash.<dot>.map 198 278 map_path = os.path.join(self.cache_dir, map_name) … … 207 287 #self.log.debug('render_macro.URL_in_graph: %s' % str(URL_in_graph)) 208 288 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) 210 290 211 291 # 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: 213 293 # 1. SVG output 214 294 cmd = [proc_cmd] + self.processor_options + \ … … 228 308 else: # Render other image formats 229 309 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] 231 311 out, err = self.launch(cmd, encoded_content) 232 312 if len(out) or len(err): … … 235 315 236 316 # 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: 238 318 239 319 # Create the map if not in cache … … 249 329 # Generate HTML output 250 330 # for SVG(z) 251 if self.out_format in Graphviz.Vector_Formats:331 if out_format in Graphviz.Vector_Formats: 252 332 try: # try to get SVG dimensions 253 333 f = open(img_path, 'r') … … 287 367 288 368 289 def expand_wiki_links(self, content):369 def expand_wiki_links(self, formatter, out_format, content): 290 370 """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://someurl299 url = href and href.groups()[0] or html_url300 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 305 385 306 386 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 313 391 314 392 # check for the cache_dir entry 315 393 self.cache_dir = self.cache_dir_option 316 394 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.") 319 396 320 397 if not os.path.isabs(self.cache_dir): … … 322 399 323 400 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) 327 403 328 404 # Get optional configuration parameters from trac.ini. 329 405 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 334 407 cmd_paths = Graphviz.Cmd_Paths.get(sys.platform, []) 335 408 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_path340 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] 342 415 343 416 if not cmd_paths: 344 msg = 'The [graphviz] section is missing the cmd_path field and there is no default for %s.' % sys.platform345 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) 347 420 348 421 self.cmds = {} 349 422 pname = self._find_cmd(self.processor, cmd_paths) 350 423 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) 353 426 354 427 for name in Graphviz.Processors: … … 356 429 357 430 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)) 359 434 Graphviz.Processors.remove(name) 360 435 361 436 self.cmds[name] = pname 362 437 363 # check for the default output format - out_format364 self.out_format = self.config.get('graphviz', 'out_format',365 Graphviz.Formats[0])366 367 # check if png anti aliasing should be done - png_antialias368 self.png_anti_alias = self.config.getbool(369 'graphviz', 'png_antialias', False)370 371 438 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 375 442 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_path377 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) 378 445 379 446 # get default graph/node/edge attributes … … 390 457 (optkey, name.replace(prefix,''), value)) 391 458 392 # check if we should run the cache manager393 self.cache_manager = self.boolean(self.config.get('graphviz', 'cache_manager', False))394 if self.cache_manager:395 # use IntOption396 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 404 459 # setup mimetypes to support the IHTMLPreviewRenderer interface 405 460 if 'graphviz' not in MIME_MAP: … … 408 463 if processor not in MIME_MAP: 409 464 MIME_MAP[processor] = 'application/graphviz' 410 411 return False, buf412 465 413 466 … … 503 556 size = size - entry_list[file][6] 504 557 505 # Extra helper functions506 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_VALUES510 return bool(value)511 512 513 558 MIME_TYPES = ('application/graphviz') 514 559 … … 521 566 return 0 522 567 523 def render(self, req, mimetype, content, filename=None, url=None):568 def render(self, context, mimetype, content, filename=None, url=None): 524 569 ext = filename.split('.')[1] 525 570 name = ext == 'graphviz' and 'graphviz' or 'graphviz.%s' % ext 526 571 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) 528 573 529 574 … … 536 581 def process_request(self, req): 537 582 # 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) 541 586 542 587 pieces = [item for item in req.path_info.split('/graphviz') if item] graphvizplugin/0.11/README.txt
r1601 r4398 113 113 114 114 cache_dir - The directory that will be used to cache the 115 generated images. 115 generated images. That directory must exist. 116 116 117 117 cmd_path - Full path to the directory where the graphviz
