Skip to content

Commit 37bfdf4

Browse files
dycwweb-flow
andauthored
Have run(uv_pip_list...) return only stdout (#1866)
Co-authored-by: github-actions-bot <noreply@github.com>
1 parent 226aa4c commit 37bfdf4

6 files changed

Lines changed: 67 additions & 20 deletions

File tree

.bumpversion.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.bumpversion]
22
allow_dirty = true
3-
current_version = "0.184.2"
3+
current_version = "0.184.3"
44

55
[[tool.bumpversion.files]]
66
filename = "pyproject.toml"

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"coloredlogs>=15.0.1",
2222
"coverage-conditional-plugin>=0.9.0",
2323
"dycw-pytest-only>=2.1.1",
24-
"dycw-utilities[test]>=0.184.1",
24+
"dycw-utilities[test]>=0.184.2",
2525
"pyright>=1.1.408",
2626
"pytest-cov>=7.0.0",
2727
"pytest-timeout>=2.4.0",
@@ -116,7 +116,7 @@
116116
name = "dycw-utilities"
117117
readme = "README.md"
118118
requires-python = ">= 3.12"
119-
version = "0.184.2"
119+
version = "0.184.3"
120120

121121
[project.entry-points.pytest11]
122122
pytest-randomly = "utilities.pytest_plugins.pytest_randomly"

src/tests/test_subprocess.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@
3535
_rsync_many_prepare,
3636
_ssh_is_strict_checking_error,
3737
_uv_pip_list_assemble_output,
38-
_UvPipListBaseError,
39-
_UvPipListOutdatedError,
38+
_uv_pip_list_loads,
39+
_UvPipListBaseVersionError,
40+
_UvPipListJsonError,
41+
_UvPipListOutdatedVersionError,
4042
_UvPipListOutput,
4143
append_text,
4244
apt_install_cmd,
@@ -1776,6 +1778,24 @@ def test_main(self) -> None:
17761778
assert is_sequence_of(result, _UvPipListOutput)
17771779

17781780

1781+
class TestUvPipListLoadsOutput:
1782+
def test_main(self) -> None:
1783+
text = strip_and_dedent("""
1784+
[{"name":"name","version":"0.0.1"}]
1785+
""")
1786+
result = _uv_pip_list_loads(text)
1787+
expected = [{"name": "name", "version": "0.0.1"}]
1788+
assert result == expected
1789+
1790+
def test_error(self) -> None:
1791+
text = strip_and_dedent("""
1792+
[{"name":"name","version":"0.0.1"}]
1793+
# warning: The package `name` requires `dep>=1.2.3`, but `1.2.2` is installed
1794+
""")
1795+
with raises(_UvPipListJsonError, match=r"Unable to parse JSON; got '.*'"):
1796+
_ = _uv_pip_list_loads(text)
1797+
1798+
17791799
class TestUvPipListAssembleOutput:
17801800
def test_main(self) -> None:
17811801
dict_ = {"name": "name", "version": "0.0.1"}
@@ -1819,13 +1839,17 @@ def test_outdated(self) -> None:
18191839
def test_error_base(self) -> None:
18201840
dict_ = {"name": "name", "version": "invalid"}
18211841
outdated = []
1822-
with raises(_UvPipListBaseError, match=r"Unable to parse version; got .*"):
1842+
with raises(
1843+
_UvPipListBaseVersionError, match=r"Unable to parse version; got .*"
1844+
):
18231845
_ = _uv_pip_list_assemble_output(dict_, outdated)
18241846

18251847
def test_error_outdated(self) -> None:
18261848
dict_ = {"name": "name", "version": "0.0.1"}
18271849
outdated = [{"name": "name", "latest_version": "invalid"}]
1828-
with raises(_UvPipListOutdatedError, match=r"Unable to parse version; got .*"):
1850+
with raises(
1851+
_UvPipListOutdatedVersionError, match=r"Unable to parse version; got .*"
1852+
):
18291853
_ = _uv_pip_list_assemble_output(dict_, outdated)
18301854

18311855

src/utilities/__init__.py

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/utilities/subprocess.py

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from dataclasses import dataclass
77
from io import StringIO
88
from itertools import chain, repeat
9+
from json import JSONDecodeError
910
from pathlib import Path
1011
from re import MULTILINE, escape, search
1112
from shlex import join
@@ -1784,22 +1785,29 @@ def uv_pip_list(
17841785
for outdated in [False, True]
17851786
]
17861787
text_base, text_outdated = [
1787-
run(*cmds, return_=True) for cmds in [cmds_base, cmds_outdated]
1788-
]
1789-
dicts_base, dicts_outdated = [
1790-
json.loads(text) for text in [text_base, text_outdated]
1788+
run(*cmds, return_stdout=True) for cmds in [cmds_base, cmds_outdated]
17911789
]
1790+
dicts_base, dicts_outdated = list(
1791+
map(_uv_pip_list_loads, [text_base, text_outdated])
1792+
)
17921793
return [_uv_pip_list_assemble_output(d, dicts_outdated) for d in dicts_base]
17931794

17941795

1796+
def _uv_pip_list_loads(text: str, /) -> list[StrMapping]:
1797+
try:
1798+
return json.loads(text)
1799+
except JSONDecodeError:
1800+
raise _UvPipListJsonError(text=text) from None
1801+
1802+
17951803
def _uv_pip_list_assemble_output(
17961804
dict_: StrMapping, outdated: Iterable[StrMapping], /
17971805
) -> _UvPipListOutput:
17981806
name = dict_["name"]
17991807
try:
18001808
version = parse_version_2_or_3(dict_["version"])
18011809
except ParseVersion2Or3Error:
1802-
raise _UvPipListBaseError(data=dict_) from None
1810+
raise _UvPipListBaseVersionError(data=dict_) from None
18031811
try:
18041812
location = Path(dict_["editable_project_location"])
18051813
except KeyError:
@@ -1812,7 +1820,7 @@ def _uv_pip_list_assemble_output(
18121820
try:
18131821
latest_version = parse_version_2_or_3(outdated_i["latest_version"])
18141822
except ParseVersion2Or3Error:
1815-
raise _UvPipListOutdatedError(data=outdated_i) from None
1823+
raise _UvPipListOutdatedVersionError(data=outdated_i) from None
18161824
latest_filetype = outdated_i["latest_filetype"]
18171825
return _UvPipListOutput(
18181826
name=dict_["name"],
@@ -1824,7 +1832,20 @@ def _uv_pip_list_assemble_output(
18241832

18251833

18261834
@dataclass(kw_only=True, slots=True)
1827-
class UvPipListError(Exception):
1835+
class UvPipListError(Exception): ...
1836+
1837+
1838+
@dataclass(kw_only=True, slots=True)
1839+
class _UvPipListJsonError(UvPipListError):
1840+
text: str
1841+
1842+
@override
1843+
def __str__(self) -> str:
1844+
return f"Unable to parse JSON; got {self.text!r}"
1845+
1846+
1847+
@dataclass(kw_only=True, slots=True)
1848+
class _UvPipListBaseVersionError(UvPipListError):
18281849
data: StrMapping
18291850

18301851
@override
@@ -1833,10 +1854,12 @@ def __str__(self) -> str:
18331854

18341855

18351856
@dataclass(kw_only=True, slots=True)
1836-
class _UvPipListBaseError(UvPipListError): ...
1837-
1857+
class _UvPipListOutdatedVersionError(UvPipListError):
1858+
data: StrMapping
18381859

1839-
class _UvPipListOutdatedError(UvPipListError): ...
1860+
@override
1861+
def __str__(self) -> str:
1862+
return f"Unable to parse version; got {self.data}"
18401863

18411864

18421865
def uv_pip_list_cmd(

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)