2020-07-27 10:37:46 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module PyPI
|
|
|
|
module_function
|
|
|
|
|
|
|
|
PYTHONHOSTED_URL_PREFIX = "https://files.pythonhosted.org/packages/"
|
|
|
|
|
2020-07-31 16:42:53 -04:00
|
|
|
AUTOMATIC_RESOURCE_UPDATE_BLOCKLIST = %w[
|
2020-07-31 20:59:49 -04:00
|
|
|
ansible
|
|
|
|
ansible@2.8
|
2020-08-10 10:28:41 -04:00
|
|
|
cdk8s
|
2020-08-02 11:30:10 -04:00
|
|
|
cloudformation-cli
|
2020-07-31 16:42:53 -04:00
|
|
|
diffoscope
|
|
|
|
dxpy
|
2020-08-15 17:30:37 -04:00
|
|
|
ipython
|
2020-07-31 16:42:53 -04:00
|
|
|
molecule
|
2020-08-12 09:26:35 -04:00
|
|
|
xonsh
|
2020-07-31 16:42:53 -04:00
|
|
|
].freeze
|
|
|
|
|
2020-07-27 10:37:46 -04:00
|
|
|
@pipgrip_installed = nil
|
|
|
|
|
2020-07-31 10:10:07 -04:00
|
|
|
def url_to_pypi_package_name(url)
|
|
|
|
return unless url.start_with? PYTHONHOSTED_URL_PREFIX
|
|
|
|
|
|
|
|
File.basename(url).match(/^(.+)-[a-z\d.]+$/)[1]
|
|
|
|
end
|
|
|
|
|
|
|
|
def update_pypi_url(url, version)
|
|
|
|
package = url_to_pypi_package_name url
|
|
|
|
return if package.nil?
|
|
|
|
|
|
|
|
_, url = get_pypi_info(package, version)
|
|
|
|
url
|
|
|
|
end
|
|
|
|
|
2020-07-31 15:12:41 -04:00
|
|
|
# Get name, url and sha256 for a given pypi package
|
2020-07-27 10:37:46 -04:00
|
|
|
def get_pypi_info(package, version)
|
|
|
|
metadata_url = "https://pypi.org/pypi/#{package}/#{version}/json"
|
|
|
|
out, _, status = curl_output metadata_url, "--location"
|
|
|
|
|
|
|
|
return unless status.success?
|
|
|
|
|
|
|
|
begin
|
|
|
|
json = JSON.parse out
|
|
|
|
rescue JSON::ParserError
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
sdist = json["urls"].find { |url| url["packagetype"] == "sdist" }
|
2020-07-31 14:21:44 -04:00
|
|
|
return json["info"]["name"] if sdist.nil?
|
|
|
|
|
2020-07-27 10:37:46 -04:00
|
|
|
[json["info"]["name"], sdist["url"], sdist["digests"]["sha256"]]
|
|
|
|
end
|
|
|
|
|
|
|
|
def update_python_resources!(formula, version = nil, print_only: false, silent: false,
|
|
|
|
ignore_non_pypi_packages: false)
|
|
|
|
|
2020-07-31 20:59:22 -04:00
|
|
|
if !print_only && AUTOMATIC_RESOURCE_UPDATE_BLOCKLIST.include?(formula.full_name)
|
2020-07-31 16:42:53 -04:00
|
|
|
odie "The resources for \"#{formula.name}\" need special attention. Please update them manually."
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2020-07-27 10:37:46 -04:00
|
|
|
# PyPI package name isn't always the same as the formula name. Try to infer from the URL.
|
|
|
|
pypi_name = if formula.stable.url.start_with?(PYTHONHOSTED_URL_PREFIX)
|
2020-07-31 10:10:07 -04:00
|
|
|
url_to_pypi_package_name formula.stable.url
|
2020-07-27 10:37:46 -04:00
|
|
|
else
|
|
|
|
formula.name
|
|
|
|
end
|
|
|
|
|
|
|
|
version ||= formula.version
|
|
|
|
|
|
|
|
if get_pypi_info(pypi_name, version).blank?
|
|
|
|
odie "\"#{pypi_name}\" at version #{version} is not available on PyPI." unless ignore_non_pypi_packages
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
non_pypi_resources = formula.resources.reject do |resource|
|
|
|
|
resource.url.start_with? PYTHONHOSTED_URL_PREFIX
|
|
|
|
end
|
|
|
|
|
|
|
|
if non_pypi_resources.present? && !print_only
|
|
|
|
odie "\"#{formula.name}\" contains non-PyPI resources. Please update the resources manually."
|
|
|
|
end
|
|
|
|
|
2020-07-31 16:56:21 +01:00
|
|
|
@pipgrip_installed ||= Formula["pipgrip"].any_version_installed?
|
|
|
|
odie '"pipgrip" must be installed (`brew install pipgrip`)' unless @pipgrip_installed
|
|
|
|
|
2020-07-30 17:09:59 -07:00
|
|
|
ohai "Retrieving PyPI dependencies for \"#{pypi_name}==#{version}\"..." if !print_only && !silent
|
2020-07-30 14:26:27 -04:00
|
|
|
pipgrip_output = Utils.popen_read Formula["pipgrip"].bin/"pipgrip", "--json", "--no-cache-dir",
|
|
|
|
"#{pypi_name}==#{version}"
|
2020-07-27 10:37:46 -04:00
|
|
|
unless $CHILD_STATUS.success?
|
|
|
|
odie <<~EOS
|
|
|
|
Unable to determine dependencies for \"#{pypi_name}\" because of a failure when running
|
2020-07-30 14:26:27 -04:00
|
|
|
`pipgrip --json --no-cache-dir #{pypi_name}==#{version}`.
|
|
|
|
Please update the resources for \"#{formula.name}\" manually.
|
2020-07-27 10:37:46 -04:00
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
|
|
|
packages = JSON.parse(pipgrip_output).sort.to_h
|
|
|
|
|
|
|
|
# Remove extra packages that may be included in pipgrip output
|
|
|
|
exclude_list = %W[#{pypi_name} argparse pip setuptools wheel wsgiref]
|
|
|
|
packages.delete_if do |package|
|
|
|
|
exclude_list.include? package
|
|
|
|
end
|
|
|
|
|
|
|
|
new_resource_blocks = ""
|
|
|
|
packages.each do |package, package_version|
|
2020-07-30 17:09:59 -07:00
|
|
|
ohai "Getting PyPI info for \"#{package}==#{package_version}\"" if !print_only && !silent
|
2020-07-27 10:37:46 -04:00
|
|
|
name, url, checksum = get_pypi_info package, package_version
|
|
|
|
# Fail if unable to find name, url or checksum for any resource
|
|
|
|
if name.blank?
|
|
|
|
odie "Unable to resolve some dependencies. Please update the resources for \"#{formula.name}\" manually."
|
|
|
|
elsif url.blank? || checksum.blank?
|
|
|
|
odie <<~EOS
|
|
|
|
Unable to find the URL and/or sha256 for the \"#{name}\" resource.
|
|
|
|
Please update the resources for \"#{formula.name}\" manually.
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
|
|
|
# Append indented resource block
|
|
|
|
new_resource_blocks += <<-EOS
|
|
|
|
resource "#{name}" do
|
|
|
|
url "#{url}"
|
|
|
|
sha256 "#{checksum}"
|
|
|
|
end
|
|
|
|
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
|
|
|
if print_only
|
|
|
|
puts new_resource_blocks.chomp
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
# Check whether resources already exist (excluding homebrew-virtualenv)
|
|
|
|
if formula.resources.blank? ||
|
|
|
|
(formula.resources.length == 1 && formula.resources.first.name == "homebrew-virtualenv")
|
|
|
|
# Place resources above install method
|
|
|
|
inreplace_regex = / def install/
|
|
|
|
new_resource_blocks += " def install"
|
|
|
|
else
|
|
|
|
# Replace existing resource blocks with new resource blocks
|
|
|
|
inreplace_regex = / (resource .* do\s+url .*\s+sha256 .*\s+ end\s*)+/
|
|
|
|
new_resource_blocks += " "
|
|
|
|
end
|
|
|
|
|
|
|
|
ohai "Updating resource blocks" unless silent
|
|
|
|
Utils::Inreplace.inreplace formula.path do |s|
|
|
|
|
if s.inreplace_string.scan(inreplace_regex).length > 1
|
|
|
|
odie "Unable to update resource blocks for \"#{formula.name}\" automatically. Please update them manually."
|
|
|
|
end
|
|
|
|
s.sub! inreplace_regex, new_resource_blocks
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|