-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Split linters in separate classes #17081
Split linters in separate classes #17081
Conversation
try: | ||
re.compile(match) | ||
except Exception as e: | ||
lint_ctx.error(f"Match '{match}' is no valid regular expression: {str(e)}", node=child) |
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.
Don't we need to have a code per message, unless one linter only emits one possible message ?
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.
We can try to do this. With the current state I would need to split StdIOAbsence
only, or? I hoped to come away with something like at most one message per linter (which would be fine for profile versions as in StdIOAbsence
.
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.
Also, it's important to note that we currently filter for linter function/class names (here) and not linter message codes. I would say that do not need the codes. But we can either name the filter classes using a code.
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.
One message per linter sounds fine, but also like a lot of work.
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.
One message per linter sounds fine, but also like a lot of work.
The good thing is that we have good unit test coverage - which now pays off.
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.
If you augment the lint_ctx.function to instead pass codes we can use those codes to determine what is classified as an error / what can be ignored. This seems like less work (we'd have to run all relatively coarse linters, but I think that's ok ?)
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.
Excellent idea, but I think we are fixed to the signature of the linter function, i.e 1st parameter ToolSource
and 2nd parameter LintContext
. The reason is that there are some linters that live in the planemo sources. One option would be to move them here (not sure if this would add extra requirements) - which might be good anyway.
But there might be another way, due to this hack we can filter the messages before adding them to the final message list (and also if without this hack we could filter the list of messages after each linter application).
I'm a bit worried how we can ensure that our codes are not duplicated.
One message per linter sounds fine, but also like a lot of work.
Main question for me is what is the "best" solution ..
we'd have to run all relatively coarse linters, but I think that's ok ?
I also think this would be OK. Runtime should not be a concern.
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 reason is that there are some linters that live in the planemo sources
I think when I first implemented the XSD validation the external dependency on xmllint (https://github.com/galaxyproject/planemo/blob/master/planemo/xml/validation.py#L14C20-L14C27) was the issue. lxml seemed less mature at the time than xmllint. I guess we use lxml a lot more aggressively now and we have been generally pleased with the XSD validation it is doing and we now have a Galaxy dependency on lxml. I think I would be fine dropping the xmllint path through planemo if it would simplify things and let the XSD validation move into Galaxy.
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 comment @jmchilton. Will try to move it here, but probably in a separate PR.
I'm still experimenting if separate classes are the way to go. It seems difficult to avoid code duplication, but it will become clearer how we end up in the different linter messages.
1780d95
to
3305cef
Compare
bb17d76
to
aa85c64
Compare
Ping @mvdbeek and @jmchilton I'm quite happy with the current state. I have moved the For me, the TODOs would be
Regarding lxml I was also wondering if we should remove https://github.com/galaxyproject/galaxy/blob/e7a168000e627dc8579ad749174bd5eabbe15873/lib/galaxy/util/__init__.py#L68C9-L68C9 |
aa85c64
to
26c2a89
Compare
* fix all XML schema errors discovered here galaxyproject/galaxy#17081 * more linter fixes * fix value * add missing macros file for deprecated tool creates a slightly annoying Traceback in the CI setup job * ampvis: fix URLs * ampvis2: more URL fixes * ampvis2 heatmap fix duplicated output label * Apply suggestions from code review Co-authored-by: Marius van den Beek <[email protected]> --------- Co-authored-by: Marius van den Beek <[email protected]>
0fc0ec4
to
555fe5f
Compare
42dd707
to
ccced2f
Compare
now in galaxy core galaxyproject/galaxy#17081
lib/galaxy/tool_util/lint.py
Outdated
return lint_context | ||
|
||
|
||
def lint_xml_with(lint_context, tool_xml, extra_modules=None) -> LintContext: | ||
extra_modules = extra_modules or [] | ||
tool_source = get_tool_source(xml_tree=tool_xml) | ||
return lint_tool_source_with(lint_context, tool_source, extra_modules=extra_modules) | ||
|
||
|
||
def list_linters(extra_modules: Optional[List[str]] = None) -> List[str]: |
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 think that's actually a rare case where you can use a Metaclass:
In [1]: linters = {}
...:
...: class LinterMeta(type):
...: def __new__(cls, clsname, bases, attrs):
...: newclass = super(LinterMeta, cls).__new__(cls, clsname, bases, attrs)
...: linters[clsname] = newclass
...: return newclass
...:
...:
...: class MyLinter(metaclass=LinterMeta):
...: pass
...:
...: linters
Out[1]: {'MyLinter': __main__.MyLinter}
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 pointing me to metaclasses. Never took the time to learn about them.
Still I hope that 3a98a3a is sufficient?
using `__subclasses__`
2021ffb
to
3a98a3a
Compare
Any chance you could resolve the conflicts ? |
This reverts commit 46ff3f2.
Co-authored-by: Nicola Soranzo <[email protected]>
…hias/galaxy into topic/linter-overhaul
Thanks a lot @bernt-matthias, this is really important work! |
This PR was merged without a "kind/" label, please correct. |
now in galaxy core galaxyproject/galaxy#17081
now in galaxy core galaxyproject/galaxy#17081
So far mainly a call for discussion. Would be nice to have this discussed first before I continue with the other linters
(discuss we could transform only stdio, a few more, or all linters within this PR).
Currently, a linter is a function prefixed by
lint_
(most of them are contained intool_util.linters
which are used by default). These linters are at the moment quite monolithic, e.g. there is a single linter for theinput
tag and we haveonly a limited number of states (check, info, warn, error).
This led to a lot of problems and frustration (see) since
So far this change does the following
Linter
class which makes it easier to decide what a linter is in the loop over the modules.lint
fix
method that may be used to fix some problems (discuss: maybe not in this PR?).code
property (inspired by pep8/flake8 codes) which we could add to theLintMessages
that are generated by thelint
methods. But then I realised that skipping of linters simply works by the linter name (i.e. class name) .. maybe this is sufficient (discuss: advantage of codes is that we definitely have short codes for the planemo CLI, alphanumeric codes might become longer but this is closer to what we have at the moment). Alternatively we could just call the classes with codes, e.g.S001
.The following already works:
planemo lint tools/filters/cutWrapper.xml --skip StdIOAbsence
How to test the changes?
(Select all options that apply)
License