Django: "referenced before assignment" but only for some variables - python

I'm writing a small app in Django and I'm keeping the state saved in a few variables I declare out of the methods in views.py. Here is the important part of this file:
from app.playerlist import fullList
auc_unsold = fullList[:]
auc_teams = []
auc_in_progress = []
auc_current_turn = -1
print(auc_in_progress)
def auc_action(request):
data = json.loads(request.GET["data"])
# ...
elif data[0] == "start":
random.shuffle(auc_teams)
print(auc_unsold)
print(auc_in_progress)
auc_in_progress = [None, 0, None]
print(auc_in_progress)
The auc_unsold and auc_teams variables work fine; the auc_in_progress variable is not seen by this method, though, giving the error in the title. If I take out the print statement and let this code assign a value to it, the exception will be thrown somewhere else in the code as soon as I use that variable again.
I have tried making another variable and this new one seems to suffer from this problem as well.
What is happening?
Edit: I found a solution: if I write global auc_in_progress just before the print statements, then everything works fine. If I try writing that as I declare the variable above it doesn't work, though, for some reason.
I am unsatisfied with this, because I don't know why this happens and because I dislike using global like that, but eh. Someone has an explanation?

You should absolutely not be doing this, either your original code or your proposed solution with global.
Anything at module level will be shared across requests, not only for the current user but for all users for that process. So everyone will see the same auction, etc.
The reason for your error is because you assign to that variable within your function, which automatically makes it a local variable: see this question for more details. But the solution recommended there, which is the same as your workaround - ie use global - is not appropriate here; you should store the data somewhere specifically associated with the user, eg the session.

Related

Use function parameter to construct name of object or dataframe

I would like to use a function's parameter to create dynamic names of dataframes and/or objects in Python. I have about 40 different names so it would be really elegant to do this in a function. Is there a way to do this or do I need to do this via 'dict'? I read that 'exec' is dangerous (not that I could get this to work). SAS has this feature for their macros which is where I am coming from. Here is an example of what I am trying to do (using '#' for illustrative purposes):
def TrainModels (mtype):
model_#mtype = ExtraTreesClassifier()
model_#mtype.fit(X_#mtype, Y_#mtype)
TrainModels ('FirstModel')
TrainModels ('SecondModel')
You could use a dictionary for this:
models = {}
def TrainModels (mtype):
models[mtype] = ExtraTreesClassifier()
models[mtype].fit()
First of all, any name you define within your TrainModels function will be local to that function, so won't be accessible in the rest of your program. So you have to define a global name.
Everything in Python is a dictionary, including the global namespace. You can define a new global name dynamically as follows:
my_name = 'foo'
globals()[my_name] = 'bar'
This is terrible and you should never do it. It adds too much indirection to your code. When someone else (or yourself in 3 months when the code is no longer fresh in your mind) reads the code and see 'foo' used elsewhere, they'll have a hard time figuring out where it came from. Code analysis tools will not be able to help you.
I would use a dict as Milkboat suggested.

Exec and variable scope

Python 3.5.1
Could you have a look at the code and the picture.
This is some Django code, but the question relates to Python.
Well, I get the error:
Exception Value:
name 'objects' is not defined
Exception Location:
/home/michael/workspace/pharchive/pharchive/general/templatetags/md_aux.py in get_aux_info, line 17
Line 17 is context = Context({"objects": objects}). In the picture it is visible.
I tried to add objects=[] a little above. But that didn't help. In this case objects was really empty. So, I commented it out.
Well, in the screenshot we can what we get if we stop at the breakpoint. We can see that objects var is really accessible in the context. And it contains something.
Anyway, I got stuck with variable scope.
I don't understand:
1. Why objects=[] didn't help me.
2. Why I get this error whereas objects is accessible.
Could you give me a kick here?
#register.simple_tag
def get_aux_info(master_document, aux_type):
md = MasterDocument.objects.get(pk=master_document)
template = get_template("general/md_tags.html")
#objects = []
code = "objects = md." + aux_type +"s.all()"
exec(code)
context = Context({"objects": objects})
return template.render(context)
A much easier approach than building and executing the code as a string would be to access the attribute using the built-in getattr function. This makes it clearer what's going on and side-steps your current scope issues entirely:
objects = getattr(md, aux_type + 's').all()
Python has extensive support for this kind of introspection; using exec (and eval) is usually a warning that you're approaching something in the wrong way.

How can I make 'uName' display the correct name that the user inputs after clarifying checks?

I am very new to Python, as you can probably tell from the code. To begin, I am trying to have the user input their name and store that in a global variable that I can access all throughout my code...preferably named uName.
What's happening is during the loop cycle, it asks the user 'Is this your name?' after they input the first response. If I hit type anything but 'yes' or 'Yes', it will re-ask them to input the name. BUT, when they finally hit 'Yes', the program prints the very first name they entered.
Also, any tips on code structure or wording is helpful...
game.py
from decisions import *
import decisions
global globalname
globalname = ''
def gameEngine(uName):
looper = 0
while looper == 0:
print ('You said your name is, ') + uName + ('...')
clarifier = raw_input('Is that correct?\n')
if clarifier == 'yes' or clarifier == 'Yes':
namePrinter(answer)
else:
decisions.userDecisions(username)
def namePrinter(uName):
print uName
gameEngine(answer)
decisions.py
username = ''
def userDecisions(inputs):
response = raw_input("Please enter your name...\n>>> ")
return response
answer = userDecisions(username)
The specific issue that you are encountering is that you are first running the contents of decisions.py though the import statement in game.py. Through that, you have set the variable "answer" to be equal to the first name that the user inputs.
Then you are calling the gameEngine function in game.py, supplying the "answer" variable from decisions.py as the argument, which is stored in "uName". Upon the user entering another name the name is not stored anywhere and is thrown out with the following line.
decisions.userDecisions(username)
You can assign the return of that statement to a variable such as "uName", and that will get you closer to what you want to do.
uName = decisions.userDecisions(username)
The next issue is that when you are printing out the name, you are printing out the variable "answer" as opposed to "uName". This is what is mainly causing the issue of the first name always being printed out.
namePrinter(answer)
This could be resolved by passing in the "uName" variable instead.
namePrinter(uName)
Also if you want the final chosen name to be stored in the global variable you can assign the final user chosen name to the gloabl variable after the user confirms that the nameis correct.
globalname = uName
However, you may want to be careful about a few parts of the structure of your code.
First, you may want to try not to use global variables. Instead you should be passing around the name though the functions which use it. If you have other player information that you need to access often, you can create a Player class and object to store that information in a single object which can be passed around into functions as needed.
Second, as the userDecisions function does not use its arguement "inputs", you can remove that arguement, as it isn't used.
Third, you may want to be careful about running code through import statements alone. Generally when you are importing a source file, you should be importing the functions, and not rely upon imports to directly run code. For example you can remove the non-function lines of decisions.py and simply run the following in game.py instead.
gameEngine(decisions.userDecisions())
I reccomend that you look up some resources on functions and passing arguement in Python, as they might be able to explain the underlying concepts a bit better.
You have screwed up with the variables and their scope. Read more about them here.
To give you a perspective regarding the scope of variables concisely, look at this code snippet:
# This is a global variable
a = 0
if a == 0:
# This is still a global variable
b = 1
def my_function(c):
# this is a local variable
d = 3
print(c)
print(d)
# Now we call the function, passing the value 7 as the first and only parameter
my_function(7)
# a and b still exist
print(a)
print(b)
# c and d don't exist anymore -- these statements will give us name errors!
print(c)
print(d)
Regarding your code, you may want to have a look at these issues:
The answer variable is not accessible in the game.py module.
So is the case with username variable in the decisions.userDecisions(username) call.
The decisions.userDecisions(username) call in the gameEngine(uName) method is not storing the response to any variable and hence the response will be lost.
You are declaring global variable globalname but not assigning any value to it (of course other than '').
P.S.: I was tempted to do your homework for you, but then probably this is good enough information for you to learn more. ;)

Modify *existing* variable in `locals()` or `frame.f_locals`

I have found some vaguely related questions to this question, but not any clean and specific solution for CPython. And I assume that a "valid" solution is interpreter specific.
First the things I think I understand:
locals() gives a non-modifiable dictionary.
A function may (and indeed does) use some kind of optimization to access its local variables
frame.f_locals gives a locals() like dictionary, but less prone to hackish things through exec. Or at least I have been less able to do hackish undocumented things like the locals()['var'] = value ; exec ""
exec is capable to do weird things to the local variables, but it is not reliable --e.g. I read somewhere that it doesn't work in Python 3. Haven't tested.
So I understand that, given those limitations, it will never be safe to add extra variables to the locals, because it breaks the interpreter structure.
However, it should be possible to change a variable already existing, isn't it?
Things that I considered
In a function f, one can access the f.func_code.co_nlocals and f.func_code.co_varnames.
In a frame, the variables can be accessed / checked / read through the frame.f_locals. This is in the use case of setting a tracer through sys.settrace.
One can easily access the function in which a frame is --cosidering the use case of setting a trace and using it to "do things" in with the local variables given a certain trigger or whatever.
The variables should be somewhere, preferably writeable... but I am not capable of finding it. Even if it is an array (for interpreter efficient access), or I need some extra C-specific wiring, I am ready to commit to it.
How can I achieve that modification of variables from a tracer function or from a decorated wrapped function or something like that?
A full solution will be of course appreciated, but even some pointers will help me greatly, because I'm stuck here with lots of non writeable dictionaries :-/
Edit: Hackish exec is doing things like this or this
It exists an undocumented C-API call for doing things like that:
PyFrame_LocalsToFast
There is some more discussion in this PyDev blog post. The basic idea seems to be:
import ctypes
...
frame.f_locals.update({
'a': 'newvalue',
'b': other_local_value,
})
ctypes.pythonapi.PyFrame_LocalsToFast(
ctypes.py_object(frame), ctypes.c_int(0))
I have yet to test if this works as expected.
Note that there might be some way to access the Fast directly, to avoid an indirection if the requirements is only modification of existing variable. But, as this seems to be mostly non-documented API, source code is the documentation resource.
Based on the notes from MariusSiuram, I wrote a recipe that show the behavior.
The conclusions are:
we can modify an existing variable
we can delete an existing variable
we can NOT add a new variable.
So, here is the code:
import inspect
import ctypes
def parent():
a = 1
z = 'foo'
print('- Trying to add a new variable ---------------')
hack(case=0) # just try to add a new variable 'b'
print(a)
print(z)
assert a == 1
assert z == 'foo'
try:
print (b)
assert False # never is going to reach this point
except NameError, why:
print("ok, global name 'b' is not defined")
print('- Trying to remove an existing variable ------')
hack(case=1)
print(a)
assert a == 2
try:
print (z)
except NameError, why:
print("ok, we've removed the 'z' var")
print('- Trying to update an existing variable ------')
hack(case=2)
print(a)
assert a == 3
def hack(case=0):
frame = inspect.stack()[1][0]
if case == 0:
frame.f_locals['b'] = "don't work"
elif case == 1:
frame.f_locals.pop('z')
frame.f_locals['a'] += 1
else:
frame.f_locals['a'] += 1
# passing c_int(1) will remove and update variables as well
# passing c_int(0) will only update
ctypes.pythonapi.PyFrame_LocalsToFast(
ctypes.py_object(frame),
ctypes.c_int(1))
if __name__ == '__main__':
parent()
The output would be like:
- Trying to add a new variable ---------------
1
foo
ok, global name 'b' is not defined
- Trying to remove an existing variable ------
2
foo
- Trying to update an existing variable ------
3

Python - Pass variable handle to evaluate

I am writing some program using python and the z3py module.
What I am trying to do is the following: I extract a constraint of an if or a while statement from a function which is located in some other file. Additionally I extract the used variables in the statement as well as their types.
As I do not want to parse the constraint by hand into a z3py friendly form, I tried to use evaluate to do this for me. Therefore I used the tip of the following page: Z3 with string expressions
Now the problem is: I do not know how the variables in the constraint are called. But it seems as I have to name the handle of each variable like the actual variable. Otherwise evaluate won't find it. My code looks like this:
solver = Solver()
# Look up the constraint:
branch = bd.getBranchNum(0)
constr = branch.code
# Create handle for each variable, depending on its type:
for k in mapper.getVariables():
var = mapper.getVariables()[k]
if k in constr:
if var.type == "intNum":
Int(k)
else:
Real(k)
# Evaluate constraint, insert the result and solve it:
f = eval(constr)
solver.insert(f)
solve(f)
As you can see I saved the variables and constraints in classes. When executing this code I get the following error:
NameError: name 'real_x' is not defined
If I do not use the looping over the variables, but instead the following code, everything works fine:
solver = Solver()
branch = bd.getBranchNum(0)
constr = branch.code
print(constr)
real_x = Real('real_x')
int_y = Int('int_y')
f = eval(constr)
print(f)
solver.insert(f)
solve(f)
The problem is: I do not know, that the variables are called "real_x" or "int_y". Furthermore I do not know how many variables there are used, which means I have to use some dynamic thing like a loop.
Now my question is: Is there a way around this? What can I do to tell python that the handles already exist, but have a different name? Or is my approach completely wrong and I have to do something totally different?
This kind of thing is almost always a bad idea (see Why eval/exec is bad for more details), but "almost always" isn't "always", and it looks like you're using a library that was specifically designed to be used this way, in which case you've found one of the exceptions.
And at first glance, it seems like you've also hit one of the rare exceptions to the Keep data out of your variable names guideline (also see Why you don't want to dynamically create variables). But you haven't.
The only reason you need these variables like real_x to exist is so that eval can see them, right? But the eval function already knows how to look for variables in a dictionary instead of in your global namespace. And it looks like what you're getting back from mapper.getVariables() is a dictionary.
So, skip that whole messy loop, and just do this:
variables = mapper.getVariables()
f = eval(constr, globals=variables)
(In earlier versions of Python, globals is a positional-only argument, so just drop the globals= if you get an error about that.)
As the documentation explains, this gives the eval function access to your actual variables, plus the ones the mapper wants to generate, and it can do all kinds of unsafe things. If you want to prevent unsafe things, do this:
variables = dict(mapper.getVariables())
variables['__builtins__'] = {}
f = eval(constr, globals=variables)

Categories

Resources