PEP new assert idiom

Fábio Mendes niels_bohr at uol.com.br
Sun Nov 7 03:22:33 EST 2004


Thanks for clarification :-)

Fabio

Em Dom, 2004-11-07 às 07:43 +0000, Bengt Richter escreveu:
> On Sun, 07 Nov 2004 03:50:43 GMT, "Raymond Hettinger" <vze4rx4y at verizon.net> wrote:
> 
> >[Fbio Mendes]
> >> This is very similar to what I'm proposing, with the only inconvinience
> >> that is uglier to type:
> >>
> >> assert \
> >>   exp1 and \
> >>   exp2 and \
> >>   ...
> >>   expn,
> >>   'ErrorMsg'
> >>
> >> Instead of:
> >>
> >> assert \
> >>   exp1,
> >>   exp2,
> >>   ...
> >>   expn,
> >>   'Errormsg'
> >>
> >> Well, I realize I didn't expressed my thoughts very clearly and that
> >> it's indeed a very minor change in python's syntax. It won't let anyone
> >> does anything new, IFAIK, but it makes a common pattern of code a little
> >> more beautiful, (and why not? more expressive). If one consider the fact
> >> that it won't break old code (only in one very unlikely case) I don't
> >> see it as a completely unreasonable suggestion. Other people may think
> >> differently though.
> >
> >The odds of Guido accepting this proposal are about zero.  As you say, it
> >doesn't do anything new, but it does require altering the grammar.  Besides,
> >TOOWTDI.
> >
> >Also, Guido tends to not be persuaded by arguments about "too much typing".
> >This is doubly true is you're talking about replacing an "and" with a comma (big
> >whoop).
> >
> >Also, one of the existing alternatives is quite readable:
> >
> >err = 'Errormsg'
> >assert exp1, err
> >assert exp2, err
> >assert exp3, err
> >
> >The alternative has the advantage that a failure will point to the exact
> >expression that failed.  The disadvantage is the repetition of the error
> >message; however, I disagree that your use case is common.  Instead, it is
> >likely more advantageous to have different error messages for each expression.
> >For example, the following comes from the post condition checks in QR matrix
> >decomposition:
> >
> >assert Q.tr().mmul(Q)==eye(min(m,n)), "Q is not orthonormal"
> >assert isinstance(R,UpperTri), "R is not upper triangular"
> >assert R.size==(m,n), "R is does not match the original dimensions"
> >assert Q.mmul(R)==self, "Q*R does not reproduce the original matrix"
> >
> >One could link all of these by an "and" or the proposed comma, but then you
> >end-up with a single, less informative error message, "The QR decomposition
> >bombed, but I won't tell you why ;-) ".
> >
> Besides, if you want the single message with comma-delimited expressions,
> you can already write:
> 
>  >>> assert False not in map(bool, (
>  ...     True,
>  ...     1,
>  ...     2==2
>  ... )), 'Error message'
> 
> Or to show what happens
>  >>> assert False not in map(bool, (
>  ...     True,
>  ...     1,
>  ...     2==3
>  ... )), 'Error message'
>  Traceback (most recent call last):
>    File "<stdin>", line 1, in ?
>  AssertionError: Error message
> 
> Or with a little helper, and then two boilerplate lines for the assert,
> you can have individual messages:
> 
>  >>> def fff(*xm):
>  ...     """find first false xpr in seq xpr ,msg, xpr, msg and yield pair"""
>  ...     it = iter(xm)
>  ...     for x in it:
>  ...         m = it.next()
>  ...         if not x: yield x, m; break
>  ...
>  >>> def test(x):
>  ...     assert not [t for t in fff(
>  ...
>  ...         True, 'true msg',
>  ...         1,    'one msg',
>  ...         2==x, '2 eq x msg',
>  ...         'xx', 'xx msg'
>  ...
>  ...     )], 'Error: expr == %r,   msg = %r'%t
>  ...
>  >>> test(2)
>  >>> test(3)
>  Traceback (most recent call last):
>    File "<stdin>", line 1, in ?
>    File "<stdin>", line 7, in test
>  AssertionError: Error: expr == False,   msg = '2 eq x msg'
> 
> Actually, why not just make a utility function "asserts" to do it:
> 
>  >>> def asserts(*expr_msg_seq):
>  ...     """find first false expression value and assert it with paired message"""
>  ...     it = iter(expr_msg_seq)
>  ...     for x in it:
>  ...         m = it.next()
>  ...         if not x:
>  ...             assert x, '%r -> %r'%(x, m)
>  ...
>  >>> def test(x):
>  ...     asserts(
>  ...         True, 'true msg',
>  ...         1, 'one msg',
>  ...         x, 'bool(x) is not True',
>  ...         x==2, '2 eq x msg',
>  ...         'xx', 'xx msg'
>  ...     )
>  ...
>  >>> test(2)
>  >>> test(3)
>  Traceback (most recent call last):
>    File "<stdin>", line 1, in ?
>    File "<stdin>", line 7, in test
>    File "<stdin>", line 7, in asserts
>  AssertionError: False -> '2 eq x msg'
>  >>> test(0)
>  Traceback (most recent call last):
>    File "<stdin>", line 1, in ?
>    File "<stdin>", line 7, in test
>    File "<stdin>", line 7, in asserts
>  AssertionError: 0 -> 'bool(x) is not True'
>  >>> test(())
>  Traceback (most recent call last):
>    File "<stdin>", line 1, in ?
>    File "<stdin>", line 7, in test
>    File "<stdin>", line 7, in asserts
>  AssertionError: () -> 'bool(x) is not True'
>  >>> test(type('foo',(),{'__nonzero__':lambda self:0})())
>  Traceback (most recent call last):
>    File "<stdin>", line 1, in ?
>    File "<stdin>", line 7, in test
>    File "<stdin>", line 7, in asserts
>  AssertionError: <__main__.foo object at 0x02EF17AC> -> 'bool(x) is not True'
>  >>> test(type('foo',(),{'__nonzero__':lambda self:1})())
>  Traceback (most recent call last):
>    File "<stdin>", line 1, in ?
>    File "<stdin>", line 7, in test
>    File "<stdin>", line 7, in asserts
>  AssertionError: False -> '2 eq x msg'
>  >>> test(type('foo',(),{'__nonzero__':lambda self:1, '__cmp__':lambda s,o:0})())
> 
> I doesn't shortcut, so you could get an exception in preparing the arg list for
> a sequence like
>     asserts(
>         den !=0, 'denom must be zero',
>         num/den>5, 'better write it as assert num>5*den'
>     )
> which would be safer as
>     assert den !=0, 'denom must be zero'
>     assert num/den>5 'better write it as assert num>5*den'
> 
> Not to mention side effects, but you shouldn't have those in asserts anyway.
> 
> Silliness ;-)
> 
> Regards,
> Bengt Richter



More information about the Python-list mailing list