smith.domain.ports
Port definitions — interfaces that infrastructure adapters must implement.
1"""Port definitions — interfaces that infrastructure adapters must implement.""" 2 3from pathlib import Path 4from typing import Protocol 5 6from smith.domain.value_objects import ( 7 FileSpec, 8 TemplateSource, 9) 10 11 12class TemplateSourceError(Exception): 13 """Raised when a template source cannot be resolved.""" 14 15 16class TemplateSourcePort(Protocol): 17 """Interface for resolving template file specifications.""" 18 19 def resolve(self) -> list[FileSpec]: 20 """Return the list of file specs from this template source.""" 21 ... 22 23 def gitignore_patterns(self) -> list[str]: 24 """Return the gitignore patterns for this template source.""" 25 ... 26 27 28class FileSystemPort(Protocol): 29 """Interface for atomic file-system operations.""" 30 31 def check_conflicts(self, paths: list[Path]) -> list[Path]: 32 """Return the subset of *paths* that already exist on disk.""" 33 ... 34 35 def write_atomic(self, specs: list[FileSpec]) -> None: 36 """Write all specs atomically; roll back on failure.""" 37 ... 38 39 def remove(self, paths: list[Path]) -> None: 40 """Remove the given paths from the project directory.""" 41 ... 42 43 def exists(self, paths: list[Path]) -> dict[Path, bool]: 44 """Return a mapping of each path to whether it exists on disk.""" 45 ... 46 47 48class GitignorePort(Protocol): 49 """Interface for managing the smith-managed .gitignore section.""" 50 51 def add_section(self, patterns: list[str]) -> None: 52 """Add or replace the smith-managed section in .gitignore.""" 53 ... 54 55 def has_section(self) -> bool: 56 """Return whether a smith-managed section exists in .gitignore.""" 57 ... 58 59 def get_patterns(self) -> list[str]: 60 """Return the patterns listed inside the smith-managed section.""" 61 ... 62 63 64class MetadataPort(Protocol): 65 """Interface for persisting and loading template source metadata.""" 66 67 def save_source(self, source: TemplateSource) -> None: 68 """Write the source identifier into the smith-managed section header.""" 69 ... 70 71 def load_source(self) -> TemplateSource | None: 72 """Read the source identifier from the smith-managed section header.""" 73 ...
class
TemplateSourceError(builtins.Exception):
Raised when a template source cannot be resolved.
class
TemplateSourcePort(typing.Protocol):
17class TemplateSourcePort(Protocol): 18 """Interface for resolving template file specifications.""" 19 20 def resolve(self) -> list[FileSpec]: 21 """Return the list of file specs from this template source.""" 22 ... 23 24 def gitignore_patterns(self) -> list[str]: 25 """Return the gitignore patterns for this template source.""" 26 ...
Interface for resolving template file specifications.
TemplateSourcePort(*args, **kwargs)
1960def _no_init_or_replace_init(self, *args, **kwargs): 1961 cls = type(self) 1962 1963 if cls._is_protocol: 1964 raise TypeError('Protocols cannot be instantiated') 1965 1966 # Already using a custom `__init__`. No need to calculate correct 1967 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1968 if cls.__init__ is not _no_init_or_replace_init: 1969 return 1970 1971 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1972 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1973 # searches for a proper new `__init__` in the MRO. The new `__init__` 1974 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1975 # instantiation of the protocol subclass will thus use the new 1976 # `__init__` and no longer call `_no_init_or_replace_init`. 1977 for base in cls.__mro__: 1978 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1979 if init is not _no_init_or_replace_init: 1980 cls.__init__ = init 1981 break 1982 else: 1983 # should not happen 1984 cls.__init__ = object.__init__ 1985 1986 cls.__init__(self, *args, **kwargs)
class
FileSystemPort(typing.Protocol):
29class FileSystemPort(Protocol): 30 """Interface for atomic file-system operations.""" 31 32 def check_conflicts(self, paths: list[Path]) -> list[Path]: 33 """Return the subset of *paths* that already exist on disk.""" 34 ... 35 36 def write_atomic(self, specs: list[FileSpec]) -> None: 37 """Write all specs atomically; roll back on failure.""" 38 ... 39 40 def remove(self, paths: list[Path]) -> None: 41 """Remove the given paths from the project directory.""" 42 ... 43 44 def exists(self, paths: list[Path]) -> dict[Path, bool]: 45 """Return a mapping of each path to whether it exists on disk.""" 46 ...
Interface for atomic file-system operations.
FileSystemPort(*args, **kwargs)
1960def _no_init_or_replace_init(self, *args, **kwargs): 1961 cls = type(self) 1962 1963 if cls._is_protocol: 1964 raise TypeError('Protocols cannot be instantiated') 1965 1966 # Already using a custom `__init__`. No need to calculate correct 1967 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1968 if cls.__init__ is not _no_init_or_replace_init: 1969 return 1970 1971 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1972 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1973 # searches for a proper new `__init__` in the MRO. The new `__init__` 1974 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1975 # instantiation of the protocol subclass will thus use the new 1976 # `__init__` and no longer call `_no_init_or_replace_init`. 1977 for base in cls.__mro__: 1978 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1979 if init is not _no_init_or_replace_init: 1980 cls.__init__ = init 1981 break 1982 else: 1983 # should not happen 1984 cls.__init__ = object.__init__ 1985 1986 cls.__init__(self, *args, **kwargs)
def
check_conflicts(self, paths: list[pathlib._local.Path]) -> list[pathlib._local.Path]:
32 def check_conflicts(self, paths: list[Path]) -> list[Path]: 33 """Return the subset of *paths* that already exist on disk.""" 34 ...
Return the subset of paths that already exist on disk.
36 def write_atomic(self, specs: list[FileSpec]) -> None: 37 """Write all specs atomically; roll back on failure.""" 38 ...
Write all specs atomically; roll back on failure.
class
GitignorePort(typing.Protocol):
49class GitignorePort(Protocol): 50 """Interface for managing the smith-managed .gitignore section.""" 51 52 def add_section(self, patterns: list[str]) -> None: 53 """Add or replace the smith-managed section in .gitignore.""" 54 ... 55 56 def has_section(self) -> bool: 57 """Return whether a smith-managed section exists in .gitignore.""" 58 ... 59 60 def get_patterns(self) -> list[str]: 61 """Return the patterns listed inside the smith-managed section.""" 62 ...
Interface for managing the smith-managed .gitignore section.
GitignorePort(*args, **kwargs)
1960def _no_init_or_replace_init(self, *args, **kwargs): 1961 cls = type(self) 1962 1963 if cls._is_protocol: 1964 raise TypeError('Protocols cannot be instantiated') 1965 1966 # Already using a custom `__init__`. No need to calculate correct 1967 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1968 if cls.__init__ is not _no_init_or_replace_init: 1969 return 1970 1971 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1972 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1973 # searches for a proper new `__init__` in the MRO. The new `__init__` 1974 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1975 # instantiation of the protocol subclass will thus use the new 1976 # `__init__` and no longer call `_no_init_or_replace_init`. 1977 for base in cls.__mro__: 1978 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1979 if init is not _no_init_or_replace_init: 1980 cls.__init__ = init 1981 break 1982 else: 1983 # should not happen 1984 cls.__init__ = object.__init__ 1985 1986 cls.__init__(self, *args, **kwargs)
def
add_section(self, patterns: list[str]) -> None:
52 def add_section(self, patterns: list[str]) -> None: 53 """Add or replace the smith-managed section in .gitignore.""" 54 ...
Add or replace the smith-managed section in .gitignore.
class
MetadataPort(typing.Protocol):
65class MetadataPort(Protocol): 66 """Interface for persisting and loading template source metadata.""" 67 68 def save_source(self, source: TemplateSource) -> None: 69 """Write the source identifier into the smith-managed section header.""" 70 ... 71 72 def load_source(self) -> TemplateSource | None: 73 """Read the source identifier from the smith-managed section header.""" 74 ...
Interface for persisting and loading template source metadata.
MetadataPort(*args, **kwargs)
1960def _no_init_or_replace_init(self, *args, **kwargs): 1961 cls = type(self) 1962 1963 if cls._is_protocol: 1964 raise TypeError('Protocols cannot be instantiated') 1965 1966 # Already using a custom `__init__`. No need to calculate correct 1967 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1968 if cls.__init__ is not _no_init_or_replace_init: 1969 return 1970 1971 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1972 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1973 # searches for a proper new `__init__` in the MRO. The new `__init__` 1974 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1975 # instantiation of the protocol subclass will thus use the new 1976 # `__init__` and no longer call `_no_init_or_replace_init`. 1977 for base in cls.__mro__: 1978 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1979 if init is not _no_init_or_replace_init: 1980 cls.__init__ = init 1981 break 1982 else: 1983 # should not happen 1984 cls.__init__ = object.__init__ 1985 1986 cls.__init__(self, *args, **kwargs)
68 def save_source(self, source: TemplateSource) -> None: 69 """Write the source identifier into the smith-managed section header.""" 70 ...
Write the source identifier into the smith-managed section header.