-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix 972 argparse #1596
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
Fix 972 argparse #1596
Conversation
Also added unused method for finding a nearby pylintrc for when we turn on per directory configs.
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.
Thank you for this PR, Ashley! And sorry for taking so long to review it.
Looks really nice so far and seems to be a great step towards replacing optparse and making the config situation in pylint more sane. Left a bunch of comments for an initial review round, let's keep it open for now until you address the remainder of the stuff (TODOs and whatnot(
pylint/config.py
Outdated
search_dir = os.path.expanduser(search_dir) | ||
path = find_pylintrc_in(search_dir) | ||
|
||
for search_dir in walk_up(search_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.
Let's check the path condition before going in the loop, something as in:
if not path:
for search_dir in walk_up(search_dir):
if not os.path.isfile(...):
break
...
if path:
...
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.
Done in an upcoming commit!
pylint/config.py
Outdated
path = find_pylintrc_in(search_dir) | ||
|
||
for search_dir in walk_up(search_dir): | ||
if path or not os.path.isfile(os.path.join(search_dir, '__init__.py')): |
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.
How will this work for Python 3.3+ namespace packages? From my understanding, it seems this change won't support them. I'm wondering if we should though, what is your opinion on it?
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, it won't. We could do something like; once we don't find an __init__.py
, keep looking for the closest pylintrc file and only use it if a find a setup.py in a parent directory. I'm not sure how I feel about looking for setup.py
files because it's possible that the package isn't installed that way, but I can't think of another way of doing it.
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.
Yeah, I feel the same way about a setup.py. Maybe expose a limit of levels, e.g. lookup maximum 5 levels, if there is no pylintrc in any of those, bail out with an exception?
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'll have a global/commond line config by this point so that should work!
yield | ||
finally: | ||
optparse.HelpFormatter.expand_default = orig_default | ||
and option.help is not argparse.SUPPRESS] |
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.
(commenting here because I don't have other useful context) Won't we support any longer the various option kinds that we have now yn
, regexp_csv
, multiple_choices
? Or the plan is to add support for them after this PR? Sorry if you mentioned this already, I can't seem to find any reference to these in this PR.
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 still support these. In _convert_definition
I'm converting any existing validators from a string (eg "yn"
, "regexp_csv"
) to their validator functions so that argparse can use it.
|
||
@staticmethod | ||
def _convert_definition(option, definition): | ||
"""Convert an option definition to a set of arguments for add_argument. |
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.
Let's maybe pick a style and stick with it? :) This file seems to have both Google and Sphinx style.
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.
Good point. Work habits sneaking in ;) I've done everything to sphinx.
# add provider's specific options | ||
for opt, optdict in options: | ||
self.add_optik_option(provider, group, opt, optdict) | ||
|
||
def add_optik_option(self, provider, optikcontainer, opt, optdict): | ||
args, optdict = self.optik_option(provider, opt, optdict) | ||
option = optikcontainer.add_option(*args, **optdict) | ||
if hasattr(optikcontainer, '_parser'): | ||
optikcontainer = optikcontainer._parser |
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 hope we can get rid of this optik
names later on.
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.
Me too. I'll be phasing it out as more and more of this config logic disappears.
pylint/config.py
Outdated
def add_option(self, option_definition): | ||
name, definition = option_definition | ||
if name in self._option_definitions: | ||
# TODO: Raise something more sensible |
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.
Yup, let's do that
pylint/config.py
Outdated
definition (dict): The argument definition to convert. | ||
|
||
Returns: | ||
tuple(str, list, dict): A tuple of the group to add the argument to, |
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.
Maybe return a namedtuple?
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.
Normally I would agree but this is a private method used in only one place, so it seems like overkill. I could make it a private namedtuple though if you'd prefer to have it?
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.
Yup, let's leave it as is!
e5b7905
to
49b2c77
Compare
Looks like it's taken me just as long to get back to you ;) Thanks for having a look. The rest of the TODOs are things to be fixed in future PRs, before this gets merged into master. |
Looks good @AWhetter ! |
This is part of the per directory config work (#618) so this is being merged into
per_dir_config
and not master. It also isn't 100% production ready.Fixing #972 and moving the command line parsing out of the PyLinter class is a huge change. So I'm splitting it up into smaller chunks. Although, this has still ended up being pretty big so I'll explain some of the changes here. Don't feel as though you can't make statements about the overall design, and feel free to be picky even though this is a big change.
This first chunk of work removes optparse in favour of argparse. I've also added the start of some classes that we'll eventually start using once we fully move the config parsing out of PyLinter:
Configuration
: This is anargparse.Namespace
like object that will store all of the configuration values and be supplied to each checker through theself.config
variable of the checker. At the moment is mostly provides an abstraction from argparse so that we aren't giving each checker anargparse.Namespace
. Eventually it will also be useful for merging configs which we'll need to implementinclude
directives in configs.ConfigurationStore
: We don't use this at the moment but eventually it'll be used for keeping track of what config objects belong to what directory. Currently it's merging configs from parent directories just to give an example of how we can mergeConfiguration
objects but eventually it will check the config for include directives and merge as appropriate (unless it ends up making more sense for the file parser to do it).ConfigParser
: This is a base class for anything that reads a configuration from somewhere. It was a bit undecided whether this needed to be a class because generally they're small, but I think keeping things inheriting fromConfigParser
will make it easier for others to create a newConfigParser
classes to get a config from other sources (eg a gui, registry) without also having to write a wholeRunner
class.CLIParser
: At the moment this is pretty much just a container for anargparse.ArgumentParser
but eventually I'm hoping that we'll only need to callpreprocess
andparse
. To make wedging this into optik a bit easier the optik code is using theargparse.ArgumentParser
directly but the end goal is for outside users to need to interact with the class throughOptionDefinition
objects only.IniFileParser
: LikeCLIParse
this is mostly a container for anOptionParser
at the moment and will be more useful later.LongHelpFormatter
andLongHelpArgumentParser
: These are objects to implement--long-help
,--long-long-help
, etc in argparse. argparse doesn't let you do much to override it's help formatting behaviour so I'm having to do a bit of fudgery to get this to work. Mostly this is setting the level on the formatter class because there's too much argparse machinery to override the methods that call the formatter class to call the formatter class methods with a level. This was the least hacky way I could see of doing things without relying on argparse implementation details. Let me know if you can think of another way!Breaking changes:
value
is no longer anoptparse.Option
but anargparse.Action
.OptionDefinition
objects to the manpage directly.The next step for #972 is to remove option callbacks altogether. This will also mean we stop dispatching options to
OptionProvider
objects and instead set options on theConfiguration
objects directly.