[Tutor] Code evaluation inside of string fails with __get_item

Steven D'Aprano steve at pearwood.info
Sun Dec 12 03:18:44 CET 2010


Tim Johnson wrote:

>   I've never had the occasion to use assert() or any other
>   python - shooting tools, any thoughts on that?


Assertions are a great tool, but never ever, under pain of great pain, 
use assert for testing user input or function arguments.

It's tempting to knock up a quick and dirty function like this:

def spam(n):
     assert n >= 0, "amount of spam must be non-negative"
     do_stuff_with(n)


Beware! This way leads to the Dark Side, or at least to strange and 
mysterious bugs that will only effect a few of your users and have you 
scratching your head.

The problem is that if you run Python with optimizations turned on, 
assertions are disabled. When run with python -O, your error checking 
disappears:

def spam(n):
     do_stuff_with(n)

Strange and terrible things may now occur, exceptions being the *least* 
of your worries. Better to write explicit tests and give sensible errors:

def spam(n):
     if n < 0:
         raise ValueError("amount of spam must be non-negative")
     do_stuff_with(n)


If you've ever written a comment like "This can't happen, but if it 
does, raise an exception", then you've essentially made an assertion. 
Here's a trivial example:

def spam(s):
     s += " spam spam spam glorious SPAM!!!"
     p = s.find("spam")
     if p < 1:
         # This can never happen!
         raise RuntimeError("Serious internal error #123456")
     do_something_with(p)

This could be written as:

def spam(s):
     s += " spam spam spam glorious SPAM!!!"
     p = s.find("spam")
     assert p >= 1, "Serious internal error #123456"
     do_something_with(p)


Now if you want to speed up your program by skipping all those "can 
never happen" tests, you can run python -O and the asserts will be 
compiled out.


Assertions are for testing program logic, not for testing user input. 
You should assume that the caller should never see your assertions: if 
the caller ever receives an AssertionError, you have failed. (If they 
receive a ValueError, or similar, that's *their* fault for passing 
rubbish input to your function.) Assertions are for making statements 
about *internal* state.

Having said that, sometimes it's hard to decide what counts as an 
internal state. Can function arguments ever be internal state? Sometimes 
I do things like this:

def ham(n):
     if n < 0:
         raise ValueError("amount of ham must be non-negative")
     x = _common(n)
     return "Ham is like spam but not as tasty." + x

def spam(n):
     if n < 0:
         raise ValueError("amount of spam must be non-negative")
     x = _common(n)
     return "Spam, glorious SPAM!!!" + x

def _common(n):
     # Internal function.
     assert n >= 0
     do_stuff_here(n)
     return something

Since _common is an internal function which the caller is not supposed 
to use directly, I feel it is acceptable to treat the input to _common 
as an internal state. If the caller wants to mess with my internal 
functions, they're responsible for whatever horrible things happen.

Another good example of assertions is for checking pre-conditions and 
post-conditions, particularly post-conditions. Here's a real example: in 
my stats module, I calculate "r", the Pearson's Correlation Coefficient. 
It doesn't matter what that means, but what does matter is that the 
result *must* be between -1 and 1, or else my code has a bug in it. Even 
though my code is perfectly bug-free *cough*, I end the function with 
the line:

     assert -1.0 <= r <= 1.0

to ensure that if there is a bug in my code, it will raise an exception 
rather than return a garbage result. If the user wants to live 
dangerously, they can run with the optimization flag and skip my assertions.


>   I am dealing with a programmatically composed format
>   string, that originates from a source (html) file

Since the format string is generated by you, then errors in the format 
string are your responsibility, and they're an internal detail. Use 
assertions for checking the format string.

However, the HTML source is not an internal detail (at least I wouldn't 
expect it to be), it is *user* input, so any checks you do while 
processing the HTML should not use assertions.



>   It may be 
> 
>   1)read from the file external to the object
>     and the source string passed into the object at instantiation.
>     The composed string is then translated correctly and
> 	the embedded is evaluated.

Who makes that choice? You, or the caller? If the caller, then any 
errors that occur are not internal state, and you shouldn't use assert. 
If you, then it's an internal detail and you can use assert.




-- 
Steven



More information about the Tutor mailing list