mirror of
https://github.com/kiviktnm/decman.git
synced 2026-09-19 12:08:28 +00:00
Unify docstring formats
This commit is contained in:
+12
-2
@@ -4,6 +4,16 @@ import typing
|
|||||||
import decman.core.command as command
|
import decman.core.command as command
|
||||||
import decman.core.output as output
|
import decman.core.output as output
|
||||||
|
|
||||||
|
# Re-export File and Directory
|
||||||
|
from decman.core.fs import Directory, File # noqa: F401
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"File",
|
||||||
|
"Directory",
|
||||||
|
"prg",
|
||||||
|
"sh",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def prg(
|
def prg(
|
||||||
cmd: list[str],
|
cmd: list[str],
|
||||||
@@ -16,7 +26,7 @@ def prg(
|
|||||||
"""
|
"""
|
||||||
Shortcut for running a command. Returns the output of that command.
|
Shortcut for running a command. Returns the output of that command.
|
||||||
|
|
||||||
Args:
|
Arguments:
|
||||||
cmd:
|
cmd:
|
||||||
Command to execute.
|
Command to execute.
|
||||||
|
|
||||||
@@ -74,7 +84,7 @@ def sh(
|
|||||||
"""
|
"""
|
||||||
Shortcut for running a shell command. Returns the output of that command.
|
Shortcut for running a shell command. Returns the output of that command.
|
||||||
|
|
||||||
Args:
|
Arguments:
|
||||||
sh_cmd:
|
sh_cmd:
|
||||||
Shell command to execute. The command is passed to the system shell /bin/sh.
|
Shell command to execute. The command is passed to the system shell /bin/sh.
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ class File:
|
|||||||
Ownership, permissions, and parent directories are enforced on creation. Missing parent
|
Ownership, permissions, and parent directories are enforced on creation. Missing parent
|
||||||
directories are created recursively and assigned the same ownership as the file when specified.
|
directories are created recursively and assigned the same ownership as the file when specified.
|
||||||
|
|
||||||
Parameters
|
Parameters:
|
||||||
----------
|
|
||||||
source_file:
|
source_file:
|
||||||
Path to an existing file to copy from. Mutually exclusive with ``content``.
|
Path to an existing file to copy from. Mutually exclusive with ``content``.
|
||||||
|
|
||||||
@@ -44,8 +43,7 @@ class File:
|
|||||||
permissions:
|
permissions:
|
||||||
File mode applied to the target file (e.g. ``0o644``).
|
File mode applied to the target file (e.g. ``0o644``).
|
||||||
|
|
||||||
Raises
|
Raises:
|
||||||
------
|
|
||||||
ValueError
|
ValueError
|
||||||
If both ``source_file`` and ``content`` are ``None`` or if both are set.
|
If both ``source_file`` and ``content`` are ``None`` or if both are set.
|
||||||
|
|
||||||
@@ -55,8 +53,7 @@ class File:
|
|||||||
GroupNotFoundError
|
GroupNotFoundError
|
||||||
If ``group`` does not exist on the system.
|
If ``group`` does not exist on the system.
|
||||||
|
|
||||||
Notes
|
Notes:
|
||||||
-----
|
|
||||||
Variable substitution is a simple string replacement where each key in ``variables`` is
|
Variable substitution is a simple string replacement where each key in ``variables`` is
|
||||||
replaced by its corresponding value. No escaping or templating semantics are applied.
|
replaced by its corresponding value. No escaping or templating semantics are applied.
|
||||||
"""
|
"""
|
||||||
@@ -98,23 +95,19 @@ class File:
|
|||||||
"""
|
"""
|
||||||
Copies the contents of this file to the target file if they differ.
|
Copies the contents of this file to the target file if they differ.
|
||||||
|
|
||||||
Parameters
|
Parameters:
|
||||||
----------
|
|
||||||
target:
|
target:
|
||||||
Path to the target file on disk.
|
Path to the target file on disk.
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
Optional mapping of literal substrings to replace in the text content before writing.
|
Optional mapping of literal substrings to replace in the text content before
|
||||||
Ignored for binary files and when ``bin_file`` is True.
|
writing. Ignored for binary files and when ``bin_file`` is True.
|
||||||
|
|
||||||
Returns
|
Returns:
|
||||||
-------
|
|
||||||
bool
|
|
||||||
True if the file contents were created or modified.
|
True if the file contents were created or modified.
|
||||||
False if the existing file already contained the desired contents.
|
False if the existing file already contained the desired contents.
|
||||||
|
|
||||||
Raises
|
Raises:
|
||||||
------
|
|
||||||
OSError
|
OSError
|
||||||
If directory creation, file I/O, permission changes, or ownership changes fail
|
If directory creation, file I/O, permission changes, or ownership changes fail
|
||||||
(e.g. permission denied, missing parent path components, I/O errors).
|
(e.g. permission denied, missing parent path components, I/O errors).
|
||||||
@@ -220,8 +213,7 @@ class Directory:
|
|||||||
permissions, encoding, and binary/text behavior. Text files can optionally undergo
|
permissions, encoding, and binary/text behavior. Text files can optionally undergo
|
||||||
variable substitution before being written.
|
variable substitution before being written.
|
||||||
|
|
||||||
Parameters
|
Parameters:
|
||||||
----------
|
|
||||||
source_directory:
|
source_directory:
|
||||||
Path to the directory whose contents will be mirrored into the target.
|
Path to the directory whose contents will be mirrored into the target.
|
||||||
|
|
||||||
@@ -241,8 +233,7 @@ class Directory:
|
|||||||
permissions:
|
permissions:
|
||||||
File mode applied to created or updated files (e.g. ``0o644``).
|
File mode applied to created or updated files (e.g. ``0o644``).
|
||||||
|
|
||||||
Raises
|
Raises:
|
||||||
------
|
|
||||||
UserNotFoundError
|
UserNotFoundError
|
||||||
If ``owner`` does not exist on the system.
|
If ``owner`` does not exist on the system.
|
||||||
|
|
||||||
@@ -287,22 +278,20 @@ class Directory:
|
|||||||
"""
|
"""
|
||||||
Copies the files in this directory to the target directory. Only replaces files that differ.
|
Copies the files in this directory to the target directory. Only replaces files that differ.
|
||||||
|
|
||||||
Parameters
|
Parameters:
|
||||||
----------
|
|
||||||
target_directory:
|
target_directory:
|
||||||
Destination directory root. Relative layout from the source is preserved beneath this
|
Destination directory root. Relative layout from the source is preserved beneath
|
||||||
path.
|
this path.
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
Optional mapping of literal substrings to replace in text files before writing. Ignored
|
Optional mapping of literal substrings to replace in text files before writing.
|
||||||
for binary files.
|
Ignored for binary files.
|
||||||
|
|
||||||
dry_run:
|
dry_run:
|
||||||
If ``True``, perform a dry-run: no files are written, but the list of files that *would*
|
If ``True``, perform a dry-run: no files are written, but the list of files that
|
||||||
be processed is returned.
|
*would* be processed is returned.
|
||||||
|
|
||||||
Returns
|
Returns:
|
||||||
-------
|
|
||||||
list[str]
|
list[str]
|
||||||
When ``dry_run`` is ``False``, paths of files that were created or whose contents
|
When ``dry_run`` is ``False``, paths of files that were created or whose contents
|
||||||
were modified.
|
were modified.
|
||||||
@@ -310,8 +299,7 @@ class Directory:
|
|||||||
When ``dry_run`` is ``True``, paths of all files that would be considered for
|
When ``dry_run`` is ``True``, paths of all files that would be considered for
|
||||||
creation or modification (no changes are actually performed).
|
creation or modification (no changes are actually performed).
|
||||||
|
|
||||||
Raises
|
Raises:
|
||||||
------
|
|
||||||
OSError
|
OSError
|
||||||
If directory traversal or file I/O fails (e.g. permission denied).
|
If directory traversal or file I/O fails (e.g. permission denied).
|
||||||
|
|
||||||
@@ -3,15 +3,15 @@ import stat
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Adjust this import to match your actual module location
|
# Adjust this import to match your actual module location
|
||||||
import decman.core.files as files
|
import decman.core.fs as fs
|
||||||
|
|
||||||
# --- files.File tests --------------------------------------------------------------
|
# --- fs.File tests --------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def test_file_from_content_creates_and_is_idempotent(tmp_path: Path) -> None:
|
def test_file_from_content_creates_and_is_idempotent(tmp_path: Path) -> None:
|
||||||
target = tmp_path / "file.txt"
|
target = tmp_path / "file.txt"
|
||||||
|
|
||||||
f = files.File(content="hello", permissions=0o600)
|
f = fs.File(content="hello", permissions=0o600)
|
||||||
|
|
||||||
# First run: file must be created and reported as changed
|
# First run: file must be created and reported as changed
|
||||||
changed1 = f.copy_to(str(target))
|
changed1 = f.copy_to(str(target))
|
||||||
@@ -31,7 +31,7 @@ def test_file_from_content_creates_and_is_idempotent(tmp_path: Path) -> None:
|
|||||||
def test_file_content_with_variables_and_change_detection(tmp_path: Path) -> None:
|
def test_file_content_with_variables_and_change_detection(tmp_path: Path) -> None:
|
||||||
target = tmp_path / "templated.txt"
|
target = tmp_path / "templated.txt"
|
||||||
|
|
||||||
f = files.File(content="hello {{NAME}}")
|
f = fs.File(content="hello {{NAME}}")
|
||||||
|
|
||||||
# First run: NAME=world
|
# First run: NAME=world
|
||||||
changed1 = f.copy_to(str(target), {"{{NAME}}": "world"})
|
changed1 = f.copy_to(str(target), {"{{NAME}}": "world"})
|
||||||
@@ -55,7 +55,7 @@ def test_file_from_source_text_with_and_without_variables(tmp_path: Path) -> Non
|
|||||||
target = tmp_path / "dst.txt"
|
target = tmp_path / "dst.txt"
|
||||||
|
|
||||||
# Without variables (raw copy)
|
# Without variables (raw copy)
|
||||||
f_raw = files.File(source_file=str(src))
|
f_raw = fs.File(source_file=str(src))
|
||||||
changed1 = f_raw.copy_to(str(target), {})
|
changed1 = f_raw.copy_to(str(target), {})
|
||||||
assert changed1 is True
|
assert changed1 is True
|
||||||
assert target.read_text(encoding="utf-8") == "VALUE={{X}}"
|
assert target.read_text(encoding="utf-8") == "VALUE={{X}}"
|
||||||
@@ -65,7 +65,7 @@ def test_file_from_source_text_with_and_without_variables(tmp_path: Path) -> Non
|
|||||||
assert changed2 is False
|
assert changed2 is False
|
||||||
|
|
||||||
# With variables (substitution)
|
# With variables (substitution)
|
||||||
f_sub = files.File(source_file=str(src))
|
f_sub = fs.File(source_file=str(src))
|
||||||
changed3 = f_sub.copy_to(str(target), {"{{X}}": "42"})
|
changed3 = f_sub.copy_to(str(target), {"{{X}}": "42"})
|
||||||
assert changed3 is True
|
assert changed3 is True
|
||||||
assert target.read_text(encoding="utf-8") == "VALUE=42"
|
assert target.read_text(encoding="utf-8") == "VALUE=42"
|
||||||
@@ -79,7 +79,7 @@ def test_file_binary_from_content(tmp_path: Path) -> None:
|
|||||||
target = tmp_path / "bin.dat"
|
target = tmp_path / "bin.dat"
|
||||||
payload = b"\x00\x01\x02hello"
|
payload = b"\x00\x01\x02hello"
|
||||||
|
|
||||||
f = files.File(content=payload.decode("latin1"), bin_file=True)
|
f = fs.File(content=payload.decode("latin1"), bin_file=True)
|
||||||
|
|
||||||
changed1 = f.copy_to(str(target))
|
changed1 = f.copy_to(str(target))
|
||||||
assert changed1 is True
|
assert changed1 is True
|
||||||
@@ -97,7 +97,7 @@ def test_file_binary_copy_from_source(tmp_path: Path) -> None:
|
|||||||
src.write_bytes(payload)
|
src.write_bytes(payload)
|
||||||
target = tmp_path / "dst.bin"
|
target = tmp_path / "dst.bin"
|
||||||
|
|
||||||
f = files.File(source_file=str(src), bin_file=True)
|
f = fs.File(source_file=str(src), bin_file=True)
|
||||||
|
|
||||||
changed1 = f.copy_to(str(target), {"IGNORED": "x"})
|
changed1 = f.copy_to(str(target), {"IGNORED": "x"})
|
||||||
assert changed1 is True
|
assert changed1 is True
|
||||||
@@ -113,7 +113,7 @@ def test_file_creates_parent_directories_and_applies_permissions(tmp_path: Path)
|
|||||||
nested_dir = tmp_path / "a" / "b" / "c"
|
nested_dir = tmp_path / "a" / "b" / "c"
|
||||||
target = nested_dir / "file.txt"
|
target = nested_dir / "file.txt"
|
||||||
|
|
||||||
f = files.File(content="data", permissions=0o644)
|
f = fs.File(content="data", permissions=0o644)
|
||||||
|
|
||||||
changed = f.copy_to(str(target))
|
changed = f.copy_to(str(target))
|
||||||
assert changed is True
|
assert changed is True
|
||||||
@@ -127,7 +127,7 @@ def test_file_creates_parent_directories_and_applies_permissions(tmp_path: Path)
|
|||||||
assert mode == 0o644
|
assert mode == 0o644
|
||||||
|
|
||||||
|
|
||||||
# --- files.Directory tests ---------------------------------------------------------
|
# --- fs.Directory tests ---------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def _create_sample_source_tree(root: Path) -> None:
|
def _create_sample_source_tree(root: Path) -> None:
|
||||||
@@ -143,14 +143,14 @@ def test_directory_copy_to_creates_and_is_idempotent(tmp_path: Path) -> None:
|
|||||||
|
|
||||||
_create_sample_source_tree(src_dir)
|
_create_sample_source_tree(src_dir)
|
||||||
|
|
||||||
d = files.Directory(
|
d = fs.Directory(
|
||||||
source_directory=str(src_dir),
|
source_directory=str(src_dir),
|
||||||
bin_files=False,
|
bin_files=False,
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
permissions=0o644,
|
permissions=0o644,
|
||||||
)
|
)
|
||||||
|
|
||||||
# First run: both files should be created and reported as changed
|
# First run: both fs should be created and reported as changed
|
||||||
changed1 = d.copy_to(str(dst_dir), variables={"{{X}}": "1"})
|
changed1 = d.copy_to(str(dst_dir), variables={"{{X}}": "1"})
|
||||||
expected_paths = {
|
expected_paths = {
|
||||||
str(dst_dir / "a.txt"),
|
str(dst_dir / "a.txt"),
|
||||||
@@ -161,7 +161,7 @@ def test_directory_copy_to_creates_and_is_idempotent(tmp_path: Path) -> None:
|
|||||||
assert (dst_dir / "a.txt").read_text(encoding="utf-8") == "A=1"
|
assert (dst_dir / "a.txt").read_text(encoding="utf-8") == "A=1"
|
||||||
assert (dst_dir / "sub" / "b.txt").read_text(encoding="utf-8") == "B=1"
|
assert (dst_dir / "sub" / "b.txt").read_text(encoding="utf-8") == "B=1"
|
||||||
|
|
||||||
# Second run with same variables: no files should be reported as changed
|
# Second run with same variables: no fs should be reported as changed
|
||||||
changed2 = d.copy_to(str(dst_dir), variables={"{{X}}": "1"})
|
changed2 = d.copy_to(str(dst_dir), variables={"{{X}}": "1"})
|
||||||
assert changed2 == []
|
assert changed2 == []
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ def test_directory_copy_to_detects_changes_via_variables(tmp_path: Path) -> None
|
|||||||
src_dir.mkdir()
|
src_dir.mkdir()
|
||||||
_create_sample_source_tree(src_dir)
|
_create_sample_source_tree(src_dir)
|
||||||
|
|
||||||
d = files.Directory(source_directory=str(src_dir))
|
d = fs.Directory(source_directory=str(src_dir))
|
||||||
|
|
||||||
# Initial materialization
|
# Initial materialization
|
||||||
changed1 = d.copy_to(str(dst_dir), variables={"{{X}}": "alpha"})
|
changed1 = d.copy_to(str(dst_dir), variables={"{{X}}": "alpha"})
|
||||||
@@ -181,7 +181,7 @@ def test_directory_copy_to_detects_changes_via_variables(tmp_path: Path) -> None
|
|||||||
str(dst_dir / "sub" / "b.txt"),
|
str(dst_dir / "sub" / "b.txt"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Change variables -> both files change
|
# Change variables -> both fs change
|
||||||
changed2 = d.copy_to(str(dst_dir), variables={"{{X}}": "beta"})
|
changed2 = d.copy_to(str(dst_dir), variables={"{{X}}": "beta"})
|
||||||
assert set(changed2) == {
|
assert set(changed2) == {
|
||||||
str(dst_dir / "a.txt"),
|
str(dst_dir / "a.txt"),
|
||||||
@@ -198,7 +198,7 @@ def test_directory_copy_to_dry_run(tmp_path: Path) -> None:
|
|||||||
src_dir.mkdir()
|
src_dir.mkdir()
|
||||||
_create_sample_source_tree(src_dir)
|
_create_sample_source_tree(src_dir)
|
||||||
|
|
||||||
d = files.Directory(source_directory=str(src_dir))
|
d = fs.Directory(source_directory=str(src_dir))
|
||||||
|
|
||||||
# First, actually materialize once
|
# First, actually materialize once
|
||||||
d.copy_to(str(dst_dir), variables={"{{X}}": "1"})
|
d.copy_to(str(dst_dir), variables={"{{X}}": "1"})
|
||||||
@@ -230,7 +230,7 @@ def test_directory_copy_to_restores_working_directory(tmp_path: Path) -> None:
|
|||||||
src_dir.mkdir()
|
src_dir.mkdir()
|
||||||
_create_sample_source_tree(src_dir)
|
_create_sample_source_tree(src_dir)
|
||||||
|
|
||||||
d = files.Directory(source_directory=str(src_dir))
|
d = fs.Directory(source_directory=str(src_dir))
|
||||||
|
|
||||||
original_cwd = os.getcwd()
|
original_cwd = os.getcwd()
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user