[Zope-dev] Try tag with else block, or finally block

Martijn Pieters mj@antraciet.nl
Tue, 24 Aug 1999 13:27:59 +0200


--=====================_75926436==_
Content-Type: text/plain; charset="us-ascii"; format=flowed

Hello fellow Zope developers

I extended the new 'try' tag to include support for an optional 'else' 
block, and whipped in support for 'try..finally' as well. The try tag now 
has this form: try [[except]* [else] | [finally]] /try. I also added more 
rigorous checks on it's syntax, like allowing only one default exception 
handler.

Attached you'll find a patch against the latest CVS, but it will work on 
Zope 2.0a4 and higher. The file to patch is 
lib/python/DocumentTemplate/DT_Try.py. Let me know what you'll think.

I'll post this patch to the Collector as well.

Here is it's documentation string:

Zope DTML Exception handling

usage:

<!--#try-->
<!--#except SomeError AnotherError-->
<!--#except YetAnotherError-->
<!--#except-->
<!--#else-->
<!--#/try-->

or:

<!--#try-->
<!--#finally-->
<!--#/try-->

The DTML try tag functions quite like Python's try command.

The contents of the try tag are rendered. If an exception is raised,
then control switches to the except blocks. The first except block to
match the type of the error raised is rendered. If an except block has
no name then it matches all raised errors.

The try tag understands class-based exceptions, as well as string-based
exceptions. Note: the 'raise' tag raises string-based exceptions.

Inside the except blocks information about the error is available via
three variables.

   'error_type' -- This variable is the name of the exception caught.

   'error_value' -- This is the caught exception's value.

   'error_tb' -- This is a traceback for the caught exception.

The optional else block is rendered when no exception occurs in the
try block. Exceptions in the else block are not handled by the preceding
except blocks.

The try..finally form specifies a `cleanup` block, to be rendered even
when an exception occurs. Note that any rendered result is discarded if
an exception occurs in either the try or finally blocks. The finally block
is only of any use if you need to clean up something that will not be
cleaned up by the transaction abort code.

The finally block will always be called, wether there was an exception in
the try block or not, or wether or not you used a return tag in the try
block. Note that any output of the finally block is discarded if you use a
return tag in the try block.

If an exception occurs in the try block, and an exception occurs in the
finally block, or you use the return tag in that block, any information
about that first exception is lost. No information about the first
exception is available in the finally block. Also, if you use a return tag
in the try block, and an exception occurs in the finally block or you use a
return tag there as well, the result returned in the try block will be lost.

Original version by Jordan B. Baker.
Try..finally and try..else implementation by Martijn Pieters.

--=====================_75926436==_
Content-Type: text/plain; charset="iso-8859-1"
Content-Disposition: attachment; filename="DT_Try.patch"
Content-Transfer-Encoding: quoted-printable

*** DT_Try.py	1999/06/30 14:00:21	1.5=0A--- DT_Try.py	1999/08/24=
 11:24:53=0A***************=0A*** 85,91 ****=0A  =0A  import string, sys,=
 traceback=0A  from cStringIO import StringIO=0A! from DT_Util import=
 parse_params, render_blocks, namespace, InstanceDict=0A  from DT_Return=
 import DTReturn=0A  =0A  class Try:=0A--- 85,92 ----=0A  =0A  import=
 string, sys, traceback=0A  from cStringIO import StringIO=0A! from DT_Util=
 import ParseError, parse_params, render_blocks=0A! from DT_Util import=
 namespace, InstanceDict=0A  from DT_Return import DTReturn=0A  =0A  class=
 Try:=0A***************=0A*** 97,107 ****=0A      <!--#except SomeError=
 AnotherError-->=0A      <!--#except YetAnotherError-->=0A     =
 <!--#except-->=0A      <!--#/try-->=0A      =0A!     The DTML try tag=
 functions quite like Python's try command. The main=0A!     difference is=
 that DTML does not have a finally or an else construct.=0A      =0A     =
 The contents of the try tag are rendered. If an exception is raised,=0A    =
  then control switches to the except blocks. The first except block to=0A  =
    match the type of the error raised is rendered. If an except block=
 has=0A--- 98,114 ----=0A      <!--#except SomeError AnotherError-->=0A     =
 <!--#except YetAnotherError-->=0A      <!--#except-->=0A+     <!--#else-->=
=0A      <!--#/try-->=0A      =0A!     or:=0A      =0A+     <!--#try-->=0A+ =
    <!--#finally-->=0A+     <!--#/try-->=0A+     =0A+     The DTML try tag=
 functions quite like Python's try command.=0A+     =0A      The contents of=
 the try tag are rendered. If an exception is raised,=0A      then control=
 switches to the except blocks. The first except block to=0A      match the=
 type of the error raised is rendered. If an except block=
 has=0A***************=0A*** 119,149 ****=0A      =0A        'error_tb' --=
 This is a traceback for the caught exception.=0A      =0A      Original=
 version by Jordan B. Baker.=0A      """=0A      =0A      name =3D 'try'=0A!=
     blockContinuations =3D 'except',=0A  =0A      def __init__(self,=
 blocks):=0A          tname, args, section =3D blocks[0]=0A  =0A         =
 self.args =3D parse_params(args)=0A          self.section =3D=
 section.blocks=0A!         =0A!         # store handlers as tuples=
 (name,block)=0A!         self.handlers =3D []=0A! =0A!         for=
 tname,nargs,nsection in blocks[1:]:=0A!             for errname in=
 string.split(nargs):=0A!                =
 self.handlers.append((errname,nsection.blocks))=0A!             if=
 string.strip(nargs)=3D=3D'':=0A!                =
 self.handlers.append(('',nsection.blocks))=0A!         =0A      def=
 render(self, md):=0A          # first we try to render the first block=0A  =
        try:=0A!             return render_blocks(self.section, md)=0A      =
    except DTReturn:=0A              raise=0A          except:=0A--- 126,222=
 ----=0A      =0A        'error_tb' -- This is a traceback for the caught=
 exception.=0A      =0A+     The optional else block is rendered when no=
 exception occurs in the=0A+     try block. Exceptions in the else block are=
 not handled by the preceding=0A+     except blocks.=0A+     =0A+     The=
 try..finally form specifies a `cleanup` block, to be rendered even=0A+    =
 when an exception occurs. Note that any rendered result is discarded if=0A+=
     an exception occurs in either the try or finally blocks. The finally=
 block=0A+     is only of any use if you need to clean up something that=
 will not be=0A+     cleaned up by the transaction abort code.=0A+     =0A+ =
    The finally block will always be called, wether there was an exception=
 in=0A+     the try block or not, or wether or not you used a return tag in=
 the try=0A+     block. Note that any output of the finally block is=
 discarded if you use a=0A+     return tag in the try block.=0A+     =0A+   =
  If an exception occurs in the try block, and an exception occurs in the=0A=
+     finally block, or you use the return tag in that block, any=
 information=0A+     about that first exception is lost. No information=
 about the first=0A+     exception is available in the finally block. Also,=
 if you use a return tag=0A+     in the try block, and an exception occurs=
 in the finally block or you use a=0A+     return tag there as well, the=
 result returned in the try block will be lost.=0A+     =0A      Original=
 version by Jordan B. Baker.=0A+     Try..finally and try..else=
 implementation by Martijn Pieters.=0A      """=0A      =0A      name =3D=
 'try'=0A!     blockContinuations =3D 'except', 'else', 'finally'=0A!    =
 finallyBlock =3D None=0A!     elseBlock =3D None=0A  =0A      def=
 __init__(self, blocks):=0A          tname, args, section =3D blocks[0]=0A =
 =0A          self.args =3D parse_params(args)=0A          self.section =3D=
 section.blocks=0A! =0A!         # Find out if this is a try..finally type=
=0A!         if len(blocks) =3D=3D 2 and blocks[1][0] =3D=3D 'finally':=0A! =
            self.finallyBlock =3D blocks[1][2].blocks=0A! =0A!         #=
 This is a try [except]* [else] block.=0A!         else:=0A!             #=
 store handlers as tuples (name,block)=0A!             self.handlers =3D=
 []=0A!             defaultHandlerFound =3D 0=0A! =0A!             for=
 tname,nargs,nsection in blocks[1:]:=0A!                 if tname =3D=3D=
 'else':=0A!                     if not self.elseBlock is None:=0A!         =
                raise ParseError, (=0A!                             'No more=
 than one else block is allowed',=0A!                            =
 self.name)=0A! =0A!                     self.elseBlock =3D=
 nsection.blocks=0A! =0A!                 elif tname =3D=3D 'finally':=0A!  =
                   raise ParseError, (=0A!                         'A=
 try..finally combination cannot contain '=0A!                         'any=
 other else, except or finally blocks',=0A!                        =
 self.name)=0A! =0A!                 else:=0A!                     if not=
 self.elseBlock is None:=0A!                         raise ParseError, (=0A!=
                             'The else block should be the last block '=0A! =
                            'in a try tag', self.name)=0A! =0A!             =
        for errname in string.split(nargs):=0A!                        =
 self.handlers.append((errname,nsection.blocks))=0A!                     if=
 string.strip(nargs)=3D=3D'':=0A!                         if=
 defaultHandlerFound:=0A!                             raise ParseError, (=0A=
!                                 'Only one default exception handler '=0A! =
                                'is allowed', self.name)=0A!                =
         else:=0A!                             defaultHandlerFound =3D 1=0A!=
                             self.handlers.append(('',nsection.blocks))=0A!=
 =0A      def render(self, md):=0A+         if (self.finallyBlock is=
 None):=0A+             return self.render_try_except(md)=0A+        =
 else:=0A+             return self.render_try_finally(md)=0A+ =0A+     def=
 render_try_except(self, md):=0A+         result =3D ''=0A+ =0A          #=
 first we try to render the first block=0A          try:=0A!            =
 result =3D render_blocks(self.section, md)=0A          except DTReturn:=0A =
             raise=0A          except:=0A***************=0A*** 171,177=
 ****=0A--- 244,268 ----=0A                  return render_blocks(handler,=
 md)=0A              finally:=0A                  md._pop(1)=0A+        =
 else:=0A+             # No errors have occured, render the optional else=
 block=0A+             if (self.elseBlock is None):=0A+                =
 return result=0A+             else:=0A+                 return result +=
 render_blocks(self.elseBlock, md)=0A               =0A+     def=
 render_try_finally(self, md):=0A+         result =3D ''=0A+ =0A+         #=
 first try to render the first block=0A+         try:=0A+             result=
 =3D render_blocks(self.section, md)=0A+         # Then handle finally=
 block=0A+         finally:=0A+             result +=
 render_blocks(self.finallyBlock, md)=0A+ =0A+         return result=0A+ =0A=
      def find_handler(self,exception):=0A          "recursively search for=
 a handler for a given exception"=0A          if type(exception)=3D=3Dtype('=
'):=0A
--=====================_75926436==_
Content-Type: text/plain; charset="us-ascii"; format=flowed

--
Martijn Pieters, Web Developer
| Antraciet http://www.antraciet.nl
| Tel: +31-35-7502100 Fax: +31-35-7502111
| mailto:mj@antraciet.nl http://www.antraciet.nl/~mj
| PGP: http://wwwkeys.nl.pgp.net:11371/pks/lookup?op=get&search=0xA8A32149
------------------------------------------
--=====================_75926436==_--