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
Conversation
ae522ef
to
3e07dc1
Compare
Otherwise False
- 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
14a540b
to
f9bf483
Compare
178e9ee
to
f9bf483
Compare
| @@ -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): | |||
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
|
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() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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_valWhile 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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_wrapperAnd 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?
There was a problem hiding this comment.
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...
There was a problem hiding this 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.
This implements the basic features for syncing the skills.json file with the server no matter the installation source