Attachment 'umlsequence.py'
Download 1 #format python
2
3 """
4 UML sequence diagram parser for MoinMoin.
5
6
7 Copyright (C) 2007 by Pascal Bauermeister
8
9 This module is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 This parser passes its input to umlgraph. There is also a more "visual"
15 alternate syntax.
16
17 -- #---------------------------------------------------------------------------
18
19 Usage:
20 {{{#!umlsequence [debug=1|2] [help=1] [percent=N]
21 PROCEDURAL_STATMENTS or ALTERNATE_SYNTAX
22 }}}
23
24 Procedural syntax (a.k.a. Spinellis syntax) see:
25 http://www.spinellis.gr/sw/umlgraph/doc/index.html
26 http://www.spinellis.gr/sw/umlgraph/doc/seq-ops.html
27
28 Examples:
29 http://www.spinellis.gr/sw/umlgraph/doc/uml-appa.html (then click next)
30
31 Alternate ("easy") syntax:
32 See http://moinmoin.wikiwikiweb.de/UmlSequence
33
34 See also:
35 Umlgraph: http://www.spinellis.gr/sw/umlgraph/
36
37 -- #---------------------------------------------------------------------------
38
39 ChangeLog:
40
41 Pascal Bauermeister <pascal DOT bauermeister AT gmail DOT com> 2007-05-13:
42 * Initial version
43
44 -- #---------------------------------------------------------------------------
45 """
46
47
48 # Parser's name
49 NAME = __name__.split(".")[-1]
50
51 Dependencies = []
52
53 import os, re, sha
54 import StringIO, string
55 from MoinMoin.action import AttachFile
56 from MoinMoin.Page import Page
57 from MoinMoin import wikiutil, config
58
59 def get_sequence_pic ():
60 return """
61 #/usr/bin/pic2plot -Tps
62 #
63 # Pic macros for drawing UML sequence diagrams
64 #
65 # (C) Copyright 2004-2005 Diomidis Spinellis.
66 #
67 # Permission to use, copy, and distribute this software and its
68 # documentation for any purpose and without fee is hereby granted,
69 # provided that the above copyright notice appear in all copies and that
70 # both that copyright notice and this permission notice appear in
71 # supporting documentation.
72 #
73 # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
74 # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
75 # MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
76 #
77 # $Id: sequence.pic,v 1.10 2005/10/19 18:36:08 dds Exp $
78 #
79
80
81 # Default parameters (can be redefined)
82
83 # Spacing between messages
84 spacing = 0.25;
85 # Active box width
86 awid = .1;
87 # Box height
88 boxht = 0.3;
89 # Commend folding
90 corner_fold=awid
91 # Comment distance
92 define comment_default_move {up 0.25 right 0.25};
93 # Comment height
94 comment_default_ht=0.5;
95 # Comment width
96 comment_default_wid=1;
97
98
99 # Create a new object(name,label)
100 define object {
101 $1: box $2; move;
102 # Could also underline text with \\mk\\ul\\ul\\ul...\\rt
103 {
104 line from $1.w + (.1, -.07) to $1.e + (-.1, -.07);
105 }
106 move to $1.e;
107 move right;
108 # Active is the level of activations of the object
109 # 0 : inactive : draw thin line swimlane
110 # 1 : active : draw thick swimlane
111 # > 1: nested : draw nested swimlane
112 active_$1 = 0;
113 lifestart_$1 = $1.s.y;
114 }
115
116 # Create a new external actor(name,label)
117 define actor {
118 $1: [
119 XSEQC: circle rad 0.06;
120 XSEQL: line from XSEQC.s down .12;
121 line from XSEQL.start - (.15,.02) to XSEQL.start + (.15,-.02);
122 XSEQL1: line from XSEQL.end left .08 down .15;
123 XSEQL2: line from XSEQL.end right .08 down .15;
124 line at XSEQC.n invis "" "" "" $2;
125 ]
126 move to $1.e;
127 move right;
128 active_$1 = 0;
129 lifestart_$1 = $1.s.y - .05;
130 }
131
132 # Create a new placeholder object(name)
133 define placeholder_object {
134 $1: box invisible;
135 move;
136 move to $1.e;
137 move right;
138 active_$1 = 0;
139 lifestart_$1 = $1.s.y;
140 }
141
142 define pobject {
143 placeholder_object($1);
144 }
145
146 define extend_lifeline {
147 if (active_$1 > 0) then {
148 # draw the left edges of the boxes
149 move to ($1.x - awid/2, Here.y);
150 for level = 1 to active_$1 do {
151 line from (Here.x, lifestart_$1) to Here;
152 move right awid/2
153 }
154
155 # draw the right edge of the innermost box
156 move right awid/2;
157 line from (Here.x, lifestart_$1) to Here;
158 } else {
159 line from ($1.x, lifestart_$1) to ($1.x, Here.y) dashed;
160 }
161 lifestart_$1 = Here.y;
162 }
163
164 # complete(name)
165 # Complete the lifeline of the object with the given name
166 define complete {
167 extend_lifeline($1)
168 if (active_$1) then {
169 # draw bottom of all active boxes
170 line right ((active_$1 + 1) * awid/2) from ($1.x - awid/2, Here.y);
171 }
172 }
173
174 # Draw a message(from_object,to_object,label)
175 define message {
176 down;
177 move spacing;
178 # Adjust so that lines and arrows do not fall into the
179 # active box. Should be .5, but the arrow heads tend to
180 # overshoot.
181 if ($1.x <= $2.x) then {
182 off_from = awid * .6;
183 off_to = -awid * .6;
184 } else {
185 off_from = -awid * .6;
186 off_to = awid * .6;
187 }
188
189 # add half a box width for each level of nesting
190 if (active_$1 > 1) then {
191 off_from = off_from + (active_$1 - 1) * awid/2;
192 }
193
194 # add half a box width for each level of nesting
195 if (active_$2 > 1) then {
196 off_to = off_to + (active_$2 - 1) * awid/2;
197 }
198
199 if ($1.x == $2.x) then {
200 arrow from ($1.x + off_from, Here.y) right then down .25 then left $3 ljust " " " " " " ;
201 } else {
202 arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) $3 " ";
203 }
204 }
205
206 # Display a lifeline constraint(object,label)
207 define lifeline_constraint {
208 off_from = awid;
209 # add half a box width for each level of nesting
210 if (active_$1 > 1) then {
211 off_from = off_from + (active_$1 - 1) * awid/2;
212 }
213
214 box at ($1.x + off_from, Here.y) invis $2 ljust " " ;
215 }
216
217 define lconstraint {
218 lifeline_constraint($1,$2);
219 }
220
221 # Display an object constraint(label)
222 # for the last object drawn
223 define object_constraint {
224 { box invis with .s at last box .nw $1 ljust; }
225 }
226
227 define oconstraint {
228 object_constraint($1);
229 }
230
231 # Draw a creation message(from_object,to_object,object_label)
232 define create_message {
233 down;
234 move spacing;
235 if ($1.x <= $2.x) then {
236 off_from = awid * .6;
237 off_to = -boxwid * .51;
238 } else {
239 off_from = -awid * .6;
240 off_to = boxwid * .51;
241 }
242
243 # add half a box width for each level of nesting
244 if (active_$1 > 1) then {
245 off_from = off_from + (active_$1 - 1) * awid/2;
246 }
247
248 # See comment in destroy_message
249 XSEQA: arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) "«create»" " ";
250 if ($1.x <= $2.x) then {
251 { XSEQB: box $3 with .w at XSEQA.end; }
252 } else {
253 { XSEQB: box $3 with .e at XSEQA.end; }
254 }
255 {
256 line from XSEQB.w + (.1, -.07) to XSEQB.e + (-.1, -.07);
257 }
258 lifestart_$2 = XSEQB.s.y;
259 move (spacing + boxht) / 2;
260 }
261
262 define cmessage {
263 create_message($1,$2,$3);
264 }
265
266 # Draw an X for a given object
267 define drawx {
268 {
269 line from($1.x - awid, lifestart_$1 - awid) to ($1.x + awid, lifestart_$1 + awid);
270 line from($1.x - awid, lifestart_$1 + awid) to ($1.x + awid, lifestart_$1 - awid);
271 }
272 }
273
274 # Draw a destroy message(from_object,to_object)
275 define destroy_message {
276 down;
277 move spacing;
278 # The troff code is \\(Fo \\(Fc
279 # The groff code is also \\[Fo] \\[Fc]
280 # The pic2plot code is \\Fo \\Fc
281 # See http://www.delorie.com/gnu/docs/plotutils/plotutils_71.html
282 # To stay compatible with all we have to hardcode the characters
283 message($1,$2,"«destroy»");
284 complete($2);
285 drawx($2);
286 }
287
288 define dmessage {
289 destroy_message($1,$2);
290 }
291
292 # An object deletes itself: delete(object)
293 define delete {
294 complete($1);
295 lifestart_$1 = lifestart_$1 - awid;
296 drawx($1);
297 }
298
299 # Draw a message return(from_object,to_object,label)
300 define return_message {
301 down;
302 move spacing;
303 # See comment in message
304 if ($1.x <= $2.x) then {
305 off_from = awid * .6;
306 off_to = -awid * .6;
307 } else {
308 off_from = -awid * .6;
309 off_to = awid * .6;
310 }
311
312 # add half a box width for each level of nesting
313 if (active_$1 > 1) then {
314 off_from = off_from + (active_$1 - 1) * awid/2;
315 }
316
317 # add half a box width for each level of nesting
318 if (active_$2 > 1) then {
319 off_to = off_to + (active_$2 - 1) * awid/2;
320 }
321
322 arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) dashed $3 " ";
323 }
324
325 define rmessage {
326 return_message($1,$2,$3);
327 }
328
329 # Object becomes active
330 # Can be nested to show recursion
331 define active {
332 extend_lifeline($1);
333 # draw top of new active box
334 line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
335 active_$1 = active_$1 + 1;
336 }
337
338 # Object becomes inactive
339 # Can be nested to show recursion
340 define inactive {
341 extend_lifeline($1);
342 active_$1 = active_$1 - 1;
343 # draw bottom of innermost active box
344 line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
345 }
346
347 # Time step
348 # Useful at the beginning and the end
349 # to show object states
350 define step {
351 down;
352 move spacing;
353 }
354
355 # Switch to asynchronous messages
356 define async {
357 arrowhead = 0;
358 arrowwid = arrowwid * 2;
359 }
360
361 # Switch to synchronous messages
362 define sync {
363 arrowhead = 1;
364 arrowwid = arrowwid / 2;
365 }
366
367 # same as lifeline_constraint, but Text and empty string are exchanged.
368 define lconstraint_below{
369 off_from = awid;
370 # add half a box width for each level of nesting
371 if (active_$1 > 1) then {
372 off_from = off_from + (active_$1 - 1) * awid/2;
373 }
374
375 box at ($1.x + off_from, Here.y) invis "" $2 ljust;
376 }
377
378 # begin_frame(left_object,name,label_text);
379 define begin_frame {
380 # The lifeline will be cut here
381 extend_lifeline($1);
382 # draw the frame-label
383 $2: box $3 invis with .n at ($1.x, Here.y);
384 d = $2.e.y - $2.se.y;
385 line from $2.ne to $2.e then down d left d then to $2.sw;
386 # continue the lifeline below the frame-label
387 move to $2.s;
388 lifestart_$1 = Here.y;
389 }
390
391 # end_frame(right_object,name);
392 define end_frame {
393 # dummy-box for the lower right corner:
394 box invis "" with .s at ($1.x, Here.y);
395 # draw the frame
396 frame_wid = last box.se.x - $2.nw.x
397 frame_ht = - last box.se.y + $2.nw.y
398 box with .nw at $2.nw wid frame_wid ht frame_ht;
399 # restore Here.y
400 move to last box.s;
401 }
402
403 # comment(object,[name],[line_movement], [box_size] text);
404 define comment {
405 old_y = Here.y
406 # draw the first connecting line, at which's end the box wil be positioned
407 move to ($1.x, Here.y)
408 if "$3" == "" then {
409 line comment_default_move() dashed;
410 } else {
411 line $3 dashed;
412 }
413
414 # draw the box, use comment_default_xx if no explicit
415 # size is given together with the text in parameter 4
416 old_boxht=boxht;
417 old_boxwid=boxwid;
418 boxht=comment_default_ht;
419 boxwid=comment_default_wid;
420 if "$2" == "" then {
421 box invis $4;
422 } else {
423 $2: box invis $4;
424 }
425 boxht=old_boxht;
426 boxwid=old_boxwid;
427
428 # draw the frame of the comment
429 line from last box.nw \\
430 to last box.ne - (corner_fold, 0) \\
431 then to last box.ne - (0, corner_fold) \\
432 then to last box.se \\
433 then to last box.sw \\
434 then to last box.nw ;
435 line from last box.ne - (corner_fold, 0) \\
436 to last box.ne - (corner_fold, corner_fold) \\
437 then to last box.ne - (0, corner_fold) ;
438
439 # restore Here.y
440 move to ($1.x, old_y)
441 }
442
443 # connect_to_comment(object,name);
444 define connect_to_comment {
445 old_y = Here.y
446 # start at the object
447 move to ($1.x, Here.y)
448 # find the best connection-point of the comment to use as line-end
449 if $1.x < $2.w.x then {
450 line to $2.w dashed;
451 } else {
452 if $1.x > $2.e.x then {
453 line to $2.e dashed;
454 } else {
455 if Here.y < $2.s.y then {
456 line to $2.s dashed;
457 } else {
458 if Here.y > $2.n.y then {
459 line to $2.n dashed;
460 }
461 }
462 }
463 }
464 # restore Here.y
465 move to ($1.x, old_y)
466 }
467 """
468
469
470 ###############################################################################
471
472 import os
473 def try_remove (path):
474 try: os.remove (path)
475 except: pass
476
477 ###############################################################################
478 class Parser:
479 import re
480 EXPRE1 = re.compile (
481 "([A-Za-z_0-9]+?)([\+\-\#~!]?)"
482 " *"
483 "(\??-+>"
484 "|<-+\??"
485 "|\??=+>\??"
486 "|\??<=+\??"
487 "|\??#+>"
488 "|<#+\??"
489 "|\??:+>"
490 "|<:+\??"
491 "|//"
492 "|:"
493 "|>"
494 "|\*"
495 "|\["
496 "|\]"
497 "|\{_?\}"
498 ")"
499 " *"
500 "([#A-Za-z_0-9]*)([\+\-\#~!]?)"
501 " *"
502 "(.*)")
503
504 EXPRE1_call = re.compile ("([A-Za-z_0-9]+) *(=) *(.*)")
505 EXPRE1_call = re.compile ("(.*?) *(=) *(.*)")
506
507 EXPRE2 = re.compile ("([A-Za-z_0-9]+[\+\-\#~!]|:)+")
508
509 EXPRE3 = re.compile ("([A-Za-z_0-9]*)([\+\-\#~!]?) *(_?)\{(.*)\}")
510
511 extensions = ['.dot']
512
513 def __init__ (self, raw, request, **kw):
514 # save call arguments for later use in format()
515 self.raw = raw.encode ('utf-8')
516 self.request = request
517
518 self.attrs, msg = wikiutil.parseAttributes (request,
519 kw.get ('format_args', ''))
520 return
521
522 def _usage (self, full=False):
523
524 """Return the interesting part of the module's doc"""
525
526 if full: return __doc__
527
528 lines = __doc__.splitlines ()
529 start = 0
530 end = len (lines)
531 for i in range (end):
532 if lines [i].strip ().lower () == "usage:":
533 start = i
534 break
535 for i in range (start, end):
536 if lines [i].startswith ('--'):
537 end = i
538 break
539 return '\n'.join (lines [start:end])
540
541
542 def _convert_from_alternate_1 (self, lines):
543 newlines = []
544
545 self.has_first_step = False
546 self.objects = []
547
548 def add_obj (name):
549 if name not in self.objects: self.objects.append (name)
550
551 def rem_obj (name):
552 if name in self.objects: self.objects.remove (name)
553
554 def add (txt):
555 txt = txt.strip ()
556 if not txt : return
557 ini = "object", "pobject", "placeholder_object", "actor"
558 obj = max ([txt.startswith (i) for i in ini])
559 if not obj and not self.has_first_step :
560 newlines.append ("step();")
561 self.has_first_step = True
562 newlines.append (txt)
563
564 def nl2str (text):
565 texts = text.split ("\\n")
566 texts = [t.strip () for t in texts]
567 maxlen = max ( [len (t) for t in texts])
568 text = ' " " '.join (texts)
569 nlines = len (texts)
570 return text, nlines, maxlen
571
572 def do_line (line):
573 oline = line
574
575 line = line.strip ()
576 if not line: return
577
578 if line.startswith ("#"):
579 newlines.append (line)
580 return
581
582 # try to match: OBJECT[OP] OP OBJECT[OP] MORE
583 terms = Parser.EXPRE1.findall (line)
584 if terms:
585 l, lop, op, r, rop, edge = terms [0]
586 newlines.append (' '*50 + "##1## "+oline)
587 ##newlines.append (' '*50 + "# "+ `(l,op,r,edge)`)
588
589 async_head = False
590 async_tail = False
591
592 if len (op) > 1:
593 # trim '?'
594 if op [0] == "?":
595 op = op [1:]
596 if op.startswith ("<"):
597 async_head = True
598 else:
599 async_tail = True
600
601 if op [-1] == "?":
602 op = op [:-1]
603 if op.endswith (">"):
604 async_head = True
605 else:
606 async_tail = True
607
608 if async_tail:
609 add ('async();')
610
611 # trim long arrows
612 if op [0] == "<": op = op [:2]
613 elif op [-1] == ">": op = op [-2:]
614
615 # transform left arrow to right arrow
616 if op [0] == "<":
617 op = op [1:] + ">"
618 l, r = r, l
619 lop, rop = rop, lop
620 ##newlines.append (' '*50 + "# "+ `(l,op,r,edge)`)
621
622 r2 = ' '.join ( (r, edge)).strip ()
623
624 # escape quotes
625 edge = edge.replace ('"','\\"')
626 r2 = r2.replace ('"','\\"')
627
628 # implement primitives
629 # http://www.spinellis.gr/sw/umlgraph/doc/seq-ops.html
630
631 if op == ":" and r2 and not r2.startswith ("#"):
632 add ('object(%s," %s ");' % (l, r2))
633 add_obj (l)
634
635 elif op == ":" :
636 add ('pobject(%s);' % (l))
637
638 elif op == "*" :
639 if r2.startswith ("#"): r2 = ""
640 add ('actor(%s," %s ");' % (l, r2))
641 add_obj (l)
642
643 elif op == "->" :
644 edge, nlines, maxlen = nl2str (edge)
645 if edge.startswith ("<(>"):
646 add ('message(%s,%s,"");' % (l, r))
647 edge = edge [3:].strip ()
648 add ('lconstraint(%s," %s ");' % (l, edge))
649 elif edge.startswith ("<)>"):
650 add ('message(%s,%s,"");' % (l, r))
651 edge = edge [3:].strip ()
652 add ('lconstraint(%s," %s ");' % (r, edge))
653 else:
654 add ('message(%s,%s," %s ");' % (l, r, edge))
655 if lop: do_line (l+lop)
656 if rop: do_line (r+rop)
657
658 elif op == ">" :
659 r2, nlines, maxlen = nl2str (r2)
660 add ('active(%s);' % l)
661 add ('step();')
662 add ('message(%s,%s," %s ");' % (l, l, r2))
663 add ('inactive(%s);' % l)
664 if lop: do_line (l+lop)
665
666 elif op == ":>" :
667 add ('cmessage(%s,%s," %s "," ");' % (l, r, edge))
668 add_obj (r)
669 if lop: do_line (l+lop)
670 if rop: do_line (r+rop)
671
672 elif op == "=>" :
673 subterms = Parser.EXPRE1_call.findall (edge)
674 if subterms: subterms = subterms [0]
675 if subterms and len (subterms)==3 and subterms [1] == "=" :
676 # short hand for request+result
677 res, op, edge = subterms
678 add ('message(%s,%s," %s ");' % (l, r, edge))
679 add ('active(%s);' % r)
680
681 if lop: do_line (l+lop)
682
683 # sync/async for arrow head end:
684 if async_tail and not async_head:
685 add ('sync();')
686 elif not async_tail and async_head:
687 add ('async();')
688
689 add ('rmessage(%s,%s," %s ");' % (r, l, res))
690 add ('inactive(%s);' % r)
691 else:
692 # result only
693 add ('rmessage(%s,%s," %s ");' % (l, r, edge))
694 if lop: do_line (l+lop)
695 if rop: do_line (r+rop)
696
697 elif op == "#>" :
698 add ('dmessage(%s,%s);' % (l, r))
699 rem_obj (r)
700 if lop: do_line (l+lop)
701 if rop: do_line (r+rop)
702
703 elif op == "//" :
704 r2 = (r + " " + edge).strip ()
705 if r2.startswith ("["):
706 opts, text = r2 [1:].split ("]",1) # opts passed
707 else: opts, text = "", r2 # no opts passed
708 opts = (opts+",,").split (",") [:3] # insure 3 elements
709 text = text.strip ()
710
711 if not text:
712 add ('connect_to_comment(%s,%s);' % (l, opts [0]))
713 else:
714 text, nlines, maxlen = nl2str (text)
715 if not opts [1]: # auto-calculate box pos
716 opts [1] = "down 0 right"
717 if not opts [2]: # auto-calculate box size
718 w = 0.10 + float (maxlen) / 13.0
719 h = 0.10 + 0.16 * nlines
720 opts [2] = "wid %.2f ht %.2f" % (w, h)
721 opts = ",".join (opts)
722 add ('comment(%s,%s "%s");' % (l, opts, text))
723
724 if lop: do_line (l+lop)
725
726 elif op == "[" or op == "]":
727 # Possible syntax:
728 # frame_name [ object frame_title
729 # ... activity ...
730 # object ] frame_name
731 #
732 # Possible syntax:
733 # object ] frame_name frame_title
734 # ... activity ...
735 # frame_name [ object
736 #
737 if edge:
738 if op == "]": r, l = l, r
739 add ('begin_frame(%s,%s," %s ");' % (r, l, edge))
740 else:
741 if op == "[": r, l = l, r
742 add ('end_frame(%s,%s);' % (l, r))
743
744 if async_head or async_tail:
745 add ('sync();')
746 return
747
748 # try to match: OBJECT[OP] [OBJECT[OP] ...]
749 terms = Parser.EXPRE2.findall (line)
750 if terms == line.split ():
751 newlines.append (' '*50 + "##2## "+oline)
752 for term in terms :
753 l, op = term [:-1], term [-1]
754 if op == "+" :
755 add ('active(%s);' % l)
756 elif op == "-" :
757 add ('inactive(%s);' % l)
758 elif op == "!" :
759 add ('active(%s);' % l)
760 add ('step();')
761 add ('inactive(%s);' % l)
762 elif op == "#" :
763 add ('complete(%s);' % l)
764 rem_obj (l)
765 elif op == "~" :
766 add ('delete(%s);' % l)
767 rem_obj (l)
768 if ":" in terms :
769 add ('step();')
770 return
771
772 # try to match: [OBJECT[OP]] [_]{CONSTRAINT}
773 terms = Parser.EXPRE3.findall (line)
774 if terms:
775 newlines.append (' '*50 + "##3## "+oline)
776 l, lop, below, r = terms [0]
777 r, nlines, maxlen = nl2str (r)
778 if not l:
779 add ('oconstraint(" %s ");' % (r))
780 elif not below:
781 add ('lconstraint(%s," %s ");' % (l, r))
782 if lop: do_line (l+lop)
783 else:
784 add ('lconstraint_below(%s," %s ");' % (l, r))
785 if lop: do_line (l+lop)
786 return
787
788 # all attemps to match by RE failed => output as-is
789 newlines.append (line)
790
791 for line in lines: do_line (line)
792
793 if self.objects:
794 add ('step();')
795 for o in self.objects: add ('complete(%s);' % o)
796 return newlines
797
798
799 def format (self, formatter):
800 """The parser's entry point"""
801
802 # parse bangpath for arguments
803 opt_dbg = False
804 opt_help = None
805 opt_percent = 100
806 for (key, val) in self.attrs.items ():
807 val = val [1:-1]
808 if key == 'debug': opt_dbg = int (val)
809 elif key == 'help': opt_help = val
810 elif key == 'percent': opt_percent = int (val)
811 else:
812 self.request.write (formatter.rawHTML ("""
813 <p><strong class="error">
814 Error: processor %s: invalid argument: %s
815 <pre>%s</pre></strong> </p>
816 """ % (NAME, self.attrs, self._usage ())))
817 return
818
819 # help ?
820 if opt_help is not None and opt_help != '0':
821 self.request.write (formatter.rawHTML ("""
822 <p>
823 Processor %s usage:
824 <pre>%s</pre></p>
825 """ % (NAME, self._usage (opt_help == '2'))))
826 return
827
828 # useful
829 pagename = formatter.page.page_name
830 raw = self.raw
831
832 # debug ? pre-print
833 if opt_dbg==1:
834 self.request.write (formatter.rawHTML (
835 "<pre>\n%s\n</pre>" % raw))
836
837 # preprocess: join lines ending with '\' with the next one
838 # (lazy way, TODO: use regex)
839 while raw.find ("\\\n ")>=0:
840 raw =raw.replace ("\\\n ", "\\\n")
841 while raw.find (" \\\n")>=0:
842 raw =raw.replace (" \\\n", "\\\n")
843 raw = raw.replace ("\\\n", " ")
844 lines = raw.split ('\n')
845
846 # alternate syntax support
847 lines = self._convert_from_alternate_1 (lines)
848
849 # debug ? post-print
850 if opt_dbg==2:
851 self.request.write (formatter.rawHTML(
852 "<pre>\n%s\n</pre>" % '\n'.join (lines)))
853
854 # go !
855 all = '\n'.join (lines).strip ()
856 name = 'autogenerated--' + NAME + '-parser--' + \
857 sha.new (all+`self.attrs.items ()`).hexdigest ()
858 pngname = name + '.png'
859
860 attdir = AttachFile.getAttachDir (self.request, pagename, create=1) + '/'
861 pngpath = attdir + pngname
862
863 dm2ri = attdir + "delete.me.to.regenerate.images"
864
865 # delete autogenerated attachments if debug or dm2ri
866 # attachment does not exist
867 if not os.path.isfile (dm2ri):
868 # create dm2ri attachment
869 open (dm2ri,'w').close ()
870 # delete autogenerated attachments
871 for root, dirs, files in os.walk (attdir, topdown=False):
872 for name in files:
873 if name.startswith ("autogenerated-"):
874 os.remove (os.path.join (root, name))
875
876 pngdir = os.path.splitext (pngpath) [0]
877 ps = pngdir + ".ps"
878 pic = pngdir + ".pic"
879 err = pngdir + "-err.txt"
880
881 want_png = opt_dbg>1 or \
882 not os.path.exists (pngpath) or \
883 os.path.exists (err)
884
885 if want_png:
886 all = ".PS\n%s\n%s\n.PE" % (get_sequence_pic () , all)
887 open (pic, "w").write (all)
888
889 # Launch pic2plot => postscript
890 os.system ('pic2plot -T ps "%s" > "%s" 2>"%s"' % (pic, ps, err))
891 if not os.stat (err).st_size:
892 try_remove (err)
893 else:
894 # Error handling
895
896 # let us parse the 1st line of the error file, saying:
897 # pic2plot:<PIC_FILENAME>:<LINE_NR>:<ERROR_MESSAGE>
898 err = open (err).readlines () [0].split (":")
899 if len (err) == 4:
900 lnr, msg = err [2:2+2]
901 ix = int (lnr)-1
902 snip = [l.rstrip () for l in open (pic).readlines ()]
903 snip, context = snip [ix], snip [max (ix-2,0):ix+1+2]
904 context = "\n".join (context)
905 else: # cannot parse it
906 msg = ":".join (err)
907 snip = ""
908
909 # print error message
910 self.request.write (formatter.rawHTML ("""
911 <p><strong class="error">
912 Error: parser '%s': %s""" % (NAME, msg)))
913
914 # print code snippet
915 if snip:
916 self.request.write (formatter.rawHTML("""
917 <br>Faulty line:
918 <pre>%s</pre>
919 Context:
920 <pre>%s</pre>
921 (for full source, specify debug=2 as parser's arg)
922 """ % (snip, context)))
923
924 # close error message and return
925 self.request.write (formatter.rawHTML ("""
926 </strong> </p>"""))
927 return
928
929 # Run the postprocessing/conversion chain
930 os.system ('convert -density %dx%d "%s" "%s"' % \
931 (opt_percent, opt_percent, ps, pngpath))
932
933 # Cleanup
934 if opt_dbg<2: try_remove (pic)
935 try_remove (ps)
936
937 # Form the <img> tag
938 url = AttachFile.getAttachUrl (pagename, pngname, self.request)
939 self.request.write (formatter.image (src = url))
940
941 # Done
942 return
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.