Add data, create_dirs to SaveFileAction
This commit is contained in:
@ -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,
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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."""
|
||||
|
@ -1 +1 @@
|
||||
__version__ = "0.1.57"
|
||||
__version__ = "0.1.58"
|
||||
|
@ -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"
|
||||
|
Reference in New Issue
Block a user