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

Enable typing in Cask::Artifact #14769

Merged
merged 1 commit into from Feb 23, 2023
Merged

Conversation

dduugg
Copy link
Sponsor Member

@dduugg dduugg commented Feb 22, 2023

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew style with your changes locally?
  • Have you successfully run brew typecheck with your changes locally?
  • Have you successfully run brew tests with your changes locally?

Enables typing in Cask::Artifact. In this PR, i've introduced a new util module, Utils::Splat, which allows a file to be typed by wrapping splat calls to Ruby core/std library methods. Initially, it has a single method, a Process.kill wrapper. It's admittedly a potentially clumsy abstraction – bringing together a bunch of methods whose only unifying factor is accepting splat arguments, but we could refactor it as necessary (though I think it will mostly be lower-level CLI-wrapped methods like system).

@BrewTestBot
Copy link
Member

Review period will end on 2023-02-23 at 17:36:21 UTC.

@BrewTestBot BrewTestBot added the waiting for feedback Merging is blocked until sufficient time has passed for review label Feb 22, 2023
@@ -73,7 +75,7 @@ def dispatch_uninstall_directive(directive_sym, **options)

args = directives[directive_sym]

send("uninstall_#{directive_sym}", *(args.is_a?(Hash) ? [args] : args), **options)
send("uninstall_#{directive_sym}", (args.is_a?(Enumerable) ? args : [args]), **options)
Copy link
Sponsor Member Author

Choose a reason for hiding this comment

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

This previously converted args to a splat, in order to subsequently call a method that accepts splat arguments. I've cut out the middleman, in order to enable typing, although I think it also makes for slightly more understandable code.

The methods below are all private, which ofc doesn't guarantee that changing the method signature is safe, so this proposed change deserves a critical eye.

Copy link
Member

Choose a reason for hiding this comment

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

Isn't Hash also an Enumerable, i.e. would now be just args instead of [args]?

Copy link
Sponsor Member Author

@dduugg dduugg Feb 23, 2023

Choose a reason for hiding this comment

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

@reitermarkus Great question! When it was a Hash, it would be wrapped as an array, then splatted back into a hash arg. Since we've removed the *, we no longer want to wrap hashes as arrays.

This might help explain things:

def foo(args, c: nil)
  args.class
end

argv = 'str'
foo(*argv)
# => String
argv = ['str']
foo(*argv)
# => String
argv = {a: 1, b: 2}
foo(*argv)
# => ArgumentError: wrong number of arguments (given 2, expected 1)
foo(*[argv])
# => Hash

Ruby will convert a hash into kwargs when preceded by *, leading to errors in methods that take a mix of positional and kwargs (iirc without the kwargs it will pass the hash as the first poisitional arg). You can try it for yourself by changing Enumerable to Array above, and running brew tests --only=cask/artifact/uninstall.

(I believe this also relates to why sorbet has a hard time supporting splats, but my memory is hazy here)

I generally find this all pretty hard to wrap my head around, so I try to avoid splats regardless of Sorbet's stance 🤕

Copy link
Member

Choose a reason for hiding this comment

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

Previously, we had e.g.

def uninstall_something(*args)
  args
end

and passing a Hash would look like

uninstall_something(*[{a: 1}]) == [{a: 1}]

while now we have

def uninstall_something(args)
  args
end

and passing a Hash, since it is Enumerable, we effectively have

uninstall_something({a: 1}) == {a: 1}

which isn't the same thing.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, so the only method we actually pass a Hash to is uninstall_script, which was not using a splat to begin with, so passing both *[{a: 1}] and {a: 1} still behave the same for that case.

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

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

Looks great, thanks again @dduugg!

Library/Homebrew/utils/splat.rb Show resolved Hide resolved
@@ -73,7 +75,7 @@ def dispatch_uninstall_directive(directive_sym, **options)

args = directives[directive_sym]

send("uninstall_#{directive_sym}", *(args.is_a?(Hash) ? [args] : args), **options)
send("uninstall_#{directive_sym}", (args.is_a?(Enumerable) ? args : [args]), **options)
Copy link
Member

Choose a reason for hiding this comment

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

Okay, so the only method we actually pass a Hash to is uninstall_script, which was not using a splat to begin with, so passing both *[{a: 1}] and {a: 1} still behave the same for that case.

@MikeMcQuaid
Copy link
Member

Thanks again @dduugg!

@BrewTestBot BrewTestBot removed the waiting for feedback Merging is blocked until sufficient time has passed for review label Feb 23, 2023
@BrewTestBot
Copy link
Member

Review period ended.

@MikeMcQuaid MikeMcQuaid merged commit d7029e9 into Homebrew:master Feb 23, 2023
@github-actions github-actions bot added the outdated PR was locked due to age label Mar 26, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated PR was locked due to age
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants