The difference lies in how you access the variable, which causes it to be treated differently. Consider this:
def isValid():
top = -1
def push():
print(top)
push()
isValid()
Here there is no error, since top can be found in the scope of isValid() and calling push() will print it correctly. However, if you add top += 1, Python correctly infers that top apparently has to be a local variable for that to work, but it isn't, so now top is showing as problematic everywhere inside push() (in most IDEs).
def isValid():
top = -1
def push():
print(top) # you'll see error messages / highlighting for top here
top += 1 # and here as well
push()
isValid()
Note that top += 1 isn't even the first statement, but its presence causes print(top) to no longer work either, since top now has to be local. But since there's no assignment to top before the other two statements, Python will correctly complain that it has been accessed before having been assigned.
The reason this doesn't happen with your stack is that you're not actually trying to change stack inside push(). You're trying to change one of its elements (stack[1]), but that doesn't change what object stack is naming - you're not assigning to stack itself, so it does not become local.
So, Python finds stack in the scope of isValid, allows you to access it (just like top in the first example) and you can access its elements without issue.