PEP new assert idiom

Discussion in 'Python' started by =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 6, 2004.

  1. I'm sorry if it's an replicate. Either my e-mail program is messing with
    things or the python-list sent my msg to /dev/null. I couldn't find
    anything related in previous PEP's, so here it goes a very early draft
    for a new "assert" syntax:

    This was inspired in Ruby's assert syntax. I'm not familiar with Ruby at
    all, so the chances are that this piece of code is broken, but I think
    the idea is very obvious. In Ruby, assert is simply a method which gets
    a block argument:

    >>> assert 'Errormsg' {
    >>> statement 1
    >>> statement 2
    >>> (...)
    >>> statement n
    >>> }


    This elegant syntax is NOT equivalent to python's not so ellegant:

    >>> erromsg = 'Errormsg'
    >>> assert statement 1, errormsg
    >>> assert statement 2, 'errormsg
    >>> (...)
    >>> assert statement n, errormsg


    In Ruby, the Assertion error is raised if executing statement 1, then 2,
    then 3... etc raises an error. This is a subtle thingm the error COULD
    NOT be raised if each statement is executed alone, as each statement may
    have side effects which make the order of execution relevant. To
    suceccefully emulate this behaviour, the python programmer have to
    resort to a even more cumbersome solution:

    >>> foo = lambda : (statement 1) and (statement 2) ... (statement n)
    >>> assert foo, 'Errormsg'


    My proposal is to add the following syntax to the language:

    >>> assert (statement 1), (statement 2), ... (statement n), 'Errormsg'


    Or, if the user prefers, the traditional comma rules also applies:

    >>> assert \
    >>> statement1,
    >>> statement2,
    >>> (...)
    >>> statement n,
    >>> 'Errormsg'


    This simple syntax change introduces a very useful idiom for unittesting
    and may equally be useful in other kinds of code. The most problematic
    issue, I see, is the potential ambiguity a assert usage:

    >>> assert statement 1, ..., statement n, string


    As the 'Errormsg' argument is optional, it could be interpreted either
    as being the statement (n + 1) or as the errormsg. This is not a
    non-issue as the following syntax is valid in the current implementation
    and is very useful:

    >>> st = ''
    >>> assert st

    Traceback
    ....
    AssertionError

    This is useful to assert that a string is not empty. My proposal is that
    assert will always check all statements, (including the eventual error
    message) and issue AssertError exceptions if find any problem. This will
    catch only empty strings. If the last argument is a string, and an a
    False statement was found, it will print the last argument in the shell.

    The only piece of code I see would be broken in the new implementation
    is:

    >>> assert statement, ''


    This will always raise an exception in the new implementation on the
    opposition to the current behaviour in which the exception is raised
    only if 'statement' is false, returning an empty error message. I don't
    see any use for this pattern of code and is very unlikelly anyone has
    ever implemented it, so I guess nobody would mind breaking this.

    I'm interested to write a formal PEP if I receive good feedback from the
    community, but I never wrote a PEP before, and (the worst problem) my
    english is not so good. Anyone helps?

    Thanks,
    Fabio
    =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 6, 2004
    #1
    1. Advertising

  2. Fábio Mendes <> wrote:
    ...
    > This elegant syntax is NOT equivalent to python's not so ellegant:
    >
    > >>> erromsg = 'Errormsg'
    > >>> assert statement 1, errormsg
    > >>> assert statement 2, 'errormsg
    > >>> (...)
    > >>> assert statement n, errormsg


    Why isn't this equivalent? Apart from the fact that you keep, here and
    elsewhere, using 'statement' where you hopefully mean 'expression' --
    you cannot assert a statement, you can only assert an expression.

    > In Ruby, the Assertion error is raised if executing statement 1, then 2,
    > then 3... etc raises an error. This is a subtle thingm the error COULD
    > NOT be raised if each statement is executed alone, as each statement may
    > have side effects which make the order of execution relevant. To


    Can you give one Python example where the sequence of asserts would
    behave differently from what you propose? I cannot see any.

    > suceccefully emulate this behaviour, the python programmer have to
    > resort to a even more cumbersome solution:
    >
    > >>> foo = lambda : (statement 1) and (statement 2) ... (statement n)
    > >>> assert foo, 'Errormsg'


    I assume you mean to call foo, because if you don't, it will surely be
    true and the assert will just never trigger.

    Now, calling expressions expressions, rather than very confusingly
    statements, can you explain how the semantics of this, with foo being
    called, would differ from those of

    assert exp1 and exp2 and exp3 ... and expN, 'errormsg'

    ???


    > My proposal is to add the following syntax to the language:
    >
    > >>> assert (statement 1), (statement 2), ... (statement n), 'Errormsg'


    Again: can you explain how the semantics of this, would differ from
    those of:

    assert exp1 and exp2 and exp3 ... and expN, 'errormsg'

    ???

    > Or, if the user prefers, the traditional comma rules also applies:


    There is no such "traditional" rule (or rules) in Python. Both a
    statement and an expression can perfectly well end in a comma; the comma
    does NOT imply any kind of continuation.


    Alex
    Alex Martelli, Nov 6, 2004
    #2
    1. Advertising

  3. "Fábio Mendes" <> wrote in message
    news:...

    > I'm sorry if it's an replicate. Either my e-mail program is messing with
    > things or the python-list sent my msg to /dev/null. I couldn't find
    > anything related in previous PEP's, so here it goes a very early draft
    > for a new "assert" syntax:


    Do you have a use case? That is, can you give a plausible example that
    shows why your proposal would be useful and how it would work?
    Andrew Koenig, Nov 6, 2004
    #3
  4. =?ISO-8859-1?Q?F=E1bio?= Mendes

    John Roth Guest

    "Fábio Mendes" <> wrote in message
    news:...
    > I'm sorry if it's an replicate. Either my e-mail program is messing with
    > things or the python-list sent my msg to /dev/null. I couldn't find
    > anything related in previous PEP's, so here it goes a very early draft
    > for a new "assert" syntax:
    >

    [...]
    >
    > I'm interested to write a formal PEP if I receive good feedback from the
    > community, but I never wrote a PEP before, and (the worst problem) my
    > english is not so good. Anyone helps?
    >
    > Thanks,
    > Fabio


    I see two difficulties with it.

    First, you can do the same thing (I think) with a
    try block around a sequence of statements and
    an assert in the except block. This means you
    will need a fairly strong use case based on
    convenience to add the syntax to the language.
    You will have to show that it will be used a lot.

    Second, the statement form has the potential to
    cause side effects. There is a very strong
    community of thought that says that asserts
    are debugging or validation statements that
    should not cause any behavior change by
    removing them. This can be dealt with by
    making it some form of block that has its
    own local variables that are discarded on
    exit.

    John Roth
    John Roth, Nov 7, 2004
    #4
  5. > > >>> erromsg = 'Errormsg'
    > > >>> assert statement 1, errormsg
    > > >>> assert statement 2, 'errormsg
    > > >>> (...)
    > > >>> assert statement n, errormsg

    >
    > Why isn't this equivalent? Apart from the fact that you keep, here and
    > elsewhere, using 'statement' where you hopefully mean 'expression' --
    > you cannot assert a statement, you can only assert an expression.


    There is only the conceptually subtle difference that in the 1st you
    asserts the whole block of expressions, the second you assert each
    expression. They're likely to be the same, probably they will be
    equivalent in all cases but I can't backup the truth or falsity of that
    statement. The pythonic counterpart involves too much typing and is more
    error prone.

    > Now, calling expressions expressions, rather than very confusingly
    > statements, can you explain how the semantics of this, with foo being
    > called, would differ from those of
    >
    > assert exp1 and exp2 and exp3 ... and expN, 'errormsg'


    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.

    Thanks,
    Fabio
    =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 7, 2004
    #5
  6. > I see two difficulties with it.
    >
    > First, you can do the same thing (I think) with a
    > try block around a sequence of statements and
    > an assert in the except block. This means you
    > will need a fairly strong use case based on
    > convenience to add the syntax to the language.
    > You will have to show that it will be used a lot.


    I meant expression instead of statement, sorry, that's my fault. It's a
    minor cosmetic change I should say, only to avoid long grouping of
    expressions with an 'and' operator. It's better expressed as: let the
    commas take the place of 'and' in assertion verifications... Maybe that
    could be extended to other parts of the language, but the only place I
    think it would be useful is in assertion statements.

    Thanks,
    Fabio
    =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 7, 2004
    #6
  7. [Fábio 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 ;-) ".


    Raymond Hettinger
    Raymond Hettinger, Nov 7, 2004
    #7
  8. =?ISO-8859-1?Q?F=E1bio?= Mendes

    Paul Rubin Guest

    Some time ago I found myself proposing a new "Validate" statement,
    which would work exactly like "assert", except:

    1) it would throw a ValidationError instead of AssertionError
    if the condition failed

    2) it would NOT be turned into a no-op by the optimizing compiler.

    The purpose is runtime validation of user data, rather than sanity
    checking the program logic. Example: I keep finding myself writing
    code like:

    x = float(raw_input('Enter a positive number: '))
    assert x > 0, 'number wasn't positive'

    This is incorrect because the assert statement is not guaranteed to be
    executed. I propose to say instead,

    validate x > 0, 'number wasn't positive'

    which would do the right thing.

    This proposal was reasonably well received when I've posted it before,
    but I don't remember if there was any conclusion, and there was no
    follow-up. Since we're again talking about PEP's regarding the assert
    statement, should I try to put one together for this, in my copious
    (hah) free time?
    Paul Rubin, Nov 7, 2004
    #8
  9. > Some time ago I found myself proposing a new "Validate" statement,
    > which would work exactly like "assert", except:
    >
    > 1) it would throw a ValidationError instead of AssertionError
    > if the condition failed
    >
    > 2) it would NOT be turned into a no-op by the optimizing compiler.
    >

    [...]
    This sounds great. I like the validate syntax. This also sounds very
    useful to me. If it doesn't go mainstream, it should, at least be a
    parameter controlled at runtime:

    >>> import validate
    >>> validate.always_compile_assertions = True # this name sucks! I know!


    Ideally, for me, python would support a decent DPC-like semantics:

    >>> class foo_bar:
    >>> contract, 'invariant':
    >>> assert (something always true), 'Errormsg1'
    >>> assert (more imutable truths), 'Errormsg2'
    >>>
    >>> def foo(self, var1, var2):
    >>> contract, 'pre':
    >>> # var1 and var2 are avaiable in this scope
    >>> assert expr, 'Error string1'
    >>> assert expr1, expr2, 'Error string2'
    >>>
    >>> contract, 'pos':
    >>> # var1, var2, avaiable. 'old' is 'self' before calling foo
    >>> # foo.return stands for the output of function foo (or None)
    >>> # foo.error is any the exception the function had trown
    >>> assert self.bar > old.bar
    >>> assert foo == some result, 'Error string'
    >>> if foo.error == some Exception:
    >>> assert (condition to trow that exception), 'Error'
    >>>
    >>> (function implementation)



    The (python) implementation of this syntax can be something like this.
    If dbc is enable, foo method becames:

    >>> def foo(self, var1, var2):
    >>> try:
    >>> contract invariant block
    >>> ...
    >>> except AssertionError, x:
    >>> contractViolations.log('invariant', x)
    >>>
    >>> try:
    >>> pre contract block
    >>> ...
    >>> except AssertionError, x:
    >>> contractViolations.log('pre', x)
    >>> try:
    >>> foo.value = __old_foo(var1, var2)
    >>> finally error, error_msg:
    >>> if error: foo.value = None
    >>> foo.error = error
    >>> try:
    >>> post contract block
    >>> except AssertionError, x:
    >>> contractViolations.log('pos', x)
    >>> if error and not contract.fail_silently:
    >>> raise error, error_msg


    The program executes normally, then, if non fatal errors happens,
    contracViolations.report() will print a nice formated report of what was
    violated, similar to what unittest module does. This is not pure DBC I
    guess, but is nice.

    Thanks,
    Fabio
    =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 7, 2004
    #9
  10. > 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 ;-) ".
    >

    You have a good point here. Maybe what I proposed is only a distraction.
    We don't want a syntax tailored to every sittuation as it will serve
    only for showcase selected programs that express the full elegance of
    the language, but to the programmer, it gains the burden of memorizing
    the 100 and so operators that makes the language very expressive. Of
    course each person will develop a personal dialect, which will make his
    code very expressive in a specific domain, but he'll talk to
    nobodyelse... For that we have perl. So I guess i'm off this thread, but
    someone pointed a PEP for a 'validate' statement which still holds. At
    least it will help to elucidate the somewhat confusing meaning of the
    assert statement. (you shouldn't use it at runtime?? which level of
    optimizations disable it?).

    Fabio
    =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 7, 2004
    #10
  11. "Paul Rubin" <http://> wrote in message
    news:...
    > Some time ago I found myself proposing a new "Validate" statement,
    > which would work exactly like "assert", except:
    >
    > 1) it would throw a ValidationError instead of AssertionError
    > if the condition failed
    >
    > 2) it would NOT be turned into a no-op by the optimizing compiler.
    >
    > The purpose is runtime validation of user data, rather than sanity
    > checking the program logic. Example: I keep finding myself writing
    > code like:
    >
    > x = float(raw_input('Enter a positive number: '))
    > assert x > 0, 'number wasn't positive'
    >
    > This is incorrect because the assert statement is not guaranteed to be
    > executed. I propose to say instead,
    >
    > validate x > 0, 'number wasn't positive'
    >
    > which would do the right thing.


    Why do you need a statement for this?
    IMO, a plain function will do the trick:

    def validate(expression, msg=None):
    if expression: return
    if msg is None:
    raise ValidationError
    else
    raise ValidationError(msg)


    Raymond Hettinger
    Raymond Hettinger, Nov 7, 2004
    #11
  12. =?ISO-8859-1?Q?F=E1bio?= Mendes

    Paul Rubin Guest

    "Raymond Hettinger" <> writes:
    > Why do you need a statement for this?
    > IMO, a plain function will do the trick:


    I do write such functions sometimes, but using a statement is more
    natural. That's why the assert statement exists, for example.

    Because of that naturalness, the temptation to misuse the assert
    statement is very great, and I find myself doing it all the time
    thinking I'll clean it up later. So I think having a statement for
    runtime checking would be in the correct spirit.
    Paul Rubin, Nov 7, 2004
    #12
  13. Fábio Mendes <> wrote:
    ...
    > > called, would differ from those of
    > >
    > > assert exp1 and exp2 and exp3 ... and expN, 'errormsg'

    >
    > 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'


    So follow PEP 8's recommendation instead:

    assert (
    exp1 and
    exp2 and
    ...
    expn
    ), 'Errormsg'

    I don't see the parentheses (which clarify the syntax issue you
    mentioned in your first post) and 'and' as any uglier than the commas.
    And having commas mean line continuation is out of the question.


    Alex
    Alex Martelli, Nov 7, 2004
    #13
  14. On Sun, 07 Nov 2004 03:50:43 GMT, "Raymond Hettinger" <> wrote:

    >[Fábio 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
    Bengt Richter, Nov 7, 2004
    #14
  15. Em Sáb, 2004-11-06 às 22:48 -0800, Paul Rubin escreveu:
    > "Raymond Hettinger" <> writes:
    > > Why do you need a statement for this?
    > > IMO, a plain function will do the trick:

    >
    > I do write such functions sometimes, but using a statement is more
    > natural. That's why the assert statement exists, for example.
    >
    > Because of that naturalness, the temptation to misuse the assert
    > statement is very great, and I find myself doing it all the time
    > thinking I'll clean it up later. So I think having a statement for
    > runtime checking would be in the correct spirit.


    If it wasn't that we should be programming in assembly as it's faster :)
    AFAIK all real world programming languages are turing equivalent, so
    sugar is what differentiates on of the other. I'd love to see that sugar
    in Python since defining assert functions is distracting.

    Fabio
    =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 7, 2004
    #15
  16. > So follow PEP 8's recommendation instead:
    >
    > assert (
    > exp1 and
    > exp2 and
    > ...
    > expn
    > ), 'Errormsg'
    >

    I didn't know this would work for 'and/or' operators (even I've read the
    style guide PEP!), sorry. It's not super beautiful, but I could live
    happy with that :)

    > I don't see the parentheses (which clarify the syntax issue you
    > mentioned in your first post) and 'and' as any uglier than the commas.
    > And having commas mean line continuation is out of the question.


    I never said that commas should mean line continuation (as a backslash).
    It CAN be used as implicit line continuations in lot cases, (like
    function arguments, for instance. This would be just one more case.

    Cheers,
    Fabio
    =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 7, 2004
    #16
  17. 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" <> 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
    =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 7, 2004
    #17
  18. Fábio Mendes <niels_bohr <at> uol.com.br> writes:
    >
    > I never said that commas should mean line continuation (as a backslash).
    > It CAN be used as implicit line continuations in lot cases, (like
    > function arguments, for instance. This would be just one more case.


    I'm not sure that I understand you correctly, but if you're saying that the
    commas in a function argument list indicate an implicit line continuation, I
    think this is probably not correct. It's the unclosed parentheses that indicate
    the line continuation, not the commas -- this is why you can write a function
    argument list across multiple lines, but it's also why you can write a tuple
    across lines:

    >>> def f(x,

    .... y, z):
    .... print x, y, z
    ....
    >>> f(1, 2, 3)

    1 2 3
    >>> (x,

    .... y, z) = 1, 2, 3
    >>> print x, y, z

    1 2 3

    Note that just a trailing comma definitely does not mean a line continuation:

    >>> x,

    Traceback (most recent call last):
    File "<interactive input>", line 1, in ?
    NameError: name 'x' is not defined

    I tried to write a tuple over multiple lines here, but (because commas do not
    indicate line continuations), Python thinks I want to print the value of the
    tuple containing x.

    Of course, unclosed brackets work the same way as unclosed parentheses:

    >>> [x,

    .... y, z] = 1, 2, 3
    >>> print x, y, z

    1 2 3

    Steve
    Steven Bethard, Nov 7, 2004
    #18
  19. Fábio Mendes <> wrote:

    > > So follow PEP 8's recommendation instead:
    > >
    > > assert (
    > > exp1 and
    > > exp2 and
    > > ...
    > > expn
    > > ), 'Errormsg'
    > >

    > I didn't know this would work for 'and/or' operators (even I've read the
    > style guide PEP!), sorry. It's not super beautiful, but I could live
    > happy with that :)


    It works for the parentheses -- what operators are inside the
    parentheses, including none at all, doesn't matter. A logical line
    isn't finished until all kinds of parentheses are balanced.


    > > I don't see the parentheses (which clarify the syntax issue you
    > > mentioned in your first post) and 'and' as any uglier than the commas.
    > > And having commas mean line continuation is out of the question.

    >
    > I never said that commas should mean line continuation (as a backslash).
    > It CAN be used as implicit line continuations in lot cases, (like
    > function arguments, for instance. This would be just one more case.


    Nope, comma can never be used as implicit line continuation in Python
    (that's different from some other languages). The opened and yet
    unclosed parentheses are what cause several physical lines to be part of
    the same logical line -- not commas, nor other operator or punctuation.

    Consider for example:

    print ( 'some text'
    'and some more'
    'and yet more' )

    look, ma: no commas (nor other punctuation nor operators, yet three
    physical lines merge into just one logical line -- thanks to the
    parentheses. This will print

    some textand some moreand yet more

    because adjacent string literals merge into one string literal (in
    Python just like in C).

    If you put commas there WITH the parentheses, you'd be printing out a
    tuple. With the commas and WITHOUT the parentheses, syntax error.

    You may be associating "commas continue lines" with "function arguments"
    because function definitions and calls DO use parentheses, and separate
    arguments with commas. But it's really not an ideal mental model.


    Alex
    Alex Martelli, Nov 7, 2004
    #19
  20. =?ISO-8859-1?Q?F=E1bio?= Mendes

    Gerrit Guest

    remove assert statement (Was: Re: PEP new assert idiom)

    Paul Rubin wrote:
    > "Raymond Hettinger" <> writes:
    > > Why do you need a statement for this?
    > > IMO, a plain function will do the trick:

    >
    > I do write such functions sometimes, but using a statement is more
    > natural. That's why the assert statement exists, for example.


    I think 'assert' being a statement is on Guido's regrets list. exec and
    print are on his regrets list: both should have been a function. I
    think the same holds for assert, although I'm not sure.

    > statement is very great, and I find myself doing it all the time
    > thinking I'll clean it up later. So I think having a statement for
    > runtime checking would be in the correct spirit.


    In my opinion, assert is almost useless. It can sometimes be useful for
    debugging purposes, but beyond that, it isn't. The exception raised by
    'assert' is always AssertionError. That is a major disadvantage, because
    it's not specific. If a method I'm calling would raise an AssertionError
    is some cases, and I want to catch it, I'm catching too much. Creating a
    new exception-class and raising that one is better because of this.

    I don't see the use of a statement like
    "assert isinstance(message, basestring)". It's by far the most common
    use of assert: some assertions about the types of the arguments. This is
    at best redundant. In the case of warnings.py, it's followed by
    re.compile(message, ...). Removing the assertion in this example would result
    in three differences:
    - The error raised would be TypeError, not AssertionError. Therefore
    it's more specific and thus easier to catch.
    - The error would me raised by the re module, not warnings module
    - The error would be "first argument must be string or compiled
    pattern" instead of "message must be a string". Therefore, the
    assertion makes it impossible to pass a compiled pattern instead
    of a string.

    Duck typing eliminates the use of assert.

    In my opinion, assert should be deprecated and then removed in Py3K:
    assertions are redundant, unspecific, and conflict with the philosophy
    of duck typing and EAFP.

    What do others think of this?

    Gerrit.

    --
    Weather in Twenthe, Netherlands 07/11 13:25:
    12.0°C wind 3.6 m/s N (57 m above NAP)
    --
    In the councils of government, we must guard against the acquisition of
    unwarranted influence, whether sought or unsought, by the
    military-industrial complex. The potential for the disastrous rise of
    misplaced power exists and will persist.
    -Dwight David Eisenhower, January 17, 1961
    Gerrit, Nov 7, 2004
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Robert Brewer
    Replies:
    1
    Views:
    484
    bsmith
    Nov 7, 2004
  2. Thomas Guettler

    assert 0, "foo" vs. assert(0, "foo")

    Thomas Guettler, Feb 23, 2005, in forum: Python
    Replies:
    3
    Views:
    2,515
    Carl Banks
    Feb 23, 2005
  3. Alex Vinokur

    assert(x) and '#define ASSERT(x) assert(x)'

    Alex Vinokur, Nov 25, 2004, in forum: C Programming
    Replies:
    5
    Views:
    908
    Keith Thompson
    Nov 25, 2004
  4. ImpalerCore

    To assert or not to assert...

    ImpalerCore, Apr 27, 2010, in forum: C Programming
    Replies:
    79
    Views:
    1,651
    Richard Bos
    May 17, 2010
  5. Kenneth McDonald

    Is there a standard "assert" idiom in Ruby?

    Kenneth McDonald, Aug 2, 2007, in forum: Ruby
    Replies:
    8
    Views:
    154
    John Carter
    Aug 3, 2007
Loading...

Share This Page