@@ -140,14 +140,9 @@ def discover_app_instance(
140140def 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
217214def 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 ))
0 commit comments