Python exceptions are not meant to represent exceptional error conditions alone, so it isn't an "abuse." As you point out, sequence iteration is terminated by raising StopIteration. This kind and level of exception usage is key to Python's dynamic, introspective nature.
How so? What is done with exceptions that don't represent an error condition that can't be done with any other construct and is so essential to Python's nature?
It is perhaps helpful to think of Python exceptions as "execution interruptions": they are an all-purpose, low-overhead synchronous notification of a change to the state you are presently interrogating. Python is highly dynamic, with deep introspection and meta-class hacking abilities front and center. You can treat objects as dictionaries, iterate over their members and examine the existence of attributes without any special syntax. A uniform means of indicating surface limits, then, is the use of exceptions—and that uniformity is prized in Python, due to the Zen of Python belief that "there should be one, and preferably only one, obvious way to do a thing."
Essentially, in Python, exceptions represent any interruption of your routine code, any special cases at all. Bounds limits, zero-division, end of file marker, etc. By virtue of being exceptions rather than explicit stateful return values, they can propagate up the call stack and be handled at the logically convenient place.
It's perhaps a little different, but it works really well in practice.