Search code examples
pythondictionarytkinterdefaultdict

Python defaultdict throws KeyError with default_factory set?


Im working on a small program that uses tkinter to build a gui and a dictionary (that is stored in a json file) to save settings. This is the relevant code:

import:

from collections import defaultdict
from json import dump, load

definition:

def vivdict(preload={}):
    return defaultdict(vivdict, preload)

loading:

try:
    with open('config.json', 'r') as jsonFile:
        self._config = vivdict(load(jsonFile))
except FileNotFoundError:
    self._config = vivdict()

and this is the error i get:

Exception in Tkinter callback
Traceback (most recent call last):
  File "c:/Users/Leonhard/Documents/git/changeMAC/changeMAC.py", line 97, in _cbDropdown
    var = self._config['adapterSettings'][self._currentAdapter]['changeOnRestart']
KeyError: '0003'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\Leonhard\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1883, in __call__
    return self.func(*args)
  File "c:/Users/Leonhard/Documents/git/changeMAC/changeMAC.py", line 110, in _cbCheckbutton
    self._config['adapterSettings'][self._currentAdapter]['changeOnRestart'] = self._tkvar2.get()
KeyError: '0003

Im quite confused because defaultdict with default_factory set shouldn't throw a KeyError? Right?


Solution

  • You are right in saying that defaultdict can't throw a KeyError but look at your code. First do self._config['adapterSettings'] then you index that with [self._currentAdapter]. I think the problem might be that self._config['adapterSettings'] is a normal dict so you need to change your loading script to convert all of the elements that load(jsonFile) returns into defaultdict.

    Change your vivdict function to:

    def vivdict(preload={}):
        for key, value in preload.items():
            if isinstance(value, dict): # Convert all `dict`s to `defaultdict`
                preload[key] = vivdict(value)
        return defaultdict(vivdict, preload)