Reputation: 8119
This is my dictionary:
vimvar = {'startline' : [ 'startline' , 'int(vim.eval("s:StartLine"))' ],
'startline-1' : [ 'startline' , 'int(vim.eval("s:StartLine"))-1'],
'endline' : [ 'endline' , 'int(vim.eval("s:EndLine"))' ],
'gcase' : [ 'gCASE' , 'vim.eval("g:CASE")' ],
'akeyw' : [ 'akeyw' , 'vim.eval("a:keyw")' ]
}
This is my checklist:
importlist = ['startline', 'gcase', 'akeyw']
What I want to do is to check if a value in importlist
is present as key in vimvar
dictionary.
If yes than:
The value of the 1st field in the sublist (associated with the key) must be a new global variable.
The value of the 2nd field in the sublist (associated with the key) must be executed. It imports a variable from my texteditor vim.
I created this code to realize above:
for n in importlist:
for key,val in vimvar.items():
if n in vimvar:
exec('global ' + vimvar[key][0])
exec(vimvar[val][0] + '=vimvar[val][1]')
But whatever I do it gives errors
undefined variable 'sa'
undefined variable 'gCASE', unhashable type list
etc
What did I wrong?
Upvotes: 2
Views: 3199
Reputation: 14846
Two problems:
Firstly, you're looping twice, when you only need to loop once:
for n in importlist:
if n in vimvar:
name, val = vimvar[n]
...
Secondly, you don't need to use exec()
for assignment. Either assign to a container object (recommended):
data = {}
for n in importlist:
if n in vimvar:
name, val = vimvar[n]
data[name] = exec(val)
Or change the globals()
dict (if you really needed it to be a global variable):
for n in importlist:
if n in vimvar:
name, val = vimvar[n]
globals()[name] = exec(val)
If you can, try to avoid storing it in a global variable. And only use exec()
on strings you can trust, is there any reason why this code can't be in your source? Eg
vimvar = {
'startline': ['startline', int(vim.eval("s:StartLine"))],
'startline-1': ['startline', int(vim.eval("s:StartLine"))-1],
'endline': ['endline', int(vim.eval("s:EndLine"))],
'gcase': ['gCASE', vim.eval("g:CASE")],
'akeyw': ['akeyw', vim.eval("a:keyw")]
}
for n in importlist:
if n in vimvar:
name, val = vimvar[n]
globals()[name] = val
Upvotes: 2
Reputation: 49330
Here is how you would do this without global
or 'global'
or exec
.
First, if you absolutely need a global variable and you can't save a returned value (the ordinary way in which a function provides a value to its caller), use a mutable object, like a dictionary (see How do I create a variable number of variables?).
Secondly, the values you want to assign to those global variables (which is now a dictionary you can simply mutate) can be ordinary expressions instead of strings to add to an =
and execute with exec
. The values in the vimvar
dictionary will be the desired "variable name" (now a key), the functions you want to use, and the final offset.
my_global_dict = {}
def f():
vimvar = {'startline' : ["s:StartLine", (vim.eval, int), 0],
'startline-1' : ["s:StartLine", (vim.eval, int), -1],
'endline' : ["s:EndLine", (vim.eval, int), 0],
'gcase' : ["g:CASE", (vim.eval,), 0],
'akeyw' : ["a:keyw", (vim.eval,), 0]
}
importlist = ['startline', 'gcase', 'akeyw']
for k in importlist:
if k in vimvar:
s,funcs,offset = vimvar[k]
for func in funcs:
s = func(s)
if offset:
s += offset
my_global_dict[k] = s
Upvotes: 1
Reputation: 140286
There are many problems here
exec
and global
when you only perform calls to vim.eval
command.I would drop all that exec
stuff and re-do a vim evaluation engine:
vimvar = {'startline' : [ 'startline' , "s:StartLine" , 0],
'startline-1' : [ 'startline' , 's:StartLine' , -1 ],
'endline' : [ 'endline' , "s:EndLine" , 0 ],
'gcase' : [ 'gCASE' , "g:CASE" ],
'akeyw' : [ 'akeyw' , "a:keyw" ]
}
importlist = ['startline', 'gcase', 'akeyw']
results = dict()
for n in importlist:
if n in vimvar: # key found in dict (no need for inner loop)
data = vimvar[n] # get value list
varname = data[0] # dict key name
command = data[1] # vim command to call
# call vim command
result = vim.eval(command)
if len(data)>2:
# perform optional conversion / offset, could work for string too
to_add = data[2]
result = type(to_add)(result) + to_add
# create dictionary entry
results[varname] = result
print(results)
note that the dictionary now only contains the parameter for the vim command. There's an extra parameter in the value list when a conversion/add is needed. If it's 0, result is just converted to integer, if it's -1, it's converted then 1 is substracted. Not sure it would cover all your commands, but certainly gives some ideas.
Then, the data is not stored in variables, but in a dictionary of variables: example:
{'akeyw': 'something', 'gCASE': 'other', 'startline': 10}
you can access it very easily, and it doesn't involve bad coding practice.
Upvotes: 2
Reputation: 77880
The problem is at line
exec(vimvar[val][0] + '=vimvar[val][1]')
I think you're confused about indexing a dictionary: you tried to use val as an index, and that just doesn't make sense. For instance, one of your references expands to:
vimvar[['akeyw', 'vim.eval("a:keyw")']]
First, you can't use a list as a dictionary key -- that's what the "unhashable" problem is. Second, there is no such element in the dictionary keys.
Perhaps you just want val[0] at that point? Since you're doing some funny stuff, I'm not certain what you want to accomplish. As TigerHawk already pointed out, assigning a global this way is generally a Bad Practice.
Upvotes: 1