Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-pyinfra for openSUSE:Factory 
checked in at 2026-03-30 18:32:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyinfra (Old)
 and      /work/SRC/openSUSE:Factory/.python-pyinfra.new.1999 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pyinfra"

Mon Mar 30 18:32:07 2026 rev:3 rq:1343557 version:3.7

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyinfra/python-pyinfra.changes    
2026-03-13 21:22:03.138330925 +0100
+++ /work/SRC/openSUSE:Factory/.python-pyinfra.new.1999/python-pyinfra.changes  
2026-03-30 18:35:35.409821634 +0200
@@ -1,0 +2,32 @@
+Mon Mar 30 04:14:29 UTC 2026 - Steve Kowalik <[email protected]>
+
+- Update to 3.7:
+  * api.facts: fix requires_command shell operator precedence with compound
+    commands
+  * operations.docker: add support for env files
+  * operations.docker: add support for dns
+  * operations.files.sync: support symlinks
+  * facts.server.Port: add UDP support and multi-platform backends
+  * facts.hardware.Memory: use LANG=C while calling vmstat
+  * facts.hardware: add CpuInfo fact
+  * facts.server.Sysctl: fix non-zero exit code causing empty result
+  * facts.runit.RunitManaged: handle missing service directory gracefully
+  * facts.hardware.Memory: fix FreeBSD support by summing page categories
+  * facts.server.SecurityLimits: handle missing limits.conf
+  * facts.server.Port: fix ss command for Alpine/BusyBox compatibility
+  * facts.iptables: add requires_command to all iptables facts
+  * facts.opkg: add requires_command and stop hardcoding /bin/opkg
+  * facts.choco: add requires_command to ChocoPackages and ChocoVersion
+  * connectors: @podmanssh connector for remote Podman container operations
+  * connectors.ssh: exclude _chdir from global arguments used when removing
+    temporary file
+  * connectors.ssh: don't include password in SSH exception authentication
+    data
+  * cli: use gevent for the progress spinner
+  * cli: add --ssh-password-prompt flag for interactive SSH password input
+  * global arguments: _su_password argument for su password authentication
+  * operations+facts: server.timezone and server.Timezone fact
+- Add patch support-paramiko-4.patch:
+  * Support paramiko 4 changes.
+
+-------------------------------------------------------------------

Old:
----
  pyinfra-3.6.1.tar.gz

New:
----
  pyinfra-3.7.tar.gz
  support-paramiko-4.patch

----------(New B)----------
  New:  * operations+facts: server.timezone and server.Timezone fact
- Add patch support-paramiko-4.patch:
  * Support paramiko 4 changes.
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pyinfra.spec ++++++
--- /var/tmp/diff_new_pack.NIDnHw/_old  2026-03-30 18:35:36.169853409 +0200
+++ /var/tmp/diff_new_pack.NIDnHw/_new  2026-03-30 18:35:36.169853409 +0200
@@ -24,16 +24,17 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-pyinfra
-Version:        3.6.1
+Version:        3.7
 Release:        0
 Summary:        Infrastructure automation, provisioning and deployment
 License:        MIT
 URL:            https://pyinfra.com
 Source:         
https://files.pythonhosted.org/packages/source/p/pyinfra/pyinfra-%{version}.tar.gz
 Source1:        
https://raw.githubusercontent.com/pyinfra-dev/testgen/ad6673/testgen/__init__.py#/testgen.py
+# PATCH-FIX-UPSTREAM gh#pyinfra-dev/pyinfra#1525
+Patch0:         support-paramiko-4.patch
 BuildRequires:  %{python_module hatchling}
 BuildRequires:  %{python_module pip}
-BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires:       python-click > 2

++++++ pyinfra-3.6.1.tar.gz -> pyinfra-3.7.tar.gz ++++++
++++ 3810 lines of diff (skipped)

++++++ support-paramiko-4.patch ++++++
>From 227490a3582c667586cb1930a0ba82900c147c2b Mon Sep 17 00:00:00 2001
From: Whyme Lyu <[email protected]>
Date: Mon, 12 Jan 2026 14:47:51 +0800
Subject: [PATCH] dependencies/paramiko: support paramiko v4, remove DSS key
 support

These keys are so out of date that this does not warrant a breaking
change version bump.
---
 pyproject.toml                     |   3 +-
 src/pyinfra/connectors/ssh_util.py |   5 +-
 tests/test_connectors/test_ssh.py  | 120 +----------------------------
 uv.lock                            |   4 +-
 4 files changed, 8 insertions(+), 124 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index 3e8aab3b9..17aa94a03 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,7 +13,7 @@ license-files = ["LICENSE.md"]
 requires-python = ">=3.10,<4.0"
 dependencies = [
     "gevent>=1.5",
-    "paramiko>=2.7,<4", # 2.7 (2019) adds OpenSSH key format + Match SSH config
+    "paramiko>=2.7,<5", # 2.7 (2019) adds OpenSSH key format + Match SSH config
     "click>2",
     "jinja2>3,<4",
     "python-dateutil>2,<3",
@@ -22,6 +22,7 @@ dependencies = [
     "packaging>=16.1",
     "pydantic>=2.11,<3",
     "typing-extensions; python_version < '3.11'", # Backport of typing for 
Unpack (added 3.11)
+    "types-paramiko>=2.7,<5",
 ]
 classifiers = [
     "Development Status :: 5 - Production/Stable",
diff --git a/src/pyinfra/connectors/ssh_util.py 
b/src/pyinfra/connectors/ssh_util.py
index a70685880..5d4bf7997 100644
--- a/src/pyinfra/connectors/ssh_util.py
+++ b/src/pyinfra/connectors/ssh_util.py
@@ -3,7 +3,6 @@
 from typing import TYPE_CHECKING, Type, Union
 
 from paramiko import (
-    DSSKey,
     ECDSAKey,
     Ed25519Key,
     PasswordRequiredException,
@@ -28,9 +27,9 @@ def raise_connect_error(host: "Host", message, data):
 def _load_private_key_file(filename: str, key_filename: str, key_password: 
str):
     exception: Union[PyinfraError, SSHException] = PyinfraError("Invalid key: 
{0}".format(filename))
 
-    key_cls: Union[Type[RSAKey], Type[DSSKey], Type[ECDSAKey], 
Type[Ed25519Key]]
+    key_cls: Union[Type[RSAKey], Type[ECDSAKey], Type[Ed25519Key]]
 
-    for key_cls in (RSAKey, DSSKey, ECDSAKey, Ed25519Key):
+    for key_cls in (RSAKey, ECDSAKey, Ed25519Key):
         try:
             return key_cls.from_private_key_file(
                 filename=filename,
diff --git a/tests/test_connectors/test_ssh.py 
b/tests/test_connectors/test_ssh.py
index 0aad2c6cb..86391eb63 100644
--- a/tests/test_connectors/test_ssh.py
+++ b/tests/test_connectors/test_ssh.py
@@ -313,10 +313,6 @@ def test_connect_with_rsa_ssh_key_wrong_password(self):
 
         with (
             mock.patch("pyinfra.connectors.ssh_util.path.isfile", lambda 
*args, **kwargs: True),
-            mock.patch(
-                "pyinfra.connectors.ssh_util.DSSKey.from_private_key_file",
-                fake_fail_from_private_key_file,
-            ),
             mock.patch(
                 "pyinfra.connectors.ssh_util.ECDSAKey.from_private_key_file",
                 fake_fail_from_private_key_file,
@@ -345,121 +341,7 @@ def fake_key_open_fail(*args, **kwargs):
 
             assert e.exception.args[0] == "Invalid private key file: testkey"
 
-        assert fake_fail_from_private_key_file.call_count == 3
-
-    def test_connect_with_dss_ssh_key(self):
-        state = State(make_inventory(hosts=(("somehost", {"ssh_key": 
"testkey"}),)), Config())
-
-        with (
-            mock.patch("pyinfra.connectors.ssh_util.path.isfile", lambda 
*args, **kwargs: True),
-            mock.patch(
-                "pyinfra.connectors.ssh_util.RSAKey.from_private_key_file",
-            ) as fake_rsa_key_open,
-            mock.patch(
-                "pyinfra.connectors.ssh_util.DSSKey.from_private_key_file",
-            ) as fake_key_open,
-        ):  # noqa
-            fake_rsa_key_open.side_effect = 
make_raise_exception_function(SSHException)
-
-            fake_key = mock.MagicMock()
-            fake_key_open.return_value = fake_key
-
-            connect_all(state)
-
-            # Check the key was created properly
-            fake_key_open.assert_called_with(filename="testkey")
-
-            # And check the Paramiko SSH call was correct
-            self.fake_connect_mock.assert_called_with(
-                "somehost",
-                allow_agent=False,
-                look_for_keys=False,
-                pkey=fake_key,
-                timeout=10,
-                username="vagrant",
-                _pyinfra_ssh_forward_agent=False,
-                _pyinfra_ssh_config_file=None,
-                _pyinfra_ssh_known_hosts_file=None,
-                _pyinfra_ssh_strict_host_key_checking="accept-new",
-                _pyinfra_ssh_paramiko_connect_kwargs=None,
-            )
-
-        # Check that loading the same key again is cached in the state
-        second_state = State(
-            make_inventory(hosts=(("somehost", {"ssh_key": "testkey"}),)),
-            Config(),
-        )
-        second_state.private_keys = state.private_keys
-
-        connect_all(second_state)
-
-    def test_connect_with_dss_ssh_key_password(self):
-        state = State(
-            make_inventory(
-                hosts=(
-                    (
-                        "somehost",
-                        {"ssh_key": "testkey", "ssh_key_password": "testpass"},
-                    ),
-                ),
-            ),
-            Config(),
-        )
-
-        with (
-            mock.patch("pyinfra.connectors.ssh_util.path.isfile", lambda 
*args, **kwargs: True),
-            mock.patch(
-                "pyinfra.connectors.ssh_util.RSAKey.from_private_key_file",
-            ) as fake_rsa_key_open,
-            mock.patch(
-                "pyinfra.connectors.ssh_util.DSSKey.from_private_key_file",
-            ) as fake_dss_key_open,
-        ):  # noqa
-
-            def fake_rsa_key_open_fail(*args, **kwargs):
-                if "password" not in kwargs:
-                    raise PasswordRequiredException
-                raise SSHException
-
-            fake_rsa_key_open.side_effect = fake_rsa_key_open_fail
-
-            fake_dss_key = mock.MagicMock()
-
-            def fake_dss_key_func(*args, **kwargs):
-                if "password" not in kwargs:
-                    raise PasswordRequiredException
-                return fake_dss_key
-
-            fake_dss_key_open.side_effect = fake_dss_key_func
-
-            connect_all(state)
-
-            # Check the key was created properly
-            fake_dss_key_open.assert_called_with(filename="testkey", 
password="testpass")
-
-            # And check the Paramiko SSH call was correct
-            self.fake_connect_mock.assert_called_with(
-                "somehost",
-                allow_agent=False,
-                look_for_keys=False,
-                pkey=fake_dss_key,
-                timeout=10,
-                username="vagrant",
-                _pyinfra_ssh_forward_agent=False,
-                _pyinfra_ssh_config_file=None,
-                _pyinfra_ssh_known_hosts_file=None,
-                _pyinfra_ssh_strict_host_key_checking="accept-new",
-                _pyinfra_ssh_paramiko_connect_kwargs=None,
-            )
-
-        # Check that loading the same key again is cached in the state
-        second_state = State(
-            make_inventory(hosts=(("somehost", {"ssh_key": "testkey"}),)),
-            Config(),
-        )
-        second_state.private_keys = state.private_keys
-
-        connect_all(second_state)
+        assert fake_fail_from_private_key_file.call_count == 2
 
     def test_connect_with_missing_ssh_key(self):
         state = State(make_inventory(hosts=(("somehost", {"ssh_key": 
"testkey"}),)), Config())
diff --git a/uv.lock b/uv.lock
index 81e0372ef..14f8eb9a9 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1262,6 +1262,7 @@ dependencies = [
     { name = "pydantic" },
     { name = "python-dateutil" },
     { name = "typeguard" },
+    { name = "types-paramiko" },
     { name = "typing-extensions", marker = "python_full_version < '3.11'" },
 ]
 
@@ -1318,10 +1319,11 @@ requires-dist = [
     { name = "gevent", specifier = ">=1.5" },
     { name = "jinja2", specifier = ">3,<4" },
     { name = "packaging", specifier = ">=16.1" },
-    { name = "paramiko", specifier = ">=2.7,<4" },
+    { name = "paramiko", specifier = ">=2.7,<5" },
     { name = "pydantic", specifier = ">=2.11,<3" },
     { name = "python-dateutil", specifier = ">2,<3" },
     { name = "typeguard", specifier = ">=4,<5" },
+    { name = "types-paramiko", specifier = ">=2.7,<5" },
     { name = "typing-extensions", marker = "python_full_version < '3.11'" },
 ]
 

Reply via email to