Skip to content

Commit 33db237

Browse files
committed
[cli] streamline serve_application function and enhance argument handling
1 parent 39a2296 commit 33db237

3 files changed

Lines changed: 85 additions & 49 deletions

File tree

docs/source/content/client/overview.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@ Register event handlers to listen for updates:
143143
- Handle template quality changes
144144
* - :meth:`~WhatsApp.on_template_components_update`
145145
- Handle template components updates
146+
* - :meth:`~WhatsApp.on_edited_message`
147+
- Handle edited messages
148+
* - :meth:`~WhatsApp.on_deleted_message`
149+
- Handle deleted messages
150+
* - :meth:`~WhatsApp.on_outgoing_message`
151+
- Handle outgoing messages
152+
* - :meth:`~WhatsApp.on_outgoing_edited_message`
153+
- Handle outgoing edited messages
154+
* - :meth:`~WhatsApp.on_outgoing_deleted_message`
155+
- Handle outgoing deleted messages
156+
* - :meth:`~WhatsApp.on_account_update`
157+
- Handle business account updates
146158
* - :meth:`~WhatsApp.on_raw_update`
147159
- Handle raw updates from WhatsApp
148160
* - :meth:`~WhatsApp.add_handlers`

pywa/cli.py

Lines changed: 72 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,9 @@ def discover_app_instance(
140140
def serve_application(
141141
command: str,
142142
path: pathlib.Path | None = None,
143-
*,
144-
host: str = "127.0.0.1",
145-
port: int = 8000,
146-
reload: bool = False,
147-
reload_dirs: list[pathlib.Path] | None = None,
148-
workers: int | None = None,
149143
app: str | None = None,
150144
entrypoint: str | None = None,
145+
**uvicorn_kwargs,
151146
) -> None:
152147
"""
153148
Core function that resolves dependencies and starts the Uvicorn server.
@@ -164,6 +159,8 @@ def serve_application(
164159
)
165160
sys.exit(1)
166161

162+
workers = uvicorn_kwargs.get("workers")
163+
167164
try:
168165
if entrypoint:
169166
module_str, _, app_name = entrypoint.partition(":")
@@ -192,6 +189,9 @@ def serve_application(
192189
f"{base_import_string}.{WhatsApp._setup_and_get_starlette_app.__name__}"
193190
)
194191

192+
host = uvicorn_kwargs.get("host", "127.0.0.1")
193+
port = uvicorn_kwargs.get("port", 8000)
194+
195195
mode = "development" if command == "dev" else "production"
196196
print(f"\n🚀 Starting Pywa in {mode} mode")
197197
print("-" * 40)
@@ -202,16 +202,13 @@ def serve_application(
202202
print("⚠️ Auto-reload: Enabled (Use 'pywa run' for production)")
203203
print("-" * 40 + "\n")
204204

205-
uvicorn.run(
206-
app=uvicorn_app_string,
207-
factory=True,
208-
host=host,
209-
port=port,
210-
reload=reload,
211-
reload_dirs=[str(d.resolve()) for d in reload_dirs] if reload_dirs else None,
212-
workers=workers,
213-
log_config=None,
214-
)
205+
clean_kwargs = {k: v for k, v in uvicorn_kwargs.items() if v is not None}
206+
207+
clean_kwargs["app"] = uvicorn_app_string
208+
clean_kwargs["factory"] = True
209+
clean_kwargs["log_config"] = None
210+
211+
uvicorn.run(**clean_kwargs)
215212

216213

217214
def send_messages(
@@ -322,27 +319,66 @@ def main() -> None:
322319
help="Explicit entrypoint string (e.g., 'main:wa'). Overrides path and --app.",
323320
)
324321

322+
serve_parser.add_argument(
323+
"--log-level",
324+
type=str,
325+
choices=["critical", "error", "warning", "info", "debug", "trace"],
326+
help="Log level.",
327+
)
328+
serve_parser.add_argument("--ssl-keyfile", type=str, help="SSL key file.")
329+
serve_parser.add_argument(
330+
"--ssl-certfile",
331+
type=str,
332+
help="SSL certificate file.",
333+
)
334+
325335
run_parser = subparsers.add_parser(
326336
"run", parents=[serve_parser], help="Run the client in production mode."
327337
)
328338
run_parser.add_argument(
329339
"--workers",
330340
type=int,
331-
default=None,
332-
help="Number of worker processes. Defaults to 1.",
341+
help="Number of worker processes.",
342+
)
343+
run_parser.add_argument(
344+
"--proxy-headers",
345+
action=argparse.BooleanOptionalAction,
346+
help="Enable/Disable X-Forwarded-Proto, X-Forwarded-For to populate url scheme and remote address info.",
347+
)
348+
run_parser.add_argument(
349+
"--forwarded-allow-ips",
350+
type=str,
351+
help="Comma separated list of IPs to trust with proxy headers. The literal '*' means trust everything.",
352+
)
353+
run_parser.add_argument(
354+
"--timeout-keep-alive",
355+
type=int,
356+
help="Close Keep-Alive connections if no new data is received within this timeout (in seconds).",
333357
)
334358

335359
dev_parser = subparsers.add_parser(
336360
"dev",
337361
parents=[serve_parser],
338362
help="Run the client in development mode with auto-reload enabled.",
339363
)
364+
365+
dev_parser.add_argument(
366+
"--reload",
367+
action=argparse.BooleanOptionalAction,
368+
default=True,
369+
help="Enable auto-reload. Default: True",
370+
)
340371
dev_parser.add_argument(
341372
"--reload-dir",
342373
action="append",
343374
dest="reload_dirs",
344375
type=str,
345-
help="Directories to watch for changes.",
376+
help="Set reload directories explicitly, instead of using the current working directory.",
377+
)
378+
dev_parser.add_argument(
379+
"--reload-delay",
380+
type=float,
381+
help="Delay between previous and next check if application needs to be reloaded.",
346382
)
347383

348384
# --- SEND PARSER ---
@@ -433,35 +469,23 @@ def main() -> None:
433469
args = parser.parse_args()
434470

435471
if args.command in ["run", "dev"]:
436-
target_path = pathlib.Path(args.path) if args.path else None
437-
if args.command == "run":
438-
serve_application(
439-
command="run",
440-
path=target_path,
441-
host=args.host,
442-
port=args.port,
443-
reload=False,
444-
workers=args.workers,
445-
app=args.app,
446-
entrypoint=args.entrypoint,
447-
)
448-
elif args.command == "dev":
449-
reload_dirs = (
450-
[pathlib.Path(d) for d in args.reload_dirs]
451-
if getattr(args, "reload_dirs", None)
452-
else None
453-
)
454-
serve_application(
455-
command="dev",
456-
path=target_path,
457-
host=args.host,
458-
port=args.port,
459-
reload=True,
460-
reload_dirs=reload_dirs,
461-
workers=None,
462-
app=args.app,
463-
entrypoint=args.entrypoint,
464-
)
472+
target_path = pathlib.Path(args.path) if getattr(args, "path", None) else None
473+
app_args = {
474+
"command": args.command,
475+
"path": target_path,
476+
"app": getattr(args, "app", None),
477+
"entrypoint": getattr(args, "entrypoint", None),
478+
}
479+
480+
exclude_keys = app_args.keys()
481+
uvicorn_kwargs = {k: v for k, v in vars(args).items() if k not in exclude_keys}
482+
483+
if uvicorn_kwargs.get("reload_dirs"):
484+
uvicorn_kwargs["reload_dirs"] = [
485+
str(pathlib.Path(d).resolve()) for d in uvicorn_kwargs["reload_dirs"]
486+
]
487+
488+
serve_application(**app_args, **uvicorn_kwargs)
465489

466490
elif args.command == "send":
467491
send_messages(**vars(args))

pywa/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def __init__(
7070
def _setup_and_get_starlette_app(self):
7171
if self._server_type is not None:
7272
raise ValueError(
73-
"When providing a custom `server`, you must run it yourself."
73+
"When providing a custom `server` instance to the WhatsApp client, pywa assumes you will handle the webhook routes and server setup yourself. "
7474
)
7575
try:
7676
from starlette.applications import Starlette as StarletteApp

0 commit comments

Comments
 (0)