class MyPrecious::PyPackageInfo

Constants

ACCEPTED_URI_SCHEMES
CODE_CACHE_DIR
COMMON_REQ_FILE_NAMES
MIN_RELEASED_DAYS
MIN_STABLE_DAYS
PACKAGE_CACHE_DIR
VERSION_PATTERN

Attributes

install[RW]
install?[RW]
name[R]
url[R]
version_reqs[R]

Public Class Methods

col_title(attr) click to toggle source

Get an appropriate, human friendly column title for an attribute

# File lib/myprecious/python_packages.rb, line 51
def self.col_title(attr)
  case attr
  when :name then 'Package'
  else Reporting.common_col_title(attr)
  end
end
guess_req_file(fpath) click to toggle source

Guess the name of the requirements file in the given directory

Best effort (currently, consulting a static list of likely file names for existence), and may return nil.

# File lib/myprecious/python_packages.rb, line 42
def self.guess_req_file(fpath)
  COMMON_REQ_FILE_NAMES.find do |fname|
    fpath.join(fname).exist?
  end
end
new(name: nil, version_reqs: [], url: nil, install: false) click to toggle source

Construct an instance

At least one of the keywords name: or url: MUST be provided.

Calls superclass method
# File lib/myprecious/python_packages.rb, line 63
def initialize(name: nil, version_reqs: [], url: nil, install: false)
  super()
  if name.nil? and url.nil?
    raise ArgumentError, "At least one of name: or url: must be specified"
  end
  @name = name
  @version_reqs = version_reqs
  @url = url && URI(url)
  @install = install
  if pinning_req = self.version_reqs.find(&:determinative?)
    current_version = pinning_req.vernum
  end
end

Public Instance Methods

age() click to toggle source

Age in days of the current version

# File lib/myprecious/python_packages.rb, line 207
def age
  return @age if defined? @age
  @age = get_age
end
changelog() click to toggle source
# File lib/myprecious/python_packages.rb, line 274
def changelog
  # This is wrong
  info = get_package_info['info']
  return info['project_url']
end
current_version() click to toggle source
# File lib/myprecious/python_packages.rb, line 153
def current_version
  @current_version
end
current_version=(val) click to toggle source
# File lib/myprecious/python_packages.rb, line 157
def current_version=(val)
  @current_version = val.kind_of?(Version) ? val : parse_version_str(val)
end
cves() click to toggle source
# File lib/myprecious/python_packages.rb, line 265
def cves
  resolve_name!
  resolve_version!
  
  CVEs.get_for(name, current_version.to_s).map do |cve, applicability|
    cve
  end
end
dev_comp() click to toggle source
# File lib/myprecious/python_packages.rb, line 679
def dev_comp
  @dev || Float::INFINITY
end
direct_reference?() click to toggle source

Was this requirement specified as a direct reference to a URL providing the package?

# File lib/myprecious/python_packages.rb, line 84
def direct_reference?
  !url.nil?
end
homepage_uri() click to toggle source
# File lib/myprecious/python_packages.rb, line 256
def homepage_uri
  get_package_info['info']['home_page']
end
incorporate(other_req) click to toggle source

Incorporate the requirements for this package specified in another object into this instance

# File lib/myprecious/python_packages.rb, line 141
def incorporate(other_req)
  if other_req.name != self.name
    raise ArgumentError, "Cannot incorporate requirements for #{other_req.name} into #{self.name}"
  end
  
  self.version_reqs.concat(other_req.version_reqs)
  self.install ||= other_req.install
  if current_version.nil? && (pinning_req = self.version_reqs.find(&:determinative?))
    current_version = pinning_req.vernum
  end
end
latest_released() click to toggle source
# File lib/myprecious/python_packages.rb, line 216
def latest_released
  Date.parse(versions_with_release[0][1].to_s).to_s      
end
latest_version() click to toggle source
# File lib/myprecious/python_packages.rb, line 212
def latest_version
  versions_with_release[0][0].to_s
end
latest_version_satisfying_reqs() click to toggle source
# File lib/myprecious/python_packages.rb, line 196
def latest_version_satisfying_reqs
  versions_with_release.each do |ver, rel_date|
    return ver if self.satisfied_by?(ver.to_s)
    return ver if version_reqs.all? {|req| req.satisfied_by?(ver.to_s)}
  end
  return nil
end
license() click to toggle source
# File lib/myprecious/python_packages.rb, line 260
def license
  # TODO: Implement better, showing difference between current and recommended
  LicenseDescription.new(get_package_info['info']['license'])
end
obsolescence() click to toggle source
# File lib/myprecious/python_packages.rb, line 299
def obsolescence
  at_least_moderate = false
  if current_version.kind_of?(Version) && recommended_version
    cv_major = [current_version.epoch, current_version.final.first]
    rv_major = [recommended_version.epoch, recommended_version.final.first]
    
    case 
    when rv_major[0] < cv_major[0]
      return nil
    when cv_major[0] < rv_major[0]
      # Can't compare, rely on days_between_current_and_recommended
    when cv_major[1] + 1 < rv_major[1]
      return :severe
    when cv_major[1] < rv_major[1]
      at_least_moderate = true
    end
    
    days_between = days_between_current_and_recommended
    
    return Reporting.obsolescence_by_age(
      days_between,
      at_least_moderate: at_least_moderate,
    )
  end
end
post_comp() click to toggle source
# File lib/myprecious/python_packages.rb, line 675
def post_comp
  @post || []
end
pre_comp() click to toggle source
# File lib/myprecious/python_packages.rb, line 671
def pre_comp
  @pre || NOT_PRE
end
release_history_url() click to toggle source
# File lib/myprecious/python_packages.rb, line 280
def release_history_url
  "https://pypi.org/project/#{name}/#history"
end
resolve_name!() click to toggle source

For packages specified without a name, do what is necessary to find the name

# File lib/myprecious/python_packages.rb, line 92
def resolve_name!
  return unless direct_reference?
  
  name_from_setup = setup_data['name']
  if !@name.nil? && @name != name_from_setup
    warn("Requirement file entry for #{@name} points to archive for #{name_from_setup}")
  else
    @name = name_from_setup
  end
end
resolve_version!() click to toggle source

For requirements not deterministically specifying a version, determine which version would be installed

# File lib/myprecious/python_packages.rb, line 107
def resolve_version!
  return @current_version if @current_version
  
  if direct_reference?
    # Use setup_data
    @current_version = parse_version_str(setup_data['version'] || '0a0.dev0')
  elsif pinning_req = self.version_reqs.find(&:determinative?)
    @current_version = parse_version_str(pinning_req.vernum)
  else
    # Use data from pypi
    puts "Resolving current version of #{name}..."
    if inferred_ver = latest_version_satisfying_reqs
      self.current_version = inferred_ver
      puts "    -> #{inferred_ver}"
    else
      puts "    (unknown)"
    end
  end
end
satisfied_by?(version) click to toggle source

Test if the version constraints on this package are satisfied by the given version

All current version requirements are in version_reqs.

# File lib/myprecious/python_packages.rb, line 133
def satisfied_by?(version)
  version_reqs.all? {|r| r.satisfied_by?(version)}
end
try_to_i(s) click to toggle source
# File lib/myprecious/python_packages.rb, line 663
def try_to_i(s)
  if /^\d+$/ =~ s
    s.to_i
  else
    s
  end
end
versions_with_release() click to toggle source

An Array of Arrays containing version (MyPrecious::PyPackageInfo::Version or String) and release date (Time)

The returned Array is sorted in order of descending version number, with strings not conforming to PEP-440 sorted lexicographically following all PEP-440 conformant versions, the latter presented as MyPrecious::PyPackageInfo::Version objects.

# File lib/myprecious/python_packages.rb, line 170
def versions_with_release
  @versions ||= begin
    all_releases = get_package_info.fetch('releases', {})
    ver_release_pairs = all_releases.each_pair.map do |ver, info|
      [
        parse_version_str(ver),
        info.select {|f| f['packagetype'] == 'sdist'}.map do |f|
          Time.parse(f['upload_time_iso_8601'])
        end.min
      ].freeze
    end
    ver_release_pairs.reject! do |vn, rd|
      (vn.kind_of?(Version) && vn.prerelease?) || rd.nil?
    end
    ver_release_pairs.sort! do |l, r|
      case 
      when l[0].kind_of?(String) && r[0].kind_of?(Version) then -1
      when l[0].kind_of?(Version) && r[0].kind_of?(String) then 1
      else l <=> r
      end
    end
    ver_release_pairs.reverse!
    ver_release_pairs.freeze
  end
end