-
Notifications
You must be signed in to change notification settings - Fork 343
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit introduces a new dependency runner called `pip`. With this runner, avocado will be able to manipulate with python packages in test environment based on the test dependency configuration. The runner will install pip into the test environment, and then it can call `pip install` or `pip uninstall` commands. For example, this feature can be used for running `coverage.py` inside different environments than process. Signed-off-by: Jan Richter <[email protected]>
- Loading branch information
Showing
8 changed files
with
162 additions
and
1 deletion.
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 |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import sys | ||
import traceback | ||
from multiprocessing import set_start_method | ||
|
||
from avocado.core.nrunner.app import BaseRunnerApp | ||
from avocado.core.nrunner.runner import BaseRunner | ||
from avocado.core.utils import messages | ||
from avocado.utils import process | ||
|
||
|
||
class PipRunner(BaseRunner): | ||
"""Runner for dependencies of type pip | ||
This runner handles, the installation, verification and removal of | ||
packages using the pip. | ||
Runnable attributes usage: | ||
* kind: 'pip' | ||
* uri: not used | ||
* args: not used | ||
* kwargs: | ||
- name: the package name (required) | ||
- action: one of 'install' or 'uninstall' (optional, defaults | ||
to 'install') | ||
""" | ||
|
||
name = "pip" | ||
description = "Runner for dependencies of type pip" | ||
|
||
def run(self, runnable): | ||
try: | ||
yield messages.StartedMessage.get() | ||
# check if there is a valid 'action' argument | ||
cmd = runnable.kwargs.get("action", "install") | ||
# avoid invalid arguments | ||
if cmd not in ["install", "uninstall"]: | ||
stderr = f"Invalid action {cmd}. Use one of 'install' or 'remove'" | ||
yield messages.StderrMessage.get(stderr.encode()) | ||
yield messages.FinishedMessage.get("error") | ||
return | ||
|
||
package = runnable.kwargs.get("name") | ||
# if package was passed correctly, run python -m pip | ||
if package is not None: | ||
try: | ||
cmd = f"python3 -m ensurepip && python3 -m pip {cmd} {package}" | ||
result = process.run(cmd, shell=True) | ||
except Exception as e: | ||
yield messages.StderrMessage.get(str(e)) | ||
yield messages.FinishedMessage.get("error") | ||
return | ||
|
||
yield messages.StdoutMessage.get(result.stdout) | ||
yield messages.StderrMessage.get(result.stderr) | ||
yield messages.FinishedMessage.get("pass") | ||
except Exception as e: | ||
yield messages.StderrMessage.get(traceback.format_exc()) | ||
yield messages.FinishedMessage.get( | ||
"error", | ||
fail_reason=str(e), | ||
fail_class=e.__class__.__name__, | ||
traceback=traceback.format_exc(), | ||
) | ||
|
||
|
||
class RunnerApp(BaseRunnerApp): | ||
PROG_NAME = "avocado-runner-pip" | ||
PROG_DESCRIPTION = "nrunner application for dependencies of type pip" | ||
RUNNABLE_KINDS_CAPABLE = ["pip"] | ||
|
||
|
||
def main(): | ||
if sys.platform == "darwin": | ||
set_start_method("fork") | ||
app = RunnerApp(print) | ||
app.run() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() | ||
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 @@ | ||
{"kind": "pip", "kwargs": {"action": "install", "name": "coverage"}} |
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,61 @@ | ||
import os | ||
import sys | ||
import unittest | ||
|
||
from avocado.utils import process | ||
from selftests.utils import BASEDIR | ||
|
||
RUNNER = f"{sys.executable} -m avocado.plugins.runners.pip" | ||
|
||
|
||
class RunnableRun(unittest.TestCase): | ||
def test_no_kwargs(self): | ||
res = process.run(f"{RUNNER} runnable-run -k pip", ignore_status=True) | ||
self.assertIn(b"'status': 'started'", res.stdout) | ||
self.assertIn(b"'status': 'finished'", res.stdout) | ||
self.assertIn(b"'time': ", res.stdout) | ||
self.assertEqual(res.exit_status, 0) | ||
|
||
@unittest.skipUnless( | ||
os.getenv("CI"), | ||
"This test runs on CI environments" | ||
" only as it depends on the system package manager," | ||
" and some environments don't have it available.", | ||
) | ||
def test_recipe(self): | ||
recipe = os.path.join( | ||
BASEDIR, | ||
"examples", | ||
"nrunner", | ||
"recipes", | ||
"runnable", | ||
"pip_coverage.json", | ||
) | ||
cmd = f"{RUNNER} runnable-run-recipe {recipe}" | ||
res = process.run(cmd, ignore_status=True) | ||
lines = res.stdout_text.splitlines() | ||
if len(lines) == 1: | ||
first_status = final_status = lines[0] | ||
else: | ||
first_status = lines[0] | ||
final_status = lines[-1] | ||
self.assertIn("'status': 'started'", first_status) | ||
self.assertIn("'time': ", first_status) | ||
self.assertIn("'status': 'finished'", final_status) | ||
self.assertIn("'time': ", final_status) | ||
self.assertEqual(res.exit_status, 0) | ||
|
||
|
||
class TaskRun(unittest.TestCase): | ||
def test_no_kwargs(self): | ||
res = process.run( | ||
f"{RUNNER} task-run -i XXXreq-pacXXX -k pip", ignore_status=True | ||
) | ||
self.assertIn(b"'status': 'finished'", res.stdout) | ||
self.assertIn(b"'result': 'error'", res.stdout) | ||
self.assertIn(b"'id': 'XXXreq-pacXXX'", res.stdout) | ||
self.assertEqual(res.exit_status, 0) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
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