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

Fix pip show displaying the incorrect result for "Required-by" #6957

Merged
merged 10 commits into from
Sep 4, 2019

Conversation

ofrinevo
Copy link
Contributor

Fixes #6947

Hello, this is my first PR here so I hope I did everything properly.
I chose to have a separate test and stub because:

  1. I needed a new test to check a Capitalized name.
  2. I didn't want to change the existing stub since it is being used elsewhere.
  3. I didn't want to use the same test because the two installs seemed too cluttered for one test.
    I hope this is good as is, but if not, please say so and I'll change it to your liking.

Add test to check if a capitalized name is being shown properly
Add new stub package to support the test
@ofrinevo ofrinevo changed the title Bugfix/pip show displaying the incorrect result for "Required-by" Fix pip show displaying the incorrect result for "Required-by" Aug 31, 2019
Copy link
Member

@chrahunt chrahunt left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! I think you did everything fine, it looks like all the checks are happy.

I have one minor comment that I have made on the code itself.

In addition, can we also add a test to check that a package (A, for example) that requires Requires_Capitalized as requires_Capitalized causes Required-by: A to appear in the output of pip show for Requires-Capitalized?

Just a note: In general we prefer to use unit tests where possible since those are usually faster (and we're trying to speed up our tests). In this case it would require some refactoring in commands/show.py so in my opinion these tests are OK and we can probably worry about it later.

tests/data/src/requires_capitalized/setup.py Outdated Show resolved Hide resolved
@@ -0,0 +1,6 @@
from setuptools import setup

setup(name='requires_Capitalized',
Copy link
Member

@chrahunt chrahunt Sep 1, 2019

Choose a reason for hiding this comment

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

Sorry, in hindsight my previous suggestion was not very clear.

I think that this package should be named "A" (or anything else, it doesn't matter), and then it should have requires_Capitalized in its install_requires list. Then we should check that the Requires_Capitalized package correctly puts "A" in its "Required-By" list. That would ensure that the names of things in install_requires that we're comparing against are also normalized.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see.
As you can see by the following commits, I messed it up a bit.
Anyway, do you prefer having both the existing test and the new one? Just the new one?

Copy link
Member

@chrahunt chrahunt Sep 1, 2019

Choose a reason for hiding this comment

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

No worries! I would keep test_show_required_by_packages_capitalized, remove test_show_required_by_with_mixed_capitalization, and add the new one (maybe called test_show_required_by_packages_requiring_capitalized).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried to do that earlier, but it didn't install the package because of:
ERROR: No matching distribution found for requires_Capitalized==1.0 (from simple-1==1.0)
(obviously)
So I figured I should Create a new tar.gz called Requires_Capitalized for this test.
I wanted to make sure with you first that this is in fact what you meant before I do so.

Copy link
Member

Choose a reason for hiding this comment

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

I think it would be OK to explicitly install Requires_Capitalized before installing A, then we should not need another .tar.gz.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you sure that installing it explicitly would work?
I've done the following:
(Don't mind the name, WIP and stuff)

  1. Added a file
setup(name='Req_Cap_Todo',
      version='1.0',
      )
  1. Installing this file:
 editable_path = os.path.join(data.src, 'Req_Cap_Todo')
    script.pip(
        'install', '--no-index', '-f', data.find_links, editable_path
    )
  1. Installed the package
setup(name='simpleone',
      version='1.0',
      install_requires=['Req_Cap_Todo==1.0']
      )

And I get the result of
['Name: Req-Cap-Todo', 'Version: 1.0', ... , 'Requires: ', 'Required-by: ']

To make sure, I tried to installed Click first and black afterwards, but it did show the correct message (with my fix, of course)

Copy link
Member

Choose a reason for hiding this comment

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

Installing explicitly was meant to avoid making another .tar.gz, and that looks like it worked. 👍 The other item is a bug.

@@ -0,0 +1,6 @@
from setuptools import setup

setup(name='requires_Capitalized',
Copy link
Member

Choose a reason for hiding this comment

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

Installing explicitly was meant to avoid making another .tar.gz, and that looks like it worked. 👍 The other item is a bug.

required_by = [
pkg.project_name for pkg in pkg_resources.working_set
if name in [required.name for required in pkg.requires()]
if canonical_name in [required.name for required in pkg.requires()]
Copy link
Member

Choose a reason for hiding this comment

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

I think we should probably be doing canonicalize_name(required.name) here, that may fix the issue you mentioned with the "Required-by" of "Req-Cap-Todo" missing "simpleone" in our new test.

Copy link
Contributor Author

@ofrinevo ofrinevo Sep 2, 2019

Choose a reason for hiding this comment

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

You are indeed correct, I really need to setup up my pycharm debugger with tox.
Everything passes now.

I also think we should extract this list comprehension to a method, under the rule of "a function should do one thing and one thing only".
I would've done so in a personal project (without thinking too much about it) but since I'm new here I'm checking with you.
Or better yet, should I put it in search_packages_info? (this methodneeds a refactor imo but that's a different story)

Copy link
Member

@chrahunt chrahunt Sep 2, 2019

Choose a reason for hiding this comment

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

I agree this could be improved, and moving it into search_packages_info seems like it would work and make more sense.

If you make the change in a separate commit then it should be OK here. It may take a little longer for someone else to review and merge, or maybe not - everyone is a volunteer so it can vary wildly. A few things that could help:

  1. If you squash/rewrite your commits into meaningful "chunks" of changes that have clear diffs (maybe one commit for the primary change here and then one for moving the requires_by calculation into search_packages_info) that would make it easier to evaluate this PR
  2. Give each of those commits a clear message (reduces noise compared to having a handful of commits)

These aren't required by any means, but can reduce the work required to review and can be useful to know how to do.

Just FYI, not applicable here - in general if the primary change being made is larger or more complex I would probably do refactoring in separate PRs before the "main" one if I think they are required to make the "main" changes clear. If the refactoring is more like a nice-to-have but doesn't really improve understanding the "main" change then I would probably do it in a followup PR after the "main" one. You don't have to figure this out ahead of time on your own - if some refactoring is needed then it's OK to put the "main" PR on hold, do refactoring in a separate PR, and then rebase and continue with the "main" one when the refactoring is merged. Similarly it's OK to say in a PR comment or issue that some refactoring followup is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you very much for all the feedback, I appreciate it greatly.
I've been working in a python team for about 3 years but open source feedback adds much more.

Just one more question before I push:
I have an option to add a private:
def _get_requiring_packages(package_name):

    return [
        pkg.project_name for pkg in pkg_resources.working_set
        if canonicalize_name(package_name) in
           [canonicalize_name(required.name) for required in pkg.requires()]
    ]

or to do it directly in search_packages_info:

 package = {
            'name': dist.project_name,
            'version': dist.version,
            'location': dist.location,
            'requires': [dep.project_name for dep in dist.requires()],
            'required_by': [
                pkg.project_name for pkg in pkg_resources.working_set
                if canonicalize_name(dist.project_name) in
                [canonicalize_name(required.name) for required in pkg.requires()]
            ]
        }

I feel like the first option is better (and passes the 79 char per line limit!) but again, wanted to make sure before I push.

Copy link
Member

Choose a reason for hiding this comment

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

1 seems better to me, but I would make it a free function in search_packages_info and drop the leading underscore (because its scope will already prevent it from being used outside, marking it private would be redundant IMO).

Copy link
Member

@chrahunt chrahunt left a comment

Choose a reason for hiding this comment

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

Looks good! Just a few minor comments.

def get_requiring_packages(package_name):
return [
pkg.project_name for pkg in pkg_resources.working_set
if canonicalize_name(package_name) in
Copy link
Member

Choose a reason for hiding this comment

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

I would assign canonicalize_name(package_name) outside the list comprehension and then use the variable inside, which looks a little simpler (also avoids recomputing every time).

@@ -0,0 +1 @@
#
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 be able to get rid of this file.

@@ -0,0 +1 @@
#
Copy link
Member

Choose a reason for hiding this comment

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

This one too.

@@ -0,0 +1 @@
#
Copy link
Member

Choose a reason for hiding this comment

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

And this one.


setup(name='simple',
version='1.0',
install_requires=['required_by_Capitalized==1.0']
Copy link
Member

Choose a reason for hiding this comment

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

I would call this package requires_requires_capitalized (impacting the name argument here and the directory name) and have it contain 'requires_Capitalized' in its install_requires, then we can remove required_by_Capitalized. There will be a few corresponding changes in test_show_required_by_packages_requiring_capitalized.

Rename stub package required_by_mixed_cap to requires_requires_cap
Move the canonicalize_name calculation outside of a loop
Copy link
Member

@chrahunt chrahunt left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks!

@pfmoore pfmoore merged commit bc78bcd into pypa:master Sep 4, 2019
@pfmoore
Copy link
Member

pfmoore commented Sep 4, 2019

Thanks @ofrinevo for the contribution, and @chrahunt for the reviews!

@pradyunsg
Copy link
Member

Great! Thanks for the contribution @ofrinevo (and @chrahunt and @pfmoore)!

@ofrinevo Do let us know if you're interested in contributing further. :)

@ofrinevo
Copy link
Contributor Author

ofrinevo commented Sep 4, 2019

@pradyunsg Indeed I do.
I'll have some spare time in the next few months and Pip seems like a good place to spend it.

@lock lock bot added the auto-locked Outdated issues that have been locked by automation label Oct 4, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Oct 4, 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.

"pip show" incorrect result for "Required-by"
4 participants