Add CLEARML_EXTRA_PIP_INSTALL_FLAGS / agent.package_manager.extra_pip_install_flags to control additional pip install flags

Fix pip version marking in "installed packages" is now preserved for and reinstalled
This commit is contained in:
allegroai 2023-07-04 14:39:40 +03:00
parent 450df2f8d3
commit 7115a9b9a7
6 changed files with 64 additions and 31 deletions

View File

@ -152,6 +152,7 @@ WORKING_STANDALONE_DIR = "code"
DEFAULT_VCS_CACHE = normalize_path(CONFIG_DIR, "vcs-cache") DEFAULT_VCS_CACHE = normalize_path(CONFIG_DIR, "vcs-cache")
PIP_EXTRA_INDICES = [] PIP_EXTRA_INDICES = []
DEFAULT_PIP_DOWNLOAD_CACHE = normalize_path(CONFIG_DIR, "pip-download-cache") DEFAULT_PIP_DOWNLOAD_CACHE = normalize_path(CONFIG_DIR, "pip-download-cache")
ENV_PIP_EXTRA_INSTALL_FLAGS = EnvironmentConfig("CLEARML_EXTRA_PIP_INSTALL_FLAGS", type=list)
ENV_DOCKER_IMAGE = EnvironmentConfig("CLEARML_DOCKER_IMAGE", "TRAINS_DOCKER_IMAGE") ENV_DOCKER_IMAGE = EnvironmentConfig("CLEARML_DOCKER_IMAGE", "TRAINS_DOCKER_IMAGE")
ENV_WORKER_ID = EnvironmentConfig("CLEARML_WORKER_ID", "TRAINS_WORKER_ID") ENV_WORKER_ID = EnvironmentConfig("CLEARML_WORKER_ID", "TRAINS_WORKER_ID")
ENV_WORKER_TAGS = EnvironmentConfig("CLEARML_WORKER_TAGS") ENV_WORKER_TAGS = EnvironmentConfig("CLEARML_WORKER_TAGS")

View File

@ -50,7 +50,7 @@ class PackageManager(object):
pass pass
@abc.abstractmethod @abc.abstractmethod
def freeze(self): def freeze(self, freeze_full_environment=False):
pass pass
@abc.abstractmethod @abc.abstractmethod
@ -141,8 +141,9 @@ class PackageManager(object):
@classmethod @classmethod
def out_of_scope_install_package(cls, package_name, *args): def out_of_scope_install_package(cls, package_name, *args):
if PackageManager._selected_manager is not None: if PackageManager._selected_manager is not None:
# noinspection PyBroadException
try: try:
result = PackageManager._selected_manager._install(package_name, *args) result = PackageManager._selected_manager.install_packages(package_name, *args)
if result not in (0, None, True): if result not in (0, None, True):
return False return False
except Exception: except Exception:
@ -150,10 +151,11 @@ class PackageManager(object):
return True return True
@classmethod @classmethod
def out_of_scope_freeze(cls): def out_of_scope_freeze(cls, freeze_full_environment=False):
if PackageManager._selected_manager is not None: if PackageManager._selected_manager is not None:
# noinspection PyBroadException
try: try:
return PackageManager._selected_manager.freeze() return PackageManager._selected_manager.freeze(freeze_full_environment)
except Exception: except Exception:
pass pass
return [] return []

View File

@ -4,7 +4,7 @@ from itertools import chain
from pathlib import Path from pathlib import Path
from typing import Text, Optional from typing import Text, Optional
from clearml_agent.definitions import PIP_EXTRA_INDICES, PROGRAM_NAME from clearml_agent.definitions import PIP_EXTRA_INDICES, PROGRAM_NAME, ENV_PIP_EXTRA_INSTALL_FLAGS
from clearml_agent.helper.package.base import PackageManager from clearml_agent.helper.package.base import PackageManager
from clearml_agent.helper.process import Argv, DEVNULL from clearml_agent.helper.process import Argv, DEVNULL
from clearml_agent.session import Session from clearml_agent.session import Session
@ -52,7 +52,7 @@ class SystemPip(PackageManager):
package, package,
'--dest', cache_dir, '--dest', cache_dir,
'--no-deps', '--no-deps',
) + self.install_flags() ) + self.download_flags()
) )
def load_requirements(self, requirements): def load_requirements(self, requirements):
@ -65,13 +65,14 @@ class SystemPip(PackageManager):
def uninstall(self, package): def uninstall(self, package):
self.run_with_env(('uninstall', '-y', package)) self.run_with_env(('uninstall', '-y', package))
def freeze(self): def freeze(self, freeze_full_environment=False):
""" """
pip freeze to all install packages except the running program pip freeze to all install packages except the running program
:return: Dict contains pip as key and pip's packages to install :return: Dict contains pip as key and pip's packages to install
:rtype: Dict[str: List[str]] :rtype: Dict[str: List[str]]
""" """
packages = self.run_with_env(('freeze',), output=True).splitlines() packages = self.run_with_env(
('freeze',) if not freeze_full_environment else ('freeze', '--all'), output=True).splitlines()
packages_without_program = [package for package in packages if PROGRAM_NAME not in package] packages_without_program = [package for package in packages if PROGRAM_NAME not in package]
return {'pip': packages_without_program} return {'pip': packages_without_program}
@ -87,6 +88,11 @@ class SystemPip(PackageManager):
# make sure we are not running it with our own PYTHONPATH # make sure we are not running it with our own PYTHONPATH
env = dict(**os.environ) env = dict(**os.environ)
env.pop('PYTHONPATH', None) env.pop('PYTHONPATH', None)
# Debug print
if self.session.debug_mode:
print(command)
return (command.get_output if output else command.check_call)(stdin=DEVNULL, env=env, **kwargs) return (command.get_output if output else command.check_call)(stdin=DEVNULL, env=env, **kwargs)
def _make_command(self, command): def _make_command(self, command):
@ -97,4 +103,17 @@ class SystemPip(PackageManager):
self.indices_args = tuple( self.indices_args = tuple(
chain.from_iterable(('--extra-index-url', x) for x in PIP_EXTRA_INDICES) chain.from_iterable(('--extra-index-url', x) for x in PIP_EXTRA_INDICES)
) )
extra_pip_flags = \
ENV_PIP_EXTRA_INSTALL_FLAGS.get() or \
self.session.config.get("agent.package_manager.extra_pip_install_flags", None)
return (self.indices_args + tuple(extra_pip_flags)) if extra_pip_flags else self.indices_args
def download_flags(self):
if self.indices_args is None:
self.indices_args = tuple(
chain.from_iterable(('--extra-index-url', x) for x in PIP_EXTRA_INDICES)
)
return self.indices_args return self.indices_args

View File

@ -147,7 +147,7 @@ class PoetryAPI(object):
any((self.path / indicator).exists() for indicator in self.INDICATOR_FILES) any((self.path / indicator).exists() for indicator in self.INDICATOR_FILES)
) )
def freeze(self): def freeze(self, freeze_full_environment=False):
lines = self.config.run("show", cwd=str(self.path)).splitlines() lines = self.config.run("show", cwd=str(self.path)).splitlines()
lines = [[p for p in line.split(' ') if p] for line in lines] lines = [[p for p in line.split(' ') if p] for line in lines]
return {"pip": [parts[0]+'=='+parts[1]+' # '+' '.join(parts[2:]) for parts in lines]} return {"pip": [parts[0]+'=='+parts[1]+' # '+' '.join(parts[2:]) for parts in lines]}

View File

@ -7,7 +7,7 @@ from .requirements import SimpleSubstitution
class PriorityPackageRequirement(SimpleSubstitution): class PriorityPackageRequirement(SimpleSubstitution):
name = ("cython", "numpy", "setuptools", ) name = ("cython", "numpy", "setuptools", "pip", )
optional_package_names = tuple() optional_package_names = tuple()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -50,31 +50,39 @@ class PriorityPackageRequirement(SimpleSubstitution):
""" """
# if we replaced setuptools, it means someone requested it, and since freeze will not contain it, # if we replaced setuptools, it means someone requested it, and since freeze will not contain it,
# we need to add it manually # we need to add it manually
if not self._replaced_packages or "setuptools" not in self._replaced_packages: if not self._replaced_packages:
return list_of_requirements return list_of_requirements
try: if "pip" in self._replaced_packages:
for k, lines in list_of_requirements.items(): full_freeze = PackageManager.out_of_scope_freeze(freeze_full_environment=True)
# k is either pip/conda # now let's look for pip
if k not in ('pip', 'conda'): pips = [line for line in full_freeze.get("pip", []) if line.split("==")[0] == "pip"]
continue if pips and "pip" in list_of_requirements:
for i, line in enumerate(lines): list_of_requirements["pip"] = [pips[0]] + list_of_requirements["pip"]
if not line or line.lstrip().startswith('#'):
continue
parts = [p for p in re.split(r'\s|=|\.|<|>|~|!|@|#', line) if p]
if not parts:
continue
# if we found setuptools, do nothing
if parts[0] == "setuptools":
return list_of_requirements
# if we are here it means we have not found setuptools if "setuptools" in self._replaced_packages:
# we should add it: try:
if "pip" in list_of_requirements: for k, lines in list_of_requirements.items():
list_of_requirements["pip"] = [self._replaced_packages["setuptools"]] + list_of_requirements["pip"] # k is either pip/conda
if k not in ('pip', 'conda'):
continue
for i, line in enumerate(lines):
if not line or line.lstrip().startswith('#'):
continue
parts = [p for p in re.split(r'\s|=|\.|<|>|~|!|@|#', line) if p]
if not parts:
continue
# if we found setuptools, do nothing
if parts[0] == "setuptools":
return list_of_requirements
except Exception as ex: # noqa # if we are here it means we have not found setuptools
return list_of_requirements # we should add it:
if "pip" in list_of_requirements:
list_of_requirements["pip"] = [self._replaced_packages["setuptools"]] + list_of_requirements["pip"]
except Exception as ex: # noqa
return list_of_requirements
return list_of_requirements return list_of_requirements

View File

@ -93,6 +93,9 @@ agent {
# extra_index_url: ["https://allegroai.jfrog.io/clearml/api/pypi/public/simple"] # extra_index_url: ["https://allegroai.jfrog.io/clearml/api/pypi/public/simple"]
extra_index_url: [] extra_index_url: []
# additional flags to use when calling pip install, example: ["--use-deprecated=legacy-resolver", ]
# extra_pip_install_flags: []
# control the pytorch wheel resolving algorithm, options are: "pip", "direct" # control the pytorch wheel resolving algorithm, options are: "pip", "direct"
# "pip" (default): would automatically detect the cuda version, and supply pip with the correct # "pip" (default): would automatically detect the cuda version, and supply pip with the correct
# extra-index-url, based on pytorch.org tables # extra-index-url, based on pytorch.org tables