Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Extra custom aliases #75

Merged
merged 5 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
```

Expand Down
23 changes: 13 additions & 10 deletions docs/examples/simple.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
12 changes: 12 additions & 0 deletions docs/usage/arguments/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions examples/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
25 changes: 12 additions & 13 deletions src/pydantic_argparse/utils/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions tests/utils/test_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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.
"""
Expand Down
Loading