Command: A Normalized, Programatically-defined Shell Command#

class my.types.Command.Command(*, command: str, args: list[Any] = [], kwargs: dict[str, ~typing.Any]={}, options: Options = <factory>)#

A builder for easily & durably issuing shell commands, async or otherwise.

Positional arguments are handled separately from keyword arguments, which are converted to flags (e.g., --key value or -k). The class handles quoting, underscore-to-dash conversion in flag names, and various shell conventions.

Commands can be manually finalized w/ assemble(), or executed directly via execute() / execute_async(). To chain commands together, use the provided out and pipe options.

Examples

Build a command, inspect it, and execute it:

>>> from my import Command
>>> cmd = Command.new('ls', '-l', color='auto')
>>> str(cmd)
'ls --color auto -l'
>>> Command.run('echo', 'hello')
Result(code=0, out='hello', err='')

Pipe one command into another:

>>> piped = Command.new('echo', 'one two', options={'pipe': Command.new('wc', '-w')})
>>> str(piped)
'echo "one two" | wc -w'
>>> piped.execute()
Result(code=0, out='2', err='')
class my.types.Command.Command.Options(**data: Any)#

Configuration options for command assembly.

model_config = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

verbose: bool#

Print the assembled command before execution.

preserve_underscores: bool#

Underscores in flag names are not changed to hyphens.

single_dashes: bool#

Use single dashes for all flags.

named_args_last: bool#

Positional arguments are placed after named arguments.

flag_assignment: bool#

Use “=” to connect flags to their values.

always_quote: bool#

Always quote argument values, even if not strictly necessary.

cwd: str | None#

Working directory for command execution.

out: str | None#

Redirect command output to a file. Not compatible w/ pipe.

pipe: Command | None#

Pipe command output to another command.

class my.types.Command.Command.Result(code: int, out: str, err: str)#

Structured result of command execution.

code: int#

The process’s return code (0 on success).

err: str#

The captured, stripped stderr text.

out: str#

The captured, stripped stdout text.

I Initial Methods#

classmethod Command.new(command: str, *args: str, options: dict | Options | None = None, **kwargs: Any) → Self#

Create a new command instance.

Parameters:
  • command – Base command string – ostensibly but NOT necessarily a name.

  • args – Positional arguments to the command.

  • options – Assembly option flags.

  • **kwargs – Keyword arguments to the command.

Returns:

Configured Command instance.

Examples

Configure assembly via an Options dict:

>>> from my import Command
>>> opts = {'flag_assignment': True}
>>> str(Command.new('grep', 'TODO', options=opts, max_count=3))
'grep --max-count=3 TODO'

II Private Methods#

property Command.positional_args: list[str]#

Convert positional arguments to shell-safe values.

Returns:

String representations with appropriate quoting.

property Command.named_args: list[str]#

Convert keyword arguments to shell-safe flags.

Returns:

Formatted command-line flags (e.g., --key value, -k).

III Primary Methods#

Command.assemble() → str#

Assemble a complete shell command from this instance’s members.

Examples

Assemble a command with values that require quoting:

>>> from my import Command
>>> cmd = Command.new('tar', 'x', options={'single_dashes': True}, file='a b.tar')
>>> cmd.assemble()
'tar -file "a b.tar" x'

IV Public Methods#

Command.execute() → Result#

Execute a command synchronously via direct argv invocation (no shell).

Note

Runs with shell=False, so argument values are handed to the OS exactly as given and are never re-parsed by a shell – this is what prevents $(...)/backtick command-substitution injection from within an argument’s contents.

Returns:

(return_code, stdout, stderr).

Examples

Execute a command and capture its output:

>>> from my import Command
>>> Command.new('echo', 'hello').execute()
Result(code=0, out='hello', err='')
async Command.execute_async() → Result#

Execute a command asynchronously via direct argv invocation (no shell).

Note

Uses create_subprocess_exec (never _shell), so argument values are handed to the OS exactly as given and are never re-parsed by a shell – this is what prevents $(...)/backtick command-substitution injection from within an argument’s contents.

Returns:

(return_code, stdout, stderr).

Examples

Execute a command from synchronous code:

>>> import asyncio
>>> from my import Command
>>> asyncio.run(Command.new('echo', 'async!').execute_async())
Result(code=0, out='async!', err='')
classmethod Command.run(command: str, *args: Any, **kwargs: Any) → Result#

Convenience method to build & execute a command in one statement.

Parameters:
  • command – Base command to execute.

  • *args – Positional arguments.

  • **kwargs – Keyword arguments (converted to flags).

Returns:

(return_code, stdout, stderr).

Examples

Build and run in one call:

>>> from my import Command
>>> Command.run('echo', 'hello')
Result(code=0, out='hello', err='')
async classmethod Command.run_async(command: str, *args: Any, **kwargs: Any) → Result#

Convenience method to build & execute a command in one statement.

Parameters:
  • command – Base command to execute.

  • *args – Positional arguments.

  • **kwargs – Keyword arguments (converted to flags).

Returns:

(return_code, stdout, stderr).

async classmethod Command.exa(*args: str, _cwd: str | Path | None = None, **kwargs: Any) → Result#

Execute a command asynchronously, with a shorthand for overriding the working directory.

Parameters:
  • *args – The base command and its positional arguments, as for run_async().

  • _cwd – Working-directory override, applied via options.cwd whether or not an options value was passed alongside it.

  • **kwargs – Keyword arguments (converted to flags).

Returns:

(return_code, stdout, stderr).