We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
skill keep reloading indefinitely if there are files with a future date
due to a temporary problem in my system, i ended up with a skill with __init.py modification time set in the future. this caused that skill to constantly be reloaded my mycroft core, and unloaded just after the loading was complted. took some hours of debug to understand this was actually the problem.

perhaps skills with files with modification date from the future should just be stopped from loading and have a debug log about it?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mycroft/skills/skill_loader.py`
Content:
```
1 # Copyright 2019 Mycroft AI Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15 """Periodically run by skill manager to load skills into memory."""
16 import gc
17 import importlib
18 import os
19 from os.path import dirname
20 import sys
21 from time import time
22 
23 from mycroft.configuration import Configuration
24 from mycroft.messagebus import Message
25 from mycroft.skills.settings import save_settings
26 from mycroft.util.log import LOG
27 
28 from .settings import SettingsMetaUploader
29 
30 SKILL_MAIN_MODULE = '__init__.py'
31 
32 
33 def remove_submodule_refs(module_name):
34     """Ensure submodules are reloaded by removing the refs from sys.modules.
35 
36     Python import system puts a reference for each module in the sys.modules
37     dictionary to bypass loading if a module is already in memory. To make
38     sure skills are completely reloaded these references are deleted.
39 
40     Arguments:
41         module_name: name of skill module.
42     """
43     submodules = []
44     LOG.debug('Skill module'.format(module_name))
45     # Collect found submodules
46     for m in sys.modules:
47         if m.startswith(module_name + '.'):
48             submodules.append(m)
49     # Remove all references them to in sys.modules
50     for m in submodules:
51         LOG.debug('Removing sys.modules ref for {}'.format(m))
52         del(sys.modules[m])
53 
54 
55 def load_skill_module(path, skill_id):
56     """Load a skill module
57 
58     This function handles the differences between python 3.4 and 3.5+ as well
59     as makes sure the module is inserted into the sys.modules dict.
60 
61     Arguments:
62         path: Path to the skill main file (__init__.py)
63         skill_id: skill_id used as skill identifier in the module list
64     """
65     module_name = skill_id.replace('.', '_')
66 
67     remove_submodule_refs(module_name)
68 
69     spec = importlib.util.spec_from_file_location(module_name, path)
70     mod = importlib.util.module_from_spec(spec)
71     sys.modules[module_name] = mod
72     spec.loader.exec_module(mod)
73     return mod
74 
75 
76 def _get_last_modified_time(path):
77     """Get the last modified date of the most recently updated file in a path.
78 
79     Exclude compiled python files, hidden directories and the settings.json
80     file.
81 
82     Arguments:
83         path: skill directory to check
84 
85     Returns:
86         int: time of last change
87     """
88     all_files = []
89     for root_dir, dirs, files in os.walk(path):
90         dirs[:] = [d for d in dirs if not d.startswith('.')]
91         for f in files:
92             ignore_file = (
93                     f.endswith('.pyc') or
94                     f == 'settings.json' or
95                     f.startswith('.') or
96                     f.endswith('.qmlc')
97             )
98             if not ignore_file:
99                 all_files.append(os.path.join(root_dir, f))
100 
101     # check files of interest in the skill root directory
102     if all_files:
103         return max(os.path.getmtime(f) for f in all_files)
104     else:
105         return 0
106 
107 
108 class SkillLoader:
109     def __init__(self, bus, skill_directory):
110         self.bus = bus
111         self.skill_directory = skill_directory
112         self.skill_id = os.path.basename(skill_directory)
113         self.load_attempted = False
114         self.loaded = False
115         self.last_modified = 0
116         self.last_loaded = 0
117         self.instance = None
118         self.active = True
119         self.config = Configuration.get()
120 
121     @property
122     def is_blacklisted(self):
123         """Boolean value representing whether or not a skill is blacklisted."""
124         blacklist = self.config['skills'].get('blacklisted_skills', [])
125         if self.skill_id in blacklist:
126             return True
127         else:
128             return False
129 
130     def reload_needed(self):
131         """Load an unloaded skill or reload unloaded/changed skill.
132 
133         Returns:
134              bool: if the skill was loaded/reloaded
135         """
136         try:
137             self.last_modified = _get_last_modified_time(self.skill_directory)
138         except FileNotFoundError as e:
139             LOG.error('Failed to get last_modification time '
140                       '({})'.format(repr(e)))
141             self.last_modified = self.last_loaded
142 
143         modified = self.last_modified > self.last_loaded
144 
145         # create local reference to avoid threading issues
146         instance = self.instance
147 
148         reload_allowed = (
149                 self.active and
150                 (instance is None or instance.reload_skill)
151         )
152         return modified and reload_allowed
153 
154     def reload(self):
155         LOG.info('ATTEMPTING TO RELOAD SKILL: ' + self.skill_id)
156         if self.instance:
157             self._unload()
158         return self._load()
159 
160     def load(self):
161         LOG.info('ATTEMPTING TO LOAD SKILL: ' + self.skill_id)
162         return self._load()
163 
164     def _unload(self):
165         """Remove listeners and stop threads before loading"""
166         self._execute_instance_shutdown()
167         if self.config.get("debug", False):
168             self._garbage_collect()
169         self.loaded = False
170         self._emit_skill_shutdown_event()
171 
172     def unload(self):
173         if self.instance:
174             self._execute_instance_shutdown()
175         self.loaded = False
176 
177     def activate(self):
178         self.active = True
179         self.load()
180 
181     def deactivate(self):
182         self.active = False
183         self.unload()
184 
185     def _execute_instance_shutdown(self):
186         """Call the shutdown method of the skill being reloaded."""
187         try:
188             self.instance.default_shutdown()
189         except Exception:
190             log_msg = 'An error occurred while shutting down {}'
191             LOG.exception(log_msg.format(self.instance.name))
192         else:
193             LOG.info('Skill {} shut down successfully'.format(self.skill_id))
194 
195     def _garbage_collect(self):
196         """Invoke Python garbage collector to remove false references"""
197         gc.collect()
198         # Remove two local references that are known
199         refs = sys.getrefcount(self.instance) - 2
200         if refs > 0:
201             log_msg = (
202                 "After shutdown of {} there are still {} references "
203                 "remaining. The skill won't be cleaned from memory."
204             )
205             LOG.warning(log_msg.format(self.instance.name, refs))
206 
207     def _emit_skill_shutdown_event(self):
208         message = Message(
209             "mycroft.skills.shutdown",
210             data=dict(path=self.skill_directory, id=self.skill_id)
211         )
212         self.bus.emit(message)
213 
214     def _load(self):
215         self._prepare_for_load()
216         if self.is_blacklisted:
217             self._skip_load()
218         else:
219             skill_module = self._load_skill_source()
220             if skill_module and self._create_skill_instance(skill_module):
221                 self._check_for_first_run()
222                 self.loaded = True
223 
224         self.last_loaded = time()
225         self._communicate_load_status()
226         if self.loaded:
227             self._prepare_settings_meta()
228         return self.loaded
229 
230     def _prepare_settings_meta(self):
231         settings_meta = SettingsMetaUploader(self.skill_directory,
232                                              self.instance.name)
233         self.instance.settings_meta = settings_meta
234 
235     def _prepare_for_load(self):
236         self.load_attempted = True
237         self.loaded = False
238         self.instance = None
239 
240     def _skip_load(self):
241         log_msg = 'Skill {} is blacklisted - it will not be loaded'
242         LOG.info(log_msg.format(self.skill_id))
243 
244     def _load_skill_source(self):
245         """Use Python's import library to load a skill's source code."""
246         main_file_path = os.path.join(self.skill_directory, SKILL_MAIN_MODULE)
247         if not os.path.exists(main_file_path):
248             error_msg = 'Failed to load {} due to a missing file.'
249             LOG.error(error_msg.format(self.skill_id))
250         else:
251             try:
252                 skill_module = load_skill_module(main_file_path, self.skill_id)
253             except Exception as e:
254                 LOG.exception('Failed to load skill: '
255                               '{} ({})'.format(self.skill_id, repr(e)))
256             else:
257                 module_is_skill = (
258                     hasattr(skill_module, 'create_skill') and
259                     callable(skill_module.create_skill)
260                 )
261                 if module_is_skill:
262                     return skill_module
263         return None  # Module wasn't loaded
264 
265     def _create_skill_instance(self, skill_module):
266         """Use v2 skills framework to create the skill."""
267         try:
268             self.instance = skill_module.create_skill()
269         except Exception as e:
270             log_msg = 'Skill __init__ failed with {}'
271             LOG.exception(log_msg.format(repr(e)))
272             self.instance = None
273 
274         if self.instance:
275             self.instance.skill_id = self.skill_id
276             self.instance.bind(self.bus)
277             try:
278                 self.instance.load_data_files()
279                 # Set up intent handlers
280                 # TODO: can this be a public method?
281                 self.instance._register_decorated()
282                 self.instance.register_resting_screen()
283                 self.instance.initialize()
284             except Exception as e:
285                 # If an exception occurs, make sure to clean up the skill
286                 self.instance.default_shutdown()
287                 self.instance = None
288                 log_msg = 'Skill initialization failed with {}'
289                 LOG.exception(log_msg.format(repr(e)))
290 
291         return self.instance is not None
292 
293     def _check_for_first_run(self):
294         """The very first time a skill is run, speak the intro."""
295         first_run = self.instance.settings.get(
296             "__mycroft_skill_firstrun",
297             True
298         )
299         if first_run:
300             LOG.info("First run of " + self.skill_id)
301             self.instance.settings["__mycroft_skill_firstrun"] = False
302             save_settings(self.instance.settings_write_path,
303                           self.instance.settings)
304             intro = self.instance.get_intro_message()
305             if intro:
306                 self.instance.speak(intro)
307 
308     def _communicate_load_status(self):
309         if self.loaded:
310             message = Message(
311                 'mycroft.skills.loaded',
312                 data=dict(
313                     path=self.skill_directory,
314                     id=self.skill_id,
315                     name=self.instance.name,
316                     modified=self.last_modified
317                 )
318             )
319             self.bus.emit(message)
320             LOG.info('Skill {} loaded successfully'.format(self.skill_id))
321         else:
322             message = Message(
323                 'mycroft.skills.loading_failure',
324                 data=dict(path=self.skill_directory, id=self.skill_id)
325             )
326             self.bus.emit(message)
327             LOG.error('Skill {} failed to load'.format(self.skill_id))
328
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mycroft/skills/skill_loader.py b/mycroft/skills/skill_loader.py
--- a/mycroft/skills/skill_loader.py
+++ b/mycroft/skills/skill_loader.py
@@ -73,6 +73,19 @@
     return mod
 
 
+def _bad_mod_times(mod_times):
+    """Return all entries with modification time in the future.
+
+    Arguments:
+        mod_times (dict): dict mapping file paths to modification times.
+
+    Returns:
+        List of files with bad modification times.
+    """
+    current_time = time()
+    return [path for path in mod_times if mod_times[path] > current_time]
+
+
 def _get_last_modified_time(path):
     """Get the last modified date of the most recently updated file in a path.
 
@@ -99,6 +112,11 @@
                 all_files.append(os.path.join(root_dir, f))
 
     # check files of interest in the skill root directory
+    mod_times = {f: os.path.getmtime(f) for f in all_files}
+    # Ensure modification times are valid
+    bad_times = _bad_mod_times(mod_times)
+    if bad_times:
+        raise OSError('{} had bad modification times'.format(bad_times))
     if all_files:
         return max(os.path.getmtime(f) for f in all_files)
     else:
@@ -118,6 +136,8 @@
         self.active = True
         self.config = Configuration.get()
 
+        self.modtime_error_log_written = False
+
     @property
     def is_blacklisted(self):
         """Boolean value representing whether or not a skill is blacklisted."""
@@ -135,10 +155,14 @@
         """
         try:
             self.last_modified = _get_last_modified_time(self.skill_directory)
-        except FileNotFoundError as e:
-            LOG.error('Failed to get last_modification time '
-                      '({})'.format(repr(e)))
+        except OSError as err:
             self.last_modified = self.last_loaded
+            if not self.modtime_error_log_written:
+                self.modtime_error_log_written = True
+                LOG.error('Failed to get last_modification time '
+                          '({})'.format(repr(err)))
+        else:
+            self.modtime_error_log_written = False
 
         modified = self.last_modified > self.last_loaded

{"golden_diff": "diff --git a/mycroft/skills/skill_loader.py b/mycroft/skills/skill_loader.py\n--- a/mycroft/skills/skill_loader.py\n+++ b/mycroft/skills/skill_loader.py\n@@ -73,6 +73,19 @@\n     return mod\n \n \n+def _bad_mod_times(mod_times):\n+    \"\"\"Return all entries with modification time in the future.\n+\n+    Arguments:\n+        mod_times (dict): dict mapping file paths to modification times.\n+\n+    Returns:\n+        List of files with bad modification times.\n+    \"\"\"\n+    current_time = time()\n+    return [path for path in mod_times if mod_times[path] > current_time]\n+\n+\n def _get_last_modified_time(path):\n     \"\"\"Get the last modified date of the most recently updated file in a path.\n \n@@ -99,6 +112,11 @@\n                 all_files.append(os.path.join(root_dir, f))\n \n     # check files of interest in the skill root directory\n+    mod_times = {f: os.path.getmtime(f) for f in all_files}\n+    # Ensure modification times are valid\n+    bad_times = _bad_mod_times(mod_times)\n+    if bad_times:\n+        raise OSError('{} had bad modification times'.format(bad_times))\n     if all_files:\n         return max(os.path.getmtime(f) for f in all_files)\n     else:\n@@ -118,6 +136,8 @@\n         self.active = True\n         self.config = Configuration.get()\n \n+        self.modtime_error_log_written = False\n+\n     @property\n     def is_blacklisted(self):\n         \"\"\"Boolean value representing whether or not a skill is blacklisted.\"\"\"\n@@ -135,10 +155,14 @@\n         \"\"\"\n         try:\n             self.last_modified = _get_last_modified_time(self.skill_directory)\n-        except FileNotFoundError as e:\n-            LOG.error('Failed to get last_modification time '\n-                      '({})'.format(repr(e)))\n+        except OSError as err:\n             self.last_modified = self.last_loaded\n+            if not self.modtime_error_log_written:\n+                self.modtime_error_log_written = True\n+                LOG.error('Failed to get last_modification time '\n+                          '({})'.format(repr(err)))\n+        else:\n+            self.modtime_error_log_written = False\n \n         modified = self.last_modified > self.last_loaded\n", "issue": "skill keep reloading indefinitely if there are files with a future date\ndue to a temporary problem in my system, i ended up with a skill with __init.py modification time set in the future. this caused that skill to constantly be reloaded my mycroft core, and unloaded just after the loading was complted. took some hours of debug to understand this was actually the problem.\r\n\r\nperhaps skills with files with modification date from the future should just be stopped from loading and have a debug log about it?\n", "before_files": [{"content": "# Copyright 2019 Mycroft AI Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\"\"\"Periodically run by skill manager to load skills into memory.\"\"\"\nimport gc\nimport importlib\nimport os\nfrom os.path import dirname\nimport sys\nfrom time import time\n\nfrom mycroft.configuration import Configuration\nfrom mycroft.messagebus import Message\nfrom mycroft.skills.settings import save_settings\nfrom mycroft.util.log import LOG\n\nfrom .settings import SettingsMetaUploader\n\nSKILL_MAIN_MODULE = '__init__.py'\n\n\ndef remove_submodule_refs(module_name):\n    \"\"\"Ensure submodules are reloaded by removing the refs from sys.modules.\n\n    Python import system puts a reference for each module in the sys.modules\n    dictionary to bypass loading if a module is already in memory. To make\n    sure skills are completely reloaded these references are deleted.\n\n    Arguments:\n        module_name: name of skill module.\n    \"\"\"\n    submodules = []\n    LOG.debug('Skill module'.format(module_name))\n    # Collect found submodules\n    for m in sys.modules:\n        if m.startswith(module_name + '.'):\n            submodules.append(m)\n    # Remove all references them to in sys.modules\n    for m in submodules:\n        LOG.debug('Removing sys.modules ref for {}'.format(m))\n        del(sys.modules[m])\n\n\ndef load_skill_module(path, skill_id):\n    \"\"\"Load a skill module\n\n    This function handles the differences between python 3.4 and 3.5+ as well\n    as makes sure the module is inserted into the sys.modules dict.\n\n    Arguments:\n        path: Path to the skill main file (__init__.py)\n        skill_id: skill_id used as skill identifier in the module list\n    \"\"\"\n    module_name = skill_id.replace('.', '_')\n\n    remove_submodule_refs(module_name)\n\n    spec = importlib.util.spec_from_file_location(module_name, path)\n    mod = importlib.util.module_from_spec(spec)\n    sys.modules[module_name] = mod\n    spec.loader.exec_module(mod)\n    return mod\n\n\ndef _get_last_modified_time(path):\n    \"\"\"Get the last modified date of the most recently updated file in a path.\n\n    Exclude compiled python files, hidden directories and the settings.json\n    file.\n\n    Arguments:\n        path: skill directory to check\n\n    Returns:\n        int: time of last change\n    \"\"\"\n    all_files = []\n    for root_dir, dirs, files in os.walk(path):\n        dirs[:] = [d for d in dirs if not d.startswith('.')]\n        for f in files:\n            ignore_file = (\n                    f.endswith('.pyc') or\n                    f == 'settings.json' or\n                    f.startswith('.') or\n                    f.endswith('.qmlc')\n            )\n            if not ignore_file:\n                all_files.append(os.path.join(root_dir, f))\n\n    # check files of interest in the skill root directory\n    if all_files:\n        return max(os.path.getmtime(f) for f in all_files)\n    else:\n        return 0\n\n\nclass SkillLoader:\n    def __init__(self, bus, skill_directory):\n        self.bus = bus\n        self.skill_directory = skill_directory\n        self.skill_id = os.path.basename(skill_directory)\n        self.load_attempted = False\n        self.loaded = False\n        self.last_modified = 0\n        self.last_loaded = 0\n        self.instance = None\n        self.active = True\n        self.config = Configuration.get()\n\n    @property\n    def is_blacklisted(self):\n        \"\"\"Boolean value representing whether or not a skill is blacklisted.\"\"\"\n        blacklist = self.config['skills'].get('blacklisted_skills', [])\n        if self.skill_id in blacklist:\n            return True\n        else:\n            return False\n\n    def reload_needed(self):\n        \"\"\"Load an unloaded skill or reload unloaded/changed skill.\n\n        Returns:\n             bool: if the skill was loaded/reloaded\n        \"\"\"\n        try:\n            self.last_modified = _get_last_modified_time(self.skill_directory)\n        except FileNotFoundError as e:\n            LOG.error('Failed to get last_modification time '\n                      '({})'.format(repr(e)))\n            self.last_modified = self.last_loaded\n\n        modified = self.last_modified > self.last_loaded\n\n        # create local reference to avoid threading issues\n        instance = self.instance\n\n        reload_allowed = (\n                self.active and\n                (instance is None or instance.reload_skill)\n        )\n        return modified and reload_allowed\n\n    def reload(self):\n        LOG.info('ATTEMPTING TO RELOAD SKILL: ' + self.skill_id)\n        if self.instance:\n            self._unload()\n        return self._load()\n\n    def load(self):\n        LOG.info('ATTEMPTING TO LOAD SKILL: ' + self.skill_id)\n        return self._load()\n\n    def _unload(self):\n        \"\"\"Remove listeners and stop threads before loading\"\"\"\n        self._execute_instance_shutdown()\n        if self.config.get(\"debug\", False):\n            self._garbage_collect()\n        self.loaded = False\n        self._emit_skill_shutdown_event()\n\n    def unload(self):\n        if self.instance:\n            self._execute_instance_shutdown()\n        self.loaded = False\n\n    def activate(self):\n        self.active = True\n        self.load()\n\n    def deactivate(self):\n        self.active = False\n        self.unload()\n\n    def _execute_instance_shutdown(self):\n        \"\"\"Call the shutdown method of the skill being reloaded.\"\"\"\n        try:\n            self.instance.default_shutdown()\n        except Exception:\n            log_msg = 'An error occurred while shutting down {}'\n            LOG.exception(log_msg.format(self.instance.name))\n        else:\n            LOG.info('Skill {} shut down successfully'.format(self.skill_id))\n\n    def _garbage_collect(self):\n        \"\"\"Invoke Python garbage collector to remove false references\"\"\"\n        gc.collect()\n        # Remove two local references that are known\n        refs = sys.getrefcount(self.instance) - 2\n        if refs > 0:\n            log_msg = (\n                \"After shutdown of {} there are still {} references \"\n                \"remaining. The skill won't be cleaned from memory.\"\n            )\n            LOG.warning(log_msg.format(self.instance.name, refs))\n\n    def _emit_skill_shutdown_event(self):\n        message = Message(\n            \"mycroft.skills.shutdown\",\n            data=dict(path=self.skill_directory, id=self.skill_id)\n        )\n        self.bus.emit(message)\n\n    def _load(self):\n        self._prepare_for_load()\n        if self.is_blacklisted:\n            self._skip_load()\n        else:\n            skill_module = self._load_skill_source()\n            if skill_module and self._create_skill_instance(skill_module):\n                self._check_for_first_run()\n                self.loaded = True\n\n        self.last_loaded = time()\n        self._communicate_load_status()\n        if self.loaded:\n            self._prepare_settings_meta()\n        return self.loaded\n\n    def _prepare_settings_meta(self):\n        settings_meta = SettingsMetaUploader(self.skill_directory,\n                                             self.instance.name)\n        self.instance.settings_meta = settings_meta\n\n    def _prepare_for_load(self):\n        self.load_attempted = True\n        self.loaded = False\n        self.instance = None\n\n    def _skip_load(self):\n        log_msg = 'Skill {} is blacklisted - it will not be loaded'\n        LOG.info(log_msg.format(self.skill_id))\n\n    def _load_skill_source(self):\n        \"\"\"Use Python's import library to load a skill's source code.\"\"\"\n        main_file_path = os.path.join(self.skill_directory, SKILL_MAIN_MODULE)\n        if not os.path.exists(main_file_path):\n            error_msg = 'Failed to load {} due to a missing file.'\n            LOG.error(error_msg.format(self.skill_id))\n        else:\n            try:\n                skill_module = load_skill_module(main_file_path, self.skill_id)\n            except Exception as e:\n                LOG.exception('Failed to load skill: '\n                              '{} ({})'.format(self.skill_id, repr(e)))\n            else:\n                module_is_skill = (\n                    hasattr(skill_module, 'create_skill') and\n                    callable(skill_module.create_skill)\n                )\n                if module_is_skill:\n                    return skill_module\n        return None  # Module wasn't loaded\n\n    def _create_skill_instance(self, skill_module):\n        \"\"\"Use v2 skills framework to create the skill.\"\"\"\n        try:\n            self.instance = skill_module.create_skill()\n        except Exception as e:\n            log_msg = 'Skill __init__ failed with {}'\n            LOG.exception(log_msg.format(repr(e)))\n            self.instance = None\n\n        if self.instance:\n            self.instance.skill_id = self.skill_id\n            self.instance.bind(self.bus)\n            try:\n                self.instance.load_data_files()\n                # Set up intent handlers\n                # TODO: can this be a public method?\n                self.instance._register_decorated()\n                self.instance.register_resting_screen()\n                self.instance.initialize()\n            except Exception as e:\n                # If an exception occurs, make sure to clean up the skill\n                self.instance.default_shutdown()\n                self.instance = None\n                log_msg = 'Skill initialization failed with {}'\n                LOG.exception(log_msg.format(repr(e)))\n\n        return self.instance is not None\n\n    def _check_for_first_run(self):\n        \"\"\"The very first time a skill is run, speak the intro.\"\"\"\n        first_run = self.instance.settings.get(\n            \"__mycroft_skill_firstrun\",\n            True\n        )\n        if first_run:\n            LOG.info(\"First run of \" + self.skill_id)\n            self.instance.settings[\"__mycroft_skill_firstrun\"] = False\n            save_settings(self.instance.settings_write_path,\n                          self.instance.settings)\n            intro = self.instance.get_intro_message()\n            if intro:\n                self.instance.speak(intro)\n\n    def _communicate_load_status(self):\n        if self.loaded:\n            message = Message(\n                'mycroft.skills.loaded',\n                data=dict(\n                    path=self.skill_directory,\n                    id=self.skill_id,\n                    name=self.instance.name,\n                    modified=self.last_modified\n                )\n            )\n            self.bus.emit(message)\n            LOG.info('Skill {} loaded successfully'.format(self.skill_id))\n        else:\n            message = Message(\n                'mycroft.skills.loading_failure',\n                data=dict(path=self.skill_directory, id=self.skill_id)\n            )\n            self.bus.emit(message)\n            LOG.error('Skill {} failed to load'.format(self.skill_id))\n", "path": "mycroft/skills/skill_loader.py"}], "after_files": [{"content": "# Copyright 2019 Mycroft AI Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\"\"\"Periodically run by skill manager to load skills into memory.\"\"\"\nimport gc\nimport importlib\nimport os\nfrom os.path import dirname\nimport sys\nfrom time import time\n\nfrom mycroft.configuration import Configuration\nfrom mycroft.messagebus import Message\nfrom mycroft.skills.settings import save_settings\nfrom mycroft.util.log import LOG\n\nfrom .settings import SettingsMetaUploader\n\nSKILL_MAIN_MODULE = '__init__.py'\n\n\ndef remove_submodule_refs(module_name):\n    \"\"\"Ensure submodules are reloaded by removing the refs from sys.modules.\n\n    Python import system puts a reference for each module in the sys.modules\n    dictionary to bypass loading if a module is already in memory. To make\n    sure skills are completely reloaded these references are deleted.\n\n    Arguments:\n        module_name: name of skill module.\n    \"\"\"\n    submodules = []\n    LOG.debug('Skill module'.format(module_name))\n    # Collect found submodules\n    for m in sys.modules:\n        if m.startswith(module_name + '.'):\n            submodules.append(m)\n    # Remove all references them to in sys.modules\n    for m in submodules:\n        LOG.debug('Removing sys.modules ref for {}'.format(m))\n        del(sys.modules[m])\n\n\ndef load_skill_module(path, skill_id):\n    \"\"\"Load a skill module\n\n    This function handles the differences between python 3.4 and 3.5+ as well\n    as makes sure the module is inserted into the sys.modules dict.\n\n    Arguments:\n        path: Path to the skill main file (__init__.py)\n        skill_id: skill_id used as skill identifier in the module list\n    \"\"\"\n    module_name = skill_id.replace('.', '_')\n\n    remove_submodule_refs(module_name)\n\n    spec = importlib.util.spec_from_file_location(module_name, path)\n    mod = importlib.util.module_from_spec(spec)\n    sys.modules[module_name] = mod\n    spec.loader.exec_module(mod)\n    return mod\n\n\ndef _bad_mod_times(mod_times):\n    \"\"\"Return all entries with modification time in the future.\n\n    Arguments:\n        mod_times (dict): dict mapping file paths to modification times.\n\n    Returns:\n        List of files with bad modification times.\n    \"\"\"\n    current_time = time()\n    return [path for path in mod_times if mod_times[path] > current_time]\n\n\ndef _get_last_modified_time(path):\n    \"\"\"Get the last modified date of the most recently updated file in a path.\n\n    Exclude compiled python files, hidden directories and the settings.json\n    file.\n\n    Arguments:\n        path: skill directory to check\n\n    Returns:\n        int: time of last change\n    \"\"\"\n    all_files = []\n    for root_dir, dirs, files in os.walk(path):\n        dirs[:] = [d for d in dirs if not d.startswith('.')]\n        for f in files:\n            ignore_file = (\n                    f.endswith('.pyc') or\n                    f == 'settings.json' or\n                    f.startswith('.') or\n                    f.endswith('.qmlc')\n            )\n            if not ignore_file:\n                all_files.append(os.path.join(root_dir, f))\n\n    # check files of interest in the skill root directory\n    mod_times = {f: os.path.getmtime(f) for f in all_files}\n    # Ensure modification times are valid\n    bad_times = _bad_mod_times(mod_times)\n    if bad_times:\n        raise OSError('{} had bad modification times'.format(bad_times))\n    if all_files:\n        return max(os.path.getmtime(f) for f in all_files)\n    else:\n        return 0\n\n\nclass SkillLoader:\n    def __init__(self, bus, skill_directory):\n        self.bus = bus\n        self.skill_directory = skill_directory\n        self.skill_id = os.path.basename(skill_directory)\n        self.load_attempted = False\n        self.loaded = False\n        self.last_modified = 0\n        self.last_loaded = 0\n        self.instance = None\n        self.active = True\n        self.config = Configuration.get()\n\n        self.modtime_error_log_written = False\n\n    @property\n    def is_blacklisted(self):\n        \"\"\"Boolean value representing whether or not a skill is blacklisted.\"\"\"\n        blacklist = self.config['skills'].get('blacklisted_skills', [])\n        if self.skill_id in blacklist:\n            return True\n        else:\n            return False\n\n    def reload_needed(self):\n        \"\"\"Load an unloaded skill or reload unloaded/changed skill.\n\n        Returns:\n             bool: if the skill was loaded/reloaded\n        \"\"\"\n        try:\n            self.last_modified = _get_last_modified_time(self.skill_directory)\n        except OSError as err:\n            self.last_modified = self.last_loaded\n            if not self.modtime_error_log_written:\n                self.modtime_error_log_written = True\n                LOG.error('Failed to get last_modification time '\n                          '({})'.format(repr(err)))\n        else:\n            self.modtime_error_log_written = False\n\n        modified = self.last_modified > self.last_loaded\n\n        # create local reference to avoid threading issues\n        instance = self.instance\n\n        reload_allowed = (\n                self.active and\n                (instance is None or instance.reload_skill)\n        )\n        return modified and reload_allowed\n\n    def reload(self):\n        LOG.info('ATTEMPTING TO RELOAD SKILL: ' + self.skill_id)\n        if self.instance:\n            self._unload()\n        return self._load()\n\n    def load(self):\n        LOG.info('ATTEMPTING TO LOAD SKILL: ' + self.skill_id)\n        return self._load()\n\n    def _unload(self):\n        \"\"\"Remove listeners and stop threads before loading\"\"\"\n        self._execute_instance_shutdown()\n        if self.config.get(\"debug\", False):\n            self._garbage_collect()\n        self.loaded = False\n        self._emit_skill_shutdown_event()\n\n    def unload(self):\n        if self.instance:\n            self._execute_instance_shutdown()\n        self.loaded = False\n\n    def activate(self):\n        self.active = True\n        self.load()\n\n    def deactivate(self):\n        self.active = False\n        self.unload()\n\n    def _execute_instance_shutdown(self):\n        \"\"\"Call the shutdown method of the skill being reloaded.\"\"\"\n        try:\n            self.instance.default_shutdown()\n        except Exception:\n            log_msg = 'An error occurred while shutting down {}'\n            LOG.exception(log_msg.format(self.instance.name))\n        else:\n            LOG.info('Skill {} shut down successfully'.format(self.skill_id))\n\n    def _garbage_collect(self):\n        \"\"\"Invoke Python garbage collector to remove false references\"\"\"\n        gc.collect()\n        # Remove two local references that are known\n        refs = sys.getrefcount(self.instance) - 2\n        if refs > 0:\n            log_msg = (\n                \"After shutdown of {} there are still {} references \"\n                \"remaining. The skill won't be cleaned from memory.\"\n            )\n            LOG.warning(log_msg.format(self.instance.name, refs))\n\n    def _emit_skill_shutdown_event(self):\n        message = Message(\n            \"mycroft.skills.shutdown\",\n            data=dict(path=self.skill_directory, id=self.skill_id)\n        )\n        self.bus.emit(message)\n\n    def _load(self):\n        self._prepare_for_load()\n        if self.is_blacklisted:\n            self._skip_load()\n        else:\n            skill_module = self._load_skill_source()\n            if skill_module and self._create_skill_instance(skill_module):\n                self._check_for_first_run()\n                self.loaded = True\n\n        self.last_loaded = time()\n        self._communicate_load_status()\n        if self.loaded:\n            self._prepare_settings_meta()\n        return self.loaded\n\n    def _prepare_settings_meta(self):\n        settings_meta = SettingsMetaUploader(self.skill_directory,\n                                             self.instance.name)\n        self.instance.settings_meta = settings_meta\n\n    def _prepare_for_load(self):\n        self.load_attempted = True\n        self.loaded = False\n        self.instance = None\n\n    def _skip_load(self):\n        log_msg = 'Skill {} is blacklisted - it will not be loaded'\n        LOG.info(log_msg.format(self.skill_id))\n\n    def _load_skill_source(self):\n        \"\"\"Use Python's import library to load a skill's source code.\"\"\"\n        main_file_path = os.path.join(self.skill_directory, SKILL_MAIN_MODULE)\n        if not os.path.exists(main_file_path):\n            error_msg = 'Failed to load {} due to a missing file.'\n            LOG.error(error_msg.format(self.skill_id))\n        else:\n            try:\n                skill_module = load_skill_module(main_file_path, self.skill_id)\n            except Exception as e:\n                LOG.exception('Failed to load skill: '\n                              '{} ({})'.format(self.skill_id, repr(e)))\n            else:\n                module_is_skill = (\n                    hasattr(skill_module, 'create_skill') and\n                    callable(skill_module.create_skill)\n                )\n                if module_is_skill:\n                    return skill_module\n        return None  # Module wasn't loaded\n\n    def _create_skill_instance(self, skill_module):\n        \"\"\"Use v2 skills framework to create the skill.\"\"\"\n        try:\n            self.instance = skill_module.create_skill()\n        except Exception as e:\n            log_msg = 'Skill __init__ failed with {}'\n            LOG.exception(log_msg.format(repr(e)))\n            self.instance = None\n\n        if self.instance:\n            self.instance.skill_id = self.skill_id\n            self.instance.bind(self.bus)\n            try:\n                self.instance.load_data_files()\n                # Set up intent handlers\n                # TODO: can this be a public method?\n                self.instance._register_decorated()\n                self.instance.register_resting_screen()\n                self.instance.initialize()\n            except Exception as e:\n                # If an exception occurs, make sure to clean up the skill\n                self.instance.default_shutdown()\n                self.instance = None\n                log_msg = 'Skill initialization failed with {}'\n                LOG.exception(log_msg.format(repr(e)))\n\n        return self.instance is not None\n\n    def _check_for_first_run(self):\n        \"\"\"The very first time a skill is run, speak the intro.\"\"\"\n        first_run = self.instance.settings.get(\n            \"__mycroft_skill_firstrun\",\n            True\n        )\n        if first_run:\n            LOG.info(\"First run of \" + self.skill_id)\n            self.instance.settings[\"__mycroft_skill_firstrun\"] = False\n            save_settings(self.instance.settings_write_path,\n                          self.instance.settings)\n            intro = self.instance.get_intro_message()\n            if intro:\n                self.instance.speak(intro)\n\n    def _communicate_load_status(self):\n        if self.loaded:\n            message = Message(\n                'mycroft.skills.loaded',\n                data=dict(\n                    path=self.skill_directory,\n                    id=self.skill_id,\n                    name=self.instance.name,\n                    modified=self.last_modified\n                )\n            )\n            self.bus.emit(message)\n            LOG.info('Skill {} loaded successfully'.format(self.skill_id))\n        else:\n            message = Message(\n                'mycroft.skills.loading_failure',\n                data=dict(path=self.skill_directory, id=self.skill_id)\n            )\n            self.bus.emit(message)\n            LOG.error('Skill {} failed to load'.format(self.skill_id))\n", "path": "mycroft/skills/skill_loader.py"}]}