ReMe/reme4/steps/crud/append.py
Sen Huang 7d0bec60be
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Tests ReMe / Unit Tests - py3.10 (push) Has been cancelled
Tests ReMe / Unit Tests - py3.13 (push) Has been cancelled
feat: rename working_dir to vault_dir and update documentation (#254)
* feat: rename working_dir to vault_dir and update documentation

- Rename working_dir to vault_dir across the application
- Update documentation to reflect vault_dir instead of working_dir
- Change FileFrontMatter title field to name field
- Update .gitignore to include vault directory
- Modify file path descriptions to reference vault instead of working_dir
- Update related configuration and property names accordingly

* refactor(steps): rename working_path to vault_path in CRUD operations

- Rename parameter from `working_path` to `vault_path` in `resolve_path` function
- Update all usages in append, edit, read, and write steps to use `self.vault_path`
- Update documentation comments to reflect the new parameter name
- Update docstring in read.py to mention `vault_dir` instead of `vault`

test(chunked_file_parser): update frontmatter field from title to name

- Change frontmatter field from `title` to `name` in test cases
- Update comment in background steps test to reference `vault_path` instead of `working_path`

* refactor(schema): remove unused ResourceEntry import

* feat(file_graph): add link scope filtering to get_inlinks/get_outlinks

* feat(file-store): add scope parameter to link methods
2026-05-22 18:10:29 +08:00

75 lines
2.9 KiB
Python

"""Append content to the end of a file (auto-creates if missing)."""
import aiofiles
from ._file_io import NON_MD_WARNING, detect_file_encoding, gate_md, resolve_path
from ..base_step import BaseStep
from ...components import R
@R.register("append_step")
class AppendStep(BaseStep):
"""Append `content` to the target file. If the file does not exist it is
created (a system notice is appended to the answer in that case).
Content is appended verbatim — callers control whether a separator newline
is included in the input."""
def _fail(self, message: str, **meta) -> None:
assert self.context is not None
self.context.response.success = False
self.context.response.answer = f"Error: {message}"
if meta:
self.context.response.metadata.update(meta)
async def execute(self): # pylint: disable=too-many-return-statements
assert self.context is not None
raw = str(self.context.get("path") or "")
content = self.context.get("content")
content_str = "" if content is None else str(content)
target, err = resolve_path(self.vault_path, raw)
if err:
self._fail(err)
return None
target, is_md = gate_md(target)
if target.exists() and not target.is_file():
self._fail(f"path {target} is not a file", path=str(target))
return None
created = not target.exists()
# Preserve the existing file's encoding so appended bytes don't corrupt
# a non-UTF-8 file (e.g. GBK CSV). New files default to UTF-8.
encoding = "utf-8" if created else await detect_file_encoding(target)
try:
if created:
target.parent.mkdir(parents=True, exist_ok=True)
try:
payload = content_str.encode(encoding)
except (UnicodeEncodeError, LookupError):
self.logger.warning(
f"[{self.name}] cannot encode appended content as {encoding!r}, falling back to utf-8",
)
payload = content_str.encode("utf-8")
async with aiofiles.open(str(target), "ab") as f:
await f.write(payload)
except Exception as e: # pylint: disable=broad-except
self._fail(f"write failed: {e}", path=str(target))
return None
nbytes = len(payload)
self.context.response.success = True
if created:
answer = f"Appended {nbytes} bytes to {target} [system notice: file did not exist and was auto-created]"
else:
answer = f"Appended {nbytes} bytes to {target}"
if not is_md:
answer = f"{answer} [system notice: {NON_MD_WARNING}]"
self.context.response.answer = answer
self.logger.info(
f"[{self.name}] appended path={target} bytes={nbytes} encoding={encoding} "
f"created={created} is_md={is_md}",
)
return self.context.response