module Autoproj::Python

Public Class Methods

activate_python(ws: Autoproj.workspace, bin: nil, version: nil) click to toggle source

Activate configuration for python in the autoproj configuration @return [String,String] python path and python version

# File lib/autoproj/python.rb, line 218
def self.activate_python(ws: Autoproj.workspace,
    bin: nil,
    version: nil)
    bin, version = resolve_python(ws: ws, bin: bin, version: version)
    ws.config.set("python_executable", bin, true)
    ws.config.set("python_version", version, true)

    ws.osdep_suffixes << "python#{$1}" if version =~ /^([0-9]+)\./

    rewrite_python_shims(bin, ws.dot_autoproj_dir)
    rewrite_pip_shims(bin, ws.dot_autoproj_dir)
    [bin, version]
end
activate_python_path(pkg, ws: Autoproj.workspace, bin: nil, version: nil) click to toggle source

Allow to update the PYTHONPATH for package if autoproj configuration USE_PYTHON is set to true. Then tries to guess the python binary from Autobuild.programs and system’s default setting @param [Autobuild::Package] pkg @param [Autoproj.workspace] ws Autoproj workspace @param [String] bin Path to a custom python version @param [String] version version constraint for python executable @return tuple of [executable, version, site-packages path] if set,

otherwise nil
# File lib/autoproj/python.rb, line 249
def self.activate_python_path(pkg,
    ws: Autoproj.workspace,
    bin: nil,
    version: nil)
    return unless ws.config.get("USE_PYTHON", nil)

    bin, version = resolve_python(ws: ws, bin: bin, version: version)
    path = File.join(pkg.prefix, "lib",
                     "python#{version}", "site-packages")
    pkg.env_add_path "PYTHONPATH", path

    [bin, version, path]
end
assert_python_activated(ws: Autoproj.workspace) click to toggle source
# File lib/autoproj/python.rb, line 263
def self.assert_python_activated(ws: Autoproj.workspace)
    return true if ws.config.get("USE_PYTHON")

    raise ConfigError,
          "Your current package selection requires the use of python," \
          " but this is either unspecified or has been denied,"\
          " see setting of USE_PYTHON in your workspace configuration." \
          " Either remove all packages depending on pip packages " \
          " from the workspace layout (manifest) or " \
          " call 'autoproj reconfigure' to change the setting."
end
auto_resolve_python(ws: Autoproj.workspace, version: nil) click to toggle source
# File lib/autoproj/python.rb, line 122
def self.auto_resolve_python(ws: Autoproj.workspace,
    version: nil)
    version_constraint = version
    resolvers = [
        -> { get_python_from_config(ws: ws, version: version_constraint) },
        -> { find_python(ws: ws, version: version_constraint) }
    ]

    bin = nil
    resolvers.each do |resolver|
        bin, version = resolver.call
        if bin && File.exist?(bin) && version
            Autoproj.debug "Autoproj::Python.resolve_python: " \
                           "found python '#{bin}' version '#{version}'"
            break
        end
    rescue RuntimeError => e
        Autoproj.debug "Autoproj::Python.resolve_python: " \
                       "resolver failed: #{e}"
    end

    unless bin
        msg = "Autoproj::Python.resolve_python: " \
              "failed to find a python executable"
        if version_constraint
            msg += " satisfying version constraint '#{version_constraint}'"
        end
        raise msg
    end
    [bin, version]
end
custom_resolve_python(bin: nil, version: nil) click to toggle source
# File lib/autoproj/python.rb, line 110
def self.custom_resolve_python(bin: nil,
    version: nil)
    version, valid = validate_python_version(bin, version)
    if valid
        [bin, version]
    else
        raise "Autoproj::Python.resolve_python: requested python"\
              "executable '#{bin}' does not satisfy version"\
              "constraints '#{version}'"
    end
end
deactivate_python(ws: Autoproj.workspace) click to toggle source
# File lib/autoproj/python.rb, line 232
def self.deactivate_python(ws: Autoproj.workspace)
    remove_python_shims(ws.dot_autoproj_dir)
    remove_pip_shims(ws.dot_autoproj_dir)
    ws.config.reset("python_executable")
    ws.config.reset("python_version")
end
find_python(ws: Autoproj.workspace, version: ws.config.get("python_version", nil)) click to toggle source

Find python given a version constraint @return [String,String] path to python executable and python version

# File lib/autoproj/python.rb, line 68
def self.find_python(ws: Autoproj.workspace,
    version: ws.config.get("python_version", nil))
    finders = [
        -> { Autobuild.programs["python"] },
        -> { `which python3`.strip },
        -> { `which python`.strip }
    ]

    finders.each do |finder|
        python_bin = finder.call
        if python_bin && !python_bin.empty?
            python_version, valid = validate_python_version(python_bin, version)
            return python_bin, python_version if valid
        end
    end
    raise "Autoproj::Python.find_python_bin: failed to find python" \
          " for version '#{version}'"
end
get_pip_version(pip_bin) click to toggle source
# File lib/autoproj/python.rb, line 28
def self.get_pip_version(pip_bin)
    unless File.exist?(pip_bin)
        raise ArgumentError, "Autoproj::Python.get_pip_version executable "\
                             "'#{pip_bin}' does not exist"
    end

    cmd = "#{pip_bin} --version"

    msg, status = Open3.capture2e(cmd)
    if status.success?
        msg.split(" ")[1]

    else
        raise "Autoproj::Python.get_pip_version identification"\
              " of pip version for '#{pip_bin}' failed: #{msg}"
    end
end
get_python_from_config(ws: Autoproj.workspace, version: nil) click to toggle source

Get information about the python executable from autoproj config, but ensure the version constraint matches

@return [String, String] Return path and version if the constraints

are fulfilled nil otherwise
# File lib/autoproj/python.rb, line 93
def self.get_python_from_config(ws: Autoproj.workspace, version: nil)
    config_bin = ws.config.get("python_executable", nil)
    return unless config_bin

    config_version = ws.config.get("python_version", nil)
    config_version ||= get_python_version(config_bin)

    # If a version constraint is given, ensure fulfillment
    if validate_version(config_version, version)
        [config_bin, config_version]
    else
        raise "python_executable in autoproj config with " \
              "version '#{config_version}' does not match "\
              "version constraints '#{version}'"
    end
end
get_python_version(python_bin) click to toggle source

Get the python version for a given python executable @return [String] The python version as <major>.<minor>

# File lib/autoproj/python.rb, line 8
def self.get_python_version(python_bin)
    unless File.exist?(python_bin)
        raise ArgumentError, "Autoproj::Python.get_python_version executable "\
                             "'#{python_bin}' does not exist"
    end

    cmd = "#{python_bin} -c \"import sys;"\
          "version=sys.version_info[:3]; "\
          "print('{0}.{1}'.format(*version))\"".strip

    msg, status = Open3.capture2e(cmd)
    if status.success?
        msg.strip

    else
        raise "Autoproj::Python.get_python_version identification"\
              " of python version for '#{python_bin}' failed: #{msg}"
    end
end
remove_pip_shims(prefix_dir) click to toggle source
# File lib/autoproj/python.rb, line 176
def self.remove_pip_shims(prefix_dir)
    shim_path = File.join(prefix_dir, "bin", "pip")
    FileUtils.rm shim_path if File.exist?(shim_path)
end
remove_python_shims(prefix_dir) click to toggle source
# File lib/autoproj/python.rb, line 171
def self.remove_python_shims(prefix_dir)
    shim_path = File.join(prefix_dir, "bin", "python")
    FileUtils.rm shim_path if File.exist?(shim_path)
end
resolve_python(ws: Autoproj.workspace, bin: nil, version: nil) click to toggle source

Resolve the python executable according to a given version constraint @param [Autoproj.workspace] ws Autoproj workspace @param [String] bin Path to the python executable that shall be used,

first fallback is the python_executable set in Autoproj's configuration,
second fallback is a full search

@param [String] version version constraint @return [String,String] python path and python version

# File lib/autoproj/python.rb, line 161
def self.resolve_python(ws: Autoproj.workspace,
    bin: nil,
    version: nil)
    if bin
        custom_resolve_python(bin: bin, version: version)
    else
        auto_resolve_python(ws: ws, version: version)
    end
end
rewrite_pip_shims(python_executable, prefix_dir) click to toggle source
# File lib/autoproj/python.rb, line 199
def self.rewrite_pip_shims(python_executable, prefix_dir)
    shim_path = File.join(prefix_dir, "bin")
    unless File.exist?(shim_path)
        FileUtils.mkdir_p shim_path
        Autoproj.warn "Autoproj::Python.rewrite_pip_shims: creating "\
                      "#{shim_path} - "\
                      "are you operating on a valid autoproj workspace?"
    end
    pip_path = File.join(shim_path, "pip")
    File.open(pip_path, "w") do |io|
        io.puts "#! /bin/sh"
        io.puts "exec #{python_executable} -m pip \"$@\""
    end
    FileUtils.chmod 0o755, pip_path
    pip_path
end
rewrite_python_shims(python_executable, prefix_dir) click to toggle source
# File lib/autoproj/python.rb, line 181
def self.rewrite_python_shims(python_executable, prefix_dir)
    shim_path = File.join(prefix_dir, "bin")
    unless File.exist?(shim_path)
        FileUtils.mkdir_p shim_path
        Autoproj.warn "Autoproj::Python.rewrite_python_shims: creating "\
                      "#{shim_path} - "\
                      "are you operating on a valid autoproj workspace?"
    end

    python_path = File.join(shim_path, "python")
    File.open(python_path, "w") do |io|
        io.puts "#! /bin/sh"
        io.puts "exec #{python_executable} \"$@\""
    end
    FileUtils.chmod 0o755, python_path
    python_path
end
setup_python_configuration_options(ws: Autoproj.workspace) click to toggle source
# File lib/autoproj/python.rb, line 275
def self.setup_python_configuration_options(ws: Autoproj.workspace)
    ws.config.declare "USE_PYTHON", "boolean",
                      default: "no",
                      doc: ["Do you want to activate python?"]

    if ws.config.get("USE_PYTHON")
        unless ws.config.has_value_for?("python_executable")
            remove_python_shims(ws.dot_autoproj_dir)
            remove_pip_shims(ws.dot_autoproj_dir)
            python_bin, = auto_resolve_python(ws: ws)
        end

        ws.config.declare "python_executable", "string",
                          default: python_bin.to_s,
                          doc: ["Select the path to the python executable"]

        activate_python(ws: ws)
    else
        deactivate_python(ws: ws)
    end
end
validate_python_version(python_bin, version_constraint) click to toggle source

Validate that a given python executable’s version fulfills a given version constraint @param [String] python_bin the python executable @param [String] version_constraint version constraint, e.g., <3.8, >= 3.7, 3.6 @return [String,Bool] Version and validation result, i.e.,

True if binary fulfills the version constraint, false otherwise
# File lib/autoproj/python.rb, line 61
def self.validate_python_version(python_bin, version_constraint)
    version = get_python_version(python_bin)
    [version, validate_version(version, version_constraint)]
end
validate_version(version, version_constraint) click to toggle source
# File lib/autoproj/python.rb, line 46
def self.validate_version(version, version_constraint)
    if version_constraint
        dependency = Gem::Dependency.new("python", version_constraint)
        dependency.match?("python", version)
    else
        true
    end
end