-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CurrentEnvironmentBackend
- Loading branch information
Showing
13 changed files
with
609 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
from ._arca import Arca | ||
from .backend import BaseBackend, VenvBackend, DockerBackend | ||
from .backend import BaseBackend, VenvBackend, DockerBackend, CurrentEnvironmentBackend, RequirementsStrategy | ||
from .result import Result | ||
from .task import Task | ||
|
||
|
||
__all__ = ["Arca", "BaseBackend", "VenvBackend", "DockerBackend", "Result", "Task"] | ||
__all__ = ["Arca", "BaseBackend", "VenvBackend", "DockerBackend", "Result", "Task", "CurrentEnvironmentBackend", | ||
"RequirementsStrategy"] | ||
__version__ = "0.0.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
from .base import BaseBackend | ||
from .venv import VenvBackend | ||
from .docker import DockerBackend | ||
from .current_environment import CurrentEnvironmentBackend, RequirementsStrategy | ||
|
||
__all__ = ["BaseBackend", "VenvBackend", "DockerBackend"] | ||
|
||
__all__ = ["BaseBackend", "VenvBackend", "DockerBackend", "CurrentEnvironmentBackend", "RequirementsStrategy"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import subprocess | ||
import sys | ||
from enum import Enum | ||
from pathlib import Path | ||
from typing import Optional, Iterable | ||
|
||
from git import Repo | ||
|
||
from arca.exceptions import ArcaMisconfigured, RequirementsMismatch, BuildError | ||
from arca.utils import LazySettingProperty, logger | ||
from .base import BaseRunInSubprocessBackend | ||
|
||
|
||
class RequirementsStrategy(Enum): | ||
IGNORE = "ignore" | ||
RAISE = "raise" | ||
INSTALL_EXTRA = "install_extra" | ||
|
||
|
||
class CurrentEnvironmentBackend(BaseRunInSubprocessBackend): | ||
|
||
current_environment_requirements = LazySettingProperty(key="current_environment_requirements") | ||
requirements_strategy = LazySettingProperty(key="requirements_strategy", | ||
default=RequirementsStrategy.RAISE, | ||
convert=RequirementsStrategy) | ||
|
||
def validate_settings(self): | ||
super().validate_settings() | ||
|
||
if self.current_environment_requirements is not None: | ||
if not Path(self.current_environment_requirements).exists(): | ||
raise ArcaMisconfigured("Can't locate current environment requirements.") | ||
|
||
def install_requirements(self, *, fl: Optional[Path]=None, requirements: Optional[Iterable[str]]=None, | ||
_action: str="install"): | ||
if _action not in ["install", "uninstall"]: | ||
raise ValueError(f"{_action} is invalid value for _invalid") | ||
|
||
cmd = [sys.executable, "-m", "pip", _action] | ||
|
||
if _action == "uninstall": | ||
cmd += ["-y"] | ||
|
||
if fl is not None: | ||
cmd += ["-r", str(fl)] | ||
elif requirements is not None: | ||
cmd += list(requirements) | ||
else: | ||
raise ValueError("Either fl or requirements has to be provided") | ||
|
||
logger.info("Installing requirements with command: %s", cmd) | ||
|
||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
|
||
[out_stream, err_stream] = process.communicate() | ||
out_stream = out_stream.decode("utf-8") | ||
err_stream = err_stream.decode("utf-8") | ||
|
||
logger.debug("Return code is %s", process.returncode) | ||
logger.debug(out_stream) | ||
logger.debug(err_stream) | ||
|
||
if process.returncode: | ||
raise BuildError(f"Unable to {_action} requirements from the target repository", extra_info={ | ||
"out_stream": out_stream, | ||
"err_stream": err_stream, | ||
"returncode": process.returncode | ||
}) | ||
|
||
def get_requirements_set(self, fl): | ||
return set([x.strip() for x in fl.read_text().split("\n") if x.strip()]) | ||
|
||
def get_or_create_environment(self, repo: str, branch: str, git_repo: Repo, repo_path: Path): | ||
""" Handles requirements difference based on configured strategy | ||
""" | ||
if self.requirements_strategy == RequirementsStrategy.IGNORE: | ||
logger.info("Requirements strategy is IGNORE") | ||
return | ||
|
||
requirements = repo_path / self.requirements_location | ||
|
||
# explicitly configured there are no requirements for the current environment | ||
if self.current_environment_requirements is None: | ||
|
||
if not requirements.exists(): | ||
return # no diff, since no requirements both in current env and repository | ||
|
||
requirements_set = self.get_requirements_set(requirements) | ||
|
||
if len(requirements_set): | ||
if self.requirements_strategy == RequirementsStrategy.RAISE: | ||
raise RequirementsMismatch(f"There are extra requirements in repository {repo}, branch {branch}.", | ||
diff=requirements.read_text()) | ||
|
||
self.install_requirements(fl=requirements) | ||
|
||
# requirements for current environment configured and exists | ||
else: | ||
current_requirements = Path(self.current_environment_requirements) | ||
|
||
current_requirements_set = self.get_requirements_set(current_requirements) | ||
|
||
if not requirements.exists(): | ||
return # no req. file in repo -> no extra requirements | ||
|
||
requirements_set = self.get_requirements_set(requirements) | ||
|
||
# only requirements that are extra in repository requirements matter | ||
extra_requirements_set = requirements_set - current_requirements_set | ||
|
||
if len(extra_requirements_set) == 0: | ||
return # no extra requirements in repository | ||
else: | ||
if self.requirements_strategy == RequirementsStrategy.RAISE: | ||
raise RequirementsMismatch(f"There are extra requirements in repository {repo}, branch {branch}.", | ||
diff="\n".join(extra_requirements_set)) | ||
|
||
elif self.requirements_strategy == RequirementsStrategy.INSTALL_EXTRA: | ||
self.install_requirements(requirements=extra_requirements_set) | ||
|
||
def _uninstall(self, *args): | ||
""" For usage in tests to uninstall packages from the current environment | ||
:param args: packages to uninstall | ||
""" | ||
self.install_requirements(requirements=args, _action="uninstall") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import os | ||
|
||
|
||
if os.environ.get("TRAVIS", False): | ||
BASE_DIR = "/home/travis/build/{}/test_loc".format(os.environ.get("TRAVIS_REPO_SLUG", "mikicz/arca")) | ||
else: | ||
BASE_DIR = "/tmp/arca/test" | ||
|
||
RETURN_STR_FUNCTION = """ | ||
def return_str_function(): | ||
return "Some string" | ||
""" | ||
|
||
SECOND_RETURN_STR_FUNCTION = """ | ||
def return_str_function(): | ||
return "Some other string" | ||
""" | ||
|
||
RETURN_DJANGO_VERSION_FUNCTION = """ | ||
import django | ||
def return_str_function(): | ||
return django.__version__ | ||
""" | ||
|
||
RETURN_PYTHON_VERSION_FUNCTION = """ | ||
import sys | ||
def return_python_version(): | ||
return "{}.{}.{}".format(sys.version_info.major, sys.version_info.minor, sys.version_info.micro) | ||
""" | ||
|
||
RETURN_IS_XSLTPROC_INSTALLED = """ | ||
import subprocess | ||
def return_is_xsltproc_installed(): | ||
try: | ||
return subprocess.Popen(["xsltpoc", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).wait() | ||
except: | ||
return False | ||
""" | ||
|
||
RETURN_IS_LXML_INSTALLED = """ | ||
def return_is_lxml_installed(): | ||
try: | ||
import lxml | ||
return True | ||
except: | ||
return False | ||
""" | ||
|
||
RETURN_PLATFORM = """ | ||
import platform | ||
def return_platform(): | ||
return platform.dist()[0] | ||
""" |
Oops, something went wrong.