diff --git a/sysutils/py-clustershell/Makefile b/sysutils/py-clustershell/Makefile index 323394811b05..aae381bcbb9b 100644 --- a/sysutils/py-clustershell/Makefile +++ b/sysutils/py-clustershell/Makefile @@ -1,20 +1,53 @@ PORTNAME= clustershell -PORTVERSION= 1.9.3 +DISTVERSION= 1.10.1 CATEGORIES= sysutils python MASTER_SITES= PYPI PKGNAMEPREFIX= ${PYTHON_PKGNAMEPREFIX} -DISTNAME= ClusterShell-${PORTVERSION} MAINTAINER= rikka.goering@outlook.de COMMENT= Python framework for efficient cluster administration (NodeSet, clush) -WWW= https://cea-hpc.github.io/clustershell/ +WWW= https://clustershell.github.io/clustershell/ LICENSE= LGPL21 LICENSE_FILE= ${WRKSRC}/COPYING.LGPLv2.1 -USES= pytest python -USE_PYTHON= autoplist distutils +RUN_DEPENDS= ${PYTHON_PKGNAMEPREFIX}pyyaml>0:devel/py-pyyaml@${PY_FLAVOR} +TEST_DEPENDS= bash:shells/bash + +USES= python +USE_PYTHON= autoplist concurrent distutils NO_ARCH= yes +do-test: + @cd ${TEST_WRKSRC} && \ + ${SETENV} ${TEST_ENV} \ + PYTHONPATH="${WRKSRC}/lib" \ + ${PYTHON_CMD} -m unittest -v \ + tests.CLICompletionTest \ + tests.CLIConfigTest \ + tests.CLINodesetTest \ + tests.CLIOptionParserTest \ + tests.DefaultsTest \ + tests.EngineErrorTest \ + tests.MisusageTest \ + tests.MsgTreeTest \ + tests.NodeSetGroupTest \ + tests.NodeSetTest \ + tests.RangeSetNDTest \ + tests.RangeSetTest \ + tests.StreamWorkerTest \ + tests.TaskEventTest \ + tests.TaskLocalTest \ + tests.TaskMsgTreeTest \ + tests.TaskPortTest \ + tests.TaskRLimitsTest \ + tests.TaskThreadJoinTest \ + tests.TaskThreadSuspendTest \ + tests.TaskTimeoutTest \ + tests.TreeGatewayTest \ + tests.TreeTaskTest \ + tests.TreeTopologyTest \ + tests.WorkerExecTest + .include diff --git a/sysutils/py-clustershell/distinfo b/sysutils/py-clustershell/distinfo index 80dfdc4a0097..71ef9d246e79 100644 --- a/sysutils/py-clustershell/distinfo +++ b/sysutils/py-clustershell/distinfo @@ -1,3 +1,3 @@ -TIMESTAMP = 1762196845 -SHA256 (ClusterShell-1.9.3.tar.gz) = e284c0e6b3fe0a0cd6be67df71dfbf6aa321188973db683a057f5637552fbc8c -SIZE (ClusterShell-1.9.3.tar.gz) = 417135 +TIMESTAMP = 1784997854 +SHA256 (clustershell-1.10.1.tar.gz) = b27af0baa9d1b10ac6a2729700b03e5a2796c838cbb6aaf1902698226ab9ea8a +SIZE (clustershell-1.10.1.tar.gz) = 440348 diff --git a/sysutils/py-clustershell/files/patch-bash__completion.d_cluset b/sysutils/py-clustershell/files/patch-bash__completion.d_cluset new file mode 100644 index 000000000000..2d9fbcc9c596 --- /dev/null +++ b/sysutils/py-clustershell/files/patch-bash__completion.d_cluset @@ -0,0 +1,11 @@ +--- bash_completion.d/cluset.orig 2026-07-16 22:06:42 UTC ++++ bash_completion.d/cluset +@@ -70,7 +70,7 @@ _cluset() + return;; + esac + # get all options from help text... not 100% accurate but good enough. +- [ -n "$options" ] || options="$(cluset --help | grep -oP -- '(?<=[ \t])(-[a-z]|--[^= \t]*)')" ++ [ -n "$options" ] || options="$(cluset --help | grep -Eo -- '[[:space:]](-[a-z]|--[^=[:space:]]*)' | sed 's/^[[:space:]]//')" + + # append space for everything that doesn't end in `:` (likely a groupsource) + mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur" | sed -e 's/[^:]$/& /') diff --git a/sysutils/py-clustershell/files/patch-bash__completion.d_clush b/sysutils/py-clustershell/files/patch-bash__completion.d_clush new file mode 100644 index 000000000000..7c4ee80792c8 --- /dev/null +++ b/sysutils/py-clustershell/files/patch-bash__completion.d_clush @@ -0,0 +1,11 @@ +--- bash_completion.d/clush.orig 2026-07-16 22:06:42 UTC ++++ bash_completion.d/clush +@@ -140,7 +140,7 @@ _clush() + case "$cur" in + -*) + # starts with dash - get all options from help text... +- options="$(clush --help | grep -oP -- '(?<=[ \t])(-[a-z]|--[^= \t]*)')" ++ options=$(clush --help | grep -Eo -- '[[:space:]](-[a-z]|--[^=[:space:]]*)' | sed 's/^[[:space:]]//') + ;; + *) + # otherwise complete command or file if appropriate and stop here diff --git a/sysutils/py-clustershell/files/patch-tests_CLICompletionTest.py b/sysutils/py-clustershell/files/patch-tests_CLICompletionTest.py new file mode 100644 index 000000000000..d9b574408bd1 --- /dev/null +++ b/sysutils/py-clustershell/files/patch-tests_CLICompletionTest.py @@ -0,0 +1,11 @@ +--- tests/CLICompletionTest.py.orig 2026-08-07 00:59:44 UTC ++++ tests/CLICompletionTest.py +@@ -53,7 +53,7 @@ COMPREPLY=() + ''' + + # Stub cluset printing fixed nodes/groups; shadows the real one via PATH. +-_CLUSET_STUB = r'''#!/bin/bash ++_CLUSET_STUB = r'''#!/bin/sh + case "$1" in + --completion) + printf '%s\n' @stubsrc: @gpu diff --git a/sysutils/py-clustershell/files/patch-tests_CLIConfigTest.py b/sysutils/py-clustershell/files/patch-tests_CLIConfigTest.py new file mode 100644 index 000000000000..e84e5ea3b172 --- /dev/null +++ b/sysutils/py-clustershell/files/patch-tests_CLIConfigTest.py @@ -0,0 +1,54 @@ +--- tests/CLIConfigTest.py.orig 2026-08-07 01:15:15 UTC ++++ tests/CLIConfigTest.py +@@ -9,6 +9,7 @@ import unittest + import tempfile + from textwrap import dedent + import unittest ++from unittest.mock import patch + + from .TLib import * + +@@ -239,7 +240,6 @@ class CLIClushConfigTest(unittest.TestCase): + + def testClushConfigSetRlimitValueError(self): + """test CLI.Config.ClushConfig (setrlimit ValueError)""" +- soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE) + f = tempfile.NamedTemporaryFile(prefix='testclushconfig') + f.write(dedent(""" + [Main] +@@ -248,8 +248,7 @@ class CLIClushConfigTest(unittest.TestCase): + command_timeout: 0 + history_size: 100 + color: auto +- # Use wrong fd_max value to generate ValueError +- fd_max: -1 ++ fd_max: 200 + verbosity: 1""").encode()) + f.flush() + parser = OptionParser("dummy") +@@ -261,17 +260,21 @@ class CLIClushConfigTest(unittest.TestCase): + f.close() + display = Display(options, config) + +- class TestException(Exception): pass ++ class TestException(Exception): ++ pass + + def mock_vprint_err(level, message): + if message.startswith('Warning: Failed to set max open files'): + raise TestException() + + display.vprint_err = mock_vprint_err +- self.assertRaises(TestException, set_fdlimit, config.fd_max, display) + +- soft2, _ = resource.getrlimit(resource.RLIMIT_NOFILE) +- self.assertEqual(soft, soft2) ++ with patch('ClusterShell.CLI.Clush.resource.getrlimit', ++ return_value=(100, 1000)), \ ++ patch('ClusterShell.CLI.Clush.resource.setrlimit', ++ side_effect=ValueError): ++ self.assertRaises(TestException, ++ set_fdlimit, config.fd_max, display) + + def testClushConfigDefaultWithOptions(self): + """test CLI.Config.ClushConfig (default with options)"""