Skip to content

Commit

Permalink
Add automatic expansion of --requirement list
Browse files Browse the repository at this point in the history
Related to issue #2529
  • Loading branch information
pdallair committed Dec 26, 2021
1 parent 1b60a80 commit c44ef9a
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 10 deletions.
130 changes: 121 additions & 9 deletions pythonforandroid/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
This module defines the entry point for command line and programmatic use.
"""

from os import environ
from pythonforandroid import __version__
from pythonforandroid.pythonpackage import get_dep_names_of_package
Expand Down Expand Up @@ -651,9 +650,12 @@ def add_parser(subparsers, *args, **kwargs):
"pyproject.toml"))):
have_setup_py_or_similar = True

# Process requirements and put version in environ
if hasattr(args, 'requirements'):
requirements = []
# Process requirements and put version in environ:
if hasattr(args, 'requirements') and args.requirements:
all_recipes = [
recipe.lower() for recipe in
set(Recipe.list_recipes(self.ctx))
]

# Add dependencies from setup.py, but only if they are recipes
# (because otherwise, setup.py itself will install them later)
Expand All @@ -672,10 +674,6 @@ def add_parser(subparsers, *args, **kwargs):
)
]
info("Dependencies obtained: " + str(dependencies))
all_recipes = [
recipe.lower() for recipe in
set(Recipe.list_recipes(self.ctx))
]
dependencies = set(dependencies).intersection(
set(all_recipes)
)
Expand All @@ -691,7 +689,118 @@ def add_parser(subparsers, *args, **kwargs):
"package? Will continue WITHOUT setup.py deps."
)

# Parse --requirements argument list:
non_recipe_requirements = []
for requirement in args.requirements.split(','):
requirement_name = re.sub(r'==\d+(\.\d+)*', '', requirement)
if requirement_name not in all_recipes:
non_recipe_requirements.append(requirement)
args.requirements = re.sub(
r',?{}'.format(requirement), '', args.requirements)

# Compile "non-recipe" requirements' dependencies and add to list.
# Otherwise, only recipe requirements' dependencies get installed.
# More info https://github.com/kivy/python-for-android/issues/2529
if non_recipe_requirements:
info("Compiling dependencies for: "
"{}".format(non_recipe_requirements))

output = shprint(
sh.bash, '-c',
"echo -e '{}' > requirements.in && "
"pip-compile -v --dry-run --annotation-style=line && "
"rm requirements.in".format(
'\n'.join(non_recipe_requirements)))

# Parse pip-compile output
parsed_requirement_info_list = []
for line in output.splitlines():
match_data = re.match(
r'^([\w.-]+)==(\d+(\.\d+)*).*'
r'#\s+via\s+([\w\s,.-]+)', line)

if match_data:
parent_requirements = match_data.group(4).split(', ')
requirement_name = match_data.group(1)
requirement_version = match_data.group(2)

# Requirement is a "non-recipe" one we started with.
if '-r requirements.in' in parent_requirements:
parent_requirements.remove('-r requirements.in')

parsed_requirement_info_list.append([
requirement_name,
requirement_version,
parent_requirements])

# Remove indirect requirements ultimately installed by a recipe
original_parsed_requirement_count = -1
while len(parsed_requirement_info_list) != \
original_parsed_requirement_count:

original_parsed_requirement_count = \
len(parsed_requirement_info_list)

for i, parsed_requirement_info in \
enumerate(reversed(parsed_requirement_info_list)):

index = original_parsed_requirement_count - i - 1
requirement_name, requirement_version, \
parent_requirements = parsed_requirement_info

# If any parent requirement has a recipe, this
# requirement ought also to be installed by it.
# Hence, it's better not to add this requirement the
# expanded list.
parent_requirements_with_recipe = list(
set(parent_requirements).intersection(
set(all_recipes)))

# Any parent requirement removed for the expanded list
# implies that it and its own requirements (including
# this requirement) will be installed by a recipe.
# Hence, it's better not to add this requirement the
# expanded list.
requirement_name_list = \
[x[0] for x in parsed_requirement_info_list]
parent_requirements_still_in_list = list(
set(parent_requirements).intersection(
set(requirement_name_list)))

is_ultimately_installed_by_a_recipe = \
len(parent_requirements) and \
(parent_requirements_with_recipe or
len(parent_requirements_still_in_list) !=
len(parent_requirements))

if is_ultimately_installed_by_a_recipe:
del parsed_requirement_info_list[index]

for parsed_requirement_info in parsed_requirement_info_list:
requirement_name, requirement_version, \
parent_requirements = parsed_requirement_info

# If the requirement has a recipe, don't use specific
# version constraints determined by pip-compile. Some
# recipes may not support the specified version. Therefor,
# it's probably safer to just let them use their default
# version. User can still force the usage of specific
# version by explicitly declaring it with --requirements.
requirement_has_recipe = requirement_name in all_recipes
requirement_str = \
requirement_name if requirement_has_recipe else \
'{}=={}'.format(requirement_name, requirement_version)

requirement_names_arg = re.sub(
r'==\d+(\.\d+)*', '', args.requirements).split(',')

# This expansion was carried out based on "non-recipe"
# requirements. Hence,the counter-part, requirements
# with a recipe, may already be part of list.
if requirement_name not in requirement_names_arg:
args.requirements += ',' + requirement_str

# Handle specific version requirement constraints (e.g. foo==x.y)
requirements = []
for requirement in split_argument_list(args.requirements):
if "==" in requirement:
requirement, version = requirement.split(u"==", 1)
Expand All @@ -701,6 +810,9 @@ def add_parser(subparsers, *args, **kwargs):
requirements.append(requirement)
args.requirements = u",".join(requirements)

info('Expanded Requirements List: '
'{}'.format(args.requirements.split(',')))

self.warn_on_deprecated_args(args)

self.storage_dir = args.storage_dir
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
install_reqs = [
'appdirs', 'colorama>=0.3.3', 'jinja2', 'six',
'enum34; python_version<"3.4"', 'sh>=1.10; sys_platform!="nt"',
'pep517<0.7.0', 'toml',
'pep517<0.7.0', 'toml', 'pip-tools'
]
# (pep517 and toml are used by pythonpackage.py)

Expand Down

0 comments on commit c44ef9a

Please sign in to comment.