Skip to content
47 changes: 20 additions & 27 deletions src/usethis/_integrations/file/toml/io_.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import contextlib
import copy
import re
from typing import TYPE_CHECKING, Any
Expand Down Expand Up @@ -176,7 +175,7 @@ def set_value(
except ValidationError:
if not exists_ok:
# The configuration is already present, which is not allowed.
_raise_already_set(keys)
_raise_already_set(shared_keys)
else:
_set_value_in_existing(
toml_document=toml_document,
Expand Down Expand Up @@ -232,19 +231,18 @@ def __delitem__(self, keys: Sequence[Key]) -> None:
for key in list(d.keys()):
del d[key]
else:
with contextlib.suppress(KeyError):
# N.B. There was a strange behaviour (bug?) in tomlkit where deleting a
# key has two separate lines:
# self._value.remove(key) # noqa: ERA001
# dict.__delitem__(self, key) # noqa: ERA001
# but it's not clear why there's this duplicate and it causes a KeyError
# in some cases.
if isinstance(d, OutOfOrderTableProxy):
# N.B. this case isn't expected based on the type annotations but
# it is possible in practice.
d.__delitem__(keys[-1])
else:
d.remove(keys[-1])
# N.B. There was a strange behaviour (bug?) in tomlkit where deleting a
# key has two separate lines:
# self._value.remove(key) # noqa: ERA001
# dict.__delitem__(self, key) # noqa: ERA001
# but it's not clear why there's this duplicate and it causes a KeyError
# in some cases.
if isinstance(d, OutOfOrderTableProxy):
# N.B. this case isn't expected based on the type annotations but
# it is possible in practice.
d.__delitem__(keys[-1])
else:
d.remove(keys[-1])

# Cleanup: any empty sections should be removed.
for idx in reversed(range(1, len(keys))):
Expand Down Expand Up @@ -309,6 +307,11 @@ def extend_list(self, *, keys: Sequence[Key], values: list[Any]) -> None:
self.commit(toml_document)

def remove_from_list(self, *, keys: Sequence[Key], values: Collection[Any]) -> None:
"""Remove values from a list in the TOML file.

If the list is not present, or the key at which is is found does not correspond
to a list, pass silently.
"""
if not keys:
msg = "At least one ID key must be provided."
raise ValueError(msg)
Expand All @@ -327,24 +330,14 @@ def remove_from_list(self, *, keys: Sequence[Key], values: Collection[Any]) -> N
TypeAdapter(dict).validate_python(p_parent)
assert isinstance(p_parent, dict)
p = p_parent[keys[-1]]
except ValidationError:
msg = (
f"Configuration value '{print_keys(keys[:-1])}' is not a valid mapping in "
f"the TOML file '{self.name}', and does not contain the key '{keys[-1]}'."
)
raise TOMLValueMissingError(msg) from None
except KeyError:
except (KeyError, ValidationError):
# The configuration is not present - do not modify
return

try:
TypeAdapter(list).validate_python(p)
except ValidationError:
msg = (
f"Configuration value '{print_keys(keys)}' is not a valid list in "
f"the TOML file '{self.name}'."
)
raise TOMLValueInvalidError(msg) from None
return
assert isinstance(p, list)

new_values = [value for value in p if value not in values]
Expand Down
34 changes: 31 additions & 3 deletions src/usethis/_integrations/file/yaml/errors.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
from __future__ import annotations

from usethis.errors import UsethisError
from usethis.errors import FileDecodeError, UsethisError


class InvalidYAMLError(UsethisError):
"""Raised when an invalid YAML is encountered."""
class YAMLError(UsethisError):
"""Base class for YAML-related errors."""


class YAMLValueAlreadySetError(YAMLError):
"""Raised when a value is unexpectedly already set in the YAML file."""


class UnexpectedYAMLValueError(YAMLError):
"""Raised when an unexpected value is encountered in the YAML file."""


class YAMLValueMissingError(KeyError, YAMLError):
"""Raised when a value is unexpectedly missing from the YAML file."""


class YAMLNotFoundError(FileNotFoundError, YAMLError):
"""Raised when a YAML file is unexpectedly not found."""


class YAMLDecodeError(FileDecodeError, YAMLError):
"""Raised when a YAML file is unexpectedly not decodable."""


class UnexpectedYAMLOpenError(YAMLError):
"""Raised when the YAML file is unexpectedly opened."""


class UnexpectedYAMLIOError(YAMLError):
"""Raised when an unexpected attempt is made to read or write the YAML file."""
Loading
Loading