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

Switch to using the native options parser values #21385

Merged
merged 10 commits into from
Sep 20, 2024

Conversation

benjyw
Copy link
Contributor

@benjyw benjyw commented Sep 9, 2024

But still compare against the legacy parser so we can warn about
discrepancies.

But still compare against the legacy parser.
legacy_values = self.get_parser(scope).parse_args(
parse_args_request, log_warnings=log_parser_warnings
)
native_values = self.get_parser(scope).parse_args_native(self._native_parser)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now we always create the native values but only create the legacy values if we need to do the discrepancy check, which is the converse of what we did before.

Copy link
Contributor

@huonw huonw left a comment

Choose a reason for hiding this comment

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

Sweet!

@@ -499,8 +499,7 @@ def log(log_func):
raise Exception(
"Option value mismatches detected, see logs above for details. Aborting."
)
# TODO: In a future release, switch to the native_values as authoritative.
return legacy_values
return native_values
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we want to allow a user to fallback to the legacy values if things are wildly wrong. E.g. [GLOBAL].use_deprecated_legacy_options_values = true or something. Thoughts?

(Happy to get this PR merged ASAP, as is, and we can cherry-pick any tweaks along these back to 2.23.x if we decide on them)

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 don't think we do - 2.22.x is where you're supposed to discover if things are wildly wrong.

Copy link
Member

@cognifloyd cognifloyd left a comment

Choose a reason for hiding this comment

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

minor exception message nitpick issue. Otherwise LGTM.

parse_args_request = self._make_parse_args_request(flags_in_scope, values_builder)
legacy_values = self.get_parser(scope).parse_args(
parse_args_request, log_warnings=log_parser_warnings
)
except Exception as e:
native_mismatch_msgs.append(
f"Failed to parse options with native parser due to error:\n {e}"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
f"Failed to parse options with native parser due to error:\n {e}"
f"Failed to parse options with legacy parser due to error:\n {e}"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not a nitpick, that was actually wrong. Fixed, but also looking into the test failures, which are due to things either not failing when they should (presumably because the checks happen later or not at all on the native parser) or just failing with a slightly different error message.

- Fix missing validations on the native side.
- Change the python error type raised by the native parser.
- Various error types and error message differences.
@benjyw
Copy link
Contributor Author

benjyw commented Sep 11, 2024

This is going to take another day or so, as I figure out one thing that the tests uncovered, namely that we still rely on the legacy parser to detect unconsumed cmd-line flags. Will have a little think on how best to address this, probably in another PR.

@huonw
Copy link
Contributor

huonw commented Sep 11, 2024

No worries. Is there a chance we could keep the 2.23 releases moving forward by either:

  • cherry-picking the switch back to 2.23.x
  • delaying the native->legacy switchover until 2.24.x

(My thinking here is that we're a bit stuck in a spiral of long releases because delays lead to bigger releases lead to more things that can go wrong lead to delays 😄 )

@cognifloyd
Copy link
Member

cognifloyd commented Sep 11, 2024

I think delaying the switch to 2.24 makes the most sense. Cherry-picking back to 2.23 is fine if the issues can be resolved quickly.

@benjyw
Copy link
Contributor Author

benjyw commented Sep 11, 2024

I don't want to delay, but we can cherry-pick.

@huonw huonw mentioned this pull request Sep 11, 2024
@huonw
Copy link
Contributor

huonw commented Sep 11, 2024

Okay, I'll merge #21383 to start that rolling

@benjyw
Copy link
Contributor Author

benjyw commented Sep 13, 2024

OK, tracking arg consumption is implemented here: #21410

@benjyw benjyw added this to the 2.23.x milestone Sep 13, 2024
So we can error if unrecognized args are found.
@@ -443,28 +448,6 @@ def register(opts: Options) -> None:
assert vals2.qux == "uu"


def test_scope_deprecation_default_config_section(caplog) -> None:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was testing emergent behavior, not API-defined behavior. Implementing this behavior in the native parser would be tricky and IMO not worth it:

A) It's not at all clear that this is the correct behavior. Generally we treat a config entry in DEFAULT as-if it was in the relevant section, so special-casing this seems unnecessary.
B) This behavior is only triggered when we deprecate an entire scope, which we haven't done in a while, and currently have no such deprecations pending, so no user will see any effect.

@@ -12,6 +11,8 @@ use options::{

use std::collections::HashMap;

pyo3::import_exception!(pants.option.errors, ParseError);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We now raise our own ParseError instead of the generic ValueError. However it seemed like overkill to raise a bunch of ParseError subtypes (they were probably overkill to begin with even in the legacy parser).

@benjyw
Copy link
Contributor Author

benjyw commented Sep 18, 2024

This is ready for another look, thanks!

Copy link
Contributor

@huonw huonw 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 reasonable.

I feel obliged to flag my discomfort with landing such a large chunk of new code so late in the release cycle, vs. just letting the switchover slip to 2.24 (and thus potentially being more likely to be able to stick to a regular/short release cycle due to fewer last-minute issues).

This is just discomfort, not a blocking concern, so lets get this in and hope for the best.

Comment on lines +716 to +717
# We set allow_unknown_options=True to ensure that it's the native
# parser that checks for unconsumed args, and not the legacy parser.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does setting this arg have this effect? It seems a bit indirect?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, if we don't set it then it will fail earlier, in for_scope(), and never get to the explicit verification call.

@@ -27,12 +27,9 @@ def test_invalid_options() -> None:
"ERROR] Invalid option 'bad_option' under [pytest]",
]

# We error on invalid CLI options before validating the config file.
Copy link
Contributor

Choose a reason for hiding this comment

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

We now validate the config file and options together?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There was a version where the order was different but it's actually not now, so I have restored this test to how it was.

Copy link
Member

@cognifloyd cognifloyd left a comment

Choose a reason for hiding this comment

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

LGTM as long as tests pass

@@ -41,7 +41,9 @@ def handle_unknown_flags(self, err: UnknownFlagsError):
possibilities.update(oshi.collect_scoped_flags())

for flag in err.flags:
print(f"Unknown flag {self.maybe_red(flag)} on {err.arg_scope or 'global'} scope")
print(
f"Unknown flag {self.maybe_red(flag)} in {(err.arg_scope + ' goal') if err.arg_scope else 'global'} context"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is more precise phrasing. We don't actually know the scope of the flag, we just know its CLI context (global position or goal position).

@benjyw benjyw removed this from the 2.23.x milestone Sep 20, 2024
@benjyw
Copy link
Contributor Author

benjyw commented Sep 20, 2024

@huonw thanks for expressing your discomfort, which I acknowledge. I can wait to 2.24.x if you are more comfortable with that (and it sounds like @cognifloyd prefers that too). For now I will merge (once tests pass) without the auto-cherrypick, so we have a chance to discuss further.

@benjyw benjyw merged commit c50c8fc into pantsbuild:main Sep 20, 2024
25 checks passed
@benjyw benjyw deleted the use_native_option_values branch September 20, 2024 06:37
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.

3 participants