On Sun, 11 Nov 2012 18:21:32 -0600, Tim Chase wrote: > On 11/11/12 17:18, Steven D'Aprano wrote: >> but that leaves you with the next two problems: >> >> 2) Fixing the assert still leaves you with the wrong exception. You >> wouldn't raise a ZeroDivisionError, or a UnicodeDecodeError, or an >> IOError would you? No of course not. So why are you suggesting raising >> an AssertionError? That is completely inappropriate, the right >> exception to use is TypeError. Don't be lazy and treat assert as a >> quick and easy way to get exceptions for free. > > I'd say that it depends on whether the dictionary/kwargs gets populated > from user-supplied data (whether mail, a file, direct input, etc), or > whether it's coder-supplied.
No, it doesn't matter if it is end-user supplied or coder-supplied. As usual, look at the standard library and Python builtins for best practices: py> len(42) # do we get an AssertionError? Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: object of type 'int' has no len() > I like to think of "assert" as a "hey, you bone-headed programmer, you > did something that violated conditions for using this code properly!" You mean like passing a list to ord()? Or trying to add a number and a string? Raise the appropriate exception for the error: TypeError for type errors and missing function parameters, ValueErrors for objects of the right type but a bad argument, etc. Assertion errors are for asserting facts about internal program logic. "I assert that such-and-such is a fact", rather than "I require that such-and-such is a fact". For example, this might be a reasonable (although trivial) use of assert: def spam(n): if isinstance(n, int) and n > 0: result = ' '.join(["spam"]*n) assert result, "expected non-empty string but got empty" return result else: raise SpamError("No spam for you!!!") This gives a measure of protection against logic errors such as writing n >= 0. The presence of the assert is partly documentation and partly error checking, but the important thing is that it checks things which depend on the internal logic of the code itself, not on the input to the code. Given that you have already explicitly tested that n > 0, then the logical consequence is that "spam"*n will be non-empty. Hence an assertion "I assert that the string is not empty", rather than an overt test "Is the string empty?" Now, I accept that sometimes it is hard to draw a line between "internal program logic" and external input. E.g. if one function generates a value x, then calls another function with x as argument, the state of x is an internal detail to the first function and an external input to the other function. The rules I use are: * If the argument is a public parameter to a public function, then you must not use assert to validate it; * If the argument is a private parameter, or if the function is a private function, then you may use assert to validate it (but probably shouldn't); * You can use assert to check conditions of function internal variables (locals that you control); * Once you cross the boundary to another function, your objects should be treated as external again. E.g. I might have: def foo(a, b): # foo, a and b are public, so don't use assert if not (isinstance(a, int) and isinstance(b, int)): raise TypeError x = abs(a) + abs(b) # x is purely internal, so okay to assert assert x >= 0, "Hey McFly, you screwed up!" return bar(x) def bar(x): if x <= 0: raise ValueError("expected positive x") ... This now guards against something other than foo calling bar. -- Steven -- http://mail.python.org/mailman/listinfo/python-list