You're trying to assign to a global. Python makes you explicitly do that.
def progress_print(chunk):
global elapsed
elapsed += len(chunk)
In general this is a pretty good sign that you need to refactor your code until you don't need globals any more.
Note that assignment and access are different beasts - the former requires you to explicitly declare globals, the latter does not. Case in point, your total
variable is another global but you do not attempt to assign to it, hence python does not complain.
The Programming FAQ goes into more detail:
What are the rules for local and global variables in Python?
In Python, variables that are only referenced inside a function are
implicitly global. If a variable is assigned a new value anywhere
within the function’s body, it’s assumed to be a local. If a variable
is ever assigned a new value inside the function, the variable is
implicitly local, and you need to explicitly declare it as ‘global’.
Though a bit surprising at first, a moment’s consideration explains
this. On one hand, requiring global for assigned variables provides a
bar against unintended side-effects. On the other hand, if global was
required for all global references, you’d be using global all the
time. You’d have to declare as global every reference to a built-in
function or to a component of an imported module. This clutter would
defeat the usefulness of the global declaration for identifying
side-effects.