UnboundLocalError in TKinter, SQLAlchemy script.

Steven D'Aprano steve+comp.lang.python at pearwood.info
Wed Mar 18 08:02:02 EDT 2015


On Wed, 18 Mar 2015 01:41 pm, Chris Kavanagh wrote:

> I have a simple script that takes user input (for an Employee) such as
> name, age, etc then puts in an sqlite3 database. The script worked fine
> until I realized one problem. The age input field is defined in SQLAlchemy
> as an Integer, so if a user inputs a string instead of a number in that
> field, an error will occur (in IDLE). . .
> 
> So, I put a try: except clause in the code for the age field in the
> add_data method. . .
> try:
>             age = self.age_var.get()
>         except ValueError:
>             showinfo("Error:", "Please Enter A Number In Age Field.")
> 
> If the user inputs a string (instead of an Int) the showinfo dialogue box
> opens as it should, but when the user clicks "ok" in the box, I get the
> error "UnboundLocalError: local variable 'age' referenced before
> assignment".

As much as I love guessing games, I reckon I have about a 2/3 chance of
guessing right, but you will get MUCH better results by posting the
*entire* traceback, not just the last line. In particular, look at the line
of code shown. That's where the problem expresses itself, and that may give
good hints as to where the problem actually is.


> I tried using a default value above the try: except clause 
> (age=0), which corrects the error, but causes "0" to be saved in the
> database instead of letting the user choose another value. I want to
> figure why I get the error (with the try: except clause) and what exactly
> to do about it. Thanks in advance for any help!

And again, guessing games are lots of fun, but showing us the actual code is
better. You might think it is "obvious" what you are talking about, but the
change that you think is obvious and the change that I think is obvious are
not necessarily that same.

 
> Here's the original code (without the try: except clause above). . .Just
> put the try: except clause in the age variable in the add_data method as
> shown above to get the error.

We don't need to see almost 300 lines of irrelevant GUI code that has
nothing to do with the problem. Here's the add_data method:


>     def add_data(self):
>         name = self.name_var.get()
>         age = self.age_var.get()
>         addr = self.address_var.get()
>         city = self.city_var.get()
>         state = self.state_var.get()
>         zip = self.zip_var.get()
>         ssn = self.ssn_var.get()
>         phone = self.phone_var.get()
>         cell = self.cell_var.get()
>         # create new Employee in .db
>         new_person = Employee(name=name, age=age, address=addr, city=city,
>                               state=state, zip=zip, ssn=ssn, phone=phone,
>                               cell=cell) 
>         session.add(new_person)
>         session.commit()
>         session.close()
>         self.callback()
>         return

Most of that is irrelevant too. My guess is that it can be simplified to
this:

    def add_data(self):
        age = self.age_var.get()
        new_person = Employee(name=name, age=age, address=addr, city=city,
                              state=state, zip=zip, ssn=ssn, phone=phone,
                              cell=cell)


Where are we supposed to put the try: except clause? My guess is:

    def add_data(self):
        try:
            age = self.age_var.get()
        except ValueError:
            showinfo("Error:", "Please Enter A Number In Age Field.")
        new_person = Employee(name=name, age=age, address=addr, city=city,
                              state=state, zip=zip, ssn=ssn, phone=phone,
                              cell=cell)

and the error occurs on the call to Employee. Is that right? 

Follow the program logic. The line of code:

    age = self.age_var.get()

tries to run, and fails. Since the line of code never completes, "age" never
gets a value set. Then the dialog box is shown, and your program blindly
continues. Eventually it reaches the Employee() line, and tries to use the
value of "age". But it doesn't exist, so you get the UnboundLocalVariable
error.

The right solution here is to set the age_var field in the GUI to force it
to be an integer. By the time you get to running the add_data method, it is
too late to pause and let the user re-type a new value in the field. You
could try this:

http://tkinter.unpythonic.net/wiki/ValidateEntry


Another option is to make the add_data method simply halt when the age is
not an integer, and do nothing. Then the user will have to click the Add
button again to make it work:


    def add_data(self):
        try:
            age = self.age_var.get()
        except ValueError:
            showinfo("Error:", "Please Enter A Number In Age Field.")
            return  # Return early, do nothing.
        new_person = Employee(name=name, age=age, address=addr, city=city,
                              state=state, zip=zip, ssn=ssn, phone=phone,
                              cell=cell)





-- 
Steven




More information about the Python-list mailing list