Skip to content

Commit

Permalink
Merge remote-tracking branch 'richtja/pip_runner'
Browse files Browse the repository at this point in the history
Signed-off-by: Cleber Rosa <[email protected]>
  • Loading branch information
clebergnu committed Jan 9, 2025
2 parents f708ac9 + 379fe0f commit e7496db
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 2 deletions.
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

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()
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"}}
11 changes: 11 additions & 0 deletions examples/tests/dependency_pip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from avocado import Test, fail_on


class Pip(Test):
"""
:avocado: dependency={"type": "pip", "name": "pip", "action": "install"}
"""

@fail_on(ImportError)
def test(self):
import pip # pylint: disable=W0611
1 change: 1 addition & 0 deletions python-avocado.spec
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ PATH=%{buildroot}%{_bindir}:%{buildroot}%{_libexecdir}/avocado:$PATH \
%{_bindir}/avocado-runner-tap
%{_bindir}/avocado-runner-asset
%{_bindir}/avocado-runner-package
%{_bindir}/avocado-runner-pip
%{_bindir}/avocado-runner-podman-image
%{_bindir}/avocado-runner-sysinfo
%{_bindir}/avocado-software-manager
Expand Down
7 changes: 5 additions & 2 deletions selftests/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
"job-api-check-file-exists": 11,
"job-api-check-output-file": 4,
"job-api-check-tmp-directory-exists": 1,
"nrunner-interface": 70,
"nrunner-interface": 80,
"nrunner-requirement": 28,
"unit": 682,
"jobs": 11,
"functional-parallel": 314,
"functional-parallel": 317,
"functional-serial": 7,
"optional-plugins": 0,
"optional-plugins-golang": 2,
Expand Down Expand Up @@ -627,6 +627,9 @@ def create_suites(args): # pylint: disable=W0621
{
"runner": "avocado-runner-podman-image",
},
{
"runner": "avocado-runner-pip",
},
],
}

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
49 changes: 49 additions & 0 deletions selftests/functional/runner_pip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import os
import sys
import unittest

from avocado.utils import process
from selftests.utils import AVOCADO, BASEDIR, TestCaseTmpDir

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)


class TaskRun(unittest.TestCase):
def test_no_kwargs(self):
res = process.run(f"{RUNNER} task-run -i pip_1 -k pip", ignore_status=True)
self.assertIn(b"'status': 'finished'", res.stdout)
self.assertIn(b"'result': 'error'", res.stdout)
self.assertIn(b"'id': 'pip_1'", res.stdout)
self.assertEqual(res.exit_status, 0)


class PipTest(TestCaseTmpDir):
def test_pip_dependencies(self):
test_path = os.path.join(
BASEDIR,
"examples",
"tests",
"dependency_pip.py",
)
res = process.run(
f"{AVOCADO} run --job-results-dir {self.tmpdir.name} {test_path}",
ignore_status=True,
)
self.assertIn(
b"RESULTS : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0",
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

0 comments on commit e7496db

Please sign in to comment.