Skip to content

Commit

Permalink
chore: Enable ruff for pydantic_argparse
Browse files Browse the repository at this point in the history
  • Loading branch information
rumpelsepp committed Nov 28, 2024
1 parent 4778027 commit 37a269c
Show file tree
Hide file tree
Showing 19 changed files with 80 additions and 207 deletions.
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ no_implicit_optional = true
[tool.ruff]
target-version = "py311"
line-length = 100
extend-exclude = ["src/gallia/pydantic_argparse"]

[tool.ruff.lint]
select = [
Expand Down
6 changes: 2 additions & 4 deletions src/gallia/pydantic_argparse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@

from gallia.pydantic_argparse.argparse import ArgumentParser

from . import argparse, parsers, utils


class BaseArgument(BaseModel):
"""Base pydantic model for argument groups."""

model_config = ConfigDict(json_schema_extra=dict(subcommand=False))
model_config = ConfigDict(json_schema_extra={"subcommand": False})


class BaseCommand(BaseModel):
Expand All @@ -34,7 +32,7 @@ class BaseCommand(BaseModel):
have `subcommand=True`.
"""

model_config = ConfigDict(json_schema_extra=dict(subcommand=True), defer_build=True)
model_config = ConfigDict(json_schema_extra={"subcommand": True}, defer_build=True)


# Public Re-Exports
Expand Down
37 changes: 13 additions & 24 deletions src/gallia/pydantic_argparse/argparse/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,10 @@
"""

import argparse
from collections.abc import Callable, Iterable, Sequence
from typing import (
Any,
Callable,
Iterable,
List,
Optional,
Sequence,
Tuple,
TypeVar,
Union,
cast,
)

Expand Down Expand Up @@ -79,8 +73,8 @@ def __call__(
self,
parser: argparse.ArgumentParser,
namespace: argparse.Namespace,
values: Union[str, Sequence[Any], None],
option_string: Optional[str] = None,
values: str | Sequence[Any] | None,
option_string: str | None = None,
) -> None:
"""Parses arguments into a namespace with the specified subparser.
Expand All @@ -102,7 +96,7 @@ def __call__(
# such, this function signature also accepts 'str' and 'None' types for
# the values argument. However, in reality, this should only ever be a
# list of strings here, so we just do a type cast.
values = cast(List[str], values)
values = cast(list[str], values)

# Get Parser Name and Remaining Argument Strings
parser_name, *arg_strings = values
Expand Down Expand Up @@ -151,14 +145,12 @@ def __init__(
self,
option_strings: Sequence[str],
dest: str,
default: Optional[Union[T, str]] = None,
type: Optional[ # noqa: A002]
Union[Callable[[str], T], argparse.FileType]
] = None,
choices: Optional[Iterable[T]] = None,
default: T | str | None = None,
type_: Callable[[str], T] | argparse.FileType | None = None,
choices: Iterable[T] | None = None,
required: bool = False,
help: Optional[str] = None, # noqa: A002
metavar: Optional[Union[str, Tuple[str, ...]]] = None,
help: str | None = None, # noqa: A002
metavar: str | tuple[str, ...] | None = None,
) -> None:
"""Instantiates the Boolean Optional Action.
Expand Down Expand Up @@ -189,18 +181,15 @@ def __init__(
# Check if this option string is a "--<OPT>" option string
if option_string.startswith("--"):
# Create a "--no-<OPT>" negated option string
option_string = "--no-" + option_string[2:]

# Append the negated option string to the new list as well
_option_strings.append(option_string)
_option_strings.append(f"--no-{option_string[2:]}")

# Initialise Super Class
super().__init__(
option_strings=_option_strings,
dest=dest,
nargs=0,
default=default,
type=type,
type=type_,
choices=choices,
required=required,
help=help,
Expand All @@ -211,8 +200,8 @@ def __call__(
self,
parser: argparse.ArgumentParser,
namespace: argparse.Namespace,
values: Optional[Union[str, Sequence[Any]]],
option_string: Optional[str] = None,
values: str | Sequence[Any] | None,
option_string: str | None = None,
) -> None:
"""Parses the provided boolean arguments into a namespace.
Expand Down
19 changes: 12 additions & 7 deletions src/gallia/pydantic_argparse/argparse/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@

import argparse
import sys
from typing import Generic, NoReturn, Optional, Type, Any, Never
from typing import Any, Generic, Never, NoReturn

from pydantic import BaseModel, ValidationError

from gallia.pydantic_argparse import parsers
from gallia.pydantic_argparse.argparse import actions
from gallia.pydantic_argparse.parsers import command
from gallia.pydantic_argparse.utils.field import ArgFieldInfo
from gallia.pydantic_argparse.utils.nesting import _NestedArgumentParser
from gallia.pydantic_argparse.utils.pydantic import PydanticField, PydanticModelT
Expand Down Expand Up @@ -97,10 +98,10 @@ def __init__(
self.extra_defaults = extra_defaults

# Add Arguments Groups
self._subcommands: Optional[argparse._SubParsersAction] = None
self._subcommands: argparse._SubParsersAction | None = None

# Add Arguments from Model
self._submodels: dict[str, Type[BaseModel]] = dict()
self._submodels: dict[str, type[BaseModel]] = {}
self.model = model
self._add_model(model)

Expand Down Expand Up @@ -170,7 +171,9 @@ def _validation_error(self, error: ValidationError, parser: _NestedArgumentParse
and argument in self.extra_defaults[model]
and self.extra_defaults[model][argument][1] == e["input"]
):
source = f"default of {argument} from {self.extra_defaults[model][argument][0]}: "
source = (
f"default of {argument} from {self.extra_defaults[model][argument][0]}: "
)
else:
# Use the same method, that was used for the CLI generation
argument_name = PydanticField(argument, fields[argument]).arg_names()
Expand Down Expand Up @@ -256,7 +259,7 @@ def _add_version_flag(self) -> None:

def _add_model(
self,
model: Type[BaseModel],
model: type[BaseModel],
arg_group: argparse._ArgumentGroup | None = None,
) -> None:
"""Adds the `pydantic` model to the argument parser.
Expand All @@ -278,7 +281,7 @@ def _add_model(
for field in PydanticField.parse_model(model):
if field.is_a(BaseModel):
if field.is_subcommand():
parsers.command.parse_field(self._commands(), field, self.extra_defaults)
command.parse_field(self._commands(), field, self.extra_defaults)
else:
# for any nested pydantic models, set default factory to model_construct
# method. This allows pydantic to handle if no arguments from a nested
Expand Down Expand Up @@ -309,7 +312,9 @@ def _add_model(

if isinstance(field.info, ArgFieldInfo) and field.info.group is not None:
if field.info.group not in explicit_groups:
explicit_groups[field.info.group] = self.add_argument_group(field.info.group)
explicit_groups[field.info.group] = self.add_argument_group(
field.info.group
)

parsers.add_field(explicit_groups[field.info.group], field)
added = True
Expand Down
5 changes: 1 addition & 4 deletions src/gallia/pydantic_argparse/parsers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@
each contain the `should_parse()` and `parse_field()` functions.
"""

from typing import Optional

from gallia.pydantic_argparse.utils.pydantic import PydanticField, PydanticValidator
from gallia.pydantic_argparse.utils.pydantic import PydanticField

from . import (
boolean,
command,
container,
enum,
literal,
Expand Down
4 changes: 2 additions & 2 deletions src/gallia/pydantic_argparse/parsers/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"""

import argparse
from typing import Type, Any
from typing import Any

from gallia.pydantic_argparse.utils.pydantic import (
PydanticField,
Expand All @@ -21,7 +21,7 @@
def parse_field(
subparser: argparse._SubParsersAction,
field: PydanticField,
extra_defaults: dict[Type, dict[str, Any]] | None = None,
extra_defaults: dict[type, dict[str, Any]] | None = None,
) -> None:
"""Adds command pydantic field to argument parser.
Expand Down
6 changes: 4 additions & 2 deletions src/gallia/pydantic_argparse/parsers/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
import enum
from typing import Any

from gallia.pydantic_argparse.utils.field import ArgFieldInfo
from gallia.pydantic_argparse.utils.pydantic import PydanticField

from .utils import SupportsAddArgument
from ..utils.field import ArgFieldInfo


def should_parse(field: PydanticField) -> bool:
Expand Down Expand Up @@ -68,4 +68,6 @@ def parse_field(
args.update(field.arg_dest())

# Add Enum Field
parser.add_argument(*field.arg_names(), action=action, help=field.description(), metavar=metavar, **args)
parser.add_argument(
*field.arg_names(), action=action, help=field.description(), metavar=metavar, **args
)
9 changes: 5 additions & 4 deletions src/gallia/pydantic_argparse/parsers/literal.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
"""

import argparse
from typing import Any, Literal, get_args

from gallia.pydantic_argparse.utils.field import ArgFieldInfo
from gallia.pydantic_argparse.utils.pydantic import PydanticField

from .utils import SupportsAddArgument
from ..utils.field import ArgFieldInfo

from typing import Literal, get_args, Any


def should_parse(field: PydanticField) -> bool:
Expand Down Expand Up @@ -60,4 +59,6 @@ def parse_field(
args.update(field.arg_dest())

# Add Literal Field
parser.add_argument(*field.arg_names(), action=action, help=field.description(), metavar=metavar, **args)
parser.add_argument(
*field.arg_names(), action=action, help=field.description(), metavar=metavar, **args
)
6 changes: 5 additions & 1 deletion src/gallia/pydantic_argparse/parsers/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ def parse_field(

# Add Standard Field
parser.add_argument(
*field.arg_names(), action=argparse._StoreAction, help=field.description(), metavar=field.metavar(), **args
*field.arg_names(),
action=argparse._StoreAction,
help=field.description(),
metavar=field.metavar(),
**args,
)
8 changes: 4 additions & 4 deletions src/gallia/pydantic_argparse/parsers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from argparse import Action, FileType
from collections.abc import Callable, Iterable
from typing import Any, Protocol, TypeVar, Union
from typing import Any, Protocol, TypeVar

_T = TypeVar("_T")

Expand All @@ -17,11 +17,11 @@ class SupportsAddArgument(Protocol):
def add_argument( # noqa: D102
self,
*name_or_flags: str,
action: Union[str, type[Action]] = ...,
nargs: Union[int, str] = ...,
action: str | type[Action] = ...,
nargs: int | str = ...,
const: Any = ...,
default: Any = ...,
type: Union[Callable[[str], _T], FileType] = ..., # noqa: A002
type: Callable[[str], _T] | FileType = ..., # noqa: A002
choices: Iterable[_T] | None = ...,
required: bool = ...,
help: str | None = ..., # noqa: A002
Expand Down
Empty file.
2 changes: 0 additions & 2 deletions src/gallia/pydantic_argparse/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,3 @@
The public interface exposed by this package is the various described utility
modules each containing helper functions.
"""

from . import namespaces, pydantic, types
4 changes: 2 additions & 2 deletions src/gallia/pydantic_argparse/utils/namespaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
"""

import argparse
from typing import Any, Dict
from typing import Any


def to_dict(namespace: argparse.Namespace) -> Dict[str, Any]:
def to_dict(namespace: argparse.Namespace) -> dict[str, Any]:
"""Converts a nested namespace to a dictionary recursively.
Args:
Expand Down
10 changes: 5 additions & 5 deletions src/gallia/pydantic_argparse/utils/nesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@
"""Utilities to help with parsing arbitrarily nested `pydantic` models."""

from argparse import Namespace
from typing import Any, Generic, Type, TypeAlias
from typing import Any, Generic, TypeAlias

from boltons.iterutils import get_path, remap
from pydantic import BaseModel

from .namespaces import to_dict
from .pydantic import PydanticField, PydanticModelT

ModelT: TypeAlias = PydanticModelT | Type[PydanticModelT] | BaseModel | Type[BaseModel]
ModelT: TypeAlias = PydanticModelT | type[PydanticModelT] | BaseModel | type[BaseModel]


class _NestedArgumentParser(Generic[PydanticModelT]):
"""Parses arbitrarily nested `pydantic` models and inserts values passed at the command line."""

def __init__(
self,
model: PydanticModelT | Type[PydanticModelT],
model: PydanticModelT | type[PydanticModelT],
namespace: Namespace,
) -> None:
self.model = model
self.args = to_dict(namespace)
self.subcommand_path: tuple[str, ...] = tuple()
self.subcommand_path: tuple[str, ...] = ()
self.schema: dict[str, Any] = self._get_nested_model_fields(self.model, namespace)
self.schema = self._remove_null_leaves(self.schema)

Expand All @@ -42,7 +42,7 @@ def contains_subcommand(ns: Namespace, subcommand_path: tuple[str, ...]) -> bool

return True

model_fields: dict[str, Any] = dict()
model_fields: dict[str, Any] = {}

for field in PydanticField.parse_model(model):
key = field.name
Expand Down
Loading

0 comments on commit 37a269c

Please sign in to comment.