Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add handling of the skills.json file #15

Merged
merged 8 commits into from Nov 8, 2018

Conversation

forslund
Copy link
Collaborator

@forslund forslund commented Oct 21, 2018

This implements the basic features for syncing the skills.json file with the server no matter the installation source

  • Initial upgrade of existing/nonexisting skills.json
  • Curating if skills are modified without msm noticing
  • Install now takes an origin argument depending on the source of the installation
  • Update will update the updated field
  • locking of the skills.json file

- Initial upgrade of existing/nonexisting skills.json
- Curating if skills are modified without msm noticing
- Install now takes an origin argument depending on the source of the installation
- Update will update the updated field
- is_beta property now indicates if a skill is installed as beta
- method to update internal skills_data
- write_skills_data will write internal skills_data if no argument is passed
- Make pruning of skill entries better when removing
- Update beta flag on update
- if the skills.json file is broken regenerate it
@forslund forslund force-pushed the feature/skills-data branch 2 times, most recently from 14a540b to f9bf483 Compare October 23, 2018 13:10
@forslund forslund added enhancement New feature or request and removed work in progress labels Oct 23, 2018
@@ -32,3 +34,13 @@ def wrapper(*args, **kwargs):
env.update(self.env)
return super(Git, self).__getattr__(item)(*args, env=env, **kwargs)
return wrapper


class MsmProcessLock(InterProcessLock):
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a good idea, but why specifically this is needed? (In what case does the skills.json get corrupted). If the power got pulled while msm was doing anything would the lock cause msm to fail on subsequent boots?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

My reasoning is that some of the skill operations are rather slow and now they can occur at any time. If 2 simultaneous installations occurs one could easily be overwritten.

The lock file exists in /tmp/ which should in most cases be reset completely. Also as far as I've been able to read the lock status of fcntl locks are stored in memory and will be cleared on restart.

But I'll do a test later tonight to verify.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok, was trickier than i realized. fcntl-locks are cleared by the kernel when the holding process terminates so I had to set up a VM to do an actual hard reset and test. And there are no issues with locks staying locked after power cycle

@forslund
Copy link
Collaborator Author

I've updated this to handle more of the currently used fields and limit the number of disk writes to only do them on skills_data change.

entry = build_skill_entry(skill.name, origin, skill.is_beta)
try:
skill.install(constraints)
entry['installed'] = time.time()
Copy link
Contributor

Choose a reason for hiding this comment

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

Just an idea, could be cleaner via:

entry.update(installed=time.time(), installation='installed', status='activate')

or

entry.update(
    installed=time.time(),
    installation='installed',
    status='activate'
)

What do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That does look nice.

printer('{}: {}'.format(exc_type, str(e)))
return get_error_code(e.__class__)
finally:
msm.write_skills_data()
Copy link
Contributor

Choose a reason for hiding this comment

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

Requiring this after any api call breaks the python api (used by the installer skill for example). I think we should either make it clear msm's python api shouldn't be used (and change the installer skill) or do something else like add a @save_skills_data decorator that functions are marked with in the MycroftSkillsManager class.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since I changed the Api i updated the minor version :) But you do have a point. (And I have made the corresponding changes in the core and installer skill.

I like the @save_skills_data decorator the case I don't really know how to handle is if the MycroftSkillsManager.install is passed to the apply-method. Is there a good way to work around that?

Basically strip the decorator if it exists when running the apply.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I could maybe add an optional keyword parameter to the decorator that would disable the saving. In this case I could also handle the locking automatically...
Or set a flag on the function called or something.

Copy link
Contributor

Choose a reason for hiding this comment

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

While not particularly elegant, the decorator could do something like:

def save_skills_data(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        will_save = False
        if not self.saving_handled:
            will_save = self.saving_handled = True
        return_val = func(self, *args, **kwargs)
        if will_save:
            self.save()
            self.saving_handled = False
        return return_val

While it seems a little hacky, I think it's better that way so that what might seem like a valid api call, MycroftSkillsManager().install('myskill'), remains indeed valid. That said, we are literally only using msm bindings in the installer skill and for like 2 lines in msk, so it's no big deal to do it this way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'll give it a go. It would be really nice if this was automagic like this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok, done some testing and the cleanest way I've found is having the decorator adding an optional parameter and have the apply method to check for the decorator and send the disable parameter if it's there:

decorator:

def save_skills_data(func):
    @wraps(func)
    def func_wrapper(self, *args, **kwargs):
        # Check for __msm_write keyword parameter
        do_write =  kwargs.get('__msm_write', True)
        # Clean the write parameter if exists
        if '__msm_write' in kwargs:
            kwargs.pop('__msm_write')
        try:
            ret = func(self, *args, **kwargs)
        finally:
            if do_write:
                self.write_skills_data()
        return ret

    func_wrapper.save_skills_data = True
    return func_wrapper

And the apply:

def apply(self, func, skills):
        """Run a function on all skills in parallel"""

        def run_item(skill):
            try:
                if 'save_skills_data' in dir(func):
                    func(skill, __msm_write=False)
                else:
                    func(skill)
    [....]

Does this look sane?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm, no. yours is better since mine won't work if the msm.install/update/etc is wrapped in a function...

Copy link
Contributor

@MatthewScholefield MatthewScholefield left a comment

Choose a reason for hiding this comment

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

For now, this looks good!
Merging.

@MatthewScholefield MatthewScholefield merged commit f76c72d into master Nov 8, 2018
@MatthewScholefield MatthewScholefield deleted the feature/skills-data branch November 8, 2018 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants