Exceptions versus error return values
Python has two ways of signalling that a function has failed; you can raise an exception or return a special error value of some sort. I use both techniques in different circumstances; since I've recently been writing some Python code, I've been thinking about exactly what those circumstances are, as far as I can tell.
(Self-analysis is tricky given that I don't particularly think through the choice when I'm making it; I handle errors however seems right for the function I'm writing at the time.)
Generally, I tend to use error return values if I expect failure to be routine, especially if there is a natural return value that is easy for callers to use. For example, getting a list of IPv4 and IPv6 addresses for a host; it's routine to look up nonexistent names (or at least names with no IP addresses), and returning an empty list is an easy return value for callers to use (since in many cases they will just iterate through the list of IPs anyways).
I use exceptions if I expect failure to be rare, especially if there is nothing that the direct caller of a function is going to do to handle the problem. If the only thing that I'll do on failure is abort the program with a pretty error message, there's no need to complicate all of the code between the program's main routine and the failing function with code to check for and immediately return the error. (The obvious exception is if there is cleanup work to be done on the way out, but I've come up with ways to handle that, similar to phase tracking.)
I'm pretty sure that I'd use exceptions even for common failures if they had to be handled by someone other than the function's direct caller; I don't like cluttering functions up with a bunch of 'if error: return error' code.
This view is not the common Python one. As we can see from the standard library, the Pythonic way uses exceptions a lot more often than I do.
(I'd argue that this is a sensible tradeoff for a library, too. The advantage of exceptions is that they are unambiguous signals of failures that you can't possibly confuse with valid return values, and they force people using your library to explicitly deal with errors.)