Skip to content

Commit 50f6027

Browse files
committed
Type testinfra.backend
1 parent d68ef1f commit 50f6027

14 files changed

+120
-84
lines changed

testinfra/backend/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ def get_backend_class(connection: str) -> type["testinfra.backend.base.BaseBacke
4444
return getattr(importlib.import_module(module), name) # type: ignore[no-any-return]
4545

4646

47-
def parse_hostspec(hostspec):
48-
kw = {}
47+
def parse_hostspec(hostspec: str) -> tuple[str, dict[str, Any]]:
48+
kw: dict[str, Any] = {}
4949
if hostspec is not None and "://" in hostspec:
5050
url = urllib.parse.urlparse(hostspec)
5151
kw["connection"] = url.scheme

testinfra/backend/ansible.py

+16-13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import logging
1414
import pprint
15+
from typing import Any, Optional
1516

1617
from testinfra.backend import base
1718
from testinfra.utils.ansible_runner import AnsibleRunner
@@ -25,13 +26,13 @@ class AnsibleBackend(base.BaseBackend):
2526

2627
def __init__(
2728
self,
28-
host,
29-
ansible_inventory=None,
30-
ssh_config=None,
31-
ssh_identity_file=None,
32-
force_ansible=False,
33-
*args,
34-
**kwargs,
29+
host: str,
30+
ansible_inventory: Optional[str] = None,
31+
ssh_config: Optional[str] = None,
32+
ssh_identity_file: Optional[str] = None,
33+
force_ansible: bool = False,
34+
*args: Any,
35+
**kwargs: Any,
3536
):
3637
self.host = host
3738
self.ansible_inventory = ansible_inventory
@@ -41,10 +42,10 @@ def __init__(
4142
super().__init__(host, *args, **kwargs)
4243

4344
@property
44-
def ansible_runner(self):
45+
def ansible_runner(self) -> AnsibleRunner:
4546
return AnsibleRunner.get_runner(self.ansible_inventory)
4647

47-
def run(self, command, *args, **kwargs):
48+
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:
4849
command = self.get_command(command, *args)
4950
if not self.force_ansible:
5051
host = self.ansible_runner.get_host(
@@ -64,8 +65,10 @@ def run(self, command, *args, **kwargs):
6465
stderr=out["stderr"],
6566
)
6667

67-
def run_ansible(self, module_name, module_args=None, **kwargs):
68-
def get_encoding():
68+
def run_ansible(
69+
self, module_name: str, module_args: Optional[str] = None, **kwargs: Any
70+
) -> Any:
71+
def get_encoding() -> str:
6972
return self.encoding
7073

7174
result = self.ansible_runner.run_module(
@@ -80,10 +83,10 @@ def get_encoding():
8083
)
8184
return result
8285

83-
def get_variables(self):
86+
def get_variables(self) -> dict[str, Any]:
8487
return self.ansible_runner.get_variables(self.host)
8588

8689
@classmethod
87-
def get_hosts(cls, host, **kwargs):
90+
def get_hosts(cls, host: str, **kwargs: Any) -> list[str]:
8891
inventory = kwargs.get("ansible_inventory")
8992
return AnsibleRunner.get_runner(inventory).get_hosts(host or "all")

testinfra/backend/chroot.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212
import os.path
13+
from typing import Any
1314

1415
from testinfra.backend import base
1516

@@ -23,11 +24,11 @@ class ChrootBackend(base.BaseBackend):
2324

2425
NAME = "chroot"
2526

26-
def __init__(self, name, *args, **kwargs):
27+
def __init__(self, name: str, *args: Any, **kwargs: Any):
2728
self.name = name
2829
super().__init__(self.name, *args, **kwargs)
2930

30-
def run(self, command, *args, **kwargs):
31+
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:
3132
if not os.path.exists(self.name) and os.path.isdir(self.name):
3233
raise RuntimeError(
3334
"chroot path {} not found or not a directory".format(self.name)

testinfra/backend/docker.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212

13+
from typing import Any
14+
1315
from testinfra.backend import base
1416

1517

1618
class DockerBackend(base.BaseBackend):
1719
NAME = "docker"
1820

19-
def __init__(self, name, *args, **kwargs):
21+
def __init__(self, name: str, *args: Any, **kwargs: Any):
2022
self.name, self.user = self.parse_containerspec(name)
2123
super().__init__(self.name, *args, **kwargs)
2224

23-
def run(self, command, *args, **kwargs):
25+
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:
2426
cmd = self.get_command(command, *args)
2527
if self.user is not None:
2628
out = self.run_local(

testinfra/backend/kubectl.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,23 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212

13+
from typing import Any
14+
1315
from testinfra.backend import base
1416

1517

1618
class KubectlBackend(base.BaseBackend):
1719
NAME = "kubectl"
1820

19-
def __init__(self, name, *args, **kwargs):
21+
def __init__(self, name: str, *args: Any, **kwargs: Any):
2022
self.name = name
2123
self.container = kwargs.get("container")
2224
self.namespace = kwargs.get("namespace")
2325
self.kubeconfig = kwargs.get("kubeconfig")
2426
self.context = kwargs.get("context")
2527
super().__init__(self.name, *args, **kwargs)
2628

27-
def run(self, command, *args, **kwargs):
29+
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:
2830
cmd = self.get_command(command, *args)
2931
# `kubectl exec` does not support specifying the user to run as.
3032
# See https://github.com/kubernetes/kubernetes/issues/30656

testinfra/backend/local.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,23 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212

13+
from typing import Any
14+
1315
from testinfra.backend import base
1416

1517

1618
class LocalBackend(base.BaseBackend):
1719
NAME = "local"
1820

19-
def __init__(self, *args, **kwargs):
21+
def __init__(self, *args: Any, **kwargs: Any):
2022
super().__init__("local", **kwargs)
2123

22-
def get_pytest_id(self):
24+
def get_pytest_id(self) -> str:
2325
return "local"
2426

2527
@classmethod
26-
def get_hosts(cls, host, **kwargs):
28+
def get_hosts(cls, host: str, **kwargs: Any) -> list[str]:
2729
return [host]
2830

29-
def run(self, command, *args, **kwargs):
31+
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:
3032
return self.run_local(self.get_command(command, *args))

testinfra/backend/lxc.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212

13+
from typing import Any
14+
1315
from testinfra.backend import base
1416

1517

1618
class LxcBackend(base.BaseBackend):
1719
NAME = "lxc"
1820

19-
def __init__(self, name, *args, **kwargs):
21+
def __init__(self, name: str, *args: Any, **kwargs: Any):
2022
self.name = name
2123
super().__init__(self.name, *args, **kwargs)
2224

23-
def run(self, command, *args, **kwargs):
25+
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:
2426
cmd = self.get_command(command, *args)
2527
out = self.run_local(
2628
"lxc exec %s --mode=non-interactive -- " "/bin/sh -c %s", self.name, cmd

testinfra/backend/openshift.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,22 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212

13+
from typing import Any
14+
1315
from testinfra.backend import base
1416

1517

1618
class OpenShiftBackend(base.BaseBackend):
1719
NAME = "openshift"
1820

19-
def __init__(self, name, *args, **kwargs):
21+
def __init__(self, name: str, *args: Any, **kwargs: Any):
2022
self.name = name
2123
self.container = kwargs.get("container")
2224
self.namespace = kwargs.get("namespace")
2325
self.kubeconfig = kwargs.get("kubeconfig")
2426
super().__init__(self.name, *args, **kwargs)
2527

26-
def run(self, command, *args, **kwargs):
28+
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:
2729
cmd = self.get_command(command, *args)
2830
# `oc exec` does not support specifying the user to run as.
2931
# See https://github.com/kubernetes/kubernetes/issues/30656

testinfra/backend/paramiko.py

+32-18
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
)
2424

2525
import functools
26+
from typing import Any, Optional
2627

28+
import paramiko.pkey
2729
import paramiko.ssh_exception
2830

2931
from testinfra.backend import base
@@ -32,7 +34,9 @@
3234
class IgnorePolicy(paramiko.MissingHostKeyPolicy):
3335
"""Policy for ignoring missing host key."""
3436

35-
def missing_host_key(self, client, hostname, key):
37+
def missing_host_key(
38+
self, client: paramiko.SSHClient, hostname: str, key: paramiko.pkey.PKey
39+
) -> None:
3640
pass
3741

3842

@@ -41,12 +45,12 @@ class ParamikoBackend(base.BaseBackend):
4145

4246
def __init__(
4347
self,
44-
hostspec,
45-
ssh_config=None,
46-
ssh_identity_file=None,
47-
timeout=10,
48-
*args,
49-
**kwargs,
48+
hostspec: str,
49+
ssh_config: Optional[str] = None,
50+
ssh_identity_file: Optional[str] = None,
51+
timeout: int = 10,
52+
*args: Any,
53+
**kwargs: Any,
5054
):
5155
self.host = self.parse_hostspec(hostspec)
5256
self.ssh_config = ssh_config
@@ -55,7 +59,13 @@ def __init__(
5559
self.timeout = int(timeout)
5660
super().__init__(self.host.name, *args, **kwargs)
5761

58-
def _load_ssh_config(self, client, cfg, ssh_config, ssh_config_dir="~/.ssh"):
62+
def _load_ssh_config(
63+
self,
64+
client: paramiko.SSHClient,
65+
cfg: dict[str, Any],
66+
ssh_config: paramiko.SSHConfig,
67+
ssh_config_dir: str = "~/.ssh",
68+
) -> None:
5969
for key, value in ssh_config.lookup(self.host.name).items():
6070
if key == "hostname":
6171
cfg[key] = value
@@ -85,7 +95,7 @@ def _load_ssh_config(self, client, cfg, ssh_config, ssh_config_dir="~/.ssh"):
8595
self._load_ssh_config(client, cfg, new_ssh_config, ssh_config_dir)
8696

8797
@functools.cached_property
88-
def client(self):
98+
def client(self) -> paramiko.SSHClient:
8999
client = paramiko.SSHClient()
90100
client.set_missing_host_key_policy(paramiko.WarningPolicy())
91101
cfg = {
@@ -118,11 +128,13 @@ def client(self):
118128

119129
if self.ssh_identity_file:
120130
cfg["key_filename"] = self.ssh_identity_file
121-
client.connect(**cfg)
131+
client.connect(**cfg) # type: ignore[arg-type]
122132
return client
123133

124-
def _exec_command(self, command):
125-
chan = self.client.get_transport().open_session()
134+
def _exec_command(self, command: bytes) -> tuple[int, bytes, bytes]:
135+
transport = self.client.get_transport()
136+
assert transport is not None
137+
chan = transport.open_session()
126138
if self.get_pty:
127139
chan.get_pty()
128140
chan.exec_command(command)
@@ -131,17 +143,19 @@ def _exec_command(self, command):
131143
stderr = b"".join(chan.makefile_stderr("rb"))
132144
return rc, stdout, stderr
133145

134-
def run(self, command, *args, **kwargs):
146+
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:
135147
command = self.get_command(command, *args)
136-
command = self.encode(command)
148+
cmd = self.encode(command)
137149
try:
138-
rc, stdout, stderr = self._exec_command(command)
150+
rc, stdout, stderr = self._exec_command(cmd)
139151
except paramiko.ssh_exception.SSHException:
140-
if not self.client.get_transport().is_active():
152+
transport = self.client.get_transport()
153+
assert transport is not None
154+
if not transport.is_active():
141155
# try to reinit connection (once)
142156
del self.client
143-
rc, stdout, stderr = self._exec_command(command)
157+
rc, stdout, stderr = self._exec_command(cmd)
144158
else:
145159
raise
146160

147-
return self.result(rc, command, stdout, stderr)
161+
return self.result(rc, cmd, stdout, stderr)

testinfra/backend/podman.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212

13+
from typing import Any
14+
1315
from testinfra.backend import base
1416

1517

1618
class PodmanBackend(base.BaseBackend):
1719
NAME = "podman"
1820

19-
def __init__(self, name, *args, **kwargs):
21+
def __init__(self, name: str, *args: Any, **kwargs: Any):
2022
self.name, self.user = self.parse_containerspec(name)
2123
super().__init__(self.name, *args, **kwargs)
2224

23-
def run(self, command, *args, **kwargs):
25+
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:
2426
cmd = self.get_command(command, *args)
2527
if self.user is not None:
2628
out = self.run_local(

0 commit comments

Comments
 (0)