Skip to content

Move legacy argparser #11872

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

Merged
merged 4 commits into from
Sep 15, 2017
Merged

Conversation

Rostepher
Copy link
Contributor

@Rostepher Rostepher commented Sep 12, 2017

Purpose

This PR is part of the staging necessary for #11827 and builds on the staging from #11880 and #11882. Put simply this PR bundles up the current argument parser and the _apply_default_arguments function into an ApplyDefaultsArgumentParser class that will call _apply_default_arguments anytime the parse_args method is called. The new parser bundles up these two interconnected behaviors making it easier to test the expected outputs.

In addition I've also taken the time to implement a new suite of unit-tests to ensure the expected behavior for the argument parser. These tests should make it easier to transition to the new system in #11827, giving reviewers more confidence that it actually works.

The unit-test suite (ab)uses Python meta classes to dynamically generate tests for each preset and argument, ensuring that they all have coverage. There's still a handful of tests types left to implement for the arguments and these tests have uncovered a preset that is broken (which will either be removed or fixed).

rdar://problem/34336890

@bob-wilson
Copy link
Contributor

You mentioned a preset that is broken. Can you elaborate?

@bob-wilson
Copy link
Contributor

@gottesmm Do you have any opinion about splitting out a build_swift package like this?

@Rostepher
Copy link
Contributor Author

My new test suite uncovered an unsupported flag in the buildbot_iphoneos_arm64_crosscompiler preset, namely the --build-swift-ios-test option. Looks like the preset was added this year, but from my searching that option has never been available. My intuition says that the preset is not used commonly or we would have seen this crop up earlier.

@Rostepher
Copy link
Contributor Author

As for splitting out to a new build_swift package I was planning to make that the defacto home for the Python sources to build-script. We do already have the swift_build_support package, but I would prefer to keep them separate and eventually phase out that package which is structured awkwardly. Any functionality necessary from swift_build_support will eventually be migrated over to the new build_swift.

I'm open to other suggestions or names for the module, I landed on build_swift because it was short and I think more appropriate than a module named build_script.

@gottesmm
Copy link
Contributor

I don't have a problem with the refactoring. I would prefer the name swift_build though. We already have precedent in that area with llvm_build and swift_build_support. But let me look at this in more depth.

]


class ApplyDefaultsArgumentParser(argparse.ArgumentParser):
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to loop back from our conversation, can you do this in a separate commit. I want to be able to eyeball the move of code and then see the refactoring done upon the nicely refactored code. It will make it easier for me to review.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing! I'll chunk out the two large code-moves into separate PRs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks! = )

@Rostepher
Copy link
Contributor Author

Alright, in order we will need to merge #11880 and then #11882 before this one can be merged. Until then the diff for this PR will look much larger since this branch has those changes in it.

@Rostepher Rostepher force-pushed the move-legacy-argparser branch 5 times, most recently from 2544c33 to 0c8ac1f Compare September 14, 2017 00:14
@Rostepher
Copy link
Contributor Author

Rostepher commented Sep 14, 2017

@gottesmm Would you mind taking a look over this PR again? Feel free to ignore the defaults module, that commit will be merged in #11911. The unit-tests and _ApplyDefaultsArgumentParser are what's important here.

@Rostepher
Copy link
Contributor Author

@swift-ci please smoke test

@Rostepher Rostepher force-pushed the move-legacy-argparser branch 2 times, most recently from f82d4fc to bc4f87b Compare September 14, 2017 19:03
…er (ab)using metaclasses to dynamically generate unit-tests for each valid argument and preset.
@Rostepher Rostepher force-pushed the move-legacy-argparser branch from bc4f87b to 1749f98 Compare September 14, 2017 19:44
@Rostepher
Copy link
Contributor Author

@swift-ci please smoke test

Copy link
Contributor

@gottesmm gottesmm 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 fine to me.

"""Option that is not supported."""

def __init__(self, *args, **kwargs):
kwargs['dest'] = kwargs.get('dest')
Copy link
Contributor

Choose a reason for hiding this comment

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

What are you trying to do here?

Copy link
Contributor Author

@Rostepher Rostepher Sep 15, 2017

Choose a reason for hiding this comment

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

That line facilitates allowing instances of UnsupportedOption to skip passing in the dest argument since it doesn't seem necessary for that sort of option. I would be better written as kwards['dest'] = kwargs.pop('dest', None) which pops dest off the input keyword args or resolves to None and sets the value manually when passed into the superclass constructor.

I had originally tried to add dest as positional argument in the UnsupportedOption init function, but I was running into issues with dest being passed twice to the superclass constructor. This was the best way I found after some searching to achieve the desired effect.


EXPECTED_OPTIONS = [
# Ignore the help options since they always call sys.exit(0)
_BaseOption('-h', dest='help', default=argparse.SUPPRESS),
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not make a special thing for help rather than using _BaseOption.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would probably clean this up a bit. I'll make that change.


# NOTE: We'll need to manually test the behavior of these since they
# validate compiler version strings.
_BaseOption('--clang-compiler-version',
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here. Can you make a wrapper rather than using an _ class? Looks weird to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do!

@Rostepher
Copy link
Contributor Author

@swift-ci please smoke test

…ould give better output in case the default tests fail.
@Rostepher
Copy link
Contributor Author

@swift-ci please smoke test

@Rostepher Rostepher merged commit 695ded2 into swiftlang:master Sep 15, 2017
@Rostepher Rostepher deleted the move-legacy-argparser branch September 15, 2017 08:05
Copy link
Contributor

@gottesmm gottesmm left a comment

Choose a reason for hiding this comment

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

Some post commit review.

@@ -252,16 +260,25 @@ class UnsupportedOption(_BaseOption):
"""Option that is not supported."""

def __init__(self, *args, **kwargs):
kwargs['dest'] = kwargs.get('dest')
kwargs['dest'] = kwargs.pop('dest', None)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is still really weird to me. What are you trying to do here?

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'm injecting my own definition for dest such that the UnsupportedOption class does not need to be instantiated with it manually passed. Here I'm passing through any manually set dest or setting it by default to None. In all cases it's defined in the kwargs and satisfies the _BaseOption constructor.

@@ -220,6 +227,8 @@ def generate_option_test(cls, option):
return cls._generate_append_option_test(option)
elif option.__class__ is expected_options.ChoicesOption:
return cls._generate_choices_option_test(option)
elif option.__class__ is expected_options.HelpOption:
return cls._generate_help_option_test(option)
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be done cleaner via a dictionary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When you say 'via a dictionary' do you mean I should have a dictionary from class to function? I'm not sure I'm following along.

@@ -143,6 +143,13 @@ def test(self):
return test

@classmethod
def _generate_help_option_test(cls, option):
def test(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't do anything, no? You aren't invoking test. What is the purpose of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The meta-class here is used to generate unit-test cases dynamically. When Python is invoked with the unittest argument it will execute all methods on classes subclassing unittest.TestCase that begin with the word test. It's not required in any Python unittest code to invoke the test cases yourself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On a second look you're right, I forgot to return the actual test here. I'll fix this in the next PR

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

Successfully merging this pull request may close these issues.

4 participants