Skip to content

Add Support for pip's --platform, --no-binary, & --only-binary options #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions locallibs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# encoding: utf-8
#
# Copyright 2018 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Misc Helper Functions"""

def vararg_callback(option, opt_str, value, parser):
"""Optparse callback to support a variable number of arguments.
This is example 6 from the official docs:
https://docs.python.org/3/library/optparse.html#callback-example-6-variable-arguments
"""

assert value is None
value = []

def floatable(str):
try:
float(str)
return True
except ValueError:
return False

for arg in parser.rargs:
# stop on --foo like options
if arg[:2] == "--" and len(arg) > 2:
break
# stop on -a, but not on -3 or -3.0
if arg[:1] == "-" and len(arg) > 1 and not floatable(arg):
break
value.append(arg)

del parser.rargs[:len(value)]
setattr(parser.values, option.dest, value)
44 changes: 42 additions & 2 deletions locallibs/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from __future__ import print_function

import itertools
import os
import subprocess
import sys
Expand Down Expand Up @@ -61,18 +62,53 @@ def upgrade_pip_install(framework_path, version):
subprocess.check_call(cmd)


def install_requirements(requirements_file, framework_path, version):
def install_requirements(
requirements_file, framework_path, version, pip_platform, no_binary, only_binary):
"""Use pip to install a Python pkg into framework_path"""

def multi_value_option(option, values, separator=None):
"""Helper function supply the same option with multiple value

Args:
option (str): the option name (e.g. --<option>)
values (list): a list of values that should be passed to the option
separator (str): if the option is separated by a value (e.g. "=") and
each option value combo should be returned as a single string

Returns:
list: a flattened list of the option and values
"""
if separator:
return list(itertools.chain.from_iterable(
[[option + separator + value] for value in values]
))
return list(itertools.chain.from_iterable([[option, value] for value in values]))

python_path = os.path.join(
framework_path, "Versions", version, "bin/python" + version
)
headers_path = os.path.abspath(os.path.join(
framework_path, "Versions", version, "include/python" + version
))
site_packages_path = os.path.join(
framework_path, "Versions", version, "lib/python" + version
+ "/site-packages"
)
if not os.path.exists(python_path):
print("No python at %s" % python_path, file=sys.stderr)
return
cmd = [python_path, "-s", "-m", "pip", "install", "-r", requirements_file]

if pip_platform:
cmd.extend(multi_value_option("--platform", pip_platform))
cmd.append("--target=%s" % site_packages_path)

if no_binary:
cmd.extend(multi_value_option("--no-binary", no_binary, "="))

elif only_binary:
cmd.extend(multi_value_option("--only-binary", only_binary, "="))

pip_env = os.environ
pip_env["CPPFLAGS"] = "-I%s" % headers_path
print("Installing modules from %s..." % requirements_file)
Expand All @@ -85,6 +121,9 @@ def install_extras(
requirements_file=None,
upgrade_pip=False,
without_pip=False,
pip_platform=None,
no_binary=None,
only_binary=None
):
"""install all extra pkgs into Python framework path"""
print()
Expand All @@ -111,6 +150,7 @@ def install_extras(
upgrade_pip_install(framework_path, version)
if requirements_file:
print()
install_requirements(requirements_file, framework_path, version)
install_requirements(
requirements_file, framework_path, version, pip_platform, no_binary, only_binary)
else:
print("Skipping all requirements, packages, etc due to without-pip specified")
32 changes: 30 additions & 2 deletions make_relocatable_python_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import optparse

from locallibs import get
from locallibs import get, vararg_callback
from locallibs.fix import fix_broken_signatures, fix_other_things
from locallibs.install import install_extras
from locallibs.relocatablizer import relocatablize
Expand Down Expand Up @@ -78,8 +78,33 @@ def main():
action="store_true",
help="Do not install pip."
)
parser.add_option(
"--pip-platform", dest="pip_platform",
action="callback", callback=vararg_callback,
help="Specify which platform the pip should be downloaded for. "
"Default is to the platform of the running system. "
"Multiple values can be passed to specify multiple platforms."
)
parser.add_option(
"--no-binary", dest="no_binary",
action="callback", callback=vararg_callback,
help="Do not use binary packages. "
"Multiple values can be passed, and each time adds to the existing value. "
"Accepts either ':all:' to disable all binary packages, ':none:' to empty the set "
"(notice the colons), or one or more package names with commas between them (no colons)."
)
parser.add_option(
"--only-binary", dest="only_binary",
action="callback", callback=vararg_callback,
help="Do not use source packages. "
"Multiple values can be passed, and each time adds to the existing value. "
"Accepts either ':all:' to disable all binary packages, ':none:' to empty the set "
"(notice the colons), or one or more package names with commas between them (no colons)."
)
parser.set_defaults(unsign=True)
options, _arguments = parser.parse_args()
if options.no_binary and options.only_binary:
parser.error("The options --no-binary and --only-binary are mutually exclusive")
framework_path = get.FrameworkGetter(
python_version=options.python_version,
os_version=options.os_version,
Expand All @@ -96,7 +121,10 @@ def main():
version=short_version,
requirements_file=options.pip_requirements,
upgrade_pip=options.upgrade_pip,
without_pip=options.without_pip
without_pip=options.without_pip,
pip_platform=options.pip_platform,
no_binary=options.no_binary,
only_binary=options.only_binary
)
if fix_other_things(framework_path, short_version):
print()
Expand Down