Why this is bad practice?
When you pass user-provided input directly to exec(), you are essentially allowing arbitrary code execution.
Like;
funcany("varInput; __import__('os').system('rm -rf /')")
The executed code runs with the same privileges as your application - code injection
Coming to your issue:
When you use exec() inside a function, the executed code operates in the function's local namespace by default.
In python:
a variable that gets assigned a value within a function is treated as local to that function unless explicitly declared global
def anyFunction(var):
for i in range(3):
try:
exec(var+"+=1")
except NameError:
exec("global "+var+"\n"+var+"=1")
First iteration - The variable doesn exist -> exception -> creates global variable with value 1
Second iteration - Tries to increment a local variable (not the global one) -> fails silently
Third iteration -> same issue as second
Instead you use a dictionary to manage your variables
def safe_increment(var_name, count=3, state=None):
if state is None:
state = {}
if var_name not in state:
state[var_name] = 0
for _ in range(count):
state[var_name] += 1
return state[var_name], state
var_state = {}
final_value, var_state = safe_increment("varInput", state=var_state)
print(f"varInput = {final_value}")
print(f"State: {var_state}")
Dynamic code execution via exec() and eval() should be avoided whenever possible especially with user input as it's one of the most severe security vulnerabilities in python applications.
exec(var+"+=1")in the try block, Python is executing this code in the function's local scope, but without a global declaration