Add data, create_dirs to SaveFileAction

This commit is contained in:
2025-07-12 21:12:34 -04:00
parent 4c1498121f
commit 294bbc9062
5 changed files with 70 additions and 28 deletions

View File

@ -4,7 +4,13 @@ from typing import Any
from pydantic import BaseModel
from falyx import Falyx
from falyx.action import Action, ActionFactory, ChainedAction, ConfirmAction
from falyx.action import (
Action,
ActionFactory,
ChainedAction,
ConfirmAction,
SaveFileAction,
)
from falyx.parser import CommandArgumentParser
@ -39,12 +45,18 @@ async def build_json_updates(dogs: list[Dog]) -> list[dict[str, Any]]:
return [dog.model_dump(mode="json") for dog in dogs]
def after_action(dogs) -> None:
async def save_dogs(dogs) -> None:
if not dogs:
print("No dogs processed.")
return
for result in dogs:
print(Dog(**result))
print(f"Saving {Dog(**result)} to file.")
await SaveFileAction(
name="Save Dog Data",
file_path=f"dogs/{result['name']}.json",
data=result,
file_type="json",
)()
async def build_chain(dogs: list[Dog]) -> ChainedAction:
@ -64,8 +76,8 @@ async def build_chain(dogs: list[Dog]) -> ChainedAction:
inject_into="dogs",
),
Action(
name="after_action",
action=after_action,
name="save_dogs",
action=save_dogs,
inject_into="dogs",
),
],
@ -91,13 +103,13 @@ def dog_config(parser: CommandArgumentParser) -> None:
async def main():
flx = Falyx("Dog Post Example")
flx = Falyx("Save Dogs Example")
flx.add_command(
key="D",
description="Post Dog Data",
description="Save Dog Data",
action=factory,
aliases=["post_dogs"],
aliases=["save_dogs"],
argument_config=dog_config,
)

View File

@ -36,9 +36,11 @@ class SaveFileAction(BaseAction):
file_path: str,
file_type: FileType | str = FileType.TEXT,
mode: Literal["w", "a"] = "w",
inject_last_result: bool = True,
inject_into: str = "data",
data: Any = None,
overwrite: bool = True,
create_dirs: bool = True,
inject_last_result: bool = False,
inject_into: str = "data",
):
"""
SaveFileAction allows saving data to a file.
@ -47,17 +49,22 @@ class SaveFileAction(BaseAction):
name (str): Name of the action.
file_path (str | Path): Path to the file where data will be saved.
file_type (FileType | str): Format to write to (e.g. TEXT, JSON, YAML).
mode (Literal["w", "a"]): File mode (default: "w").
data (Any): Data to be saved (if not using inject_last_result).
overwrite (bool): Whether to overwrite the file if it exists.
create_dirs (bool): Whether to create parent directories if they do not exist.
inject_last_result (bool): Whether to inject result from previous action.
inject_into (str): Kwarg name to inject the last result as.
overwrite (bool): Whether to overwrite the file if it exists.
"""
super().__init__(
name=name, inject_last_result=inject_last_result, inject_into=inject_into
)
self._file_path = self._coerce_file_path(file_path)
self._file_type = self._coerce_file_type(file_type)
self.data = data
self.overwrite = overwrite
self.mode = mode
self.create_dirs = create_dirs
@property
def file_path(self) -> Path | None:
@ -126,6 +133,14 @@ class SaveFileAction(BaseAction):
elif self.file_path.exists() and not self.overwrite:
raise FileExistsError(f"File already exists: {self.file_path}")
if self.file_path.parent and not self.file_path.parent.exists():
if self.create_dirs:
self.file_path.parent.mkdir(parents=True, exist_ok=True)
else:
raise FileNotFoundError(
f"Directory does not exist: {self.file_path.parent}"
)
try:
if self.file_type == FileType.TEXT:
self.file_path.write_text(data, encoding="UTF-8")
@ -175,7 +190,7 @@ class SaveFileAction(BaseAction):
async def _run(self, *args, **kwargs):
combined_kwargs = self._maybe_inject_last_result(kwargs)
data = combined_kwargs.get(self.inject_into)
data = self.data or combined_kwargs.get(self.inject_into)
context = ExecutionContext(
name=self.name, args=args, kwargs=combined_kwargs, action=self

View File

@ -9,22 +9,37 @@ from falyx.parser.argument_action import ArgumentAction
@dataclass
class Argument:
"""Represents a command-line argument."""
"""
Represents a command-line argument.
Attributes:
flags (tuple[str, ...]): Short and long flags for the argument.
dest (str): The destination name for the argument.
action (ArgumentAction): The action to be taken when the argument is encountered.
type (Any): The type of the argument (e.g., str, int, float) or a callable that converts the argument value.
default (Any): The default value if the argument is not provided.
choices (list[str] | None): A list of valid choices for the argument.
required (bool): True if the argument is required, False otherwise.
help (str): Help text for the argument.
nargs (int | str | None): Number of arguments expected. Can be an int, '?', '*', '+', or None.
positional (bool): True if the argument is positional (no leading - or -- in flags), False otherwise.
resolver (BaseAction | None):
An action object that resolves the argument, if applicable.
lazy_resolver (bool): True if the resolver should be called lazily, False otherwise
"""
flags: tuple[str, ...]
dest: str # Destination name for the argument
action: ArgumentAction = (
ArgumentAction.STORE
) # Action to be taken when the argument is encountered
type: Any = str # Type of the argument (e.g., str, int, float) or callable
default: Any = None # Default value if the argument is not provided
choices: list[str] | None = None # List of valid choices for the argument
required: bool = False # True if the argument is required
help: str = "" # Help text for the argument
nargs: int | str | None = None # int, '?', '*', '+', None
positional: bool = False # True if no leading - or -- in flags
resolver: BaseAction | None = None # Action object for the argument
lazy_resolver: bool = False # True if resolver should be called lazily
dest: str
action: ArgumentAction = ArgumentAction.STORE
type: Any = str
default: Any = None
choices: list[str] | None = None
required: bool = False
help: str = ""
nargs: int | str | None = None
positional: bool = False
resolver: BaseAction | None = None
lazy_resolver: bool = False
def get_positional_text(self) -> str:
"""Get the positional text for the argument."""

View File

@ -1 +1 @@
__version__ = "0.1.57"
__version__ = "0.1.58"

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "falyx"
version = "0.1.57"
version = "0.1.58"
description = "Reliable and introspectable async CLI action framework."
authors = ["Roland Thomas Jr <roland@rtj.dev>"]
license = "MIT"