Skip to content

Commit 9d7ef9f

Browse files
committed
Update docs
1 parent 83fd893 commit 9d7ef9f

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

docs/index.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,69 @@ For `BaseModel` and `pydantic.dataclasses.dataclass` types, `CliApp.run` will in
10611061
* `cli_implicit_flags=True`
10621062
* `cli_kebab_case=True`
10631063

1064+
### Asynchronous CLI Commands
1065+
1066+
Pydantic settings now supports running asynchronous CLI commands via CliApp.run and CliApp.run_subcommand. With this feature, you can define async def methods within your Pydantic models (including subcommands) and have them executed just like their synchronous counterparts. Specifically:
1067+
1068+
1. Asynchronous methods are supported: You can now mark your cli_cmd or similar CLI entrypoint methods as async def and have CliApp execute them.
1069+
2. Subcommands may also be asynchronous: If you have nested CLI subcommands, the final (lowest-level) subcommand methods can likewise be asynchronous.
1070+
3. Limit asynchronous methods to final subcommands: Defining parent commands as asynchronous is not recommended, because it can result in additional threads and event loops being created. For best performance and to avoid unnecessary resource usage, only implement your deepest (child) subcommands as async def.
1071+
1072+
Below is a simple example demonstrating an asynchronous top-level command:
1073+
1074+
```py
1075+
from pydantic_settings import BaseSettings, CliApp
1076+
1077+
1078+
class AsyncSettings(BaseSettings):
1079+
async def cli_cmd(self) -> None:
1080+
print('Hello from an async CLI method!')
1081+
1082+
1083+
if __name__ == '__main__':
1084+
# If an event loop is already running, a new thread will be used;
1085+
# otherwise, asyncio.run() is used to execute this async method.
1086+
CliApp.run(AsyncSettings)
1087+
```
1088+
1089+
#### Asynchronous Subcommands
1090+
1091+
As mentioned above, you can also define subcommands as async. However, only do so for the leaf (lowest-level) subcommand to avoid spawning new threads and event loops unnecessarily in parent commands:
1092+
1093+
```py
1094+
from pydantic import BaseModel
1095+
1096+
from pydantic_settings import (
1097+
BaseSettings,
1098+
CliApp,
1099+
CliPositionalArg,
1100+
CliSubCommand,
1101+
)
1102+
1103+
1104+
class Clone(BaseModel):
1105+
repository: CliPositionalArg[str]
1106+
directory: CliPositionalArg[str]
1107+
1108+
async def cli_cmd(self) -> None:
1109+
print(f'Cloning async from "{self.repository}" into "{self.directory}"')
1110+
# Perform async tasks here, e.g. network or I/O operations
1111+
1112+
1113+
class Git(BaseSettings):
1114+
clone: CliSubCommand[Clone]
1115+
1116+
def cli_cmd(self) -> None:
1117+
# Run the final subcommand (clone/init). It is recommended to define async methods only at the deepest level.
1118+
CliApp.run_subcommand(self)
1119+
1120+
1121+
if __name__ == '__main__':
1122+
CliApp.run(Git, cli_args=['clone', 'repo', 'dir'])
1123+
```
1124+
1125+
When executing a subcommand with an asynchronous cli_cmd, Pydantic settings automatically detects whether the current thread already has an active event loop. If so, the async command is run in a fresh thread to avoid conflicts. Otherwise, it uses asyncio.run() in the current thread. This handling ensures your asynchronous subcommands “just work” without additional manual setup.
1126+
10641127
### Mutually Exclusive Groups
10651128

10661129
CLI mutually exclusive groups can be created by inheriting from the `CliMutuallyExclusiveGroup` class.

0 commit comments

Comments
 (0)