diff --git a/README.md b/README.md index 69e4b7f..5c2dc87 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ import pydantic_argparse class Arguments(pydantic.BaseModel): # Required Args - string: str = pydantic.Field(description="a required string") - integer: int = pydantic.Field(description="a required integer") - flag: bool = pydantic.Field(description="a required flag") + string: str = pydantic.Field(description="a required string", aliases=["-s"]) + integer: int = pydantic.Field(description="a required integer", aliases=["-i"]) + flag: bool = pydantic.Field(description="a required flag", aliases=["-f"]) # Optional Args second_flag: bool = pydantic.Field(False, description="an optional flag") @@ -69,29 +69,32 @@ if __name__ == "__main__": ```console $ python3 example.py --help -usage: Example Program [-h] [-v] --string STRING --integer INTEGER --flag | - --no-flag [--second-flag] [--no-third-flag] +usage: Example Program [-h] [-v] [-s STRING] [-i INTEGER] [-f | --flag | --no-flag] + [--second-flag] [--no-third-flag] Example Description required arguments: - --string STRING a required string - --integer INTEGER a required integer - --flag, --no-flag a required flag + -s STRING, --string STRING + a required string + -i INTEGER, --integer INTEGER + a required integer + -f, --flag, --no-flag + a required flag optional arguments: - --second-flag an optional flag (default: False) - --no-third-flag an optional flag (default: True) + --second-flag an optional flag (default: False) + --no-third-flag an optional flag (default: True) help: - -h, --help show this help message and exit - -v, --version show program's version number and exit + -h, --help show this help message and exit + -v, --version show program's version number and exit Example Epilog ``` ```console -$ python3 example.py --string hello --integer 42 --flag +$ python3 example.py --string hello -i 42 -f string='hello' integer=42 flag=True second_flag=False third_flag=True ``` diff --git a/docs/examples/simple.md b/docs/examples/simple.md index 4811d84..4b26c0e 100644 --- a/docs/examples/simple.md +++ b/docs/examples/simple.md @@ -6,29 +6,32 @@ ### Check Help ```console $ python3 simple.py --help -usage: Example Program [-h] [-v] --string STRING --integer INTEGER --flag | - --no-flag [--second-flag] [--no-third-flag] +usage: Example Program [-h] [-v] [-s STRING] [-i INTEGER] [-f | --flag | --no-flag] + [--second-flag] [--no-third-flag] Example Description required arguments: - --string STRING a required string - --integer INTEGER a required integer - --flag, --no-flag a required flag + -s STRING, --string STRING + a required string + -i INTEGER, --integer INTEGER + a required integer + -f, --flag, --no-flag + a required flag optional arguments: - --second-flag an optional flag (default: False) - --no-third-flag an optional flag (default: True) + --second-flag an optional flag (default: False) + --no-third-flag an optional flag (default: True) help: - -h, --help show this help message and exit - -v, --version show program's version number and exit + -h, --help show this help message and exit + -v, --version show program's version number and exit Example Epilog ``` ### Parse Arguments ```console -$ python3 simple.py --string hello --integer 42 --flag +$ python3 simple.py --string hello -i 42 -f string='hello' integer=42 flag=True second_flag=False third_flag=True ``` diff --git a/docs/usage/arguments/index.md b/docs/usage/arguments/index.md index 904c319..742f5c7 100644 --- a/docs/usage/arguments/index.md +++ b/docs/usage/arguments/index.md @@ -111,6 +111,18 @@ class Arguments(BaseModel): You can see the list of reserved keywords in Python at any time by typing `:::python help("keywords")` into the Python interpreter. +A field can also be provided with a list of `aliases`, which will allow the +argument to be provided via multiple different (potentially shorter) aliases in +the command-line interface. + +```python +class Arguments(BaseModel): + # We want our argument to be named `my_long_argument_name` (i.e., + # `--my-long-argument-name`), but we also want to provide the argument via + # the aliases `-m` and `-mlan`. + my_long_argument_name: int = Field(aliases=["-m", "-mlan"]) +``` + ## Environment Variables Functionality to parse both required and optional arguments from environment variables is provided via the `pydantic.BaseSettings` base class. diff --git a/examples/simple.py b/examples/simple.py index b177a22..3de0b37 100644 --- a/examples/simple.py +++ b/examples/simple.py @@ -9,9 +9,9 @@ class Arguments(pydantic.BaseModel): """Simple Command-Line Arguments.""" # Required Args - string: str = pydantic.Field(description="a required string") - integer: int = pydantic.Field(description="a required integer") - flag: bool = pydantic.Field(description="a required flag") + string: str = pydantic.Field(description="a required string", aliases=["-s"]) + integer: int = pydantic.Field(description="a required integer", aliases=["-i"]) + flag: bool = pydantic.Field(description="a required flag", aliases=["-f"]) # Optional Args second_flag: bool = pydantic.Field(False, description="an optional flag") diff --git a/src/pydantic_argparse/utils/arguments.py b/src/pydantic_argparse/utils/arguments.py index 32bdbbc..9d35635 100644 --- a/src/pydantic_argparse/utils/arguments.py +++ b/src/pydantic_argparse/utils/arguments.py @@ -6,30 +6,29 @@ from pydantic_argparse.compatibility import pydantic +from typing import List -def names(field: pydantic.fields.ModelField, invert: bool = False) -> list[str]: - """Standardises argument name. + +def names(field: pydantic.fields.ModelField, invert: bool = False) -> List[str]: + """Standardises the argument name and any custom aliases. Args: field (pydantic.fields.ModelField): Field to construct name for. invert (bool): Whether to invert the name by prepending `--no-`. Returns: - str: Standardised name of the argument. + List[str]: Standardised names for the argument. """ - # Construct Prefix - prefix = "--no-" if invert else "--" - - flags = [] + # Add any custom aliases first + # We trust that the user has provided these correctly + flags: List[str] = [] + flags.extend(field.field_info.extra.get("aliases", [])) - # Add custom aliases - aliases = field.field_info.extra.get("aliases", []) - for alias in aliases: - flags.append(f"{prefix}{alias.replace('_', '-')}") - - # Prepend prefix, replace '_' with '-' + # Construct prefix, prepend it, replace '_' with '-' + prefix = "--no-" if invert else "--" flags.append(f"{prefix}{field.alias.replace('_', '-')}") + # Return the standardised name and aliases return flags diff --git a/tests/conftest.py b/tests/conftest.py index fb7d55e..5442062 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -48,7 +48,7 @@ def create_test_field( type: Type[Any] = str, # noqa: A002 default: Any = ..., description: Optional[str] = None, - aliases: Optional[list[str]] = None, + aliases: Optional[List[str]] = None, ) -> pydantic.fields.ModelField: """Constructs a `pydantic` field with sensible defaults for testing. @@ -57,7 +57,7 @@ def create_test_field( type (Type[Any]): Type of the field. default (Any): Default value for the field. description (Optional[str]): Description for the field. - aliases (Optional[list[str]]): List of flag aliases. + aliases (Optional[List[str]]): List of flag aliases. Returns: pydantic.fields.ModelField: Dynamically constructed `pydantic` model. diff --git a/tests/utils/test_arguments.py b/tests/utils/test_arguments.py index 7900cb6..688ec86 100644 --- a/tests/utils/test_arguments.py +++ b/tests/utils/test_arguments.py @@ -9,7 +9,7 @@ from pydantic_argparse import utils from tests import conftest as conf -from typing import Any, Optional +from typing import Any, List, Optional @pytest.mark.parametrize( @@ -28,15 +28,15 @@ ) def test_argument_names( name: str, - aliases: list[str], + aliases: List[str], invert: bool, expected: str, ) -> None: - """Tests `utils.arguments.name` Function. + """Tests `utils.arguments.names` Function. Args: name (str): Argument name to test. - aliases (list[str]): List of aliases. + aliases (List[str]): List of aliases. invert (bool): Whether to invert the name. expected (str): Expected result of the test. """