Skip to content

Commit c0e7f42

Browse files
author
Jean-Louis Fuchs
authored
Merge pull request #62 from adfinis/jlf/feat-import-CalledProcessError-for-users-of-run_command
feat: import `CalledProcessError` for users of `run_command` (no merge, nl)
2 parents dc6f461 + 8e79657 commit c0e7f42

File tree

8 files changed

+41
-88
lines changed

8 files changed

+41
-88
lines changed

pyaptly/command.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
"""Commands with dependencies."""
22
import collections
33
import logging
4-
import subprocess
54

65
from frozendict import frozendict
76

8-
from . import state_reader
7+
from . import state_reader, util
98

109
lg = logging.getLogger(__name__)
1110

@@ -94,9 +93,18 @@ def execute(self):
9493

9594
if not Command.pretend_mode:
9695
lg.debug("Running command: %s", " ".join(self.cmd))
97-
self._finished = bool(subprocess.check_call(self.cmd))
96+
# It seems the intention of the original code is to a redo a command if it
97+
# scheduled multiple times and fails the first time. But there is also:
98+
99+
# `commands = set([c for c in commands if c is not None])`
100+
101+
# which prevents that. I guess the feature is currently not needed.
102+
# So I decided to change that. For now we fail hard if a `Command` fails.
103+
# I guess we will see in production what happens.
104+
util.run_command(self.cmd, check=True)
98105
else:
99106
lg.info("Pretending to run command: %s", " ".join(self.cmd))
107+
self._finished = True
100108

101109
return self._finished
102110

pyaptly/conftest.py

+1-12
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
import freezegun
1010
import pytest
11-
import tomli
12-
import yaml
1311

1412
from pyaptly import main, state_reader, util
1513

@@ -114,16 +112,7 @@ def test_mirror_create(environment, config, caplog):
114112
...
115113
```
116114
"""
117-
config_file = test_base / request.param
118-
with config_file.open("rb") as f:
119-
config = tomli.load(f)
120-
# TODO: remove yaml conversion
121-
try:
122-
with tempfile.NamedTemporaryFile(mode="w", encoding="UTF-8", delete=False) as f:
123-
yaml.dump(config, f)
124-
yield f.name
125-
finally:
126-
Path(f.name).unlink()
115+
yield str(test_base / request.param)
127116

128117

129118
@pytest.fixture()

pyaptly/main.py

+3-28
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
"""Aptly mirror/snapshot managment automation."""
22
import argparse
3-
import codecs
43
import logging
5-
import subprocess
64
import sys
75

8-
import yaml
6+
import tomli
97

108
from . import command, mirror, publish, repo, snapshot, state_reader
119

@@ -15,29 +13,6 @@
1513
lg = logging.getLogger(__name__)
1614

1715

18-
# TODO remove
19-
def call_output(args, input_=None):
20-
"""Call command and return output.
21-
22-
:param args: Command to execute
23-
:type args: list
24-
:param input_: Input to command
25-
:type input_: bytes
26-
"""
27-
p = subprocess.Popen(
28-
args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
29-
)
30-
output, err = p.communicate(input_)
31-
if p.returncode != 0:
32-
raise subprocess.CalledProcessError(
33-
p.returncode,
34-
args,
35-
output,
36-
err,
37-
)
38-
return (output.decode("UTF-8"), err.decode("UTF-8"))
39-
40-
4116
def main(argv=None):
4217
"""Define parsers and executes commands.
4318
@@ -108,8 +83,8 @@ def main(argv=None):
10883
_logging_setup = True # noqa
10984
lg.debug("Args: %s", vars(args))
11085

111-
with codecs.open(args.config, "r", encoding="UTF-8") as cfgfile:
112-
cfg = yaml.load(cfgfile, Loader=yaml.FullLoader)
86+
with open(args.config, "rb") as f:
87+
cfg = tomli.load(f)
11388
state_reader.state.read()
11489

11590
# run function for selected subparser

pyaptly/mirror.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Create and update mirrors in aptly."""
22
import logging
3-
import subprocess
43

54
from . import state_reader, util
65

@@ -47,16 +46,16 @@ def add_gpg_keys(mirror_config):
4746
key,
4847
]
4948
lg.debug("Adding gpg key with call: %s", key_command)
50-
subprocess.check_call(key_command)
51-
except subprocess.CalledProcessError: # pragma: no cover
49+
util.run_command(key_command, check=True)
50+
except util.CalledProcessError: # pragma: no cover
5251
url = keys_urls[key]
5352
if url:
5453
key_shell = (
5554
"curl %s | "
5655
"gpg --no-default-keyring --keyring trustedkeys.gpg "
5756
"--import"
5857
) % url
59-
subprocess.check_call(["bash", "-c", key_shell])
58+
util.run_command(["bash", "-c", key_shell], check=True)
6059
else:
6160
raise
6261
state_reader.state.read_gpg()
@@ -129,7 +128,7 @@ def cmd_mirror_create(cfg, mirror_name, mirror_config):
129128
aptly_cmd.extend(util.unit_or_list_to_list(mirror_config["components"]))
130129

131130
lg.debug("Running command: %s", " ".join(aptly_cmd))
132-
subprocess.check_call(aptly_cmd)
131+
util.run_command(aptly_cmd, check=True)
133132

134133

135134
def cmd_mirror_update(cfg, mirror_name, mirror_config):
@@ -151,4 +150,4 @@ def cmd_mirror_update(cfg, mirror_name, mirror_config):
151150

152151
aptly_cmd.append(mirror_name)
153152
lg.debug("Running command: %s", " ".join(aptly_cmd))
154-
subprocess.check_call(aptly_cmd)
153+
util.run_command(aptly_cmd, check=True)

pyaptly/state_reader.py

+13-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import logging
33
import re
44

5-
from . import main
5+
from . import util
66

77
lg = logging.getLogger(__name__)
88

@@ -70,9 +70,8 @@ def read_gpg(self):
7070
"--list-keys",
7171
"--with-colons",
7272
]
73-
data, _ = main.call_output(cmd)
74-
lg.debug("GPG returned: %s", data)
75-
for line in data.split("\n"):
73+
result = util.run_command(cmd, stdout=util.PIPE, check=True)
74+
for line in result.stdout.split("\n"):
7675
field = line.split(":")
7776
if field[0] in ("pub", "sub"):
7877
key = field[4]
@@ -87,9 +86,9 @@ def read_publish_map(self):
8786
re_snap = re.compile(r"\s+[\w\d-]+\:\s([\w\d-]+)\s\[snapshot\]")
8887
for publish in self.publishes:
8988
prefix, dist = publish.split(" ")
90-
data, _ = main.call_output(["aptly", "publish", "show", dist, prefix])
91-
92-
sources = self._extract_sources(data)
89+
cmd = ["aptly", "publish", "show", dist, prefix]
90+
result = util.run_command(cmd, stdout=util.PIPE, check=True)
91+
sources = self._extract_sources(result.stdout)
9392
matches = [re_snap.match(source) for source in sources]
9493
snapshots = [match.group(1) for match in matches if match]
9594
self.publish_map[publish] = set(snapshots)
@@ -105,8 +104,10 @@ def read_snapshot_map(self):
105104
# match example: test-snapshot [snapshot]
106105
re_snap = re.compile(r"\s+([\w\d-]+)\s\[snapshot\]")
107106
for snapshot_outer in self.snapshots:
108-
data, _ = main.call_output(["aptly", "snapshot", "show", snapshot_outer])
109-
sources = self._extract_sources(data)
107+
cmd = ["aptly", "snapshot", "show", snapshot_outer]
108+
109+
result = util.run_command(cmd, stdout=util.PIPE, check=True)
110+
sources = self._extract_sources(result.stdout)
110111
matches = [re_snap.match(source) for source in sources]
111112
snapshots = [match.group(1) for match in matches if match]
112113
self.snapshot_map[snapshot_outer] = set(snapshots)
@@ -141,9 +142,9 @@ def read_aptly_list(self, type_, list_):
141142
:param list_: Read into this list
142143
:param list_: list
143144
"""
144-
data, _ = main.call_output(["aptly", type_, "list", "-raw"])
145-
lg.debug("Aptly returned %s: %s", type_, data)
146-
for line in data.split("\n"):
145+
cmd = ["aptly", type_, "list", "-raw"]
146+
result = util.run_command(cmd, stdout=util.PIPE, check=True)
147+
for line in result.stdout.split("\n"):
147148
clean_line = line.strip()
148149
if clean_line:
149150
list_.add(clean_line)

pyaptly/tests/test_dateround.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os.path
55

66
import pytest
7-
import yaml
7+
import tomli
88
from hypothesis import given
99
from hypothesis.strategies import datetimes, integers, times
1010

@@ -173,8 +173,8 @@ def test_daily_examples():
173173
@pytest.mark.parametrize("config", ["publish-previous.toml"], indirect=True)
174174
def test_snapshot_spec_to_name(config, test_path, freeze):
175175
"""Test the complete functionality (snapshot_spec_to_name)."""
176-
with (test_path / config).open("r") as f:
177-
tyml = yaml.load(f, Loader=yaml.FullLoader)
176+
with open(config, "rb") as f:
177+
tyml = tomli.load(f)
178178
snaps = tyml["snapshot"]["superfake-%T"]["merge"]
179179

180180
rounded1 = snapshot.snapshot_spec_to_name(tyml, snaps[0])

pyaptly/tests/test_helpers.py

+1-20
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,5 @@
11
"""Testing testing helper functions."""
2-
import subprocess
3-
4-
from .. import command, main, state_reader
5-
6-
7-
def test_call_output_error():
8-
"""Test if call_output raises errors correctly."""
9-
# TDOD remove
10-
args = [
11-
"bash",
12-
"-c",
13-
"exit 42",
14-
]
15-
error = False
16-
try:
17-
main.call_output(args)
18-
except subprocess.CalledProcessError as e:
19-
assert e.returncode == 42
20-
error = True
21-
assert error
2+
from .. import command, state_reader
223

234

245
def test_command_dependency_fail():

pyaptly/util.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import logging
44
import subprocess
55
from pathlib import Path
6-
from subprocess import DEVNULL, PIPE # noqa: F401
7-
from typing import Optional, Union
6+
from subprocess import DEVNULL, PIPE, CalledProcessError # noqa: F401
7+
from typing import Optional, Sequence
88

99
_DEFAULT_KEYSERVER: str = "hkps://keys.openpgp.org"
1010
_PYTEST_KEYSERVER: Optional[str] = None
@@ -51,7 +51,7 @@ def is_debug_mode():
5151
return _DEBUG or _PYTEST_DEBUG
5252

5353

54-
def run_command(cmd_args: list[Union[str, Path]], *, decode: bool = True, **kwargs):
54+
def run_command(cmd_args: Sequence[str | Path], *, decode: bool = True, **kwargs):
5555
"""Instrumented subprocess.run for easier debugging.
5656
5757
By default this run command will add `encoding="UTF-8"` to kwargs. Disable
@@ -83,7 +83,7 @@ def run_command(cmd_args: list[Union[str, Path]], *, decode: bool = True, **kwar
8383
return result
8484

8585

86-
def indent_out(output: Union[bytes, str]) -> str:
86+
def indent_out(output: bytes | str) -> str:
8787
"""Indent command output for nicer logging-messages.
8888
8989
It will convert bytes to strings if need or display the result as bytes if

0 commit comments

Comments
 (0)