| 
									
										
										
										
											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-21 10:40:02 +01:00
										 |  |  |     salt | 
					
						
							| 
									
										
										
										
											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 |