Repository: opsdroid/opsdroid

Issue:
Treat skills with explicit path as local
When defining custom skills in the configuration file, we have the ability to define a path for the skill. When this path is defined, we should be able to safely assume that the skill is local and accessible by the bot and therefore should not need to be cached.

My suggestion is to treat skills which have been explicitly defined with a local path as local skills and automatically apply the `nocache=true` unless caching is otherwise defined in the config.


Discussion:
I like that idea and it does make sense to have the nocache set to true if the path is available in the config when you are developing the skill. 

We need to check with the community if this is something we should implement as well, the reason for that being that some devs are probably using their own skills with opsdroid and perhaps having the nocache set could cause a burden to them when opsdroid is run.
To add some context this issue has come from a chat on Gitter/Matrix. We were saying that for anything specific with `path` should default to `no-cache: true` and anything with `repo` should default to `no-cache: false`. The user is still free to configure in whichever way they like.

My only concern is that this inconsistency may cause confusion. However as the current caching is already causing confusion I think this is probably a better approach.
Ah okay I checked Gitter but is nothing there about this. Are we using matrix more?
The two are bridged so you can use either. We were discussing it in general yesterday, but it might've gotten lost in the noise :).
This just came up again. We really should do this.

Resolving patch:
diff --git a/opsdroid/loader.py b/opsdroid/loader.py
--- a/opsdroid/loader.py
+++ b/opsdroid/loader.py
@@ -114,8 +114,8 @@ def import_module(config):
         _LOGGER.error(_("Failed to load %s: %s"), config["type"], config["module_path"])
         return None
 
-    @staticmethod
-    def check_cache(config):
+    @classmethod
+    def check_cache(cls, config):
         """Remove module if 'no-cache' set in config.
 
         Args:
@@ -124,10 +124,29 @@ def check_cache(config):
         """
         if "no-cache" in config and config["no-cache"]:
             _LOGGER.debug(_("'no-cache' set, removing %s"), config["install_path"])
-            if os.path.isdir(config["install_path"]):
-                shutil.rmtree(config["install_path"])
-            if os.path.isfile(config["install_path"] + ".py"):
-                os.remove(config["install_path"] + ".py")
+            cls.remove_cache(config)
+
+        if "no-cache" not in config and cls._is_local_module(config):
+            _LOGGER.debug(
+                _(
+                    "Removing cache for local module %s, set 'no-cache: false' to disable this."
+                ),
+                config["install_path"],
+            )
+            cls.remove_cache(config)
+
+    @staticmethod
+    def remove_cache(config):
+        """Remove module cache.
+
+        Args:
+            config: dict of config information related to the module
+
+        """
+        if os.path.isdir(config["install_path"]):
+            shutil.rmtree(config["install_path"])
+        if os.path.isfile(config["install_path"] + ".py"):
+            os.remove(config["install_path"] + ".py")
 
     @staticmethod
     def is_builtin_module(config):


Test patch:
diff --git a/tests/test_loader.py b/tests/test_loader.py
--- a/tests/test_loader.py
+++ b/tests/test_loader.py
@@ -298,6 +298,28 @@ def test_loading_intents(self):
         self.assertEqual(intent_contents, loaded_intents)
         shutil.rmtree(config["install_path"], onerror=del_rw)
 
+    def test_check_cache_clears_local_by_default(self):
+        config = {}
+        config["path"] = "abc"
+        config["install_path"] = os.path.join(
+            self._tmp_dir, os.path.normpath("test/module")
+        )
+        os.makedirs(config["install_path"], mode=0o777)
+        ld.Loader.check_cache(config)
+        self.assertFalse(os.path.isdir(config["install_path"]))
+
+    def test_check_cache_clear_local_by_default_disabled(self):
+        config = {}
+        config["no-cache"] = False
+        config["path"] = "abc"
+        config["install_path"] = os.path.join(
+            self._tmp_dir, os.path.normpath("test/module")
+        )
+        os.makedirs(config["install_path"], mode=0o777)
+        ld.Loader.check_cache(config)
+        self.assertTrue(os.path.isdir(config["install_path"]))
+        shutil.rmtree(config["install_path"], onerror=del_rw)
+
     def test_loading_intents_failed(self):
         config = {}
         config["no-cache"] = True