Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.bumpversion]
allow_dirty = true
current_version = "0.184.0"
current_version = "0.184.1"

[[tool.bumpversion.files]]
filename = "pyproject.toml"
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"coloredlogs>=15.0.1",
"coverage-conditional-plugin>=0.9.0",
"dycw-pytest-only>=2.1.1",
"dycw-utilities[test]>=0.183.5",
"dycw-utilities[test]>=0.184.1",
"pyright>=1.1.408",
"pytest-cov>=7.0.0",
"pytest-timeout>=2.4.0",
Expand Down Expand Up @@ -116,7 +116,7 @@
name = "dycw-utilities"
readme = "README.md"
requires-python = ">= 3.12"
version = "0.184.0"
version = "0.184.1"

[project.entry-points.pytest11]
pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
Expand Down
142 changes: 141 additions & 1 deletion src/tests/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
from typing import TYPE_CHECKING
from uuid import uuid4

from pytest import LogCaptureFixture, mark, param, raises
from pytest import LogCaptureFixture, approx, mark, param, raises
from pytest_lazy_fixtures import lf

from utilities.constants import (
EFFECTIVE_GROUP_NAME,
EFFECTIVE_USER_NAME,
HOME,
MINUTE,
PWD,
SECOND,
)
from utilities.iterables import one
Expand All @@ -33,6 +34,10 @@
RsyncCmdSourcesNotFoundError,
_rsync_many_prepare,
_ssh_is_strict_checking_error,
_uv_pip_list_assemble_output,
_UvPipListBaseError,
_UvPipListOutdatedError,
_UvPipListOutput,
append_text,
apt_install_cmd,
apt_remove_cmd,
Expand Down Expand Up @@ -93,6 +98,8 @@
useradd_cmd,
uv_index_cmd,
uv_native_tls_cmd,
uv_pip_list,
uv_pip_list_cmd,
uv_run_cmd,
uv_tool_install_cmd,
uv_tool_run_cmd,
Expand All @@ -102,6 +109,8 @@
)
from utilities.tempfile import TemporaryDirectory, TemporaryFile
from utilities.text import strip_and_dedent, unique_str
from utilities.typing import is_sequence_of
from utilities.version import Version3

if TYPE_CHECKING:
from pytest import CaptureFixture
Expand Down Expand Up @@ -1759,6 +1768,137 @@ def test_multiple(self) -> None:
assert result == expected


class TestUvPipList:
@skipif_ci
def test_main(self) -> None:
result = uv_pip_list()
assert len(result) == approx(159, rel=0.1)
assert is_sequence_of(result, _UvPipListOutput)


class TestUvPipListAssembleOutput:
def test_main(self) -> None:
dict_ = {"name": "name", "version": "0.0.1"}
outdated = []
result = _uv_pip_list_assemble_output(dict_, outdated)
expected = _UvPipListOutput(name="name", version=Version3(0, 0, 1))
assert result == expected

def test_editable(self) -> None:
dict_ = {
"name": "name",
"version": "0.0.1",
"editable_project_location": str(PWD),
}
outdated = []
result = _uv_pip_list_assemble_output(dict_, outdated)
expected = _UvPipListOutput(
name="name", version=Version3(0, 0, 1), editable_project_location=PWD
)
assert result == expected

def test_outdated(self) -> None:
dict_ = {"name": "name", "version": "0.0.1"}
outdated = [
{
"name": "name",
"version": "0.0.1",
"latest_version": "0.0.2",
"latest_filetype": "wheel",
}
]
result = _uv_pip_list_assemble_output(dict_, outdated)
expected = _UvPipListOutput(
name="name",
version=Version3(0, 0, 1),
latest_version=Version3(0, 0, 2),
latest_filetype="wheel",
)
assert result == expected

def test_error_base(self) -> None:
dict_ = {"name": "name", "version": "invalid"}
outdated = []
with raises(_UvPipListBaseError, match=r"Unable to parse version; got .*"):
_ = _uv_pip_list_assemble_output(dict_, outdated)

def test_error_outdated(self) -> None:
dict_ = {"name": "name", "version": "0.0.1"}
outdated = [{"name": "name", "latest_version": "invalid"}]
with raises(_UvPipListOutdatedError, match=r"Unable to parse version; got .*"):
_ = _uv_pip_list_assemble_output(dict_, outdated)


class TestUvPipListCmd:
def test_main(self) -> None:
result = uv_pip_list_cmd()
expected = [
"uv",
"pip",
"list",
"--format",
"columns",
"--strict",
"--managed-python",
]
assert result == expected

def test_editable(self) -> None:
result = uv_pip_list_cmd(editable=True)
expected = [
"uv",
"pip",
"list",
"--editable",
"--format",
"columns",
"--strict",
"--managed-python",
]
assert result == expected

def test_exclude_editable(self) -> None:
result = uv_pip_list_cmd(exclude_editable=True)
expected = [
"uv",
"pip",
"list",
"--exclude-editable",
"--format",
"columns",
"--strict",
"--managed-python",
]
assert result == expected

def test_format(self) -> None:
result = uv_pip_list_cmd(format_="json")
expected = [
"uv",
"pip",
"list",
"--format",
"json",
"--strict",
"--managed-python",
]
assert result == expected

def test_outdated(self) -> None:
result = uv_pip_list_cmd(outdated=True)
expected = [
"uv",
"pip",
"list",
"--format",
"columns",
"--outdated",
"--strict",
"--managed-python",
]
assert result == expected


class TestUvNativeTLSCmd:
def test_none(self) -> None:
result = uv_native_tls_cmd()
Expand Down
113 changes: 110 additions & 3 deletions src/tests/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,132 @@

from hypothesis import given
from hypothesis.strategies import booleans, integers, none
from pytest import raises
from pytest import mark, param, raises

from utilities.hypothesis import sentinels, text_ascii, version3s
from utilities.hypothesis import sentinels, text_ascii, version2s, version3s
from utilities.version import (
ParseVersion2Or3Error,
Version2,
Version2Or3,
Version3,
_Version2EmptySuffixError,
_Version2NegativeMajorVersionError,
_Version2NegativeMinorVersionError,
_Version2ParseError,
_Version2ZeroError,
_Version3EmptySuffixError,
_Version3NegativeMajorVersionError,
_Version3NegativeMinorVersionError,
_Version3NegativePatchVersionError,
_Version3ParseError,
_Version3ZeroError,
parse_version_2_or_3,
to_version3,
)

if TYPE_CHECKING:
from utilities.constants import Sentinel


class TestVersion:
class TestParseVersion2Or3:
@mark.parametrize(
("text", "expected"),
[param("0.1", Version2(0, 1)), param("0.0.1", Version3(0, 0, 1))],
)
def test_main(self, *, text: str, expected: Version2Or3) -> None:
result = parse_version_2_or_3(text)
assert result == expected

def test_error(self) -> None:
with raises(
ParseVersion2Or3Error,
match=r"Unable to parse Version2 or Version3; got '.*'",
):
_ = parse_version_2_or_3("invalid")


class TestVersion2:
@given(version=version2s())
def test_hashable(self, *, version: Version2) -> None:
_ = hash(version)

@given(version1=version2s(), version2=version2s())
def test_orderable(self, *, version1: Version2, version2: Version2) -> None:
assert (version1 <= version2) or (version1 >= version2)

@given(version=version2s())
def test_parse(self, *, version: Version2) -> None:
parsed = Version2.parse(str(version))
assert parsed == version

@given(version=version2s(suffix=booleans()))
def test_repr(self, *, version: Version2) -> None:
result = repr(version)
assert search(r"^\d+.\d+", result)

@given(version=version2s())
def test_bump_major(self, *, version: Version2) -> None:
bumped = version.bump_major()
assert version < bumped
assert bumped.major == version.major + 1
assert bumped.minor == 0
assert bumped.suffix is None

@given(version=version2s())
def test_bump_minor(self, *, version: Version2) -> None:
bumped = version.bump_minor()
assert version < bumped
assert bumped.major == version.major
assert bumped.minor == version.minor + 1
assert bumped.suffix is None

@given(version=version2s(), suffix=text_ascii(min_size=1) | none())
def test_with_suffix(self, *, version: Version2, suffix: str | None) -> None:
new = version.with_suffix(suffix=suffix)
assert new.major == version.major
assert new.minor == version.minor
assert new.suffix == suffix

@given(version=version2s())
def test_error_order(self, *, version: Version2) -> None:
with raises(TypeError):
_ = version <= None

def test_error_zero(self) -> None:
with raises(
_Version2ZeroError, match=r"Version must be greater than zero; got 0\.0"
):
_ = Version2(0, 0)

@given(major=integers(max_value=-1))
def test_error_negative_major_version(self, *, major: int) -> None:
with raises(
_Version2NegativeMajorVersionError,
match=r"Major version must be non-negative; got .*",
):
_ = Version2(major=major)

@given(minor=integers(max_value=-1))
def test_error_negative_minor_version(self, *, minor: int) -> None:
with raises(
_Version2NegativeMinorVersionError,
match=r"Minor version must be non-negative; got .*",
):
_ = Version2(minor=minor)

def test_error_empty_suffix(self) -> None:
with raises(
_Version2EmptySuffixError, match=r"Suffix must be non-empty; got .*"
):
_ = Version2(suffix="")

@mark.parametrize("text", [param("invalid"), param("0.0.1")])
def test_error_parse(self, *, text: str) -> None:
with raises(_Version2ParseError, match=r"Unable to parse version; got '.*'"):
_ = Version2.parse(text)


class TestVersion3:
@given(version=version3s())
def test_hashable(self, *, version: Version3) -> None:
_ = hash(version)
Expand Down
2 changes: 1 addition & 1 deletion src/utilities/__init__.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/utilities/libcst.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def __str__(self) -> str:
##


@dataclass(kw_only=True, slots=True)
@dataclass(order=True, unsafe_hash=True, kw_only=True, slots=True)
class _ParseImportOutput:
module: str
name: str | None = None
Expand Down
Loading
Loading