Enable strict typing in DependenciesHelpers
This commit is contained in:
parent
673f19086b
commit
0037b1f626
@ -1,11 +1,9 @@
|
|||||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module OS
|
module OS
|
||||||
module Mac
|
module Mac
|
||||||
module_function
|
SYSTEM_DIRS = T.let([
|
||||||
|
|
||||||
SYSTEM_DIRS = [
|
|
||||||
"/",
|
"/",
|
||||||
"/Applications",
|
"/Applications",
|
||||||
"/Applications/Utilities",
|
"/Applications/Utilities",
|
||||||
@ -234,14 +232,13 @@ module OS
|
|||||||
"/var/spool/mail",
|
"/var/spool/mail",
|
||||||
"/var/tmp",
|
"/var/tmp",
|
||||||
]
|
]
|
||||||
.map(&method(:Pathname))
|
.to_set { Pathname(_1) }
|
||||||
.to_set
|
.freeze, T::Set[Pathname])
|
||||||
.freeze
|
|
||||||
private_constant :SYSTEM_DIRS
|
private_constant :SYSTEM_DIRS
|
||||||
|
|
||||||
# TODO: There should be a way to specify a containing
|
# TODO: There should be a way to specify a containing
|
||||||
# directory under which nothing can be deleted.
|
# directory under which nothing can be deleted.
|
||||||
UNDELETABLE_PATHS = [
|
UNDELETABLE_PATHS = T.let([
|
||||||
"~/",
|
"~/",
|
||||||
"~/Applications",
|
"~/Applications",
|
||||||
"~/Applications/.localized",
|
"~/Applications/.localized",
|
||||||
@ -378,14 +375,16 @@ module OS
|
|||||||
]
|
]
|
||||||
.to_set { |path| Pathname(path.sub(%r{^~(?=(/|$))}, Dir.home)).expand_path }
|
.to_set { |path| Pathname(path.sub(%r{^~(?=(/|$))}, Dir.home)).expand_path }
|
||||||
.union(SYSTEM_DIRS)
|
.union(SYSTEM_DIRS)
|
||||||
.freeze
|
.freeze, T::Set[Pathname])
|
||||||
private_constant :UNDELETABLE_PATHS
|
private_constant :UNDELETABLE_PATHS
|
||||||
|
|
||||||
def system_dir?(dir)
|
sig { params(dir: T.any(Pathname, String)).returns(T::Boolean) }
|
||||||
|
def self.system_dir?(dir)
|
||||||
SYSTEM_DIRS.include?(Pathname.new(dir).expand_path)
|
SYSTEM_DIRS.include?(Pathname.new(dir).expand_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def undeletable?(path)
|
sig { params(path: T.any(Pathname, String)).returns(T::Boolean) }
|
||||||
|
def self.undeletable?(path)
|
||||||
UNDELETABLE_PATHS.include?(Pathname.new(path).expand_path)
|
UNDELETABLE_PATHS.include?(Pathname.new(path).expand_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -70,26 +70,29 @@ module Homebrew
|
|||||||
method: T.nilable(Symbol),
|
method: T.nilable(Symbol),
|
||||||
uniq: T::Boolean,
|
uniq: T::Boolean,
|
||||||
warn: T::Boolean,
|
warn: T::Boolean,
|
||||||
).returns(T::Array[T.any(Formula, Keg, Cask::Cask)])
|
).returns(T::Array[T.any(Formula, Cask::Cask)])
|
||||||
}
|
}
|
||||||
def to_formulae_and_casks(
|
def to_formulae_and_casks(
|
||||||
only: parent.only_formula_or_cask, ignore_unavailable: false, method: nil, uniq: true, warn: false
|
only: parent.only_formula_or_cask, ignore_unavailable: false, method: nil, uniq: true, warn: false
|
||||||
)
|
)
|
||||||
@to_formulae_and_casks ||= T.let(
|
@to_formulae_and_casks ||= T.let(
|
||||||
{}, T.nilable(T::Hash[T.nilable(Symbol), T::Array[T.any(Formula, Keg, Cask::Cask)]])
|
{}, T.nilable(T::Hash[T.nilable(Symbol), T::Array[T.any(Formula, Cask::Cask)]])
|
||||||
|
)
|
||||||
|
@to_formulae_and_casks[only] ||= T.cast(
|
||||||
|
downcased_unique_named.flat_map do |name|
|
||||||
|
options = { warn: }.compact
|
||||||
|
load_formula_or_cask(name, only:, method:, **options)
|
||||||
|
rescue FormulaUnreadableError, FormulaClassUnavailableError,
|
||||||
|
TapFormulaUnreadableError, TapFormulaClassUnavailableError,
|
||||||
|
Cask::CaskUnreadableError
|
||||||
|
# Need to rescue before `*UnavailableError` (superclass of this)
|
||||||
|
# The formula/cask was found, but there's a problem with its implementation
|
||||||
|
raise
|
||||||
|
rescue NoSuchKegError, FormulaUnavailableError, Cask::CaskUnavailableError, FormulaOrCaskUnavailableError
|
||||||
|
ignore_unavailable ? [] : raise
|
||||||
|
end.freeze,
|
||||||
|
T::Array[T.any(Formula, Cask::Cask)],
|
||||||
)
|
)
|
||||||
@to_formulae_and_casks[only] ||= downcased_unique_named.flat_map do |name|
|
|
||||||
options = { warn: }.compact
|
|
||||||
load_formula_or_cask(name, only:, method:, **options)
|
|
||||||
rescue FormulaUnreadableError, FormulaClassUnavailableError,
|
|
||||||
TapFormulaUnreadableError, TapFormulaClassUnavailableError,
|
|
||||||
Cask::CaskUnreadableError
|
|
||||||
# Need to rescue before `*UnavailableError` (superclass of this)
|
|
||||||
# The formula/cask was found, but there's a problem with its implementation
|
|
||||||
raise
|
|
||||||
rescue NoSuchKegError, FormulaUnavailableError, Cask::CaskUnavailableError, FormulaOrCaskUnavailableError
|
|
||||||
ignore_unavailable ? [] : raise
|
|
||||||
end.freeze
|
|
||||||
|
|
||||||
if uniq
|
if uniq
|
||||||
@to_formulae_and_casks.fetch(only).uniq.freeze
|
@to_formulae_and_casks.fetch(only).uniq.freeze
|
||||||
@ -120,7 +123,7 @@ module Homebrew
|
|||||||
def to_formulae_and_casks_with_taps
|
def to_formulae_and_casks_with_taps
|
||||||
formulae_and_casks_with_taps, formulae_and_casks_without_taps =
|
formulae_and_casks_with_taps, formulae_and_casks_without_taps =
|
||||||
to_formulae_and_casks.partition do |formula_or_cask|
|
to_formulae_and_casks.partition do |formula_or_cask|
|
||||||
T.cast(formula_or_cask, T.any(Formula, Cask::Cask)).tap&.installed?
|
formula_or_cask.tap&.installed?
|
||||||
end
|
end
|
||||||
|
|
||||||
return formulae_and_casks_with_taps if formulae_and_casks_without_taps.empty?
|
return formulae_and_casks_with_taps if formulae_and_casks_without_taps.empty?
|
||||||
|
|||||||
@ -81,7 +81,7 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
raise "Invalid type: #{formula_or_cask.class}"
|
T.absurd(formula_or_cask)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -221,8 +221,8 @@ module Homebrew
|
|||||||
deps = dependency.runtime_dependencies if @use_runtime_dependencies
|
deps = dependency.runtime_dependencies if @use_runtime_dependencies
|
||||||
|
|
||||||
if recursive
|
if recursive
|
||||||
deps ||= recursive_includes(Dependency, dependency, includes, ignores)
|
deps ||= recursive_dep_includes(dependency, includes, ignores)
|
||||||
reqs = recursive_includes(Requirement, dependency, includes, ignores)
|
reqs = recursive_req_includes(dependency, includes, ignores)
|
||||||
else
|
else
|
||||||
deps ||= select_includes(dependency.deps, ignores, includes)
|
deps ||= select_includes(dependency.deps, ignores, includes)
|
||||||
reqs = select_includes(dependency.requirements, ignores, includes)
|
reqs = select_includes(dependency.requirements, ignores, includes)
|
||||||
|
|||||||
@ -65,7 +65,7 @@ module Homebrew
|
|||||||
description = formula_or_cask.desc.presence || Formatter.warning("[no description]")
|
description = formula_or_cask.desc.presence || Formatter.warning("[no description]")
|
||||||
desc[formula_or_cask.full_name] = "(#{formula_or_cask.name.join(", ")}) #{description}"
|
desc[formula_or_cask.full_name] = "(#{formula_or_cask.name.join(", ")}) #{description}"
|
||||||
else
|
else
|
||||||
raise TypeError, "Unsupported formula_or_cask type: #{formula_or_cask.class}"
|
T.absurd(formula_or_cask)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Descriptions.new(desc).print
|
Descriptions.new(desc).print
|
||||||
|
|||||||
@ -29,8 +29,7 @@ module Homebrew
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
# to_formulae_and_casks is typed to possibly return Kegs (but won't without explicitly asking)
|
formulae_or_casks = args.named.to_formulae_and_casks
|
||||||
formulae_or_casks = T.cast(args.named.to_formulae_and_casks, T::Array[T.any(Formula, Cask::Cask)])
|
|
||||||
homepages = formulae_or_casks.map do |formula_or_cask|
|
homepages = formulae_or_casks.map do |formula_or_cask|
|
||||||
puts "Opening homepage for #{name_of(formula_or_cask)}"
|
puts "Opening homepage for #{name_of(formula_or_cask)}"
|
||||||
formula_or_cask.homepage
|
formula_or_cask.homepage
|
||||||
|
|||||||
@ -99,9 +99,7 @@ module Homebrew
|
|||||||
else
|
else
|
||||||
args.named.to_formulae_and_casks(only: :cask, method: :resolve)
|
args.named.to_formulae_and_casks(only: :cask, method: :resolve)
|
||||||
end
|
end
|
||||||
# The cast is because `Keg`` does not define `full_name`
|
full_cask_names = cask_names.map(&:full_name).sort(&tap_and_name_comparison)
|
||||||
full_cask_names = T.cast(cask_names, T::Array[T.any(Formula, Cask::Cask)])
|
|
||||||
.map(&:full_name).sort(&tap_and_name_comparison)
|
|
||||||
full_cask_names = Formatter.columns(full_cask_names) unless args.public_send(:"1?")
|
full_cask_names = Formatter.columns(full_cask_names) unless args.public_send(:"1?")
|
||||||
puts full_cask_names if full_cask_names.present?
|
puts full_cask_names if full_cask_names.present?
|
||||||
end
|
end
|
||||||
|
|||||||
@ -94,7 +94,7 @@ module Homebrew
|
|||||||
|
|
||||||
sig {
|
sig {
|
||||||
params(use_runtime_dependents: T::Boolean, used_formulae: T::Array[T.any(Formula, UnavailableFormula)])
|
params(use_runtime_dependents: T::Boolean, used_formulae: T::Array[T.any(Formula, UnavailableFormula)])
|
||||||
.returns(T::Array[Formula])
|
.returns(T::Array[T.any(Formula, CaskDependent)])
|
||||||
}
|
}
|
||||||
def intersection_of_dependents(use_runtime_dependents, used_formulae)
|
def intersection_of_dependents(use_runtime_dependents, used_formulae)
|
||||||
recursive = args.recursive?
|
recursive = args.recursive?
|
||||||
@ -150,26 +150,30 @@ module Homebrew
|
|||||||
|
|
||||||
sig {
|
sig {
|
||||||
params(
|
params(
|
||||||
dependents: T::Array[Formula], used_formulae: T::Array[T.any(Formula, UnavailableFormula)],
|
dependents: T::Array[T.any(Formula, CaskDependent)],
|
||||||
recursive: T::Boolean, includes: T::Array[Symbol], ignores: T::Array[Symbol]
|
used_formulae: T::Array[T.any(Formula, UnavailableFormula)],
|
||||||
).returns(
|
recursive: T::Boolean,
|
||||||
T::Array[Formula],
|
includes: T::Array[Symbol],
|
||||||
)
|
ignores: T::Array[Symbol],
|
||||||
|
).returns(T::Array[T.any(Formula, CaskDependent)])
|
||||||
}
|
}
|
||||||
def select_used_dependents(dependents, used_formulae, recursive, includes, ignores)
|
def select_used_dependents(dependents, used_formulae, recursive, includes, ignores)
|
||||||
dependents.select do |d|
|
dependents.select do |d|
|
||||||
deps = if recursive
|
deps = if recursive
|
||||||
recursive_includes(Dependency, d, includes, ignores)
|
recursive_dep_includes(d, includes, ignores)
|
||||||
else
|
else
|
||||||
select_includes(d.deps, ignores, includes)
|
select_includes(d.deps, ignores, includes)
|
||||||
end
|
end
|
||||||
|
|
||||||
used_formulae.all? do |ff|
|
used_formulae.all? do |ff|
|
||||||
deps.any? do |dep|
|
deps.any? do |dep|
|
||||||
match = begin
|
match = case dep
|
||||||
|
when Dependency
|
||||||
dep.to_formula.full_name == ff.full_name if dep.name.include?("/")
|
dep.to_formula.full_name == ff.full_name if dep.name.include?("/")
|
||||||
rescue
|
when CaskDependent, Formula
|
||||||
nil
|
nil
|
||||||
|
else
|
||||||
|
T.absurd(dep)
|
||||||
end
|
end
|
||||||
next match unless match.nil?
|
next match unless match.nil?
|
||||||
|
|
||||||
|
|||||||
@ -39,11 +39,6 @@ class Dependencies < SimpleDelegator
|
|||||||
def inspect
|
def inspect
|
||||||
"#<#{self.class.name}: #{__getobj__}>"
|
"#<#{self.class.name}: #{__getobj__}>"
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { returns(T::Array[Dependency]) }
|
|
||||||
def to_a
|
|
||||||
__getobj__.to_a
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# A collection of requirements.
|
# A collection of requirements.
|
||||||
|
|||||||
@ -1,13 +1,24 @@
|
|||||||
# typed: strict
|
# typed: strict
|
||||||
|
|
||||||
class Dependencies < SimpleDelegator
|
class Dependencies < SimpleDelegator
|
||||||
|
include Enumerable
|
||||||
include Kernel
|
include Kernel
|
||||||
|
|
||||||
# This is a workaround to enable `alias eql? ==`
|
# This is a workaround to enable `alias eql? ==`
|
||||||
# @see https://github.com/sorbet/sorbet/issues/2378#issuecomment-569474238
|
# @see https://github.com/sorbet/sorbet/issues/2378#issuecomment-569474238
|
||||||
sig { params(other: BasicObject).returns(T::Boolean) }
|
sig { params(other: BasicObject).returns(T::Boolean) }
|
||||||
def ==(other); end
|
def ==(other); end
|
||||||
|
|
||||||
|
sig { params(blk: T.proc.params(arg0: Dependency).void).returns(T.self_type) }
|
||||||
|
sig { returns(T::Enumerator[Dependency]) }
|
||||||
|
def each(&blk); end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Requirements < SimpleDelegator
|
class Requirements < SimpleDelegator
|
||||||
|
include Enumerable
|
||||||
include Kernel
|
include Kernel
|
||||||
|
|
||||||
|
sig { params(blk: T.proc.params(arg0: Requirement).void).returns(T.self_type) }
|
||||||
|
sig { returns(T::Enumerator[Requirement]) }
|
||||||
|
def each(&blk); end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,14 +1,10 @@
|
|||||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cask_dependent"
|
require "cask_dependent"
|
||||||
|
|
||||||
# Helper functions for dependencies.
|
# Helper functions for dependencies.
|
||||||
module DependenciesHelpers
|
module DependenciesHelpers
|
||||||
extend T::Helpers
|
|
||||||
|
|
||||||
requires_ancestor { Kernel }
|
|
||||||
|
|
||||||
def args_includes_ignores(args)
|
def args_includes_ignores(args)
|
||||||
includes = [:required?, :recommended?] # included by default
|
includes = [:required?, :recommended?] # included by default
|
||||||
includes << :implicit? if args.include_implicit?
|
includes << :implicit? if args.include_implicit?
|
||||||
@ -23,9 +19,35 @@ module DependenciesHelpers
|
|||||||
[includes, ignores]
|
[includes, ignores]
|
||||||
end
|
end
|
||||||
|
|
||||||
def recursive_includes(klass, root_dependent, includes, ignores)
|
sig {
|
||||||
raise ArgumentError, "Invalid class argument: #{klass}" if klass != Dependency && klass != Requirement
|
params(root_dependent: T.any(Formula, CaskDependent), includes: T::Array[Symbol], ignores: T::Array[Symbol])
|
||||||
|
.returns(T::Array[Dependency])
|
||||||
|
}
|
||||||
|
def recursive_dep_includes(root_dependent, includes, ignores)
|
||||||
|
# The use of T.unsafe is recommended by the Sorbet docs:
|
||||||
|
# https://sorbet.org/docs/overloads#multiple-methods-but-sharing-a-common-implementation
|
||||||
|
T.unsafe(recursive_includes(Dependency, root_dependent, includes, ignores))
|
||||||
|
end
|
||||||
|
|
||||||
|
sig {
|
||||||
|
params(root_dependent: T.any(Formula, CaskDependent), includes: T::Array[Symbol], ignores: T::Array[Symbol])
|
||||||
|
.returns(Requirements)
|
||||||
|
}
|
||||||
|
def recursive_req_includes(root_dependent, includes, ignores)
|
||||||
|
# The use of T.unsafe is recommended by the Sorbet docs:
|
||||||
|
# https://sorbet.org/docs/overloads#multiple-methods-but-sharing-a-common-implementation
|
||||||
|
T.unsafe(recursive_includes(Requirement, root_dependent, includes, ignores))
|
||||||
|
end
|
||||||
|
|
||||||
|
sig {
|
||||||
|
params(
|
||||||
|
klass: T.any(T.class_of(Dependency), T.class_of(Requirement)),
|
||||||
|
root_dependent: T.any(Formula, CaskDependent),
|
||||||
|
includes: T::Array[Symbol],
|
||||||
|
ignores: T::Array[Symbol],
|
||||||
|
).returns(T.any(T::Array[Dependency], Requirements))
|
||||||
|
}
|
||||||
|
def recursive_includes(klass, root_dependent, includes, ignores)
|
||||||
cache_key = "recursive_includes_#{includes}_#{ignores}"
|
cache_key = "recursive_includes_#{includes}_#{ignores}"
|
||||||
|
|
||||||
klass.expand(root_dependent, cache_key:) do |dependent, dep|
|
klass.expand(root_dependent, cache_key:) do |dependent, dep|
|
||||||
@ -43,6 +65,11 @@ module DependenciesHelpers
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sig {
|
||||||
|
params(dependables: T::Array[T.any(Formula, CaskDependent)], ignores: T::Array[Symbol],
|
||||||
|
includes: T::Array[Symbol])
|
||||||
|
.returns(T::Array[T.any(Formula, CaskDependent)])
|
||||||
|
}
|
||||||
def select_includes(dependables, ignores, includes)
|
def select_includes(dependables, ignores, includes)
|
||||||
dependables.select do |dep|
|
dependables.select do |dep|
|
||||||
next false if ignores.any? { |ignore| dep.public_send(ignore) }
|
next false if ignores.any? { |ignore| dep.public_send(ignore) }
|
||||||
@ -51,6 +78,9 @@ module DependenciesHelpers
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sig {
|
||||||
|
params(formulae_or_casks: T::Array[T.any(Formula, Cask::Cask)]).returns(T::Array[T.any(Formula, CaskDependent)])
|
||||||
|
}
|
||||||
def dependents(formulae_or_casks)
|
def dependents(formulae_or_casks)
|
||||||
formulae_or_casks.map do |formula_or_cask|
|
formulae_or_casks.map do |formula_or_cask|
|
||||||
if formula_or_cask.is_a?(Formula)
|
if formula_or_cask.is_a?(Formula)
|
||||||
@ -60,5 +90,4 @@ module DependenciesHelpers
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
module_function :dependents
|
|
||||||
end
|
end
|
||||||
|
|||||||
12
Library/Homebrew/dependencies_helpers.rbi
Normal file
12
Library/Homebrew/dependencies_helpers.rbi
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# typed: strict
|
||||||
|
|
||||||
|
module DependenciesHelpers
|
||||||
|
include Kernel
|
||||||
|
|
||||||
|
# This sig is in an RBI to avoid both circular dependencies and unnecessary requires
|
||||||
|
sig {
|
||||||
|
params(args: T.any(Homebrew::Cmd::Deps::Args, Homebrew::Cmd::Uses::Args))
|
||||||
|
.returns([T::Array[Symbol], T::Array[Symbol]])
|
||||||
|
}
|
||||||
|
def args_includes_ignores(args); end
|
||||||
|
end
|
||||||
@ -35,7 +35,7 @@ RSpec.describe DependenciesHelpers do
|
|||||||
:any_version_installed?,
|
:any_version_installed?,
|
||||||
]
|
]
|
||||||
|
|
||||||
dependents = described_class.dependents([foo, foo_cask, bar, bar_cask])
|
dependents = Class.new.extend(described_class).dependents([foo, foo_cask, bar, bar_cask])
|
||||||
|
|
||||||
dependents.each do |dependent|
|
dependents.each do |dependent|
|
||||||
methods.each do |method|
|
methods.each do |method|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user