Python raises NameError when code uses a name that, at that moment, has never been assigned, defined, or imported in a scope it can see. The cause list is short: a typo, a missing import, a variable used outside its scope, execution reaching the use before the definition — or a string that lost its quotes.
userName, username and user_name are three different names. The error message quotes the name it couldn’t find — diff it against the definition.math.pi without import math) raises NameError, not an import error.def line (in the execution path) fails. Move definitions above their first use, or call from a main block at the bottom.print(hello) looks for a variable named hello; print("hello") prints the word.If the name is defined and the spelling matches, the definition probably isn’t running: it sits in a branch that isn’t taken, a cell that wasn’t executed, or a file that isn’t the one being imported (check for a same-named file shadowing yours).
What’s the difference between NameError and UnboundLocalError? UnboundLocalError is the local-scope special case: the name IS assigned in the function, but later than the line using it — any assignment in a function makes the name local throughout.
Why does it work in my notebook but not as a script? Notebooks keep state from every cell you’ve ever run, including deleted ones. A fresh script runs only what’s in the file — the missing definition was living in the notebook’s memory, not the code.