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 [cli.alias] config section for command line alias support. #13228

Merged
merged 10 commits into from
Oct 13, 2021

Conversation

kaos
Copy link
Member

@kaos kaos commented Oct 12, 2021

Pre-work for user friendly ad-hoc script execution support.
slack thread

Included in this PR is an example alias for running pyupgrade, as well as green and all-changed aliases:
Example use to run on the docker backend:

$ ./pants pyupgrade 'src/python/pants/backend/docker/**/*.py'
13:41:45.74 [INFO] Initialization options changed: reinitializing scheduler...
13:41:46.38 [INFO] Scheduler initialized.
13:41:49.67 [INFO] Completed: Format with Autoflake - autoflake made no changes.
13:41:51.98 [INFO] Completed: Format with Black - Black made no changes.
All done! ✨ 🍰 ✨
33 files left unchanged.


13:41:53.93 [INFO] Completed: Format with docformatter - Docformatter made no changes.
13:41:54.43 [INFO] Completed: Format with isort - isort made no changes.
13:41:56.41 [INFO] Completed: Format with pyupgrade - pyupgrade made no changes.

✓ Black made no changes.
✓ Docformatter made no changes.
✓ autoflake made no changes.
✓ isort made no changes.
✓ pyupgrade made no changes.

By adding this to pants.toml:

[cli.alias]
pyupgrade = "--backend-packages=pants.backend.python.lint.pyupgrade fmt"

So this is generally useful already on its own.
I have not run any benchmarks to see how big impact this will have on pants startup times.

UPDATED

Added checks for alias names, so they can not be confused with potential options or dir specs. Also verifies that an alias does not shadow an existing goal/subsystem. Thanks @chrisjrn for pointing that out.

Added debug logging of alias expansion, to aid troubleshooting. I haven't looked, but if there is a verbose flag, perhaps that log could be made an info level when verbose==true.

Also supports nesting aliases. This will be useful to build up aliases in incremental steps, while still keeping them DRY.

Such as this example:

[cli.alias]
all-changed = "--changed-since=HEAD --changed-dependees=transitive"
all-green = "all-changed green"
green = "fmt lint check"

Signed-off-by: Andreas Stenius <andreas.stenius@svenskaspel.se>

# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
@chrisjrn
Copy link
Contributor

First of all, this is just a wonderful idea. Thank you!

Secondly, have you given thought to how you might deal with new goals introduced in Pants that clash with an alias that's already defined? (my first thoughts are this should error, but I haven't thought much about it)

Copy link
Contributor

@Eric-Arellano Eric-Arellano 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 SO cool! 🎉

src/python/pants/option/global_options.py Outdated Show resolved Hide resolved
src/python/pants/option/global_options.py Outdated Show resolved Hide resolved
src/python/pants/option/alias.py Show resolved Hide resolved
@benjyw
Copy link
Contributor

benjyw commented Oct 12, 2021

First of all, this is just a wonderful idea. Thank you!

Secondly, have you given thought to how you might deal with new goals introduced in Pants that clash with an alias that's already defined? (my first thoughts are this should error, but I haven't thought much about it)

This is a good question. You could clash even on an existing goal, right? I guess this is a "there be dragons" situation, and we document that you have to be careful...

@chrisjrn
Copy link
Contributor

chrisjrn commented Oct 12, 2021

You could clash even on an existing goal, right? I guess this is a "there be dragons" situation, and we document that you have to be careful...

I suspect erroring completely would be better behaviour, unless explicitly silenced – better to know that something's wrong and fix it, than having weird and hard-to-debug weirdnesses that nobody else can make sense of

@benjyw
Copy link
Contributor

benjyw commented Oct 12, 2021

Yeah, I guess we should:

A) Validate that aliases don't contain slashes or dashes (so they cannot collide with an option or a spec)
B) Validate that they don't collide with known goal names.

@kaos kaos changed the title Add [GLOBAL.alias] config section for command line alias support. Add [CLI.alias] config section for command line alias support. Oct 13, 2021
kaos added 5 commits October 13, 2021 09:19
# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
@kaos kaos changed the title Add [CLI.alias] config section for command line alias support. Add [cli.alias] config section for command line alias support. Oct 13, 2021
# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
Copy link
Contributor

@benjyw benjyw left a comment

Choose a reason for hiding this comment

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

Users are going to absolutely love this! This is definitely worth a blog post.

Copy link
Contributor

@Eric-Arellano Eric-Arellano left a comment

Choose a reason for hiding this comment

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

Amazing 🎉

@@ -31,7 +32,7 @@


# Subsystems used outside of any rule.
_GLOBAL_SUBSYSTEMS: Set[Type[Subsystem]] = {GlobalOptions, Changed}
_GLOBAL_SUBSYSTEMS: Set[Type[Subsystem]] = {GlobalOptions, Changed, CliOptions}
Copy link
Contributor

Choose a reason for hiding this comment

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

@stuhood @benjyw is this fine? I thought for some reason we wanted to chip away at this list.

Copy link
Member

@stuhood stuhood Oct 13, 2021

Choose a reason for hiding this comment

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

Maybe, but I don't recall why. If anything, I think that there are a lot of potential usecases for breaking out our bootstrap and global options into more global subsystems.

src/python/pants/option/alias.py Outdated Show resolved Hide resolved
src/python/pants/option/alias.py Outdated Show resolved Hide resolved
# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
@kaos kaos enabled auto-merge (squash) October 13, 2021 15:45
Copy link
Member

@stuhood stuhood left a comment

Choose a reason for hiding this comment

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

Great idea: thanks!

pants.toml Outdated

[cli.alias]
all-changed = "--changed-since=HEAD --changed-dependees=transitive"
green = "fmt lint check"
Copy link
Member

Choose a reason for hiding this comment

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

Let's hold off on adding this one... it has bikeshed potential due to the ordering of the commands.

@@ -31,7 +32,7 @@


# Subsystems used outside of any rule.
_GLOBAL_SUBSYSTEMS: Set[Type[Subsystem]] = {GlobalOptions, Changed}
_GLOBAL_SUBSYSTEMS: Set[Type[Subsystem]] = {GlobalOptions, Changed, CliOptions}
Copy link
Member

@stuhood stuhood Oct 13, 2021

Choose a reason for hiding this comment

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

Maybe, but I don't recall why. If anything, I think that there are a lot of potential usecases for breaking out our bootstrap and global options into more global subsystems.

Comment on lines 162 to 164
# Finally, we expand any aliases and re-populates the bootstrap args, in case there was
# any from an alias.
alias = CliAlias.from_dict(post_bootstrap_config.get("cli", "alias", dict, {}))
Copy link
Member

@stuhood stuhood Oct 13, 2021

Choose a reason for hiding this comment

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

It's possible that this will break the rust client when aliases are used, since it also does options parsing: #11922 ... but maybe only if bootstrap options are defined in the alias? Probably not a blocker.

I have made some headway on porting options parsing to fully consuming that backend, but haven't been able to make a push to land it yet.

Copy link
Contributor

Choose a reason for hiding this comment

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

Note that #21695 ports the CLI aliasing feature to Rust, so it will be available to the rust client.

Comment on lines 178 to 189
flags = set()
short_flags = set()

def capture_the_flags(*args: str, **kwargs) -> None:
for arg in args:
flags.add(arg)
if len(arg) == 2:
short_flags.add(arg)
elif kwargs.get("type") == bool:
flags.add(f"--no-{arg[2:]}")

GlobalOptions.register_bootstrap_options(capture_the_flags)
Copy link
Member

Choose a reason for hiding this comment

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

This registration will now run each time this method is called: should consider moving it into a dataclass to be used for both calls in OptionsBootstrapper.create.

Copy link
Member Author

Choose a reason for hiding this comment

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

Just as a refactoring, to get the method out of the OptionsBootstrapper?

Copy link
Member

Choose a reason for hiding this comment

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

Note: I commented on specific lines in here.

Mostly for performance reasons. _get_bootstrap_args is called twice, and so will re-register the bootstrap options twice, which will render silenced deprecation warnings twice, etc.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, but the alias may expand to a bootstrap arg, so we need to visit them again.. will look a bit closer at this..

Copy link
Member

@stuhood stuhood Oct 13, 2021

Choose a reason for hiding this comment

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

Sure: but look at the specific section I commented on: it's only the portion that captures the flags and short-flags. So that part can be reused I think?

This isn't a huge deal: don't worry too much about it.

Copy link
Member Author

@kaos kaos Oct 13, 2021

Choose a reason for hiding this comment

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

No, it's fine, and I see my confusion now, the args in the closure is shadowing the args from the method. Will fix.
I.e. I came to the wrong conclusion for my last comment.

Comment on lines 51 to 52
"Notice: this option must be placed in a config file (e.g. `pants.toml`) to have "
"any effect."
Copy link
Member

@stuhood stuhood Oct 13, 2021

Choose a reason for hiding this comment

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

Based on the implementation, it's not clear to me that this is true (although it would be pretty pointless to define aliases on the CLI): they'll be ignored during the bootstrap pass, then loaded from the bootstrap args, which includes CLI args?

It also looks like these should work in pantsrc files, so maybe worth mentioning that: users are likely to want to define their own aliases.

Copy link
Member Author

@kaos kaos Oct 13, 2021

Choose a reason for hiding this comment

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

Agree with the pantsrc, mentioning it too.

The aliases are loaded from post_bootstrap_config which is a Config.load(), with only config files and bootstrap options, so any options for the cli subsystem from a command line flag won't ever be in there, right?

Copy link
Member

Choose a reason for hiding this comment

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

Ah, you're right. Should be fine... can possibly move where it is constructed later if it ends up mattering, but it likely won't.

kaos added 2 commits October 13, 2021 18:20
# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
@kaos
Copy link
Member Author

kaos commented Oct 13, 2021

There's a TODO for a typing issue with memoized_classmethod in here now, tracked by #13244.

@kaos
Copy link
Member Author

kaos commented Oct 13, 2021

I'll address any remaining comments in a follow up PR, if there are any.

@kaos kaos merged commit 2714522 into pantsbuild:main Oct 13, 2021
@kaos kaos deleted the cli_alias_command_line branch October 13, 2021 20:37
Eric-Arellano pushed a commit to Eric-Arellano/pants that referenced this pull request Oct 14, 2021
…tsbuild#13228)

This allows you to run pants with commands that are easier to remember, even if there are long or many options involved. The following two invocations are equivalent, given the pants configuration below:
```
$ ./pants all-changed fmt
$ ./pants --changed-since=HEAD --changed-dependees=transitive fmt
```

`pants.toml`:
```
[cli.alias]
all-changed = "--changed-since=HEAD --changed-dependees=transitive"
```

# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
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