Add HTTPAction, update main demo
This commit is contained in:
parent
bc1637143c
commit
fe9758adbf
|
@ -5,73 +5,166 @@ Copyright (c) 2025 rtj.dev LLC.
|
|||
Licensed under the MIT License. See LICENSE file for details.
|
||||
"""
|
||||
import asyncio
|
||||
import random
|
||||
from argparse import Namespace
|
||||
|
||||
from falyx.action import Action, ActionGroup, ChainedAction
|
||||
from falyx.falyx import Falyx
|
||||
from falyx.parsers import FalyxParsers, get_arg_parsers
|
||||
from falyx.version import __version__
|
||||
|
||||
|
||||
def build_falyx() -> Falyx:
|
||||
class Foo:
|
||||
def __init__(self, flx: Falyx) -> None:
|
||||
self.flx = flx
|
||||
|
||||
async def build(self):
|
||||
await asyncio.sleep(1)
|
||||
print("✅ Build complete!")
|
||||
return "Build complete!"
|
||||
|
||||
async def test(self):
|
||||
await asyncio.sleep(1)
|
||||
print("✅ Tests passed!")
|
||||
return "Tests passed!"
|
||||
|
||||
async def deploy(self):
|
||||
await asyncio.sleep(1)
|
||||
print("✅ Deployment complete!")
|
||||
return "Deployment complete!"
|
||||
|
||||
async def clean(self):
|
||||
print("🧹 Cleaning...")
|
||||
await asyncio.sleep(1)
|
||||
print("✅ Clean complete!")
|
||||
return "Clean complete!"
|
||||
|
||||
async def build_package(self):
|
||||
print("🔨 Building...")
|
||||
await asyncio.sleep(1)
|
||||
print("✅ Build finished!")
|
||||
return "Build finished!"
|
||||
|
||||
async def package(self):
|
||||
print("📦 Packaging...")
|
||||
await asyncio.sleep(1)
|
||||
print("✅ Package complete!")
|
||||
return "Package complete!"
|
||||
|
||||
async def run_tests(self):
|
||||
print("🧪 Running tests...")
|
||||
await asyncio.sleep(random.randint(1, 3))
|
||||
print("✅ Tests passed!")
|
||||
return "Tests passed!"
|
||||
|
||||
async def run_integration_tests(self):
|
||||
print("🔗 Running integration tests...")
|
||||
await asyncio.sleep(random.randint(1, 3))
|
||||
print("✅ Integration tests passed!")
|
||||
return "Integration tests passed!"
|
||||
|
||||
async def run_linter(self):
|
||||
print("🧹 Running linter...")
|
||||
await asyncio.sleep(random.randint(1, 3))
|
||||
print("✅ Linter passed!")
|
||||
return "Linter passed!"
|
||||
|
||||
async def run(self):
|
||||
await self.flx.run()
|
||||
|
||||
|
||||
def parse_args() -> Namespace:
|
||||
parsers: FalyxParsers = get_arg_parsers()
|
||||
return parsers.parse_args()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Build and return a Falyx instance with all your commands."""
|
||||
flx = Falyx(title="🚀 Falyx CLI")
|
||||
args = parse_args()
|
||||
flx = Falyx(
|
||||
title="🚀 Falyx CLI",
|
||||
cli_args=args,
|
||||
columns=5,
|
||||
welcome_message="Welcome to Falyx CLI!",
|
||||
exit_message="Goodbye!",
|
||||
)
|
||||
foo = Foo(flx)
|
||||
|
||||
# Example commands
|
||||
# --- Bottom bar info ---
|
||||
flx.bottom_bar.columns = 3
|
||||
flx.bottom_bar.add_toggle_from_option("V", "Verbose", flx.options, "verbose")
|
||||
flx.bottom_bar.add_toggle_from_option("U", "Debug Hooks", flx.options, "debug_hooks")
|
||||
flx.bottom_bar.add_static("Version", f"Falyx v{__version__}")
|
||||
|
||||
# --- Command actions ---
|
||||
|
||||
# --- Single Actions ---
|
||||
flx.add_command(
|
||||
key="B",
|
||||
description="Build project",
|
||||
action=Action("Build", lambda: print("📦 Building...")),
|
||||
tags=["build"]
|
||||
action=Action("Build", foo.build),
|
||||
tags=["build"],
|
||||
spinner=True,
|
||||
spinner_message="📦 Building...",
|
||||
)
|
||||
|
||||
flx.add_command(
|
||||
key="T",
|
||||
description="Run tests",
|
||||
action=Action("Test", lambda: print("🧪 Running tests...")),
|
||||
tags=["test"]
|
||||
action=Action("Test", foo.test),
|
||||
tags=["test"],
|
||||
spinner=True,
|
||||
spinner_message="🧪 Running tests...",
|
||||
)
|
||||
|
||||
flx.add_command(
|
||||
key="D",
|
||||
description="Deploy project",
|
||||
action=Action("Deploy", lambda: print("🚀 Deploying...")),
|
||||
tags=["deploy"]
|
||||
action=Action("Deploy", foo.deploy),
|
||||
tags=["deploy"],
|
||||
spinner=True,
|
||||
spinner_message="🚀 Deploying...",
|
||||
)
|
||||
|
||||
# Example of ChainedAction (pipeline)
|
||||
build_pipeline = ChainedAction(
|
||||
# --- Build pipeline (ChainedAction) ---
|
||||
pipeline = ChainedAction(
|
||||
name="Full Build Pipeline",
|
||||
actions=[
|
||||
Action("Clean", lambda: print("🧹 Cleaning...")),
|
||||
Action("Build", lambda: print("🔨 Building...")),
|
||||
Action("Package", lambda: print("📦 Packaging...")),
|
||||
],
|
||||
auto_inject=False,
|
||||
Action("Clean", foo.clean),
|
||||
Action("Build", foo.build_package),
|
||||
Action("Package", foo.package),
|
||||
]
|
||||
)
|
||||
flx.add_command(
|
||||
key="P",
|
||||
description="Run Build Pipeline",
|
||||
action=build_pipeline,
|
||||
tags=["build", "pipeline"]
|
||||
action=pipeline,
|
||||
tags=["build", "pipeline"],
|
||||
spinner=True,
|
||||
spinner_message="🔨 Running build pipeline...",
|
||||
spinner_type="line",
|
||||
)
|
||||
|
||||
# Example of ActionGroup (parallel tasks)
|
||||
# --- Test suite (ActionGroup) ---
|
||||
test_suite = ActionGroup(
|
||||
name="Test Suite",
|
||||
actions=[
|
||||
Action("Unit Tests", lambda: print("🧪 Running unit tests...")),
|
||||
Action("Integration Tests", lambda: print("🔗 Running integration tests...")),
|
||||
Action("Lint", lambda: print("🧹 Running linter...")),
|
||||
Action("Unit Tests", foo.run_tests),
|
||||
Action("Integration Tests", foo.run_integration_tests),
|
||||
Action("Lint", foo.run_linter),
|
||||
]
|
||||
)
|
||||
flx.add_command(
|
||||
key="G",
|
||||
description="Run All Tests",
|
||||
action=test_suite,
|
||||
tags=["test", "parallel"]
|
||||
tags=["test", "parallel"],
|
||||
spinner=True,
|
||||
spinner_type="line",
|
||||
)
|
||||
await foo.run()
|
||||
|
||||
return flx
|
||||
|
||||
if __name__ == "__main__":
|
||||
flx = build_falyx()
|
||||
asyncio.run(flx.run())
|
||||
|
||||
try:
|
||||
asyncio.run(main())
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
pass
|
||||
|
|
|
@ -830,6 +830,7 @@ class Falyx:
|
|||
except (EOFError, KeyboardInterrupt):
|
||||
logger.info("EOF or KeyboardInterrupt. Exiting menu.")
|
||||
break
|
||||
finally:
|
||||
logger.info(f"Exiting menu: {self.get_title()}")
|
||||
if self.exit_message:
|
||||
self.print_message(self.exit_message)
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
from rich.tree import Tree
|
||||
|
||||
from falyx.action import Action
|
||||
from falyx.context import ExecutionContext, SharedContext
|
||||
from falyx.themes.colors import OneColors
|
||||
from falyx.utils import logger
|
||||
|
||||
|
||||
async def close_shared_http_session(context: ExecutionContext) -> None:
|
||||
try:
|
||||
shared_context: SharedContext = context.get_shared_context()
|
||||
session = shared_context.get("http_session")
|
||||
should_close = shared_context.get("_session_should_close", False)
|
||||
if session and should_close:
|
||||
await session.close()
|
||||
except Exception as error:
|
||||
logger.warning("⚠️ Error closing shared HTTP session: %s", error)
|
||||
|
||||
|
||||
class HTTPAction(Action):
|
||||
"""
|
||||
Specialized Action that performs an HTTP request using aiohttp and the shared context.
|
||||
|
||||
Automatically reuses a shared aiohttp.ClientSession stored in SharedContext.
|
||||
Closes the session at the end of the ActionGroup (via an after-hook).
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
method: str,
|
||||
url: str,
|
||||
*,
|
||||
args: tuple[Any, ...] = (),
|
||||
headers: dict[str, str] | None = None,
|
||||
params: dict[str, Any] | None = None,
|
||||
json: dict[str, Any] | None = None,
|
||||
data: Any = None,
|
||||
hooks=None,
|
||||
inject_last_result: bool = False,
|
||||
inject_last_result_as: str = "last_result",
|
||||
retry: bool = False,
|
||||
retry_policy=None,
|
||||
):
|
||||
self.method = method.upper()
|
||||
self.url = url
|
||||
self.headers = headers
|
||||
self.params = params
|
||||
self.json = json
|
||||
self.data = data
|
||||
|
||||
super().__init__(
|
||||
name=name,
|
||||
action=self._request,
|
||||
args=args,
|
||||
kwargs={},
|
||||
hooks=hooks,
|
||||
inject_last_result=inject_last_result,
|
||||
inject_last_result_as=inject_last_result_as,
|
||||
retry=retry,
|
||||
retry_policy=retry_policy,
|
||||
)
|
||||
|
||||
async def _request(self, *args, **kwargs) -> dict[str, Any]:
|
||||
assert self.shared_context is not None, "SharedContext is not set"
|
||||
context: SharedContext = self.shared_context
|
||||
|
||||
session = context.get("http_session")
|
||||
if session is None:
|
||||
session = aiohttp.ClientSession()
|
||||
context.set("http_session", session)
|
||||
context.set("_session_should_close", True)
|
||||
|
||||
async with session.request(
|
||||
self.method,
|
||||
self.url,
|
||||
headers=self.headers,
|
||||
params=self.params,
|
||||
json=self.json,
|
||||
data=self.data,
|
||||
) as response:
|
||||
body = await response.text()
|
||||
return {
|
||||
"status": response.status,
|
||||
"url": str(response.url),
|
||||
"headers": dict(response.headers),
|
||||
"body": body,
|
||||
}
|
||||
|
||||
async def preview(self, parent: Tree | None = None):
|
||||
label = [
|
||||
f"[{OneColors.CYAN_b}]🌐 HTTPAction[/] '{self.name}'",
|
||||
f"\n[dim]Method:[/] {self.method}",
|
||||
f"\n[dim]URL:[/] {self.url}",
|
||||
]
|
||||
if self.inject_last_result:
|
||||
label.append(f"\n[dim]Injects:[/] '{self.inject_last_result_as}'")
|
||||
if self.retry_policy and self.retry_policy.enabled:
|
||||
label.append(
|
||||
f"\n[dim]↻ Retries:[/] {self.retry_policy.max_retries}x, "
|
||||
f"delay {self.retry_policy.delay}s, backoff {self.retry_policy.backoff}x"
|
||||
)
|
||||
|
||||
if parent:
|
||||
parent.add("".join(label))
|
||||
else:
|
||||
self.console.print(Tree("".join(label)))
|
|
@ -20,7 +20,6 @@ pytest-asyncio = "^0.20"
|
|||
ruff = "^0.3"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
falyx = "falyx.cli.main:main"
|
||||
sync-version = "scripts.sync_version:main"
|
||||
|
||||
[build-system]
|
||||
|
|
Loading…
Reference in New Issue