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 --extra-index-url from all extra indexes #1980

Merged
merged 17 commits into from
Apr 17, 2018
Merged

Conversation

techalchemy
Copy link
Member

@techalchemy techalchemy commented Apr 13, 2018

Handle indexes, extra indexes, uncached sources

Signed-off-by: Dan Ryan dan@danryan.co

@techalchemy
Copy link
Member Author

@uranusjr I want to favor this over #1950 but the get_source method in pipenv.Project only checks the cached lockfile, which doesn't work if you add a new source to the pipfile that hasn't been locked yet (i.e. you use --skip-lock) -- thoughts?

@techalchemy techalchemy force-pushed the 1973-extra-index-urls branch 5 times, most recently from 205edcf to 7cc1d08 Compare April 14, 2018 00:47
@techalchemy techalchemy added the Category: Private PyPIs 😎 Problem relates to private PyPI usage. label Apr 14, 2018
@techalchemy techalchemy added this to the 11.10.1 milestone Apr 14, 2018
@techalchemy techalchemy force-pushed the 1973-extra-index-urls branch 4 times, most recently from b3512ed to 2315eab Compare April 17, 2018 00:27
@techalchemy
Copy link
Member Author

@uranusjr @jtratner @ncoghlan @frostming would appreciate if at least one of you can look this over before it gets merged so I can go ahead and cut a bugfix release

techalchemy and others added 12 commits April 16, 2018 23:14
- Always add extra indexes when installing
- Look up indexes by key if key is given instead of url
- Fixes #1973, #1974, #1852

Signed-off-by: Dan Ryan <dan@danryan.co>
Signed-off-by: Dan Ryan <dan@danryan.co>
Signed-off-by: Dan Ryan <dan@danryan.co>
- Handle extra-index-urls when resolving
- Handle extra-index-url when using `--skip-lock`
- Parse index arguments when installing individual packages
- Translate index aliases to urls
- Always include extra indexes when installing a packages
- `get_source()` falls back to `parsed_pipfile['source']` for sources when
   not present in the lockfile (#1994)
- Include index and extra-index-url arguments in `pipenv lock -r` output
- Fixes #1973, #1974, #1852, #1977, #1994

Signed-off-by: Dan Ryan <dan@danryan.co>
Signed-off-by: Dan Ryan <dan@danryan.co>
Signed-off-by: Dan Ryan <dan@danryan.co>
Signed-off-by: Dan Ryan <dan@danryan.co>
Signed-off-by: Dan Ryan <dan@danryan.co>
Signed-off-by: Dan Ryan <dan@danryan.co>
Copy link
Member

@uranusjr uranusjr left a comment

Choose a reason for hiding this comment

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

Made some comments. The most problematic to me is test_install_named_index_alias. My other comments are mostly petty.

elif url:
if source.get('url') in url:
return source
source = [s for s in sources if s.get('url') in url]
Copy link
Member

Choose a reason for hiding this comment

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

I know this was the case before, but is it a good idea to use in here, or should we use startswith instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

probably, it seemed odd when I was reimplementing it but I kind of copied what was there... I can't really imagine why we'd use in vs startswith

Copy link
Member

Choose a reason for hiding this comment

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

git blame shows @kennethreitz and it dates all the way back to the original implementation (v2.8.7!). Maybe no particular reason, just to hammer out something working first.

return source
source = [s for s in sources if s.get('url') in url]
if source:
return source[0]
Copy link
Member

Choose a reason for hiding this comment

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

Isn’t this essentially what first does? I don’t dislike this (I never liked the first library, in fact), but maybe we should be consistent here.

Copy link
Member Author

Choose a reason for hiding this comment

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

if it's imported I'll swap it over, in the spirit of speeding up startup time (which primarily was coming from indirect loading of the project module) I was trying to avoid extra imports

""".format(os.environ.get('PIPENV_TEST_INDEX')).strip()
f.write(contents)
c = p.pipenv('install pytz --index pypi')
assert c.return_code == 0
Copy link
Member

Choose a reason for hiding this comment

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

A few problems:

  • Comment seems wrong. A lot of tests below have the wrong comment, it seems? None of them are using a Git repo afaict.
  • This is not future-proof. If we add pytz to the mock PyPI (we might) this cannot detect an incorrect --index implementation.
  • Probably don’t want to use dict.get. If PIPENV_TEST_INDEX is not set this needs to fail loudly.

Copy link
Member Author

Choose a reason for hiding this comment

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

oh yeah I think I've been copy pasting a bunch, I can do some comment cleanup

  • I agree about the package, we can use something that's only available on the test index like the one I am using in the other test

@uranusjr
Copy link
Member

Forgot to mention in the review—split_index and split_extra_index seem a bit too similar to me. Is it possible to refactor the logic out?

Signed-off-by: Dan Ryan <dan@danryan.co>
@@ -1367,6 +1367,7 @@ def pip_install(
pre=False,
selective_upgrade=False,
requirements_dir=None,
extra_indexes=None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

wdyt about, rather than making this type polymorphic, enforcing that extra_indexes must always be a list and when calling this function on line 841, ensuring that it's always a list (or having split_extra_index always return a list of elements)

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 think that makes sense but I want to stay out of the weeds on that logic until I get this bugfix released, so lets plan for 11.11.0

extra_indexes = [extra_indexes,]
for idx in extra_indexes:
try:
extra_src = project.find_source(idx).get('url')
Copy link
Collaborator

Choose a reason for hiding this comment

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

what exactly is going on here? how would you hit this case?

Copy link
Member Author

Choose a reason for hiding this comment

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

If you have multiple indexes in your pipfile but you don't specify anything special when you run pipenv install, this makes sure we use all of the indexes you specify as extra index urls

source = [s for s in project.pipfile_sources if s.get('name') == name]
assert source[0]['name'] == name
assert source[0]['url'] == url
source = project.get_source(name=name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

why can't you just do one big equality check here?

source = [s for s in project.pipfile_sources if s.get('name') == name]
assert source
source = source[0]
assert source['name'] == name
assert source['url'] == url
assert source == project.get_source(name=name)
assert source == project.get_source(url=url)
assert source == project.find_source(name)
assert source == project.find_source(url)

additionally where are there get and find methods that do the same thing?

Copy link
Member Author

Choose a reason for hiding this comment

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

so the pipfile_sources property hits the pipfile directly, exclusively. The get_source method (which I didn't write and wasn't sure about) appears to consult the lockfile, and when I happened upon it it only consulted the lockfile. find_source is a helper method to deal with #1852 so it simply saves reimplementing the same calls/checks a bunch in order to figure out if we have a named source

Copy link
Member Author

Choose a reason for hiding this comment

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

However yes this can probably be done the way you described

Copy link
Member Author

Choose a reason for hiding this comment

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

turns out equality checks don't work but I did fix this


@pytest.mark.project
@pytest.mark.sources
def test_get_cached_source(PipenvInstance, pypi):
Copy link
Collaborator

Choose a reason for hiding this comment

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

what do you think about combining these two tests) which look nearly the same except for lock step into one test that looks like:

@pytest.mark.parameterized('lock_first', [True, False])
def test_get_source(PipenvInstnace, pypi, lock_first):
     with PipenvInstance(...)
        with open(p.pi...):
            ...
    if lock_first:
        # force source to be cached
        c = p.pipenv('lock')
        assert c.return_code == 0

that way it's clearer how these two checks differ

# dependency anyway.
with PipenvInstance() as p:
with open(p.pipfile_path, 'w') as f:
contents = """
Copy link
Collaborator

Choose a reason for hiding this comment

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

since these two are the same, how do you feel about using a constant and/or a fixture:

@pytest.mark.install # private indexes need to be uncached for resolution
@pytest.fixture
def PrivateIndexPipenvInstance(PipenvInstance):
    # This uses the real PyPI since we need Internet to access the Git
    # dependency anyway.
    with PipenvInstance() as p:
        with open(p.pipfile_path, 'w') as f:
             f.write(contents)
        yield p

that way you don't have to repeat the @pytest.mark.install as much and you can slim down the test to make it easier to read.

Copy link
Collaborator

Choose a reason for hiding this comment

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

def test_private_index_skip_lock(PrivateIndexPipenvInstance):
    p = PrivateIndexPipenvInstance
    c = p.pipenv('install --skip-lock')
    assert p.pipenv('install --skip-lock')

or if you want to keep the convention that you call PipenvInstances in a with statement, can do:

def PrivatePipenvIndexInstance(PipenvInstance):
     @contextlib.contextmanager
     def inner():
         with PipenvInstance() as p:
             # write stuff
             yield p
     return inner

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'm not that great with pytest so I don't want to hold the release up while I tinker with this but I hear the comments and agree broadly speaking that we can afford to clean this up some more

- Interpolate environment vars into pipfile sources
- Use first to more efficiently handle list comprehensions

Signed-off-by: Dan Ryan <dan@danryan.co>
Signed-off-by: Dan Ryan <dan@danryan.co>
@techalchemy
Copy link
Member Author

Ok mostly changed stuff, didn't rewrite all of the testing w/ the fixtures and refactor points per @jtratner but did refactor them a bit to be more concise & got them passing again

Signed-off-by: Dan Ryan <dan@danryan.co>
Signed-off-by: Dan Ryan <dan@danryan.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Category: Private PyPIs 😎 Problem relates to private PyPI usage.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants