Increment variable in callback during upload - python

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()

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.

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()

Related

Variable change does not update on 2nd file

Im using 2 files here. Say the variable is in the 1st file and Im using it on the 2nd. It has the initialized value of it but when I change its value the 2nd file does not recognize it. How can I update the variable so that the 2nd file reads its current value?
first module:
var = 0
def plus():
global var
var += 1
print(var)
second module(used tkinter for example)
from tkinter import *
from kk import *
root = Tk()
root.geometry("400x400+15+15")
btn = Button(root, text = "1", command = plus)
btn.pack()
def pr():
print(var)
btn1 = Button(root, text = "2", command = pr)
btn1.pack()
root.mainloop()
Following up from my comment, when you do from kk import var (or import *) you are effectively copying the variable value from the other module.
You need to copy a reference to the same object (instead of its value), you can achieve this using an object wrapper.
Try this:
class VarWrapper:
def __init__(self, initial_value):
self._value = initial_value
def __repr__(self):
return repr(self._value)
var = VarWrapper(0)
def plus():
global var
var._value += 1
print(var)
you need to import the first file function and execute it.
suppose first file name is first.py then import in second file.
from first import *
then it will work.
now you have access to first file. and to update variable use plus function.
it doesnt seem like you called plus(), if you call it it might work
#revision
perhaps if you do this?
def update ():
varInternal = var
def plusInternal ():
plus()
update()
I tried just importing again the wanted variable inside pr like so
def pr():
from kk import var
print(var)
It works, however I want to know if this way is appropriate to conventions

Python Tkinter get result of askopenfilenames()

I use Tkinter as GUI library and i need to get list of files which i choose by calling askopenfilenames.
Here is my function^
def choose_file(file_list)
file_list = fd.askopenfilenames()
file_list = ()
b1 = Button(command = lambda: choose_file(file_list))
file_list - is variable in outer scope. But after calling function, this var is empty. What i did wrong?
Try:
import tkinter.filedialog as fd
import tkinter as tk
def choose_file():
global file_list
file_list = fd.askopenfilenames()
file_list = ()
root = tk.Tk()
b1 = tk.Button(root, text="Click me", command=choose_file)
b1.pack()
root.mainloop()
The variable file_list is not global as it is immutable. To make it global you have to add global file_list to the start of your function definition. For more info read: Why you can change immutable if it's global.
You have two different file_list variables in your code. One at global scope
file_list = ()
and one at function scope.
def choose_file(file_list):
With
file_list = fd.askopenfilenames()
you are assigning the list returned by askopenfilenames to the function scope variable - everywhere in the function, file_list will have the list from askopenfilenames as value, what you can see by adding print(file_list) to your function twice, one time at the beginning and one time at the end.
To modify the global variable instead of the local one, you can either make the local (function scope) variable global
file_list = ()
def choose_file():
global file_list
file_list = fd.askopenfilenames()
b1 = Button(command = choose_file)
where the variable initialization must be moved before the function declaration (I think else it gives an UnboundLocalError or something), the lambda can be removed, and you do not need to pass file_list as an argument.

Are there cases where global keyword is necessary in python?

I want to know if there are some cases where declaring global keyword is necessary in python.
Yes, there are some cases where global is neccessary.
Have a look at this code, which will work fine:
i = 42 # this is a global var
def f():
print(i)
But what if you would like to edit i (which is a global variable).
If you do this, you get an error:
i = 42 # this is a global var
def f():
i += 1 # this will not work
print(i)
We can only access i. If python compiles the function to bytecode it detects an assignment to a variable and it assumes it is a local variable. But this is not the case here (it is a global variable). Therefore if we also want to modify the global var i we must use the global keyword.
i = 42 # this is a global var
def f():
global i
i += 1 # this will change the global var without error
print(i)
When you have shared resources and want to make changes in global one.
a = 0
def add_five():
global a
a += 5
def remove_two():
global a
a -= 2
add_five() # a = 5
add_five() # a = 10
add_five() # a = 15
remove_two() # a = 13

Python Global Variables - Not Defined?

I'm running into an issue where a global variable isn't "remembered" after it's modified in 2 different functions. The variable df is supposed to be a data frame, and it doesn't point to anything until the user loads in the right file. This is similar to something I have (using pandas and tkinter):
global df
class World:
def __init__(self, master):
df = None
....
def load(self):
....
df = pd.read_csv(filepath)
def save(self):
....
df = df.append(...)
save() is always called after load(). Thing is, when I call save(), I get the error that "df is not defined." I thought df got its initial assignment in init(), and then got "updated" in load()? What am I doing wrong here?
You have to use global df inside the function that needs to modify the global variable. Otherwise (if writing to it), you are creating a local scoped variable of the same name inside the function and your changes won't be reflected in the global one.
p = "bla"
def func():
print("print from func:", p) # works, readonly access, prints global one
def func1():
try:
print("print from func:", p) # error, python does not know you mean the global one
p = 22 # because function overrides global with local name
except UnboundLocalError as unb:
print(unb)
def func2():
global p
p = "blubb" # modifies the global p
print(p)
func()
func1()
print(p)
func2()
print(p)
Output:
bla # global
print from func: bla # readonly global
local variable 'p' referenced before assignment # same named local var confusion
bla # global
blubb # changed global
for anyone coming here using python3 - try using nonlocal instead of global - a new construct introduced in python3 which allows you to mutate and read global variables in local scope
You have to use the global keyword inside the function rather than outside. All the df that you have defined inside your function are locally scoped. Here is the right way -
df = pd.DataFrame() # No need to use global here
def __init__(self, master):
global df # declare here
df = None
....
def load(self):
global df # declare here
....
df = pd.read_csv(filepath)
def save(self):
global df # declare here
....
df = df.append(...)

Python FTP hangs in callback

I'm using ftplib to create a simple script to push out a file to multiple IP addresses, all set up as FTP servers. I wanted to display progress in the file upload process, but I'm having an issue. I use the callback argument of FTP.storbinary() and it works with something like this:
count = 0
def update(block):
count2 = str(count + 1)
print count2
However, if I try to do any arithmetic outside of a str() call, the program hangs. So the following doesn't work:
count = 0
def update(block):
count += 1
print count
Even wrapping count in a str() call doesn't work. It just hangs on the first call.
If you just try calling update yourself, instead of passing it to FTP.storbinary, you'll see the problem immediately:
>>> update('')
UnboundLocalError: local variable 'count' referenced before assignment
If you want to update a global variable, you have to mark it global explicitly:
def update(block):
global count
count += 1
print count
See the FAQ entry Why am I getting an UnboundLocalError when the variable has a value? and the following question What are the rules for local and global variables in Python?, and the docs on global, for more details.
A better way to solve this would be to write a class:
class FtpHandler(object):
def __init__(self):
self.count = 0
def update(self, block):
self.count += 1
print self.count
Then, to use it, you construct an instance of the class, and pass a bound method instead of a plain function to the FTP code. For example, instead of this:
ftp = ftplib.FTP(...)
# ...
ftp.storbinary(spam, eggs, callback=update)
… do this:
myhandler = FtpHandler()
ftp = ftplib.FTP(...)
# ...
ftp.storbinary(spam, eggs, callback=myhandler.update)
It doesn't just hang, it produces an Exception (specifically an UnboundLocalError). You're trying to modify a global variable inside of a function; to do this the variable must be declared global:
count = 0
def update(block):
global count
count += 1
print count
This is almost always a sign of bad design, in your case it would probably be better to use a class with an attribute:
class MyCallbackHandler(object):
def __init__(self):
self.count = 0
def update(self, block):
self.count += 1
#... etc.

Categories