Skip to content

Commit 96d5a66

Browse files
committed
Cleanup and fix typing issues with latest mypy
Mainly facts - the changes to the `FactBase` types are breaking but not any functionality. For typing fact classes to pass they must implement the `command` & `requires_command` attributes as class methods, not plain strings. This change hugely simplifies the typing logic here (method or string?) which the latest (1.11) mypy raised issues for. The underlying logic remains unchanged and string attributes will continue to work for the foreseeable future.
1 parent a6debb9 commit 96d5a66

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+349
-255
lines changed

pyinfra/api/facts.py

Lines changed: 7 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,7 @@
1414
import re
1515
from inspect import getcallargs
1616
from socket import error as socket_error, timeout as timeout_error
17-
from typing import (
18-
TYPE_CHECKING,
19-
Any,
20-
Callable,
21-
Generic,
22-
Iterable,
23-
Optional,
24-
Type,
25-
TypeVar,
26-
Union,
27-
cast,
28-
)
17+
from typing import TYPE_CHECKING, Any, Callable, Generic, Iterable, Optional, Type, TypeVar, cast
2918

3019
import click
3120
import gevent
@@ -38,7 +27,6 @@
3827
get_kwargs_str,
3928
log_error_or_warning,
4029
log_host_command_error,
41-
make_hash,
4230
print_host_combined_output,
4331
)
4432
from pyinfra.connectors.util import CommandOutput
@@ -66,11 +54,12 @@ class FactBase(Generic[T]):
6654

6755
abstract: bool = True
6856

69-
shell_executable: Optional[str] = None
57+
shell_executable: str | None = None
7058

71-
requires_command: Optional[str] = None
59+
command: Callable[..., str | StringCommand]
7260

73-
command: Union[str, Callable]
61+
def requires_command(self, *args, **kwargs) -> str | None:
62+
return None
7463

7564
def __init_subclass__(cls) -> None:
7665
super().__init_subclass__()
@@ -113,8 +102,7 @@ def __init_subclass__(cls) -> None:
113102
module_name = cls.__module__.replace("pyinfra.facts.", "")
114103
cls.name = f"{module_name}.{cls.__name__}"
115104

116-
@staticmethod
117-
def process_data(data):
105+
def process_data(self, data):
118106
return data
119107

120108

@@ -130,30 +118,6 @@ def _make_command(command_attribute, host_args):
130118
return command_attribute
131119

132120

133-
def _get_executor_kwargs(
134-
state: "State",
135-
host: "Host",
136-
override_kwargs: Optional[dict[str, Any]] = None,
137-
override_kwarg_keys: Optional[list[str]] = None,
138-
):
139-
if override_kwargs is None:
140-
override_kwargs = {}
141-
if override_kwarg_keys is None:
142-
override_kwarg_keys = []
143-
144-
# Use the current operation global kwargs, or generate defaults
145-
global_kwargs = host.current_op_global_arguments
146-
if not global_kwargs:
147-
global_kwargs, _ = pop_global_arguments({}, state, host)
148-
149-
# Apply any current op kwargs that *weren't* found in the overrides
150-
override_kwargs.update(
151-
{key: value for key, value in global_kwargs.items() if key not in override_kwarg_keys},
152-
)
153-
154-
return {key: value for key, value in override_kwargs.items() if key in CONNECTOR_ARGUMENT_KEYS}
155-
156-
157121
def _handle_fact_kwargs(state, host, cls, args, kwargs):
158122
args = args or []
159123
kwargs = kwargs or {}
@@ -296,7 +260,7 @@ def _get_fact(
296260
log_host_command_error(
297261
host,
298262
e,
299-
timeout=global_kwargs["_timeout"],
263+
timeout=global_kwargs.get("_timeout"),
300264
)
301265

302266
stdout_lines, stderr_lines = output.stdout_lines, output.stderr_lines
@@ -346,13 +310,6 @@ def _get_fact(
346310
return data
347311

348312

349-
def _get_fact_hash(state: "State", host: "Host", cls, args, kwargs):
350-
if issubclass(cls, ShortFactBase):
351-
cls = cls.fact
352-
fact_kwargs, executor_kwargs = _handle_fact_kwargs(state, host, cls, args, kwargs)
353-
return make_hash((cls, fact_kwargs, executor_kwargs))
354-
355-
356313
def get_host_fact(
357314
state: "State",
358315
host: "Host",

pyinfra/api/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ def log_error_or_warning(
241241
)
242242

243243

244-
def log_host_command_error(host: "Host", e: Exception, timeout: int = 0) -> None:
244+
def log_host_command_error(host: "Host", e: Exception, timeout: int | None = 0) -> None:
245245
if isinstance(e, timeout_error):
246246
logger.error(
247247
"{0}{1}".format(

pyinfra/connectors/sshuserclient/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class SSHClient(ParamikoClient):
124124
original idea at http://bitprophet.org/blog/2012/11/05/gateway-solutions/.
125125
"""
126126

127-
def connect(
127+
def connect( # type: ignore[override]
128128
self,
129129
hostname,
130130
_pyinfra_ssh_forward_agent=None,

pyinfra/facts/apk.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ class ApkPackages(FactBase):
1818
}
1919
"""
2020

21-
command = "apk list --installed"
22-
requires_command = "apk"
21+
def command(self) -> str:
22+
return "apk list --installed"
23+
24+
def requires_command(self) -> str:
25+
return "apk"
2326

2427
default = dict
2528

pyinfra/facts/apt.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,14 @@ class AptSources(FactBase):
5252
]
5353
"""
5454

55-
command = make_cat_files_command(
56-
"/etc/apt/sources.list",
57-
"/etc/apt/sources.list.d/*.list",
58-
)
59-
requires_command = "apt" # if apt installed, above should exist
55+
def command(self) -> str:
56+
return make_cat_files_command(
57+
"/etc/apt/sources.list",
58+
"/etc/apt/sources.list.d/*.list",
59+
)
60+
61+
def requires_command(self) -> str:
62+
return "apt" # if apt installed, above should exist
6063

6164
default = list
6265

@@ -86,5 +89,8 @@ class AptKeys(GpgFactBase):
8689
"""
8790

8891
# This requires both apt-key *and* apt-key itself requires gpg
89-
command = "! command -v gpg || apt-key list --with-colons"
90-
requires_command = "apt-key"
92+
def command(self) -> str:
93+
return "! command -v gpg || apt-key list --with-colons"
94+
95+
def requires_command(self) -> str:
96+
return "apt-key"

pyinfra/facts/brew.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,22 @@ class BrewVersion(FactBase):
3737
3838
"""
3939

40-
command = "brew --version"
41-
requires_command = "brew"
40+
def command(self) -> str:
41+
return "brew --version"
42+
43+
def requires_command(self) -> str:
44+
return "brew"
4245

4346
@staticmethod
4447
def default():
4548
return [0, 0, 0]
4649

4750
def process(self, output):
48-
m = VERSION_MATCHER.match(output[0])
51+
out = list(output)[0]
52+
m = VERSION_MATCHER.match(out)
4953
if m is not None:
5054
return [int(m.group(key)) for key in ["major", "minor", "patch"]]
51-
logger.warning("could not parse version string from brew: %s", output[0])
55+
logger.warning("could not parse version string from brew: %s", out)
5256
return self.default()
5357

5458

@@ -63,8 +67,11 @@ class BrewPackages(FactBase):
6367
}
6468
"""
6569

66-
command = "brew list --versions"
67-
requires_command = "brew"
70+
def command(self) -> str:
71+
return "brew list --versions"
72+
73+
def requires_command(self) -> str:
74+
return "brew"
6875

6976
default = dict
7077

@@ -83,20 +90,26 @@ class BrewCasks(BrewPackages):
8390
}
8491
"""
8592

86-
command = (
87-
r'if brew --version | grep -q -e "Homebrew\ +(1\.|2\.[0-5]).*" 1>/dev/null;'
88-
r"then brew cask list --versions; else brew list --cask --versions; fi"
89-
)
90-
requires_command = "brew"
93+
def command(self) -> str:
94+
return (
95+
r'if brew --version | grep -q -e "Homebrew\ +(1\.|2\.[0-5]).*" 1>/dev/null;'
96+
r"then brew cask list --versions; else brew list --cask --versions; fi"
97+
)
98+
99+
def requires_command(self) -> str:
100+
return "brew"
91101

92102

93103
class BrewTaps(FactBase):
94104
"""
95105
Returns a list of brew taps.
96106
"""
97107

98-
command = "brew tap"
99-
requires_command = "brew"
108+
def command(self) -> str:
109+
return "brew tap"
110+
111+
def requires_command(self) -> str:
112+
return "brew"
100113

101114
default = list
102115

pyinfra/facts/bsdinit.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ class RcdStatus(InitdStatus):
99
BSD init scripts are well behaved and as such their output can be trusted.
1010
"""
1111

12-
command = """
13-
for SERVICE in `find /etc/rc.d /usr/local/etc/rc.d -type f`; do
14-
$SERVICE status 2> /dev/null || $SERVICE check 2> /dev/null
15-
echo "`basename $SERVICE`=$?"
16-
done
17-
"""
12+
def command(self) -> str:
13+
return """
14+
for SERVICE in `find /etc/rc.d /usr/local/etc/rc.d -type f`; do
15+
$SERVICE status 2> /dev/null || $SERVICE check 2> /dev/null
16+
echo "`basename $SERVICE`=$?"
17+
done
18+
"""
1819

1920
default = dict

pyinfra/facts/cargo.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ class CargoPackages(FactBase):
2222

2323
default = dict
2424

25-
requires_command = "cargo"
26-
27-
def command(self):
25+
def command(self) -> str:
2826
return "cargo install --list"
2927

28+
def requires_command(self) -> str:
29+
return "cargo"
30+
3031
def process(self, output):
3132
return parse_packages(CARGO_REGEX, output)

pyinfra/facts/choco.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ class ChocoPackages(FactBase):
1818
}
1919
"""
2020

21-
command = "choco list"
21+
def command(self) -> str:
22+
return "choco list"
23+
2224
shell_executable = "ps"
2325

2426
default = dict
@@ -32,8 +34,8 @@ class ChocoVersion(FactBase):
3234
Returns the choco (Chocolatey) version.
3335
"""
3436

35-
command = "choco --version"
37+
def command(self) -> str:
38+
return "choco --version"
3639

37-
@staticmethod
38-
def process(output):
40+
def process(self, output):
3941
return "".join(output).replace("\n", "")

pyinfra/facts/deb.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ class DebArch(FactBase):
1616
Returns the architecture string used in apt repository sources, eg ``amd64``.
1717
"""
1818

19-
command = "dpkg --print-architecture"
20-
requires_command = "dpkg"
19+
def command(self) -> str:
20+
return "dpkg --print-architecture"
21+
22+
def requires_command(self) -> str:
23+
return "dpkg"
2124

2225

2326
class DebPackages(FactBase):
@@ -31,8 +34,11 @@ class DebPackages(FactBase):
3134
}
3235
"""
3336

34-
command = "dpkg -l"
35-
requires_command = "dpkg"
37+
def command(self) -> str:
38+
return "dpkg -l"
39+
40+
def requires_command(self) -> str:
41+
return "dpkg"
3642

3743
default = dict
3844

@@ -55,7 +61,8 @@ class DebPackage(FactBase):
5561
"version": r"^Version:\s+({0})$".format(DEB_PACKAGE_VERSION_REGEX),
5662
}
5763

58-
requires_command = "dpkg"
64+
def requires_command(self, package) -> str:
65+
return "dpkg"
5966

6067
def command(self, package):
6168
return "! test -e {0} && (dpkg -s {0} 2>/dev/null || true) || dpkg -I {0}".format(

0 commit comments

Comments
 (0)