lib/spandx/python/pypi.rb in spandx-0.11.0 vs lib/spandx/python/pypi.rb in spandx-0.12.0

- old
+ new

@@ -1,19 +1,102 @@ # frozen_string_literal: true module Spandx module Python - class PyPI - def initialize(sources: [Source.default]) - @sources = sources + class Pypi < ::Spandx::Core::Gateway + SUBSTITUTIONS = [ + '-py2.py3', + '-py2', + '-py3', + '-none-any.whl', + '.tar.gz', + '.zip', + ].freeze + + def initialize(http: Spandx.http) + @http = http + @definitions = {} end - def definition_for(name, version) - @sources.each do |source| - response = source.lookup(name, version) - return JSON.parse(response.body).fetch('info', {}) if response + def matches?(dependency) + dependency.package_manager == :pypi + end + + def each(sources: default_sources) + each_package(sources) { |x| yield x } + end + + def licenses_for(dependency) + definition = definition_for( + dependency.name, + dependency.version, + sources: sources_for(dependency) + ) + [definition['license']] + end + + def definition_for(name, version, sources: default_sources) + @definitions.fetch([name, version]) do |key| + sources.each do |source| + response = source.lookup(name, version) + next if response.empty? + + match = response.fetch('info', {}) + @definitions[key] = match + return match + end + {} end - {} end + + def version_from(url) + path = SUBSTITUTIONS.inject(URI.parse(url).path.split('/')[-1]) do |memo, item| + memo.gsub(item, '') + end + + return if path.rindex('-').nil? + + path.scan(/-\d+\..*/)[-1][1..-1] + end + + private + + attr_reader :http + + def sources_for(dependency) + return default_sources if dependency.meta.empty? + + ::Spandx::Python::Source.sources_from(dependency.meta) + end + + def default_sources + [Source.default] + end + + def each_package(sources) + sources.each do |source| + html_from(source, '/simple/').css('a[href*="/simple"]').each do |node| + each_version(source, node[:href]) do |dependency| + definition = source.lookup(dependency[:name], dependency[:version]) + yield dependency.merge(license: definition['license']) + end + end + end + end + + def each_version(source, path) + html = html_from(source, path) + name = html.css('h1')[0].content.gsub('Links for ', '') + html.css('a').each do |node| + yield({ name: name, version: version_from(node[:href]) }) + end + end + + def html_from(source, path) + url = URI.join(source.uri.to_s, path).to_s + Nokogiri::HTML(http.get(url).body) + end end + + PyPI = Pypi end end