Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PIP runner introduction #6072

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions avocado/plugins/runners/pip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import sys
import traceback
from multiprocessing import set_start_method

Check warning on line 3 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L1-L3

Added lines #L1 - L3 were not covered by tests

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

Check warning on line 8 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L5-L8

Added lines #L5 - L8 were not covered by tests


class PipRunner(BaseRunner):

Check warning on line 11 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L11

Added line #L11 was not covered by tests
"""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"

Check warning on line 32 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L31-L32

Added lines #L31 - L32 were not covered by tests

def run(self, runnable):
try:
yield messages.StartedMessage.get()

Check warning on line 36 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L34-L36

Added lines #L34 - L36 were not covered by tests
# check if there is a valid 'action' argument
cmd = runnable.kwargs.get("action", "install")

Check warning on line 38 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L38

Added line #L38 was not covered by tests
# 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

Check warning on line 44 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L40-L44

Added lines #L40 - L44 were not covered by tests

package = runnable.kwargs.get("name")

Check warning on line 46 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L46

Added line #L46 was not covered by tests
# 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

Check warning on line 55 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L48-L55

Added lines #L48 - L55 were not covered by tests

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(

Check warning on line 62 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L57-L62

Added lines #L57 - L62 were not covered by tests
"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"]

Check warning on line 73 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L70-L73

Added lines #L70 - L73 were not covered by tests


def main():
if sys.platform == "darwin":
set_start_method("fork")
app = RunnerApp(print)
app.run()

Check warning on line 80 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L76-L80

Added lines #L76 - L80 were not covered by tests


if __name__ == "__main__":
main()

Check warning on line 84 in avocado/plugins/runners/pip.py

View check run for this annotation

Codecov / codecov/patch

avocado/plugins/runners/pip.py#L83-L84

Added lines #L83 - L84 were not covered by tests
11 changes: 11 additions & 0 deletions docs/source/guides/user/chapters/dependencies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,17 @@ Following is an example of a test using the Package dependency:

.. literalinclude:: ../../../../../examples/tests/passtest_with_dependency.py

Pip
+++

Support managing python packages via pip. The
parameters available to use the asset `type` of dependencies are:

* `type`: `pip`
* `name`: the package name (required)
* `action`: `install` or `uninstall`
(optional, defaults to `install`)

Asset
+++++

Expand Down
1 change: 1 addition & 0 deletions examples/nrunner/recipes/runnable/pip_coverage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"kind": "pip", "kwargs": {"action": "install", "name": "coverage"}}
2 changes: 1 addition & 1 deletion selftests/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"nrunner-requirement": 28,
"unit": 678,
"jobs": 11,
"functional-parallel": 314,
"functional-parallel": 317,
"functional-serial": 7,
"optional-plugins": 0,
"optional-plugins-golang": 2,
Expand Down
1 change: 1 addition & 0 deletions selftests/functional/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ def test_runnables_recipe(self):
exec-test: 3
noop: 3
package: 1
pip: 1
python-unittest: 1
sysinfo: 1"""
cmd_line = f"{AVOCADO} -V list {runnables_recipe_path}"
Expand Down
61 changes: 61 additions & 0 deletions selftests/functional/runner_pip.py
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()
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ def run(self):
"avocado-runner-tap = avocado.plugins.runners.tap:main",
"avocado-runner-asset = avocado.plugins.runners.asset:main",
"avocado-runner-package = avocado.plugins.runners.package:main",
"avocado-runner-pip = avocado.plugins.runners.pip:main",
"avocado-runner-podman-image = avocado.plugins.runners.podman_image:main",
"avocado-runner-sysinfo = avocado.plugins.runners.sysinfo:main",
"avocado-software-manager = avocado.utils.software_manager.main:main",
Expand Down Expand Up @@ -479,6 +480,7 @@ def run(self):
"python-unittest = avocado.plugins.runners.python_unittest:PythonUnittestRunner",
"asset = avocado.plugins.runners.asset:AssetRunner",
"package = avocado.plugins.runners.package:PackageRunner",
"pip = avocado.plugins.runners.pip:PipRunner",
"podman-image = avocado.plugins.runners.podman_image:PodmanImageRunner",
"sysinfo = avocado.plugins.runners.sysinfo:SysinfoRunner",
],
Expand Down
Loading