Add data, create_dirs to SaveFileAction
This commit is contained in:
@ -4,7 +4,13 @@ from typing import Any
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from falyx import Falyx
|
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
|
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]
|
return [dog.model_dump(mode="json") for dog in dogs]
|
||||||
|
|
||||||
|
|
||||||
def after_action(dogs) -> None:
|
async def save_dogs(dogs) -> None:
|
||||||
if not dogs:
|
if not dogs:
|
||||||
print("No dogs processed.")
|
print("No dogs processed.")
|
||||||
return
|
return
|
||||||
for result in dogs:
|
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:
|
async def build_chain(dogs: list[Dog]) -> ChainedAction:
|
||||||
@ -64,8 +76,8 @@ async def build_chain(dogs: list[Dog]) -> ChainedAction:
|
|||||||
inject_into="dogs",
|
inject_into="dogs",
|
||||||
),
|
),
|
||||||
Action(
|
Action(
|
||||||
name="after_action",
|
name="save_dogs",
|
||||||
action=after_action,
|
action=save_dogs,
|
||||||
inject_into="dogs",
|
inject_into="dogs",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -91,13 +103,13 @@ def dog_config(parser: CommandArgumentParser) -> None:
|
|||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
flx = Falyx("Dog Post Example")
|
flx = Falyx("Save Dogs Example")
|
||||||
|
|
||||||
flx.add_command(
|
flx.add_command(
|
||||||
key="D",
|
key="D",
|
||||||
description="Post Dog Data",
|
description="Save Dog Data",
|
||||||
action=factory,
|
action=factory,
|
||||||
aliases=["post_dogs"],
|
aliases=["save_dogs"],
|
||||||
argument_config=dog_config,
|
argument_config=dog_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,9 +36,11 @@ class SaveFileAction(BaseAction):
|
|||||||
file_path: str,
|
file_path: str,
|
||||||
file_type: FileType | str = FileType.TEXT,
|
file_type: FileType | str = FileType.TEXT,
|
||||||
mode: Literal["w", "a"] = "w",
|
mode: Literal["w", "a"] = "w",
|
||||||
inject_last_result: bool = True,
|
data: Any = None,
|
||||||
inject_into: str = "data",
|
|
||||||
overwrite: bool = True,
|
overwrite: bool = True,
|
||||||
|
create_dirs: bool = True,
|
||||||
|
inject_last_result: bool = False,
|
||||||
|
inject_into: str = "data",
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
SaveFileAction allows saving data to a file.
|
SaveFileAction allows saving data to a file.
|
||||||
@ -47,17 +49,22 @@ class SaveFileAction(BaseAction):
|
|||||||
name (str): Name of the action.
|
name (str): Name of the action.
|
||||||
file_path (str | Path): Path to the file where data will be saved.
|
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).
|
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_last_result (bool): Whether to inject result from previous action.
|
||||||
inject_into (str): Kwarg name to inject the last result as.
|
inject_into (str): Kwarg name to inject the last result as.
|
||||||
overwrite (bool): Whether to overwrite the file if it exists.
|
|
||||||
"""
|
"""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
name=name, inject_last_result=inject_last_result, inject_into=inject_into
|
name=name, inject_last_result=inject_last_result, inject_into=inject_into
|
||||||
)
|
)
|
||||||
self._file_path = self._coerce_file_path(file_path)
|
self._file_path = self._coerce_file_path(file_path)
|
||||||
self._file_type = self._coerce_file_type(file_type)
|
self._file_type = self._coerce_file_type(file_type)
|
||||||
|
self.data = data
|
||||||
self.overwrite = overwrite
|
self.overwrite = overwrite
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
self.create_dirs = create_dirs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def file_path(self) -> Path | None:
|
def file_path(self) -> Path | None:
|
||||||
@ -126,6 +133,14 @@ class SaveFileAction(BaseAction):
|
|||||||
elif self.file_path.exists() and not self.overwrite:
|
elif self.file_path.exists() and not self.overwrite:
|
||||||
raise FileExistsError(f"File already exists: {self.file_path}")
|
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:
|
try:
|
||||||
if self.file_type == FileType.TEXT:
|
if self.file_type == FileType.TEXT:
|
||||||
self.file_path.write_text(data, encoding="UTF-8")
|
self.file_path.write_text(data, encoding="UTF-8")
|
||||||
@ -175,7 +190,7 @@ class SaveFileAction(BaseAction):
|
|||||||
|
|
||||||
async def _run(self, *args, **kwargs):
|
async def _run(self, *args, **kwargs):
|
||||||
combined_kwargs = self._maybe_inject_last_result(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(
|
context = ExecutionContext(
|
||||||
name=self.name, args=args, kwargs=combined_kwargs, action=self
|
name=self.name, args=args, kwargs=combined_kwargs, action=self
|
||||||
|
@ -9,22 +9,37 @@ from falyx.parser.argument_action import ArgumentAction
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Argument:
|
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, ...]
|
flags: tuple[str, ...]
|
||||||
dest: str # Destination name for the argument
|
dest: str
|
||||||
action: ArgumentAction = (
|
action: ArgumentAction = ArgumentAction.STORE
|
||||||
ArgumentAction.STORE
|
type: Any = str
|
||||||
) # Action to be taken when the argument is encountered
|
default: Any = None
|
||||||
type: Any = str # Type of the argument (e.g., str, int, float) or callable
|
choices: list[str] | None = None
|
||||||
default: Any = None # Default value if the argument is not provided
|
required: bool = False
|
||||||
choices: list[str] | None = None # List of valid choices for the argument
|
help: str = ""
|
||||||
required: bool = False # True if the argument is required
|
nargs: int | str | None = None
|
||||||
help: str = "" # Help text for the argument
|
positional: bool = False
|
||||||
nargs: int | str | None = None # int, '?', '*', '+', None
|
resolver: BaseAction | None = None
|
||||||
positional: bool = False # True if no leading - or -- in flags
|
lazy_resolver: bool = False
|
||||||
resolver: BaseAction | None = None # Action object for the argument
|
|
||||||
lazy_resolver: bool = False # True if resolver should be called lazily
|
|
||||||
|
|
||||||
def get_positional_text(self) -> str:
|
def get_positional_text(self) -> str:
|
||||||
"""Get the positional text for the argument."""
|
"""Get the positional text for the argument."""
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "0.1.57"
|
__version__ = "0.1.58"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "falyx"
|
name = "falyx"
|
||||||
version = "0.1.57"
|
version = "0.1.58"
|
||||||
description = "Reliable and introspectable async CLI action framework."
|
description = "Reliable and introspectable async CLI action framework."
|
||||||
authors = ["Roland Thomas Jr <roland@rtj.dev>"]
|
authors = ["Roland Thomas Jr <roland@rtj.dev>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
Reference in New Issue
Block a user