2020-01-22 09:22:43 +00:00
|
|
|
from copy import deepcopy
|
2019-10-25 19:28:44 +00:00
|
|
|
from functools import wraps
|
|
|
|
|
|
|
|
import attr
|
2020-01-16 09:10:38 +00:00
|
|
|
import sys
|
2020-01-22 09:22:43 +00:00
|
|
|
import os
|
2019-10-25 19:28:44 +00:00
|
|
|
from pathlib2 import Path
|
2022-01-23 08:40:05 +00:00
|
|
|
|
2024-02-29 11:59:26 +00:00
|
|
|
from clearml_agent.definitions import ENV_AGENT_FORCE_POETRY
|
2020-12-22 21:00:57 +00:00
|
|
|
from clearml_agent.helper.process import Argv, DEVNULL, check_if_command_exists
|
|
|
|
from clearml_agent.session import Session, POETRY
|
2019-10-25 19:28:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
def prop_guard(prop, log_prop=None):
|
|
|
|
assert isinstance(prop, property)
|
|
|
|
assert not log_prop or isinstance(log_prop, property)
|
|
|
|
|
|
|
|
def decorator(func):
|
|
|
|
message = "%s:%s calling {}, {} = %s".format(
|
|
|
|
func.__name__, prop.fget.__name__
|
|
|
|
)
|
|
|
|
|
|
|
|
@wraps(func)
|
|
|
|
def new_func(self, *args, **kwargs):
|
|
|
|
prop_value = prop.fget(self)
|
|
|
|
if log_prop:
|
|
|
|
log_prop.fget(self).debug(
|
|
|
|
message,
|
|
|
|
type(self).__name__,
|
|
|
|
"" if prop_value else " not",
|
|
|
|
prop_value,
|
|
|
|
)
|
|
|
|
if prop_value:
|
|
|
|
return func(self, *args, **kwargs)
|
|
|
|
|
|
|
|
return new_func
|
|
|
|
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
|
|
|
|
class PoetryConfig:
|
|
|
|
|
2024-03-11 21:36:10 +00:00
|
|
|
def __init__(self, session):
|
2024-02-29 11:59:26 +00:00
|
|
|
# type: (Session, str) -> None
|
2019-10-25 19:28:44 +00:00
|
|
|
self.session = session
|
|
|
|
self._log = session.get_logger(__name__)
|
2024-03-11 21:36:10 +00:00
|
|
|
self._python = sys.executable # default, overwritten from session config in initialize()
|
2020-01-16 09:10:38 +00:00
|
|
|
self._initialized = False
|
2019-10-25 19:28:44 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def log(self):
|
|
|
|
return self._log
|
|
|
|
|
|
|
|
@property
|
|
|
|
def enabled(self):
|
2024-02-29 11:59:26 +00:00
|
|
|
return ENV_AGENT_FORCE_POETRY.get() or self.session.config["agent.package_manager.type"] == POETRY
|
2019-10-25 19:28:44 +00:00
|
|
|
|
|
|
|
_guard_enabled = prop_guard(enabled, log)
|
|
|
|
|
|
|
|
def run(self, *args, **kwargs):
|
|
|
|
func = kwargs.pop("func", Argv.get_output)
|
|
|
|
kwargs.setdefault("stdin", DEVNULL)
|
2020-01-22 09:22:43 +00:00
|
|
|
kwargs['env'] = deepcopy(os.environ)
|
2020-01-26 13:05:59 +00:00
|
|
|
if 'VIRTUAL_ENV' in kwargs['env'] or 'CONDA_PREFIX' in kwargs['env']:
|
|
|
|
kwargs['env'].pop('VIRTUAL_ENV', None)
|
|
|
|
kwargs['env'].pop('CONDA_PREFIX', None)
|
|
|
|
kwargs['env'].pop('PYTHONPATH', None)
|
|
|
|
if hasattr(sys, "real_prefix") and hasattr(sys, "base_prefix"):
|
|
|
|
path = ':'+kwargs['env']['PATH']
|
|
|
|
path = path.replace(':'+sys.base_prefix, ':'+sys.real_prefix, 1)
|
|
|
|
kwargs['env']['PATH'] = path
|
|
|
|
|
2023-11-01 13:10:40 +00:00
|
|
|
if self.session and self.session.config and args and args[0] == "install":
|
2023-03-28 11:37:48 +00:00
|
|
|
extra_args = self.session.config.get("agent.package_manager.poetry_install_extra_args", None)
|
|
|
|
if extra_args:
|
|
|
|
args = args + tuple(extra_args)
|
|
|
|
|
2020-01-16 09:17:05 +00:00
|
|
|
if check_if_command_exists("poetry"):
|
|
|
|
argv = Argv("poetry", *args)
|
|
|
|
else:
|
|
|
|
argv = Argv(self._python, "-m", "poetry", *args)
|
2019-10-25 19:28:44 +00:00
|
|
|
self.log.debug("running: %s", argv)
|
|
|
|
return func(argv, **kwargs)
|
|
|
|
|
|
|
|
def _config(self, *args, **kwargs):
|
|
|
|
return self.run("config", *args, **kwargs)
|
|
|
|
|
|
|
|
@_guard_enabled
|
2020-01-16 09:10:38 +00:00
|
|
|
def initialize(self, cwd=None):
|
|
|
|
if not self._initialized:
|
2024-03-11 21:36:10 +00:00
|
|
|
# use correct python version -- detected in Worker.install_virtualenv() and written to
|
|
|
|
# session
|
|
|
|
if self.session.config.get("agent.python_binary", None):
|
|
|
|
self._python = self.session.config.get("agent.python_binary")
|
|
|
|
|
2022-01-23 08:40:05 +00:00
|
|
|
if self.session.config.get("agent.package_manager.poetry_version", None) is not None:
|
|
|
|
version = str(self.session.config.get("agent.package_manager.poetry_version"))
|
|
|
|
|
2024-02-29 11:59:26 +00:00
|
|
|
# get poetry version
|
|
|
|
version = version.replace(' ', '')
|
|
|
|
if ('=' in version) or ('~' in version) or ('<' in version) or ('>' in version):
|
|
|
|
version = version
|
|
|
|
elif version:
|
|
|
|
version = "==" + version
|
|
|
|
# (we are not running it yet)
|
|
|
|
argv = Argv(self._python, "-m", "pip", "install", "poetry{}".format(version),
|
|
|
|
"--upgrade", "--disable-pip-version-check")
|
|
|
|
# this is just for beauty and checks, we already set the verion in the Argv
|
|
|
|
if not version:
|
|
|
|
version = "latest"
|
|
|
|
else:
|
|
|
|
# mark to install poetry if not already installed (we are not running it yet)
|
|
|
|
argv = Argv(self._python, "-m", "pip", "install", "poetry", "--disable-pip-version-check")
|
|
|
|
version = ""
|
|
|
|
|
|
|
|
# first upgrade pip if we need to
|
|
|
|
try:
|
|
|
|
from clearml_agent.helper.package.pip_api.venv import VirtualenvPip
|
|
|
|
pip = VirtualenvPip(
|
|
|
|
session=self.session, python=self._python,
|
|
|
|
requirements_manager=None, path=None, interpreter=self._python)
|
|
|
|
pip.upgrade_pip()
|
|
|
|
except Exception as ex:
|
|
|
|
self.log.warning("failed upgrading pip: {}".format(ex))
|
|
|
|
|
|
|
|
# check if we do not have a specific version and poetry is found skip installation
|
|
|
|
if not version and check_if_command_exists("poetry"):
|
|
|
|
print("Notice: Poetry was found, no specific version required, skipping poetry installation")
|
|
|
|
else:
|
|
|
|
print('Installing / Upgrading Poetry package to {}'.format(version))
|
2022-01-23 08:40:05 +00:00
|
|
|
# now install poetry
|
|
|
|
try:
|
|
|
|
print(argv.get_output())
|
|
|
|
except Exception as ex:
|
2024-02-29 11:59:26 +00:00
|
|
|
self.log.warning("failed installing poetry: {}".format(ex))
|
2022-01-23 08:40:05 +00:00
|
|
|
|
2024-02-29 11:59:26 +00:00
|
|
|
# now setup poetry
|
2020-01-16 09:10:38 +00:00
|
|
|
self._initialized = True
|
2020-02-23 12:43:21 +00:00
|
|
|
try:
|
|
|
|
self._config("--local", "virtualenvs.in-project", "true", cwd=cwd)
|
|
|
|
# self._config("repositories.{}".format(self.REPO_NAME), PYTHON_INDEX)
|
|
|
|
# self._config("http-basic.{}".format(self.REPO_NAME), *PYTHON_INDEX_CREDENTIALS)
|
|
|
|
except Exception as ex:
|
|
|
|
print("Exception: {}\nError: Failed configuring Poetry virtualenvs.in-project".format(ex))
|
|
|
|
raise
|
2019-10-25 19:28:44 +00:00
|
|
|
|
|
|
|
def get_api(self, path):
|
|
|
|
# type: (Path) -> PoetryAPI
|
|
|
|
return PoetryAPI(self, path)
|
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class PoetryAPI(object):
|
|
|
|
config = attr.ib(type=PoetryConfig)
|
|
|
|
path = attr.ib(type=Path, converter=Path)
|
|
|
|
|
|
|
|
INDICATOR_FILES = "pyproject.toml", "poetry.lock"
|
|
|
|
|
|
|
|
def install(self):
|
|
|
|
# type: () -> bool
|
|
|
|
if self.enabled:
|
2020-01-16 09:10:38 +00:00
|
|
|
self.config.run("install", "-n", cwd=str(self.path), func=Argv.check_call)
|
2019-10-25 19:28:44 +00:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
@property
|
|
|
|
def enabled(self):
|
|
|
|
return self.config.enabled and (
|
|
|
|
any((self.path / indicator).exists() for indicator in self.INDICATOR_FILES)
|
|
|
|
)
|
|
|
|
|
2023-07-04 11:39:40 +00:00
|
|
|
def freeze(self, freeze_full_environment=False):
|
2020-01-16 09:10:38 +00:00
|
|
|
lines = self.config.run("show", cwd=str(self.path)).splitlines()
|
|
|
|
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]}
|
2019-10-25 19:28:44 +00:00
|
|
|
|
|
|
|
def get_python_command(self, extra):
|
2020-01-16 09:17:05 +00:00
|
|
|
if check_if_command_exists("poetry"):
|
|
|
|
return Argv("poetry", "run", "python", *extra)
|
|
|
|
else:
|
|
|
|
return Argv(self.config._python, "-m", "poetry", "run", "python", *extra)
|
2019-12-14 22:00:55 +00:00
|
|
|
|
|
|
|
def upgrade_pip(self, *args, **kwargs):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def set_selected_package_manager(self, *args, **kwargs):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def out_of_scope_install_package(self, *args, **kwargs):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def install_from_file(self, *args, **kwargs):
|
|
|
|
pass
|