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 a configuration command #4240

Merged
merged 56 commits into from
Jun 13, 2017
Merged

Conversation

pradyunsg
Copy link
Member

@pradyunsg pradyunsg commented Jan 18, 2017

Ref: #1736

This is a configuration command for managing configuration files for pip through pip's CLI. It makes configuration more discoverable and easier to manage.

TODO:

  • Add tests
    • pip.configuration
    • config command
  • Improve error messages
  • Fix bugs
  • More?
    • Use enum for attributes instead of constants
    • Use a sub-command structure
    • Assume a default when modifying files

@pradyunsg pradyunsg changed the title [WIP]Add a configuration command [WIP] Add a configuration command Jan 18, 2017
@pradyunsg
Copy link
Member Author

pradyunsg commented Jan 18, 2017

@dstufft @xavfernandez RFC?

(other than the lack of tests)

@@ -132,7 +132,7 @@ def __init__(self, *args, **kwargs):
self.name = kwargs.pop('name')

isolated = kwargs.pop("isolated", False)
self.config = Configuration(isolated)
self._configuration_obj = Configuration(isolated)
Copy link
Contributor

Choose a reason for hiding this comment

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

We already know it's an object here, by convention (and pretty much everything in python is one), so this naming is redundant.

If you really wish to indicate it's a private member then I'd just rename it _config.

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed. It's exactly why I put that there.

I had changed it to prevent collisions if I write that while cracking out the implementation because I don't use _obj suffix either. Forgot to revert that change.

@pradyunsg
Copy link
Member Author

Thanks @Ivoz. Any other comments?

@pradyunsg
Copy link
Member Author

pradyunsg commented Jan 24, 2017

I'm going to put this on hold for now.

Will be working on stuff this weekend.

Copy link
Member Author

@pradyunsg pradyunsg left a comment

Choose a reason for hiding this comment

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

Just went through this to make some notes.

I don't have much time at hand for this PR in the very near future.

return ERROR

# Determine which configuration files are to be loaded
if options.user_file and options.global_file:
Copy link
Member Author

Choose a reason for hiding this comment

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

Check for all 3 (global, venv, user)

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

try:
self.configuration.save()
except Exception:
logger.error("Houston, we have a problem", exc_info=1)
Copy link
Member Author

Choose a reason for hiding this comment

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

Decide how to handle this correctly...

Copy link
Member Author

Choose a reason for hiding this comment

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

Ask the user to report as bug.

logger = logging.getLogger(__name__)


# NOTE: Maybe use the optionx attribtue to normalize keynames.
Copy link
Member Author

Choose a reason for hiding this comment

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

Typo.

Copy link
Member Author

@pradyunsg pradyunsg Apr 8, 2017

Choose a reason for hiding this comment

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

Good spot me. 😛

def _make_key(variant, name):
return ".".join((variant, name))

# Some terminology:
Copy link
Member Author

Choose a reason for hiding this comment

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

Should this stay separate as a comment or be added to a doc-string?

Maybe, because it involves terminology only for this module - keep it as it is.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added to docstring.

if load_only not in ["user", "site-wide", "venv", None]:
raise ValueError(
"Got invalid value for load_only - should be one of 'user', "
"'site-wide', 'venv'"
Copy link
Member Author

Choose a reason for hiding this comment

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

Include None in the error message.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

section, name = _disassemble_key(key)

# Remove the key in the parser
modified_something = (
Copy link
Member Author

Choose a reason for hiding this comment

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

Make this less terse? Or have a comment explaining why this works.

Copy link
Member Author

Choose a reason for hiding this comment

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

It serves the intent. Comments get outdated easily.

Copy link
Member

Choose a reason for hiding this comment

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

This is a bit hard to follow, I generally prefer not to have methods with side effects in a boolean expression because it's easy to miss what's going on. I think this would be more readable as:

modified_something = False
if parser_has_section(section):
    modified_something = parser.remove_option(section, name)

I actually missed that this was having any side effect at first and it took me a minute to understand why you wanted a comment here to start with.

@@ -19,6 +20,7 @@

commands_dict = {
Copy link
Member Author

Choose a reason for hiding this comment

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

This can be changed into a dict constructor call with a list comprehension... DRY.

Copy link
Member Author

Choose a reason for hiding this comment

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

Now that we've dropped 2.6 support, it can be a dict comprehension!! :)


def set_name_value(self, options):
key, value = options.set_name_value.split("=", 1)
self.configuration.set_value(key, value) # Error here is propagated.
Copy link
Member Author

Choose a reason for hiding this comment

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

Print a nice message instead of propagating the error? Or is something doing that?

Same on line 212.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@pradyunsg
Copy link
Member Author

@dstufft @xavfernandez @pfmoore

I'm writing the tests for this and I am a little confused - how extensive should the tests be and what exact functionalities should be tested?

As such, there are so many things related to configuration that it cannot be verified to be working without modifying the entire environment. The earlier suite only tested precedence and overriding of configuration options. Things like which configuration files would be loaded, the exact contents of the files etc were not tested earlier. I'm thinking they should not be tested now either.

Thoughts?

@pfmoore
Copy link
Member

pfmoore commented May 12, 2017

@pradyunsg I'd have assumed the best way to test would be

  • Run a config command
  • Check that the underlying configuration file was changed in the way you expected

I wouldn't try to test that the behaviour changes as expected. In theory we should already have tests that check "if the config says do X, then we do it", so that side of the problem should already be handled. In practice we likely haven't covered all such cases, but it's not the issue for this PR to address that problem.

@pradyunsg
Copy link
Member Author

pradyunsg commented May 12, 2017

I am writing those tests right now - that run the command and check the result.

I was wondering about the exact degree that the tests check things... Not checking behaviour changes sounds good to me.

@pradyunsg
Copy link
Member Author

pradyunsg commented May 12, 2017

The functional tests check the behaviour by doing a subprocess run. This means that if a configuration file has to be loaded, it needs to be in one of the standard locations in order to be used by the test => if I want to check if the user file can be modified using the config command in pip, I will need the modify the actual user file - since I cannot patch in the sub process to change what file it opens. Further, it means that to check if the "site-wide" files can be modified, the test would have to be run escalated and would modify the global file somewhere in /etc/ or something.

This cannot be done as it might involve modifying a file that possibly already exists and contains probably useful contents. And well, escalation to run a test - no.

I'm not able to come up with any way to make the subprocess to use a different path for the test run. I don't want to be touching the default files when running the tests (no modify in-place or move-replace). 😕

@pradyunsg
Copy link
Member Author

PS: Travis failed due to some network issues IIUC.

@pradyunsg
Copy link
Member Author

@xavfernandez Could you add this to v10.0 milestone? Since #4193 depends on the changes made here, it means that either this also should be a part of v10.0 or that should be removed.

@pradyunsg
Copy link
Member Author

FWIW, I just pushed what test code I have. I think the tests can look the same regardless of how the patching is done but I think I'll try to figure out how to patch the files then proceed to actually writing the rest of the tests.

@pradyunsg
Copy link
Member Author

I apologize if I'm being noisy.

/ping @dstufft @xavfernandez @pfmoore

Copy link
Member

@dstufft dstufft left a comment

Choose a reason for hiding this comment

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

This looks like it's going in a good direction. I've marked some changes I'd like to see. One that I didn't mark everywhere is not shadowing builtins like file.

@@ -160,6 +160,9 @@ def _update_defaults(self, defaults):
# ignore empty values
if not val:
continue
section, key = key.split(".", 1)
if section not in [self.name, "global", ":env:"]:
Copy link
Member

Choose a reason for hiding this comment

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

Small nit, but this can be a set literal now which is slightly nicer.

Copy link
Member Author

Choose a reason for hiding this comment

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

Roger!

if action is None:
logger.error(
"Need exactly one action (--list, --edit, "
"--get, --set, --unset) to perform."
Copy link
Member

Choose a reason for hiding this comment

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

I feel like it would be better to instead of using a mandatory flag like this, to make this a positional argument to the command. So basically, instead of pip config --list ..., you would do pip config list .... Positional arguments feel like a better fit for mandatory arguments to me.

Copy link
Member

Choose a reason for hiding this comment

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

I agree and it would also prevent the (twisted) corner case where you put:

[config]
list = 1

making the use of the config command rather complicated.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good.

if sum([options.user_file, options.global_file, options.venv_file]):
logger.error(
"Need at-most one configuration file to use - pass "
"only one of --global, --user, --venv."
Copy link
Member

Choose a reason for hiding this comment

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

We should probably just default to one of these files. Either the user file, or if we want to be smarter, we could say the venv file if one exists, and otherwise the user file.

Copy link
Member Author

Choose a reason for hiding this comment

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

The smarter variant sounds better. :)

Copy link
Member Author

Choose a reason for hiding this comment

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

The only time assuming a file could be useful is when we have to modify something. At the other times - list and get, it makes more sense to be asking the user to specify if they want to load a specific file - in which case we only load that file.

I feel it makes more sense to require the user to pass which file is to be modified or used since it gives them the ability to inspect the effect of each level or overridding as well as a fine degree of control.

kwargs["load_only"] = "venv"
elif action in ["set", "unset", "edit"]:
logger.error(
"Need one configuration file to modify - pass one of "
Copy link
Member

Choose a reason for hiding this comment

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

This seems to be duplicate logic to the above since you can't get to this point without one of these files existing. However, my same comment up above applies, I think we should just pick something to default to.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep. This'll be redundant.

if options.editor is None:
logger.error(
"--edit requires an editor to be passed, either using "
"--editor or by setting it in a configuration file."
Copy link
Member

Choose a reason for hiding this comment

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

I don't see an --editor flag defined up above, did I miss it? It would also be nice to support the semi standard $EDITOR environment variable.

)
return ERROR

file = self.configuration.get_file()
Copy link
Member

Choose a reason for hiding this comment

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

Small nit: I prefer not using the name file because it shadows a builtin, file_ is ok, or filename or something like that.

Copy link
Member Author

Choose a reason for hiding this comment

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

WHooops!

pip/locations.py Show resolved Hide resolved
Copy link
Member

@xavfernandez xavfernandez left a comment

Choose a reason for hiding this comment

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

Quite long PR, I'll try to re-review it soon.

if options.editor is None:
logger.error(
"--edit requires an editor to be passed, either using "
"--editor or by setting it in a configuration file."
Copy link
Member

Choose a reason for hiding this comment

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

I agree with @dstufft, it would make sense to default on the value of $VISUAL or $EDITOR (at least for POSIX systems).

Copy link
Member Author

Choose a reason for hiding this comment

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

--editor - I think I dropped it by mistake while doing the rebase with master. Oops. I have added it back.

Made the change to check $EDITOR if --editor is not passed. I don't think we need to differ our behaviour across OSs.

return ERROR

# Determine which configuration files are to be loaded
if sum([options.user_file, options.global_file, options.venv_file]):
Copy link
Member

Choose a reason for hiding this comment

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

Is it missing a >1 or a not or am I missing something ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Not having it compare with anything is equivalent to checking the falsiness.

Sum of bools gives an integer. All non-zero integers are true-ish.

Copy link
Member Author

Choose a reason for hiding this comment

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

Anyway, I changed this part.

if action is None:
logger.error(
"Need exactly one action (--list, --edit, "
"--get, --set, --unset) to perform."
Copy link
Member

Choose a reason for hiding this comment

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

I agree and it would also prevent the (twisted) corner case where you put:

[config]
list = 1

making the use of the config command rather complicated.

import os
import sys
import logging
Copy link
Member

Choose a reason for hiding this comment

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

nit: alphabetical order ;)

Copy link
Member Author

Choose a reason for hiding this comment

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

Roger.

Copy link
Member Author

Choose a reason for hiding this comment

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

I actually like this increasing length form... :/

Made the change though. :)

subprocess.check_call([options.editor, file])
except subprocess.CalledProcessError as e:
logger.error(
"Subprocess exited with exit code %d", e.returncode
Copy link
Member

Choose a reason for hiding this comment

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

Maybe also add the editor used in the error msg (so that it is clear that we are not refering to pip, which will also return an error code).

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good.

def __init__(self, isolated, load_only=None):
super(Configuration, self).__init__()

if load_only not in ["user", "site-wide", "venv", None]:
Copy link
Member

Choose a reason for hiding this comment

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

Since we are using these values in quite a few places, I'd rather we use some constants USER, SITEWIDE, etc.

Copy link
Member Author

Choose a reason for hiding this comment

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

Umm, I don't feel very strongly about it... I guess.

Will do.

raise ConfigurationError("Needed a specific file to be modifying.")

@property
def _dictionary(self):
Copy link
Member

Choose a reason for hiding this comment

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

A chainmap would have been nice but the need of a vendored backport in python2.7 would maybe make it a little overkill though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah... I was thinking about it but decided it was overkill too.

try:
self.configuration.unset_value(key)
except ConfigurationError:
logger.error("Could not unset value in configuration")
Copy link
Member

Choose a reason for hiding this comment

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

a return ERROR here would be cleaner

Copy link
Member Author

Choose a reason for hiding this comment

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

Roger!

try:
self.configuration.set_value(key, value)
except ConfigurationError:
logger.error("Could not set value in configuration")
Copy link
Member

Choose a reason for hiding this comment

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

a return ERROR here would be cleaner

Copy link
Member Author

Choose a reason for hiding this comment

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

Roger!

@BrownTruck
Copy link
Contributor

Hello!

I am an automated bot and I have noticed that this pull request is not currently able to be merged. If you are able to either merge the master branch into this pull request or rebase this pull request against master then it will eligible for code review and hopefully merging!

@BrownTruck BrownTruck added the needs rebase or merge PR has conflicts with current master label May 14, 2017
@AraHaan
Copy link
Contributor

AraHaan commented May 14, 2017

Will this configuration command also allow setting additional places to download or search for packages that may or may not be on PyPi? Also, it would be nice if the confirmation format was in json in case people want a list of multiple places for pip to look in additionally to PyPi for packages with source or wheel distributions. (Like if they use an github repository to store source distributions / wheels they make in an cental location separate from PyPi for potentially multiple packages.

@pypa-bot pypa-bot removed the needs rebase or merge PR has conflicts with current master label May 14, 2017
@pradyunsg
Copy link
Member Author

@AraHaan

Will this configuration command also allow setting additional places to download or search for packages that may or may not be on PyPi?

Do you mean adding to a list or removing from it? As of now, it doesn't.
If you're asking if there's some sort of behaviour change in pip, there is no change on the end-user's side.

If that's not what you mean, I'm afraid it's not clear to me what you're asking.

Also, it would be nice if the confirmation format was in json

I think there will be support for TOML based configuration soon (tracking issue: #3809).

@pradyunsg
Copy link
Member Author

Do we have options that expect their values to be lists? If so, they'll need special handling, correct?

@pradyunsg
Copy link
Member Author

pradyunsg commented May 14, 2017

@dstufft @xavfernandez Thanks for the reviews! ^.^

I think I addressed all the review comments. I still have to write a few tests for the command - other than that, is there anything else withstanding?

Should I write more tests for the command?

@AraHaan
Copy link
Contributor

AraHaan commented May 14, 2017

The reason why I wanted additional paths to be lists is because they can be easy to append / remove entries on and so then people can do things like specify a specific location in the edit command to add or remove entries on the list. Like for example what if they want pip to look for wheels / source distributions at https://github.com/AraHaan/wheels/blob/master/ without clowning the git repository and a list of other ones from certain other groups doing something similar to other packages.

# Load a new configuration
isolated = options.isolated_mode
self.configuration = Configuration(
isolated=isolated, load_only=load_only
Copy link
Member

Choose a reason for hiding this comment

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

nit: isolated does not seem very useful

"""

import pytest
import textwrap
Copy link
Member

Choose a reason for hiding this comment

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

nit: standard library imports first, then library

Copy link
Member Author

Choose a reason for hiding this comment

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

Roger!

import textwrap
import tempfile
import functools
import contextlib
Copy link
Member

Choose a reason for hiding this comment

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

import orders 🙄

Copy link
Member Author

Choose a reason for hiding this comment

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

Whoops!


# Error handling happens here, not in the action-handlers.
try:
handlers[action](options, args)
Copy link
Member

Choose a reason for hiding this comment

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

args[1:] would make more sense to me: no need to pass args[0] to the handler.

Copy link
Member Author

Choose a reason for hiding this comment

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

Roger!



_environ_prefix_re = re.compile(r"^PIP_", re.I)
def _make_key(variant, name):
Copy link
Member

Choose a reason for hiding this comment

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

Since it is only used in one place and quite simple, I'd say you could ditch it.

# at the base we have any site-wide configuration
files = list(site_config_files)
# environment variables have the lowest priority
config_file = os.environ.get('kinds.PIP_CONFIG', None)
Copy link
Member

Choose a reason for hiding this comment

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

It looks like a sed gone wrong ^^

yield kinds.GLOBAL, list(site_config_files)

# per-kinds.user configuration next
kinds.should_load_user_config = not self.isolated and not (
Copy link
Member

Choose a reason for hiding this comment

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

no kinds

kinds.should_load_user_config = not self.isolated and not (
config_file and os.path.exists(config_file)
)
if kinds.should_load_user_config:
Copy link
Member

Choose a reason for hiding this comment

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

same

@pradyunsg
Copy link
Member Author

@xavfernandez Thanks for this! ^.^

I had made that change in a hurry and pushed.

@pradyunsg pradyunsg changed the title [WIP] Add a configuration command Add a configuration command May 15, 2017
@pradyunsg
Copy link
Member Author

/request-review @dstufft @xavfernandez @pfmoore


# Pool the options into different groups
section_items = {name: [] for name in override_order}
for key, val in self.config.items():
Copy link
Member

Choose a reason for hiding this comment

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

Maybe use for section_key, val in self.config.items(): here and section, key = section_key.split(".", 1) a line below

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep!

section_items = {name: [] for name in override_order}
for key, val in self.config.items():
# ignore empty values
if not val:
Copy link
Member

Choose a reason for hiding this comment

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

Maybe a logger.debug stating that we are ignoring empty value for such (section, key)

Copy link
Member Author

Choose a reason for hiding this comment

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

There's no logger in this module. I'll add one. :)

.format(e.returncode)
)

def _get_n_args(self, args, n):
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add an help/example text, which would allow key, value = self._get_n_args(args, n=2, example="config set timeout 20") in def set_name_value and a nicer error message like:

Got unexpected number of arguments, expected 2 (example: "pip config set timeout 20")

(using pip.utils.get_prog)

Copy link
Member Author

Choose a reason for hiding this comment

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

Done!

if options.editor is not None:
return options.editor
elif "EDITOR" in os.environ:
return os.environ["EDITOR"]
Copy link
Member

Choose a reason for hiding this comment

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

support for the standard $VISUAL would be nice also (git for example supports both)

Copy link
Member Author

Choose a reason for hiding this comment

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

On it!

from pip.utils import appdirs
from pip.utils import ensure_dir, enum

_need_file_err_msg = "Needed a specific file to be modifying."
Copy link
Member

Choose a reason for hiding this comment

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

This does not seem to be used

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed.

"""Loads configuration
# The order here determines the override order.
self._override_order = [
kinds.GLOBAL, kinds.USER, kinds.VENV, kinds.ENV, kinds.ENV_VAR
Copy link
Member

Choose a reason for hiding this comment

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

A comment explaining the difference between kinds.ENV (the config from the file from PIP_CONFIG_FILE) and kinds.ENV_VAR (the config from environment vars) would have helped :o

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll add one where kinds is defined.

if not self.isolated:
self._load_environment_vars()

def get_file(self):
Copy link
Member

Choose a reason for hiding this comment

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

get_file_to_edit() maybe ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Made the change.

"""
self._ensure_have_load_only()

if key not in self._config[self.load_only]:
Copy link
Member

Choose a reason for hiding this comment

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

maybe a logger.debug recalling the value of load_only

Copy link
Member Author

@pradyunsg pradyunsg Jun 1, 2017

Choose a reason for hiding this comment

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

Added in _ensure_have_load_only.

for file_ in files:
# If there's specific variant set in `load_only`, load only
# that variant, not the others.
if self.load_only is not None and variant != self.load_only:
Copy link
Member

Choose a reason for hiding this comment

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

logger.debug ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added.


# XXX: This is patched in the tests.
def _get_config_files(self):
Copy link
Member

Choose a reason for hiding this comment

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

Maybe iter_config_files would be a better name for a generator.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed.

@pradyunsg
Copy link
Member Author

Some external change broke the CI builds.

@pradyunsg
Copy link
Member Author

pradyunsg commented Jun 13, 2017

Is there anything outstanding on this PR?

section, name = _disassemble_key(key)

# Remove the key in the parser
modified_something = (
Copy link
Member

Choose a reason for hiding this comment

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

This is a bit hard to follow, I generally prefer not to have methods with side effects in a boolean expression because it's easy to miss what's going on. I think this would be more readable as:

modified_something = False
if parser_has_section(section):
    modified_something = parser.remove_option(section, name)

I actually missed that this was having any side effect at first and it took me a minute to understand why you wanted a comment here to start with.

@dstufft dstufft merged commit 78573d9 into pypa:master Jun 13, 2017
@pradyunsg pradyunsg deleted the configuration-command branch June 13, 2017 19:27
@xavfernandez xavfernandez added this to the 10.0 milestone Jun 13, 2017
@lock lock bot added the auto-locked Outdated issues that have been locked by automation label Jun 3, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Jun 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
auto-locked Outdated issues that have been locked by automation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants