-
Notifications
You must be signed in to change notification settings - Fork 7
Plugin class #8
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
Plugin class #8
Conversation
antgonza
left a comment
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.
A couple of comments/questions
| self.fp_types = filepath_types | ||
|
|
||
|
|
||
| class BaseQiitaPlugin(object): |
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.
Do the methods in this object get tested?
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 is a BaseClass so I'm not instantiating it directly, but all the methods are being executed through the child clases
qiita_client/plugin.py
Outdated
|
|
||
| self._register_command(html_cmd) | ||
|
|
||
| def _install(self, qclient): |
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.
What about this one?
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.
it is getting executed here: https://github.com/qiita-spots/qiita_client/pull/8/files#diff-259ba12727397257087e45ba8a60f40dR163
wasade
left a comment
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.
Few comments, looking pretty good :)
qiita_client/plugin.py
Outdated
| function : callable | ||
| The function that executes the command. Should be a callable that | ||
| conforms to the signature: | ||
| `function(qclient, job_id, job_parameters, output_dir)` |
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.
are any of these nullable, and is there a return signature?
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.
No, they can't be nullable. Yup, there is a return signature, I've updated the documentation to note that.
| # `function` will be called with the following Parameters | ||
| # qclient, job_id, job_parameters, output_dir | ||
| # Make sure that `function` can receive 4 parameters | ||
| if function.__code__.co_argcount != 4: |
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 is still python 2, right? Annotations would be so awesome here
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.
py2/py3 compatible. I want this utility library to be as general as possible so we can use it with QIIME1 and QIIME2, although I expect more devs use this to create the plugins of their own tools (which we don't control if they're py2 or py3).
| The artifact type name | ||
| description : str | ||
| The artifact type description | ||
| can_be_submitted_to_ebi : bool |
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.
what's the likelihood that external submission or data exchanges will expand in the future? if it is reasonable, then it may make sense to do a more discoverable structure, something like external_exchange which could be {'ebi': True, 'vamps': False, ...}. if expansion here is unlikely, then I think the attrs make sense
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.
Uhm, not sure. @antgonza any thoughts? If this can expand we may want to rethink also how to change the current DB structure to support this (although we can refer that for later)
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.
It's likely to change but not sure when. I agree with the approach of dealing with it once we know it's actually going to happen. Note that this will also require changes in the main qiita code base ...
| self.name = name | ||
| self.version = version | ||
| self.description = description | ||
| self.publications = dumps(publications) if publications else "" |
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.
is there a plugin type which we anticipate wont have a publication or doi? if they don't, we could always require tying a version to zenodo. perhaps unnecessary
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 would be surprised if something doesn't have a DOI - I think in previous discussions we agreed to use DOI since it is more common to have a DOI.
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.
There is going to be cases we are going to be using tools without publication, the first one that comes to mind is: KneedData.
| conf_dir = environ.get( | ||
| 'QIITA_PLUGINS_DIR', join(expanduser('~'), '.qiita_plugins')) | ||
| self.conf_fp = join(conf_dir, "%s_%s.conf" % (self.name, self.version)) | ||
|
|
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.
would it make sense for an assert over os.path.exists?
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.
The conf_fp doesn't necessarily exists at this point (see the function generate_config). The change that I made is that now both Qiita and the plugin can actually use the same config file. There is some information that needs to be known by both of them in order to communicate so at least they need to share a file (which can then be copied over multiple locations and allow plugin execution from anywhere).
qiita_client/plugin.py
Outdated
| """ | ||
| self.task_dict[command.name] = command | ||
|
|
||
| def _install(self, qclient): |
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 is semantic, but i think of install as actually putting a program, library, or documentation somewhere in a logical place. in this case, this I think more mirrors a registration, where a call to ._register would establish this plugins existence with qiita?
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.
You're right - changed to _register.
| def _install(self, qclient): | ||
| """Installs the plugin in Qiita""" | ||
| # Get the command information from qiita | ||
| info = qclient.get('/qiita_db/plugins/%s/%s/' |
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'm a bit confused here, qiita already knows about the plugin prior to the install?
Should the .status_code be checked to test for 200?
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.
Yes, so when Qiita starts the server, it will load the plugin configuration files. The reason behind this is that in order to communicate the plugin (client) and Qiita (server) they need to both know the credentials. So the config file is generating those credentials, so in order to actually add a plugin to the system you need access to the FS to be able to add this configuration file. However, the configuration file only allows to "register" the plugin itself, but not the commands. The commands get added dynamically using the REST api, which is what is done here.
Is a bit complex, but I think this is the best balance between security and flexibility. This flexibility in the commands is needed mainly for QIIME 2, which is a system that works with plugins itself, so it is possible that there are commands that are no longer available, while new ones may appear.
Regarding the 200, the QiitaClient object (qclient) already performs those checks internally and it will raise a useful error in case that the return code is different from 200. The object returned from the get and post is actually a dictionary, which contains the decoded JSON from the request body.
qiita_client/plugin.py
Outdated
| if cmd.name in info['commands']: | ||
| qclient.post('/qiita_db/plugins/%s/%s/commands/%s/activate/' | ||
| % (self.name, self.version, | ||
| cmd.name.replace(' ', "%20"))) |
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.
what about
> import urllib
> urllib.parse.quote("foo bar")
'foo%20bar'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.
should this post, and the one on line 181 have their status codes checked?
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.
Thanks for the tip! See my answer above regarding the status codes.
qiita_client/plugin.py
Outdated
| ----- | ||
| Both `validate_func` and `html_generator_func` should be a callable | ||
| that conforms to the signature: | ||
| function(qclient, job_id, job_parameters, output_dir) |
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.
is there a return?
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.
Yes, it is actually the same signature than above - changed.
| command: QiitaCommand | ||
| The command to be added to the plugin | ||
| """ | ||
| self._register_command(command) |
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.
it seems like this should just be public?
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 don't want the _register_command function to be public on the QiitaTypePlugin class (although I'm using it internally). The rationale is that the commands in the QiitaTypePlugin are controlled (there are only 2 - 'Validate' and 'Generate HTML summary'), while QiitaPlugin can have any number of commands. However, both are actually represented in the same way, and that's why most of the functionality is added in the base class.
Adds the plugin class so plugins do not need to duplicate a lot of code.
That class includes all the relevant code so the plugins can install themselves.