class MyPrecious::Reader

Reads package requirements from a file

Public Class Methods

new(packages_fpath, only_constrain: false) click to toggle source
Calls superclass method
# File lib/myprecious/python_packages.rb, line 747
def initialize(packages_fpath, only_constrain: false)
  super()
  @files = [Pathname(packages_fpath)]
  @only_constrain = only_constrain
end

Public Instance Methods

each_installed_package() { |item| ... } click to toggle source

Enumerate packages targeted for installation by this instance

Each invocation of the block receives a PyPackageInfo object targeted for installation. Each of these PyPackageInfo object will have a resolved name and current_version (if possible).

An Enumerator is returned if no block is given.

# File lib/myprecious/python_packages.rb, line 801
def each_installed_package
  generator = Enumerator.new do |items|
    packages = {}
    
    each_package_constrained do |pkg|
      pkg.resolve_name!
      if packages.has_key?(pkg.name)
        packages[pkg.name].incorporate(pkg)
      else
        packages[pkg.name] = pkg
      end
    end
    
    to_install = []
    packages.each_value do |pkg|
      next unless pkg.install?
      to_install << pkg.name
    end
    
    while pkg_name = to_install.shift
      pkg = packages[pkg_name]
      pkg.resolve_version!
      items << pkg
    end
  end
  
  if block_given?
    generator.each {|item| yield item}
  else
    generator
  end
end
each_package_constrained() { |item| ... } click to toggle source

Enumerate packages described by requirements targeted by this instance

Each invocation of the block receives a PyPackageInfo object, which will have, at minimum, either a name or url not nil. It is possible that multiple iterations will process separate PyPackageInfo for the same package, in which case PyPackageInfo#incorporate is useful.

An Enumerator is returned if no block is given.

# File lib/myprecious/python_packages.rb, line 763
def each_package_constrained
  generator = Enumerator.new do |items|
    continued_line = ''
    current_file.each_line do |pkg_line|
      pkg_line = pkg_line.chomp
      next if /^#/ =~ pkg_line
      if /(?<=\s)#.*$/ =~ pkg_line
        pkg_line = pkg_line[0...-$&.length]
      end
      
      # Yes, this _does_ happen after comment lines are skipped :facepalm:
      if /\\$/ =~ pkg_line
        continued_line += pkg_line[0..-2]
        next
      end
      pkg_line, continued_line = (continued_line + pkg_line).strip, ''
      next if pkg_line.empty?
      
      process_line_into(items, pkg_line)
    end
  end
  
  if block_given?
    generator.each {|item| yield item}
  else
    generator
  end
end

Private Instance Methods

current_file() click to toggle source
# File lib/myprecious/python_packages.rb, line 835
def current_file
  @files.last
end
explain_parse_tree(parse_tree) click to toggle source
# File lib/myprecious/python_packages.rb, line 909
def explain_parse_tree(parse_tree)
  case parse_tree
  when Array
    "[#{parse_tree.map {|i| "#<#{i.class.name}>"}.join(', ')}]"
  when Hash
    "{#{parse_tree.map {|k, v| "#{k.inspect} => #<#{v.class.name}>"}.join(', ')}}"
  else
    "#<#{parse_tree.class.name}>"
  end
end
in_file(fpath) { || ... } click to toggle source
# File lib/myprecious/python_packages.rb, line 839
def in_file(fpath)
  @files << Pathname(fpath)
  begin
    yield
  ensure
    @files.pop
  end
end
insert_package_from_line_into(items, pkg_line) click to toggle source
# File lib/myprecious/python_packages.rb, line 883
def insert_package_from_line_into(items, pkg_line)
  parse_tree = begin
    ReqSpecParser.new.parse(pkg_line)
  rescue Parslet::ParseFailed
    if (uri = URI.try_parse(pkg_line)) && ACCEPTED_URI_SCHEMES.include?(uri.scheme)
      if only_constrain?
        warn("#{current_file} is a constraints file but specifies URL #{uri}")
      else
        items << PyPackageInfo.new(url: uri, install: true)
      end
      return
    end
    warn("Unreportable line in #{current_file}: #{pkg_line}")
    return
  end
  
  # Transform parse tree into a spec
  spec = ReqSpecTransform.new.apply_spec(parse_tree)
  if spec.kind_of?(PyPackageInfo)
    spec.install ||= !only_constrain?
    items << spec
  else
    warn("Unhandled requirement parse tree: #{explain_parse_tree parse_tree}")
  end
end
only_constrain?() click to toggle source
# File lib/myprecious/python_packages.rb, line 848
def only_constrain?
  @only_constrain
end
process_line_into(items, pkg_line) click to toggle source
# File lib/myprecious/python_packages.rb, line 861
def process_line_into(items, pkg_line)
  case pkg_line
  when /^-r (.)$/
    if only_constrain?
      warn("-r directive appears in constraints file #{current_file}")
    end
    in_file(current_file.dirname / $1) do
      each_package_constrained {|pkg| items << pkg}
    end
  when /^-c (.)$/
    in_file(current_file.dirname / $1) do
      reading_constraints do
        each_package_constrained {|pkg| items << pkg}
      end
    end
  when /^-e/
    warn %Q{#{current_file} lists "editable" package: #{pkg_line}}
  else
    insert_package_from_line_into(items, pkg_line)
  end
end
reading_constraints() { || ... } click to toggle source
# File lib/myprecious/python_packages.rb, line 852
def reading_constraints
  prev_val, @only_constrain = @only_constrain, true
  begin
    yield
  ensure
    @only_constrain = prev_val
  end
end