Repository: lutris/lutris

Issue:
[JSON] ModuleNotFoundError: No module named 'lutris.runners.citra'
**Describe the bug**

With the new [jsonfied](https://github.com/lutris/lutris/commit/77b239ca369b68e1ec8ef78b4708b79b9576c502) runners one can't save when you made changes to a game. IE opening a Citra game's configuration window, and pressing save gives the following error on the terminal:

```
Traceback (most recent call last):
  File "~/src/lutris/lutris/gui/config/common.py", line 503, in on_save
    if not self.is_valid():
  File "~/src/lutris/lutris/gui/config/common.py", line 481, in is_valid
    runner_module = importlib.import_module("lutris.runners." + self.runner_name)
  File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'lutris.runners.citra'
```




Discussion:

Resolving patch:
diff --git a/lutris/gui/config/common.py b/lutris/gui/config/common.py
--- a/lutris/gui/config/common.py
+++ b/lutris/gui/config/common.py
@@ -1,14 +1,10 @@
 """Shared config dialog stuff"""
-# Standard Library
 # pylint: disable=no-member,not-an-iterable
-import importlib
 import os
 from gettext import gettext as _
 
-# Third Party Libraries
 from gi.repository import Gdk, GLib, Gtk, Pango
 
-# Lutris Modules
 from lutris import runners, settings
 from lutris.cache import get_cache_path, save_cache_path
 from lutris.config import LutrisConfig, make_game_config_id
@@ -22,6 +18,7 @@
 from lutris.util.linux import gather_system_info_str
 from lutris.util.log import logger
 from lutris.util.strings import slugify
+from lutris.runners import import_runner
 
 
 # pylint: disable=too-many-instance-attributes
@@ -478,8 +475,7 @@ def is_valid(self):
             ErrorDialog(_("Steam AppId not provided"))
             return False
         invalid_fields = []
-        runner_module = importlib.import_module("lutris.runners." + self.runner_name)
-        runner_class = getattr(runner_module, self.runner_name)
+        runner_class = import_runner(self.runner_name)
         runner_instance = runner_class()
         for config in ["game", "runner"]:
             for k, v in getattr(self.lutris_config, config + "_config").items():
diff --git a/lutris/runners/json.py b/lutris/runners/json.py
--- a/lutris/runners/json.py
+++ b/lutris/runners/json.py
@@ -4,9 +4,12 @@
 
 from lutris.runners.runner import Runner
 from lutris.util import system
+from lutris.util import datapath
+from lutris import settings
 
 JSON_RUNNER_DIRS = [
-    os.path.join(os.path.dirname(__file__), "json")
+    os.path.join(datapath.get(), "json"),
+    os.path.join(settings.RUNNER_DIR, "json"),
 ]
 
 
@@ -53,6 +56,8 @@ def play(self):
 def load_json_runners():
     json_runners = {}
     for json_dir in JSON_RUNNER_DIRS:
+        if not os.path.exists(json_dir):
+            continue
         for json_path in os.listdir(json_dir):
             if not json_path.endswith(".json"):
                 continue


Test patch: