Skip to content
This repository has been archived by the owner on Sep 8, 2024. It is now read-only.

Switch from the deprecated imp to importlib #2327

Merged
merged 2 commits into from
Jul 1, 2020
Merged

Conversation

forslund
Copy link
Collaborator

@forslund forslund commented Sep 26, 2019

Description

Replaces the current imp based skill loader with an importlib based solution. This also adds a small change allowing better use of submodules. Before submodules were never reloaded forcing the over-use of the __init__.py file in skills. The change makes the submodules reload together with the main module by removing the refs from sys.modules, allowing better structure of skills.

How to test

Make sure skills load. Make a change to a skill and ensure that the skill is reloaded and that the change is accessible.
Add a log statement to the Spotify skill's spotify.py and check that it's included when the skill reloads.

Contributor license agreement signed?

CLA [ Yes ]

@devs-mycroft devs-mycroft added the CLA: Yes Contributor License Agreement exists (see https://github.com/MycroftAI/contributors) label Sep 26, 2019
@chrisveilleux
Copy link
Member

chrisveilleux commented Sep 26, 2019

If we are going to do this, I want it to include the ability to load the code from a file other than __init__.py. Putting all your code in this file is a Python anti-pattern. This PR should make it possible for an author to move their skill code from __init__.py to something like skill.py. At the very least, it should make it possible for __init__.py to be able to import code from another file, like skill.py and then contain only the import and the create_skill function.

@forslund
Copy link
Collaborator Author

Isn't that already possible?

The only thing that's needed is that __init__.py has the create_skill() function, either locally or imported from a module...

@chrisveilleux
Copy link
Member

chrisveilleux commented Sep 26, 2019

I tried moving the skill class to a file called skill.py a while ago and importing that into __init__.py. it did not work. It is possible that I did it wrong. It is also possible that the new version of importlib supports this whereas the older imp library did not.

I can test against the code in this PR and see if there is different behavior.

@forslund
Copy link
Collaborator Author

Ah, right imported submodules might not be reloaded...let me do some tests here

@forslund
Copy link
Collaborator Author

Well the spotify skill has the majority of the spotify logic in a separate file so it has worked before at least...

let me try with your case with the entire skill in a skill.py file...

@chrisveilleux
Copy link
Member

chrisveilleux commented Sep 26, 2019

I would be happy if we just didn't hardcode the __init__.py as the only place skill code can come from. Make it so that the code can be loaded from there or from another location, like skill.py. We can support both for a time then remove support for __init__.py.

@forslund
Copy link
Collaborator Author

Do you mean like search all files for create_skill()?

Looking at a skill as a module it would require the __init__.py to show what's exported right?

@chrisveilleux
Copy link
Member

using __init__.py as a way to expose the API of a package is not a Python requirement. Lots of folks just leave that file completely empty. It is my preference the __init__.py be used in all of our code in this way. So if we can get that to work with importlib, that would be ideal. Ideally, we could import create_skill() from skill.py, load it in the skill loader and execute it. I just don't know off the top of my head what is possible and what is not with importlib. If we need to do something a bit different to support how we load skills the way we do, I am OK with that.

@chrisveilleux
Copy link
Member

chrisveilleux commented Sep 26, 2019

OOPS. Hit the close button accidentally.

@chrisveilleux chrisveilleux reopened this Sep 26, 2019
@forslund
Copy link
Collaborator Author

Gotcha.

In any case you're query highlighted an issue here. with this implementation relative imports no longer works in skills so I guess I need to look into that at least.

Will look at loading the skill from any file while I'm at it.

@JarbasAl
Copy link
Contributor

JarbasAl commented Sep 26, 2019

Gotcha.

In any case you're query highlighted an issue here. with this implementation relative imports no longer works in skills so I guess I need to look into that at least.

Will look at loading the skill from any file while I'm at it.

relative imports haven't worked ever, i remember in the old times the usual suggestion in slack was to add the skill directory to the python path, i usually started from MYCROFT_ROOT_PATH (remember when skill where part of core?) but i remember seeing all sorts of ugly stuff with os.path and some approaches with skill variables

@chrisveilleux
Copy link
Member

In case there was any question, manipulating the Python path should be avoided at all costs.

@ChanceNCounter
Copy link
Contributor

relative imports haven't worked ever, i remember in the old times the usual suggestion in slack was to add the skill directory to the python path, i usually started from MYCROFT_ROOT_PATH (remember when skill where part of core?) but i remember seeing all sorts of ugly stuff with os.path and some approaches with skill variables

I'm using relative imports in a skill I'm developing right now, and it seems to be working. Aside from this PR, is that going to break later? There seem to be conflicting experiences here.

@forslund
Copy link
Collaborator Author

With the current imp implentation it works. I did some work last night and managed to get the relative imports working with importlib as well. It would be terrible if the skills were limited to a single file.

There is an issue with the relative imports not reloading when the __init__.py is reloaded (both in old imp and importlib implementations) but I think I've got a handle on how to tackle that issue.

@forslund
Copy link
Collaborator Author

Pushed a WIP update supporting relative imports again and adding reloading for them as well...

@forslund forslund added the Status: Work in progress PR being actively worked on, not yet ready for review. label Feb 27, 2020
Separate python 3.4 and 3.5+ implementation
@forslund forslund added type: feature and removed Status: Work in progress PR being actively worked on, not yet ready for review. labels Jun 29, 2020
@forslund
Copy link
Collaborator Author

This has been updated to use importlib and a nice and easy fix to allow skill submodules to be reloaded.

@MycroftAI MycroftAI deleted a comment from devops-mycroft Jun 30, 2020
@devops-mycroft
Copy link

Voight Kampff Integration Test Succeeded (Results)

Copy link
Contributor

@krisgesling krisgesling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working great!

One tiny wording nitpick (probably just a personal preference thing) and one clarification.

@@ -29,6 +34,49 @@
SKILL_MAIN_MODULE = '__init__.py'


def remove_submodule_refs(module_name):
"""Ensure submodules being reloaded by removing the refs from sys.modules.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#nitpick...

Ensure submodule is reloaded by removing the refs from sys.modules.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you ok with

Ensure submodules are reloaded by removing the refs from sys.modules.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep that reads great.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment updated

module_name: name of skill module.
"""
submodules = []
LOG.info('Skill module'.format(module_name))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intended to be an info level log message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that should be a debug level, will fix on my train ride home

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to debug

@devops-mycroft
Copy link

Voight Kampff Integration Test Succeeded (Results)

Submodule reload is enforced by removing sys.modules reference. This
will make imports do the full uncached importing
@devops-mycroft
Copy link

Voight Kampff Integration Test Failed (Results)

1 similar comment
@devops-mycroft
Copy link

Voight Kampff Integration Test Failed (Results)

@devops-mycroft
Copy link

Voight Kampff Integration Test Succeeded (Results)

@krisgesling krisgesling merged commit aca6a18 into dev Jul 1, 2020
@forslund forslund deleted the feature/importlib branch September 6, 2020 19:45
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
CLA: Yes Contributor License Agreement exists (see https://github.com/MycroftAI/contributors)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants