Reman
Reman

Reputation: 8119

How to assign global variables from dictionary?

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:

  1. The value of the 1st field in the sublist (associated with the key) must be a new global variable.

  2. 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

Answers (4)

Will Hardy
Will Hardy

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

TigerhawkT3
TigerhawkT3

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

Jean-François Fabre
Jean-François Fabre

Reputation: 140286

There are many problems here

  • one loop too much in the dictionary, desyncing values
  • using 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

Prune
Prune

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

Related Questions