The test for this is pretty simple (Lib/tests/test_exceptions.py
):
self.raise_catch(TabError, "TabError")
try: compile("try:\n\t1/0\n \t1/0\nfinally:\n pass\n",
'<string>', 'exec')
except TabError: pass
else: self.fail("TabError not raised")
Looking at the code where this error is actually thrown (Parser/tokenizer.c
, tok_get()
) it looks like this merely compares the indentation kind to what the previous line used, and not what is used throughout the file.
The documentation says (Doc/reference/lexical_analysis.rst
, emphasis mine)
Indentation is rejected as inconsistent if a source file mixes tabs and spaces in a way that makes the meaning dependent on the worth of a tab in spaces; a TabError
is raised in that case.
It's okay to mix tabs and spaces if the "blocks" are completely "separated" by going back to indentation level 0; as there can be no confusion about the program's logic due to tab width settings. The problem with mixing tabs and spaces in Python is that Python assumes that a tab is eight spaces wide, but that the programmer's editor may use something else. For example, code like this:
def my_fun(i):
if i == 6:
foo()
------->bar() # Tab
Will be seen as this with a tabstop of 4:
def my_fun(i):
if i == 6:
foo()
bar()
Which is obviously not what the program does!
But in cases like:
def my_fun(i):
if i == 6:
foo()
bar()
def my_fun2(i):
--->if i == 7:
--->--->foo()
--->--->bar()
It's okay from a "logic" point of view as no matter how I view tabs, it's always clear what the logic is. It's of course still a bad idea to mix tabs and spaces in a single file, but that's merely a stylistic error, and not a logic error ;-)
So to answer the question: judging from that one line in the documentation, I
would say that this is by design. I can't find a PEP for this, though, and this
case isn't tested. I would be not rely on it that all Python versions in the
future behave the same!