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
6 changes: 4 additions & 2 deletions .importlinter
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ layers =
_integrations
_backend
_file
_io | _subprocess | _console | _python
_subprocess | _console | _python
_config
_types | errors
_pipeweld
Expand Down Expand Up @@ -119,7 +119,9 @@ containers =
layers =
pyproject_toml | setup_cfg
ini | toml | yaml
dir | merge
manager
print_ | dir | merge
types_
exhaustive = true

[importlinter:contract:ui_interface]
Expand Down
8 changes: 3 additions & 5 deletions src/usethis/_file/ini/io_.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
UnexpectedINIIOError,
UnexpectedINIOpenError,
)
from usethis._io import (
from usethis._file.manager import (
KeyValueFileManager,
UnexpectedFileIOError,
UnexpectedFileOpenError,
print_keys,
)
from usethis._file.print_ import print_keys

if TYPE_CHECKING:
from collections.abc import Iterable, Sequence
Expand All @@ -34,9 +34,7 @@

from typing_extensions import Self

from usethis._io import (
Key,
)
from usethis._file.types_ import Key


class INIFileManager(KeyValueFileManager, metaclass=ABCMeta):
Expand Down
43 changes: 7 additions & 36 deletions src/usethis/_io.py → src/usethis/_file/manager.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from __future__ import annotations

import re
from abc import ABCMeta, abstractmethod
from typing import TYPE_CHECKING, Any, Generic, TypeAlias, TypeVar
from typing import TYPE_CHECKING, Any, Generic, TypeVar

from typing_extensions import assert_never, override
from typing_extensions import override

from usethis._config import usethis_config
from usethis.errors import UsethisError
Expand All @@ -17,6 +16,8 @@

from typing_extensions import Self

from usethis._file.types_ import Key


DocumentT = TypeVar("DocumentT")

Expand All @@ -29,7 +30,7 @@ class UnexpectedFileIOError(UsethisError, IOError):
"""Raised when an unexpected attempt is made to read or write the pyproject.toml file."""


class UsethisFileManager(Generic[DocumentT], metaclass=ABCMeta):
class FileManager(Generic[DocumentT], metaclass=ABCMeta):
"""Manages file access with deferred writes using a context manager.

This class implements the Command Pattern, encapsulating file operations. It defers
Expand All @@ -53,7 +54,7 @@ def __init__(self) -> None:

@override
def __eq__(self, other: object) -> bool:
if not isinstance(other, UsethisFileManager):
if not isinstance(other, FileManager):
return NotImplemented

return self.relative_path == other.relative_path
Expand Down Expand Up @@ -181,10 +182,7 @@ def unlock(self) -> None:
self._content_by_path.pop(self.path, None)


Key: TypeAlias = str | re.Pattern[str]


class KeyValueFileManager(UsethisFileManager, Generic[DocumentT], metaclass=ABCMeta):
class KeyValueFileManager(FileManager, Generic[DocumentT], metaclass=ABCMeta):
"""A manager for files which store (at least some) values in key-value mappings."""

@abstractmethod
Expand Down Expand Up @@ -225,30 +223,3 @@ def remove_from_list(
) -> None:
"""Remove values from a list in the configuration file."""
raise NotImplementedError


def print_keys(keys: Sequence[Key]) -> str:
r"""Convert a list of keys to a string.

Args:
keys: A list of keys.

Returns:
A string representation of the keys.

Examples:
>>> print_keys(["tool", "ruff", "line-length"])
'tool.ruff.line-length'
>>> print_keys([re.compile(r"importlinter:contracts:.*")])
'<REGEX("importlinter:contracts:.*")>'
"""
components: list[str] = []
for key in keys:
if isinstance(key, str):
components.append(key)
elif isinstance(key, re.Pattern):
components.append(f'<REGEX("{key.pattern}")>')
else:
assert_never(key)

return ".".join(components)
38 changes: 38 additions & 0 deletions src/usethis/_file/print_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from __future__ import annotations

import re
from typing import TYPE_CHECKING

from typing_extensions import assert_never

if TYPE_CHECKING:
from collections.abc import Sequence

from usethis._file.types_ import Key


def print_keys(keys: Sequence[Key]) -> str:
r"""Convert a list of keys to a string.

Args:
keys: A list of keys.

Returns:
A string representation of the keys.

Examples:
>>> print_keys(["tool", "ruff", "line-length"])
'tool.ruff.line-length'
>>> print_keys([re.compile(r"importlinter:contracts:.*")])
'<REGEX("importlinter:contracts:.*")>'
"""
components: list[str] = []
for key in keys:
if isinstance(key, str):
components.append(key)
elif isinstance(key, re.Pattern):
components.append(f'<REGEX("{key.pattern}")>')
else:
assert_never(key)

return ".".join(components)
2 changes: 1 addition & 1 deletion src/usethis/_file/pyproject_toml/io_.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from tomlkit import TOMLDocument
from typing_extensions import Self

from usethis._io import Key
from usethis._file.types_ import Key


class PyprojectTOMLManager(TOMLFileManager):
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_file/setup_cfg/io_.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from configupdater import ConfigUpdater as INIDocument
from typing_extensions import Self

from usethis._io import Key
from usethis._file.types_ import Key


class SetupCFGManager(INIFileManager):
Expand Down
16 changes: 8 additions & 8 deletions src/usethis/_file/toml/io_.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@
from tomlkit.exceptions import TOMLKitError
from typing_extensions import assert_never, override

from usethis._file.manager import (
KeyValueFileManager,
UnexpectedFileIOError,
UnexpectedFileOpenError,
)
from usethis._file.merge import _deep_merge
from usethis._file.print_ import print_keys
from usethis._file.toml.errors import (
TOMLDecodeError,
TOMLNotFoundError,
Expand All @@ -23,13 +29,7 @@
UnexpectedTOMLIOError,
UnexpectedTOMLOpenError,
)
from usethis._io import (
Key,
KeyValueFileManager,
UnexpectedFileIOError,
UnexpectedFileOpenError,
print_keys,
)
from usethis._file.types_ import Key

if TYPE_CHECKING:
from collections.abc import Collection, Sequence
Expand All @@ -40,7 +40,7 @@
from tomlkit.items import Item
from typing_extensions import Never, Self

from usethis._io import Key
from usethis._file.types_ import Key


class TOMLFileManager(KeyValueFileManager, metaclass=ABCMeta):
Expand Down
4 changes: 4 additions & 0 deletions src/usethis/_file/types_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import re
from typing import TypeAlias

Key: TypeAlias = str | re.Pattern[str]
14 changes: 7 additions & 7 deletions src/usethis/_file/yaml/io_.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
from typing_extensions import assert_never, override

from usethis._console import info_print
from usethis._file.manager import (
KeyValueFileManager,
UnexpectedFileIOError,
UnexpectedFileOpenError,
)
from usethis._file.merge import _deep_merge
from usethis._file.print_ import print_keys
from usethis._file.yaml.errors import (
UnexpectedYAMLIOError,
UnexpectedYAMLOpenError,
Expand All @@ -27,12 +33,6 @@
YAMLValueMissingError,
)
from usethis._file.yaml.update import update_ruamel_yaml_map
from usethis._io import (
KeyValueFileManager,
UnexpectedFileIOError,
UnexpectedFileOpenError,
print_keys,
)

if TYPE_CHECKING:
from collections.abc import Generator, Sequence
Expand All @@ -41,8 +41,8 @@

from typing_extensions import Self

from usethis._file.types_ import Key
from usethis._file.yaml.typing_ import YAMLLiteral
from usethis._io import Key


class YAMLFileManager(KeyValueFileManager, metaclass=ABCMeta):
Expand Down
3 changes: 2 additions & 1 deletion src/usethis/_tool/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
if TYPE_CHECKING:
from collections.abc import Sequence

from usethis._io import Key, KeyValueFileManager
from usethis._file.manager import KeyValueFileManager
from usethis._file.types_ import Key
from usethis._tool.config import ConfigItem
from usethis._tool.rule import Rule

Expand Down
7 changes: 4 additions & 3 deletions src/usethis/_tool/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
from pydantic import BaseModel, InstanceOf

from usethis._config import usethis_config
from usethis._file.manager import KeyValueFileManager
from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager
from usethis._file.types_ import Key
from usethis._init import ensure_pyproject_toml
from usethis._io import Key, KeyValueFileManager

if TYPE_CHECKING:
from typing_extensions import Self

from usethis._io import UsethisFileManager
from usethis._file.manager import FileManager

ResolutionT: TypeAlias = Literal["first", "first_content", "bespoke"]

Expand Down Expand Up @@ -146,7 +147,7 @@ def paths(self) -> set[Path]:
return {(usethis_config.cpd() / path).resolve() for path in self.root}


def ensure_managed_file_exists(file_manager: UsethisFileManager[object]) -> None:
def ensure_managed_file_exists(file_manager: FileManager[object]) -> None:
"""Ensure a file manager's managed file exists."""
if isinstance(file_manager, PyprojectTOMLManager):
ensure_pyproject_toml()
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/base/deptry.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
if TYPE_CHECKING:
from collections.abc import Sequence

from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager


@final
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/base/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from usethis._types.deps import Dependency

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager

_PYTEST_PIP_CMD = "pip install pytest"

Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/base/ruff.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
if TYPE_CHECKING:
from collections.abc import Sequence

from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager
from usethis._tool.rule import RuleConfig

_RUFF_VERSION = "v0.15.7" # Manually bump this version when necessary
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/spec/codespell.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from usethis._types.deps import Dependency

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager

_CODESPELL_VERSION = "v2.4.2" # Manually bump this version when necessary

Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/spec/coverage_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from usethis._tool.config import ConfigEntry, ConfigItem, ConfigSpec

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager


class CoveragePyToolSpec(ToolSpec):
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/spec/import_linter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from usethis._types.deps import Dependency

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager
from usethis._tool.config import ResolutionT

IMPORT_LINTER_CONTRACT_MIN_MODULE_COUNT = 3
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/spec/mkdocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from usethis._types.deps import Dependency

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager


class MkDocsToolSpec(ToolSpec):
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/spec/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from usethis._tool.rule import RuleConfig

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager


class PytestToolSpec(ToolSpec):
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/spec/ruff.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from usethis._types.deps import Dependency

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager


class RuffToolSpec(ToolSpec):
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_tool/impl/spec/ty.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from usethis._types.deps import Dependency

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._file.manager import KeyValueFileManager


class TyToolSpec(ToolSpec):
Expand Down
Loading
Loading