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