Skip to content

Enable ruff D ruleset for pydocstyle static analysis of docstrings #1429

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

Merged
merged 1 commit into from
May 25, 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
16 changes: 12 additions & 4 deletions cmd2/argparse_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ class CompletionItem(str): # noqa: SLOT000
"""

def __new__(cls, value: object, *_args: Any, **_kwargs: Any) -> 'CompletionItem':
"""Responsible for creating and returning a new instance, called before __init__ when an object is instantiated."""
return super().__new__(cls, value)

def __init__(self, value: object, description: str = '', *args: Any) -> None:
Expand Down Expand Up @@ -313,14 +314,16 @@ def orig_value(self) -> Any:
class ChoicesProviderFuncBase(Protocol):
"""Function that returns a list of choices in support of tab completion."""

def __call__(self) -> list[str]: ... # pragma: no cover
def __call__(self) -> list[str]: # pragma: no cover
"""Enable instances to be called like functions."""


@runtime_checkable
class ChoicesProviderFuncWithTokens(Protocol):
"""Function that returns a list of choices in support of tab completion and accepts a dictionary of prior arguments."""

def __call__(self, *, arg_tokens: dict[str, list[str]] = {}) -> list[str]: ... # pragma: no cover # noqa: B006
def __call__(self, *, arg_tokens: dict[str, list[str]] = {}) -> list[str]: # pragma: no cover # noqa: B006
"""Enable instances to be called like functions."""


ChoicesProviderFunc = Union[ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens]
Expand All @@ -336,7 +339,8 @@ def __call__(
line: str,
begidx: int,
endidx: int,
) -> list[str]: ... # pragma: no cover
) -> list[str]: # pragma: no cover
"""Enable instances to be called like functions."""


@runtime_checkable
Expand All @@ -351,7 +355,8 @@ def __call__(
endidx: int,
*,
arg_tokens: dict[str, list[str]] = {}, # noqa: B006
) -> list[str]: ... # pragma: no cover
) -> list[str]: # pragma: no cover
"""Enable instances to be called like functions."""


CompleterFunc = Union[CompleterFuncBase, CompleterFuncWithTokens]
Expand Down Expand Up @@ -391,13 +396,15 @@ def __init__(

@property
def completer(self) -> CompleterFunc:
"""Retreive the internal Completer function, first type checking to ensure it is the right type."""
if not isinstance(self.to_call, (CompleterFuncBase, CompleterFuncWithTokens)): # pragma: no cover
# this should've been caught in the constructor, just a backup check
raise TypeError('Function is not a CompleterFunc')
return self.to_call

@property
def choices_provider(self) -> ChoicesProviderFunc:
"""Retreive the internal ChoicesProvider function, first type checking to ensure it is the right type."""
if not isinstance(self.to_call, (ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens)): # pragma: no cover
# this should've been caught in the constructor, just a backup check
raise TypeError('Function is not a ChoicesProviderFunc')
Expand Down Expand Up @@ -1350,6 +1357,7 @@ class Cmd2AttributeWrapper:
"""

def __init__(self, attribute: Any) -> None:
"""Initialize Cmd2AttributeWrapper instances."""
self.__attribute = attribute

def get(self) -> Any:
Expand Down
37 changes: 17 additions & 20 deletions cmd2/cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1341,7 +1341,8 @@ def ppaged(self, msg: Any, *, end: str = '\n', chop: bool = False) -> None:
# ----- Methods related to tab completion -----

def _reset_completion_defaults(self) -> None:
"""Resets tab completion settings
"""Reset tab completion settings.

Needs to be called each time readline runs tab completion.
"""
self.allow_appended_space = True
Expand Down Expand Up @@ -2406,47 +2407,43 @@ def _raise_keyboard_interrupt(self) -> None:
raise KeyboardInterrupt("Got a keyboard interrupt")

def precmd(self, statement: Union[Statement, str]) -> Statement:
"""Hook method executed just before the command is executed by
[cmd2.Cmd.onecmd][] and after adding it to history.
"""Ran just before the command is executed by [cmd2.Cmd.onecmd][] and after adding it to history (cmd Hook method).

:param statement: subclass of str which also contains the parsed input
:return: a potentially modified version of the input Statement object

See [cmd2.Cmd.register_postparsing_hook][] and
[cmd2.Cmd.register_precmd_hook][] for more robust ways
to run hooks before the command is executed. See
[Hooks](../features/hooks.md) for more information.
See [cmd2.Cmd.register_postparsing_hook][] and [cmd2.Cmd.register_precmd_hook][] for more robust ways
to run hooks before the command is executed. See [Hooks](../features/hooks.md) for more information.
"""
return Statement(statement) if not isinstance(statement, Statement) else statement

def postcmd(self, stop: bool, statement: Union[Statement, str]) -> bool: # noqa: ARG002
"""Hook method executed just after a command is executed by
[cmd2.Cmd.onecmd][].
"""Ran just after a command is executed by [cmd2.Cmd.onecmd][] (cmd inherited Hook method).

:param stop: return `True` to request the command loop terminate
:param statement: subclass of str which also contains the parsed input

See [cmd2.Cmd.register_postcmd_hook][] and [cmd2.Cmd.register_cmdfinalization_hook][] for more robust ways
to run hooks after the command is executed. See
[Hooks](../features/hooks.md) for more information.
to run hooks after the command is executed. See [Hooks](../features/hooks.md) for more information.
"""
return stop

def preloop(self) -> None:
"""Hook method executed once when the [cmd2.Cmd.cmdloop][]
method is called.
"""Ran once when the [cmd2.Cmd.cmdloop][] method is called (cmd inherited Hook method).

This method is a stub that does nothing and exists to be overridden by subclasses.

See [cmd2.Cmd.register_preloop_hook][] for a more robust way
to run hooks before the command loop begins. See
[Hooks](../features/hooks.md) for more information.
See [cmd2.Cmd.register_preloop_hook][] for a more robust wayto run hooks before the command loop begins.
See [Hooks](../features/hooks.md) for more information.
"""

def postloop(self) -> None:
"""Hook method executed once when the [cmd2.Cmd.cmdloop][] method is about to return.
"""Ran once when the [cmd2.Cmd.cmdloop][] method is about to return (cmd inherited Hook Method).

This method is a stub that does nothing and exists to be overridden by subclasses.

See [cmd2.Cmd.register_postloop_hook][] for a more robust way
to run hooks after the command loop completes. See
[Hooks](../features/hooks.md) for more information.
See [cmd2.Cmd.register_postloop_hook][] for a more robust way to run hooks after the command loop completes.
See [Hooks](../features/hooks.md) for more information.
"""

def parseline(self, line: str) -> tuple[str, str, str]:
Expand Down
10 changes: 7 additions & 3 deletions cmd2/command_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ class CommandSet:
"""

def __init__(self) -> None:
# Private reference to the CLI instance in which this CommandSet running.
# This will be set when the CommandSet is registered and it should be
# accessed by child classes using the self._cmd property.
"""Private reference to the CLI instance in which this CommandSet running.

This will be set when the CommandSet is registered and it should be
accessed by child classes using the self._cmd property.
"""
self.__cmd_internal: Optional[cmd2.Cmd] = None

self._settables: dict[str, Settable] = {}
Expand Down Expand Up @@ -147,10 +149,12 @@ def on_unregistered(self) -> None:

@property
def settable_prefix(self) -> str:
"""Read-only accessor for the underlying private settable_prefix field."""
return self._settable_prefix

@property
def settables(self) -> Mapping[str, Settable]:
"""Read-only accessor for the underlying private settables field."""
return self._settables

def add_settable(self, settable: Settable) -> None:
Expand Down
13 changes: 7 additions & 6 deletions cmd2/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@


def with_category(category: str) -> Callable[[CommandFunc], CommandFunc]:
"""A decorator to apply a category to a ``do_*`` command method.
"""Decorate a ``do_*`` command method to apply a category.

:param category: the name of the category in which this command should
be grouped when displaying the list of commands.
Expand Down Expand Up @@ -138,7 +138,7 @@ def with_argument_list(
RawCommandFuncOptionalBoolReturn[CommandParent],
Callable[[ArgListCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]],
]:
"""A decorator to alter the arguments passed to a ``do_*`` method.
"""Decorate a ``do_*`` method to alter the arguments passed to it so it is passed a list[str].

Default passes a string of whatever the user typed. With this decorator, the
decorated method will receive a list of arguments parsed from user input.
Expand All @@ -160,7 +160,8 @@ def do_echo(self, arglist):
import functools

def arg_decorator(func: ArgListCommandFunc[CommandParent]) -> RawCommandFuncOptionalBoolReturn[CommandParent]:
"""Decorator function that ingests an Argument List function and returns a raw command function.
"""Decorate function that ingests an Argument List function and returns a raw command function.

The returned function will process the raw input into an argument list to be passed to the wrapped function.

:param func: The defined argument list command function
Expand Down Expand Up @@ -278,8 +279,7 @@ def with_argparser(
preserve_quotes: bool = False,
with_unknown_args: bool = False,
) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]:
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
with the given instance of argparse.ArgumentParser.
"""Decorate a ``do_*`` method to populate its ``args`` argument with the given instance of argparse.ArgumentParser.

:param parser: unique instance of ArgumentParser or a callable that returns an ArgumentParser
:param ns_provider: An optional function that accepts a cmd2.Cmd or cmd2.CommandSet object as an argument and returns an
Expand Down Expand Up @@ -327,7 +327,8 @@ def do_argprint(self, args, unknown):
import functools

def arg_decorator(func: ArgparseCommandFunc[CommandParent]) -> RawCommandFuncOptionalBoolReturn[CommandParent]:
"""Decorator function that ingests an Argparse Command Function and returns a raw command function.
"""Decorate function that ingests an Argparse Command Function and returns a raw command function.

The returned function will process the raw input into an argparse Namespace to be passed to the wrapped function.

:param func: The defined argparse command function
Expand Down
1 change: 1 addition & 0 deletions cmd2/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class to gain access to the historical record.
_history_items_field = 'history_items'

def __init__(self, seq: Iterable[HistoryItem] = ()) -> None:
"""Initialize History instances."""
super().__init__(seq)
self.session_start_index = 0

Expand Down
1 change: 1 addition & 0 deletions cmd2/py_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class PyBridge:
"""

def __init__(self, cmd2_app: 'cmd2.Cmd', *, add_to_history: bool = True) -> None:
"""Initialize PyBridge instances."""
self._cmd2_app = cmd2_app
self._add_to_history = add_to_history
self.cmd_echo = False
Expand Down
3 changes: 3 additions & 0 deletions cmd2/transcript.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Cmd2TestCase(unittest.TestCase):
cmdapp: Optional['Cmd'] = None

def setUp(self) -> None:
"""Instructions that will be executed before each test method."""
if self.cmdapp:
self._fetch_transcripts()

Expand All @@ -50,11 +51,13 @@ def setUp(self) -> None:
self.cmdapp.stdout = cast(TextIO, utils.StdSim(cast(TextIO, self.cmdapp.stdout)))

def tearDown(self) -> None:
"""Instructions that will be executed after each test method."""
if self.cmdapp:
# Restore stdout
self.cmdapp.stdout = self._orig_stdout

def runTest(self) -> None: # was testall # noqa: N802
"""Override of the default runTest method for the unittest.TestCase class."""
if self.cmdapp:
its = sorted(self.transcripts.items())
for fname, transcript in its:
Expand Down
17 changes: 13 additions & 4 deletions cmd2/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ def line_buffering(self) -> bool:
return False

def __getattr__(self, item: str) -> Any:
"""When an attribute lookup fails to find the attribute in the usual places, this special method is called."""
if item in self.__dict__:
return self.__dict__[item]
return getattr(self.inner_stream, item)
Expand All @@ -540,6 +541,7 @@ class ByteBuf:
NEWLINES = (b'\n', b'\r')

def __init__(self, std_sim_instance: StdSim) -> None:
"""Initialize the ByteBuf instance."""
self.byte_buf = bytearray()
self.std_sim_instance = std_sim_instance

Expand Down Expand Up @@ -671,17 +673,22 @@ class ContextFlag:
"""

def __init__(self) -> None:
# When this flag has a positive value, it is considered set.
# When it is 0, it is not set. It should never go below 0.
"""When this flag has a positive value, it is considered set. When it is 0, it is not set.

It should never go below 0.
"""
self.__count = 0

def __bool__(self) -> bool:
"""Define the truth value of an object when it is used in a boolean context."""
return self.__count > 0

def __enter__(self) -> None:
"""When a with block is entered, the __enter__ method of the context manager is called."""
self.__count += 1

def __exit__(self, *args: object) -> None:
"""When the execution flow exits a with statement block this is called, regardless of whether an exception occurred."""
self.__count -= 1
if self.__count < 0:
raise ValueError("count has gone below 0")
Expand Down Expand Up @@ -1224,8 +1231,10 @@ def strip_doc_annotations(doc: str) -> str:


def similarity_function(s1: str, s2: str) -> float:
# The ratio from s1,s2 may be different to s2,s1. We keep the max.
# See https://docs.python.org/3/library/difflib.html#difflib.SequenceMatcher.ratio
"""Ratio from s1,s2 may be different to s2,s1. We keep the max.

See https://docs.python.org/3/library/difflib.html#difflib.SequenceMatcher.ratio
"""
return max(SequenceMatcher(None, s1, s2).ratio(), SequenceMatcher(None, s2, s1).ratio())


Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ select = [
"C90", # McCabe cyclomatic complexity (warn about functions that are too complex)
"COM", # flake8-commas (forces commas at the end of every type of iterable/container
# "CPY", # flake8-copyright (warn about missing copyright notice at top of file - currently in preview)
# "D", # pydocstyle (warn about things like missing docstrings)
"D", # pydocstyle (warn about things like missing docstrings)
# "DOC", # pydoclint (docstring warnings - currently in preview)
# "DJ", # flake8-django (Django-specific warnings)
"DTZ", # flake8-datetimez (warn about datetime calls where no timezone is specified)
Expand Down
Loading