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

gh-103558: Add coverage tests for argparse #103570

Merged
merged 16 commits into from
Jun 5, 2023

Conversation

gaogaotiantian
Copy link
Member

@gaogaotiantian gaogaotiantian commented Apr 15, 2023

Current coverage at:

image

I also commented on some dead code - they are not reachable unless we explicitly call the private methods directly. I'm hesitated to do that.

From my point of view, the coverage of this file is pretty good, the missing lines are not even "corner". Most of them are simply impossible to reach unless the user explicitly trying to (meaning not using the public APIs in the documentation, but in that case, a lot of stuff can crash).

I have made two changes to the source code besides the comments:

  1. for version action, we explicitly mentioned in the docs that it expects a version keyword argument. So I made it fail at add_argument() instead of parse_args(). This will only change the failing point when the user does not specify version argument as the documentation says. I believe this actually makes more sense because we know it won't work if they do not specify it. We should not wait until the user send a --version command to let them know. (the original code access parser.version which is not even a thing)
  2. For parent argument to argparse.ArgumentParser(), it should expect a list of ArgumentParser according to the docs. If it's an ArgumentParser object, it will have _defaults attribute, unless the user explicitly deletes it, which does not have any realistic use case. So instead of ignoring the attribute missing(which we can achieve with normal usage), I checked the object type instead.

Copy link
Member

@sobolevn sobolevn left a comment

Choose a reason for hiding this comment

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

Thanks a lot, big work!

However, I think that we should not change version and parents validation just yet. Let's focus on tests itself for now, without any changes that can actually break stuff.

Because right now, we gain a bit of coverage and risk the backwards compatibility. I don't think that it is worth it.

@@ -1789,13 +1800,11 @@ def identity(string):

# add parent arguments and defaults
for parent in parents:
if not isinstance(parent, ArgumentParser):
Copy link
Member

Choose a reason for hiding this comment

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

Are there any other options on what can be actually passed here? Why it was like that? It is introduced in a pretty old commit without any mentions of "why": 698a18a

Maybe some structural subtyping? optparse?

However, it is documented that parents must be a list of ArgumentParser objects:

parents - A list of ArgumentParser objects whose arguments should also be included

So, I guess it is ok.

parser = ErrorRaisingArgumentParser(prog='PROG')
parser.add_argument('--foo', nargs="*")
parser.add_argument('foo')
with io.StringIO() as buf, redirect_stderr(buf):
Copy link
Member

Choose a reason for hiding this comment

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

We have with test.support.captured_stderr(): instead, it is easier to use and is more preferable.

Lib/argparse.py Outdated
@@ -1124,7 +1128,7 @@ class _VersionAction(Action):

def __init__(self,
option_strings,
version=None,
version,
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 that this change can influence user-defined subclasses in a breaking way.

Copy link
Member Author

Choose a reason for hiding this comment

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

You mean if user builds a class based on _VersionAction? The class is not documented and should be an implementation detail, but yes, it the users inherits the class this might break their code.

I guess that's the fundamental question for argparse - do we change it at all? Because the realitiy is, if we change it in any kind of way, we might break someone's code, even if it's a bug fix. We can improve the test coverage, but what's right and what's wrong? A wrong test is worse than no test, because the next person trying to fix it would be scared away.

Take #103498 as an example, assume we found the missing coverage there, what would we do? Do we write a "coverage test" that honors the current behavior even if it's obviously wrong according to the documentation?

We will find issues when we try to write comprehensive coverage tests, I think that's inevitable. Always keeping the current behavior would make writing tests more difficult (and to be honest, less meaningful).

However, I can revert the code change and just create a new issue for this(I still think this is a bug).

@gaogaotiantian
Copy link
Member Author

@sobolevn do we want a large PR with as many new test cases as possible while we are discovering new issues from argparse or do we want to check the tests in in batch?

@gaogaotiantian
Copy link
Member Author

@sobolevn and @hauntsaninja , this PR is not actively being worked on, but the tests still have worth. Should we consider merge them in for now?

Copy link
Member

@sobolevn sobolevn left a comment

Choose a reason for hiding this comment

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

Yes, I think so!

Copy link
Contributor

@furkanonder furkanonder left a comment

Choose a reason for hiding this comment

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

LGTM

@gaogaotiantian
Copy link
Member Author

@hauntsaninja it looks like we need a core dev to review this. Could you give it a review?

Copy link
Contributor

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

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

This is great, thanks for working on this!

I made a couple changes, but all trivial (left some more details in individual commit titles):
a) removed some incorrect copy pasted docstrings
b) added back version action tests since we're not changing behaviour there anymore
c) changed some of the comments. I'm not very familiar with argparse and don't understand the code well enough to prove the _MutuallyExclusiveGroup._remove_action claim myself. Since it's not important to this PR I removed the comment for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants