7

I have the following python script for an upload that needs to show percent done. I am having trouble incrementing the variable that tracks the amount of data transferred. I get an
UnboundLocalError: local variable 'intProgress' referenced before assignment
error. Yet if I try to print this variable it prints fine so it seems that it is referenced.

import os, sys, ftplib
pathname = 'C:/Paradigm1/1.PNG'
intFileSize = os.path.getsize(pathname)
intPercentDone = 0
intProgress = 0

def callback(p):
    intProgress = intProgress + 1024
    ##sys.stdout.write(str(intProgress))
    sys.stdout.write("-")
session = ftplib.FTP('Server','UserName','Password')
f = open(pathname,'rb')# file to send
session.storbinary('STOR /Ftp Accounts/PublicDownloads/test.png', f, 1024, callback)
f.close()
HelloW
  • 1,587
  • 2
  • 13
  • 24

2 Answers2

16

If you want the callback() function to change the global variable intProgress, you have to declare it as global in the function...

def callback(p):
    global intProgress
    intProgress = intProgress + 1024
    ##sys.stdout.write(str(intProgress))
    sys.stdout.write("-")

...otherwise it'll assume intProgress is a local variable, and get confused by the fact that you're trying to reference it when setting it.

Aya
  • 39,884
  • 6
  • 55
  • 55
4

intProgress = inside a function forces Python to treat it as a local variable overshadowing the variable from the outer scope.

To avoid mutable globals, you could create a closure:

import os
import sys

def make_callback(filesize):
    total = [0] # use list to emulate nonlocal keyword
    width = len(str(filesize))

    def report_progress(block):
        total[0] += len(block)
        sys.stderr.write("\r{:{}d} / {}".format(total[0], width, filesize))

    return report_progress

def main():
    # ...
    ftp.storbinary(..., make_callback(os.path.getsize(filename)))

main()
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • Thanks, that was exactly the paradigm I was searching for. I did have to add a "nonlocal" declaration at the start of the report_progress() function. Specifically, that was "nonlocal total". – Matthew Walker Jun 04 '20 at 09:06
  • @MatthewWalker: the code in the answer is for Python 2 (see the [tag:python2.7] tag on the question. On Python 3, you don't need the list hack, you could use `nonlocal total` and then `total = 0`, and `total += len(block)` there. – jfs Jun 04 '20 at 16:56