, let me once again try to clarify why that still doesn't make sense to me.
x = y
Write that in your code, it will fly anytime, no matter if y is defined anywhere in the code or not. And that makes sense, because who knows when and how it will be called, right?
But call the function, then it will raise an error if there isn't really a y anywhere.
The moment make_printer is called, the inner function is not even yet defined. And when it is defined, it seems still not to be checked: you can write x=y again, return that function, call that inner function from outside - and only then you get your error.
As far as I know, the code of a function is initially only checked for SyntaxError, and that was it. But at some point the crucial information - inner accesses a value from envelope - must be gathered somehow in order to decide if it will be a closure or not.