From 1e67e84bcf484b9300ba815a3bc36ed0ec6a8f0a Mon Sep 17 00:00:00 2001 From: linuxdaemon Date: Thu, 18 Apr 2024 00:52:24 +0000 Subject: [PATCH] docs: add docstrings to public API methods --- .devcontainer/devcontainer.json | 3 +- .vscode/extensions.json | 3 +- .vscode/settings.json | 4 +- irclib/__init__.py | 2 +- irclib/errors.py | 4 +- irclib/parser.py | 154 ++++++++++++++++++++------------ irclib/util/__init__.py | 2 +- irclib/util/commands.py | 11 ++- irclib/util/compare.py | 8 +- irclib/util/frozendict.py | 25 +++--- irclib/util/numerics.py | 8 +- irclib/util/string.py | 51 +++++++++-- pyproject.toml | 5 +- tests/__init__.py | 1 + tests/commands_test.py | 8 +- tests/frozendict_test.py | 10 +-- tests/numerics_test.py | 8 +- tests/parser_test.py | 152 ++++++++++++++++--------------- tests/string_test.py | 38 ++++---- tests/test_construct.py | 6 +- tests/test_masks.py | 6 +- 21 files changed, 297 insertions(+), 212 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bf1c007..f2430af 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -38,7 +38,8 @@ "ms-python.black-formatter", "ms-python.isort", "ms-azuretools.vscode-docker", - "ms-python.mypy-type-checker" + "ms-python.mypy-type-checker", + "sunipkm.autodocstringpy" ], "settings": { "python.defaultInterpreterPath": "~/.virtualenvs/py-irclib/bin/python", diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 055ed2e..9af92b8 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -10,6 +10,7 @@ "ms-python.black-formatter", "ms-python.isort", "ms-azuretools.vscode-docker", - "ms-python.mypy-type-checker" + "ms-python.mypy-type-checker", + "sunipkm.autodocstringpy" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 5384b6b..de9d932 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,7 @@ "editor.wordBasedSuggestions": "off", "editor.defaultFormatter": "ms-python.black-formatter" }, - "python.analysis.diagnosticMode": "workspace" + "python.analysis.diagnosticMode": "workspace", + "autoDocstringPy.docstringFormat": "google-notypes", + "autoDocstringPy.startOnNewLine": true } diff --git a/irclib/__init__.py b/irclib/__init__.py index 9c9ef1a..3a38a18 100644 --- a/irclib/__init__.py +++ b/irclib/__init__.py @@ -1,4 +1,4 @@ -"""IRC parser and utility library""" +"""IRC parser and utility library.""" from irclib._version import ( __version__, diff --git a/irclib/errors.py b/irclib/errors.py index 6c8a792..d48fa4b 100644 --- a/irclib/errors.py +++ b/irclib/errors.py @@ -1,7 +1,7 @@ -"""Library exceptions""" +"""Library exceptions.""" __all__ = ("ParseError",) class ParseError(ValueError): - """An exception representing some error during parsing""" + """An exception representing some error during parsing.""" diff --git a/irclib/parser.py b/irclib/parser.py index 3f4b611..9b692fc 100644 --- a/irclib/parser.py +++ b/irclib/parser.py @@ -1,5 +1,4 @@ -""" -Simple IRC line parser +"""Simple IRC line parser. Backported from async-irc (https://github.com/snoonetIRC/async-irc.git) """ @@ -71,12 +70,12 @@ class Parseable(metaclass=ABCMeta): - """Abstract class for parseable objects""" + """Abstract class for parseable objects.""" @classmethod @abstractmethod def parse(cls, text: str) -> Self: - """Parse the object from a string""" + """Parse the object from a string.""" raise NotImplementedError @abstractmethod @@ -85,33 +84,40 @@ def __str__(self) -> str: class Cap(Parseable): - """Represents a CAP entity as defined in IRCv3.2""" + """Represents a CAP entity as defined in IRCv3.2.""" def __init__(self, name: str, value: Optional[str] = None) -> None: + """Construct Cap object. + + Args: + name: Cap name + value: Optional Cap value. Defaults to None. + """ self._name = name self._value = value @property def name(self) -> str: - """CAP name""" + """CAP name.""" return self._name @property def value(self) -> Optional[str]: - """CAP value""" + """CAP value.""" return self._value def as_tuple(self) -> Tuple[str, Optional[str]]: - """Get data as a tuple of values""" + """Get data as a tuple of values.""" return self.name, self.value @classmethod def parse(cls, text: str) -> Self: - """Parse a CAP entity from a string""" + """Parse a CAP entity from a string.""" name, _, value = text.partition(CAP_VALUE_SEP) return cls(name, value or None) def __eq__(self, other: object) -> bool: + """Compare against another cap.""" if isinstance(other, str): return self == self.parse(other) @@ -121,6 +127,7 @@ def __eq__(self, other: object) -> bool: return NotImplemented def __ne__(self, other: object) -> bool: + """Compare against another cap.""" if isinstance(other, str): return self != self.parse(other) @@ -130,6 +137,7 @@ def __ne__(self, other: object) -> bool: return NotImplemented def __str__(self) -> str: + """Represent cap as a string.""" if self.value: return CAP_VALUE_SEP.join((self.name, self.value)) @@ -137,11 +145,11 @@ def __str__(self) -> str: class CapList(Parseable, List[Cap]): - """Represents a list of CAP entities""" + """Represents a list of CAP entities.""" @classmethod def parse(cls, text: str) -> Self: - """Parse a list of CAPs from a string""" + """Parse a list of CAPs from a string.""" if text.startswith(":"): text = text[1:] # Remove leading colon @@ -157,6 +165,7 @@ def parse(cls, text: str) -> Self: return cls(caps) def __eq__(self, other: object) -> bool: + """Compare to a string cap list or list of Cap objects.""" if isinstance(other, str): return self == self.parse(other) @@ -166,6 +175,7 @@ def __eq__(self, other: object) -> bool: return NotImplemented def __ne__(self, other: object) -> bool: + """Compare to a string cap list or sequence of Cap objects.""" if isinstance(other, str): return self != self.parse(other) @@ -175,35 +185,40 @@ def __ne__(self, other: object) -> bool: return NotImplemented def __str__(self) -> str: + """Represent the list of caps as a string.""" return CAP_SEP.join(map(str, self)) class MessageTag(Parseable): - """ - Basic class to wrap a message tag - """ + """Basic class to wrap a message tag.""" def __init__( self, name: str, value: str = "", *, has_value: bool = False ) -> None: + """Construct a message tag object. + + Args: + name: Tag name + value: Optional tag value. Defaults to "". + has_value: Whether this tag has a value set. Defaults to False. + """ self._name = name self._value = value or "" self._has_value = has_value @property def name(self) -> str: - """Message tag name""" + """Message tag name.""" return self._name @property def value(self) -> str: - """Message tag value""" + """Message tag value.""" return self._value @staticmethod def unescape(value: str) -> str: - """ - Replace the escaped characters in a tag value with their literals + """Replace the escaped characters in a tag value with their literals. :param value: Escaped string :return: Unescaped string @@ -223,8 +238,7 @@ def unescape(value: str) -> str: @staticmethod def escape(value: str) -> str: - """ - Replace characters with their escaped variants + """Replace characters with their escaped variants. :param value: The raw string :return: The escaped string @@ -233,11 +247,13 @@ def escape(value: str) -> str: @classmethod def parse(cls, text: str) -> Self: - """ - Parse a tag from a string + """Parse a tag from a string. - :param text: The basic tag string - :return: The MessageTag object + Arguments: + text: The basic tag string + + Returns: + The MessageTag object """ name, sep, value = text.partition(TAG_VALUE_SEP) if value: @@ -246,6 +262,7 @@ def parse(cls, text: str) -> Self: return cls(name, value, has_value=bool(sep)) def __eq__(self, other: object) -> bool: + """Compare tag to a string representing a tag or another tag object.""" if isinstance(other, str): return self == self.parse(other) @@ -255,6 +272,7 @@ def __eq__(self, other: object) -> bool: return NotImplemented def __ne__(self, other: object) -> bool: + """Compare tag to a string representing a tag or another tag object.""" if isinstance(other, str): return self != self.parse(other) @@ -264,9 +282,11 @@ def __ne__(self, other: object) -> bool: return NotImplemented def __repr__(self) -> str: + """Represent the tag object in a form useful for debugging.""" return f"MessageTag(name={self.name!r}, value={self.value!r})" def __str__(self) -> str: + """Represent the tag object as a string representation of a tag.""" if self.value or self._has_value: return f"{self.name}{TAG_VALUE_SEP}{self.escape(self.value)}" @@ -274,9 +294,14 @@ def __str__(self) -> str: class TagList(Parseable, Dict[str, MessageTag]): - """Object representing the list of message tags on a line""" + """Object representing the list of message tags on a line.""" def __init__(self, tags: Iterable[MessageTag] = ()) -> None: + """Construct a tag list with optional tags. + + Args: + tags: Tags to initialize the list with. Defaults to (). + """ super().__init__((tag.name, tag) for tag in tags) @staticmethod @@ -300,8 +325,7 @@ def _cmp_type_map(obj: object) -> Dict[str, MessageTag]: @classmethod def parse(cls, text: str) -> Self: - """ - Parse the list of tags from a string + """Parse the list of tags from a string. :param text: The string to parse :return: The parsed object @@ -310,10 +334,11 @@ def parse(cls, text: str) -> Self: @classmethod def from_dict(cls, tags: Dict[str, str]) -> Self: - """Create a TagList from a dict of tags""" + """Create a TagList from a dict of tags.""" return cls(MessageTag(k, v) for k, v in tags.items()) def __eq__(self, other: object) -> bool: + """Compare to another tag list, string, or list of MessageTag objects.""" obj = self._cmp_type_map(other) if obj is NotImplemented: return NotImplemented @@ -321,6 +346,7 @@ def __eq__(self, other: object) -> bool: return dict(self) == dict(obj) def __ne__(self, other: object) -> bool: + """Compare to another tag list, string, or list of MessageTag objects.""" obj = self._cmp_type_map(other) if obj is NotImplemented: return NotImplemented @@ -328,13 +354,12 @@ def __ne__(self, other: object) -> bool: return dict(self) != dict(obj) def __str__(self) -> str: + """Represent the tag list as a string.""" return TAGS_SEP.join(map(str, self.values())) class Prefix(Parseable): - """ - Object representing the prefix of a line - """ + """Object representing the prefix of a line.""" def __init__( self, @@ -342,18 +367,19 @@ def __init__( user: Optional[str] = None, host: Optional[str] = None, ) -> None: + """Construct a prefix.""" self._nick = nick or "" self._user = user or "" self._host = host or "" @property def nick(self) -> str: - """IRC nickname""" + """IRC nickname.""" return self._nick @property def user(self) -> str: - """IRC ident/username""" + """IRC ident/username.""" return self._user @property @@ -363,14 +389,12 @@ def ident(self) -> str: @property def host(self) -> str: - """Hostname""" + """Hostname.""" return self._host @property def mask(self) -> str: - """ - The complete n!u@h mask - """ + """The complete n!u@h mask.""" mask = self.nick if self.user: mask += PREFIX_USER_SEP + self.user @@ -386,11 +410,13 @@ def _data(self) -> Tuple[str, str, str]: @classmethod def parse(cls, text: str) -> Self: - """ - Parse the prefix from a string + """Parse the prefix from a string. - :param text: String to parse - :return: Parsed Object + Arguments: + text: String to parse + + Returns: + Parsed Object """ if not text: return cls() @@ -405,9 +431,11 @@ def parse(cls, text: str) -> Self: return cls(nick, user, host) def __iter__(self) -> Iterator[str]: + """Iterator over the prefix components.""" return iter(self._data) def __eq__(self, other: object) -> bool: + """Compare to prefix string or another prefix object.""" if isinstance(other, str): return self == self.parse(other) @@ -417,6 +445,7 @@ def __eq__(self, other: object) -> bool: return NotImplemented def __ne__(self, other: object) -> bool: + """Compare to prefix string or another prefix object.""" if isinstance(other, str): return self != self.parse(other) @@ -426,24 +455,25 @@ def __ne__(self, other: object) -> bool: return NotImplemented def __bool__(self) -> bool: + """Return whether the prefix contains any values.""" return any(self) def __str__(self) -> str: + """Represent the prefix as a string.""" return self.mask class ParamList(Parseable, List[str]): - """ - An object representing the parameter list from a line - """ + """An object representing the parameter list from a line.""" def __init__(self, *params: str, has_trail: bool = False) -> None: + """Construct list of parameters.""" super().__init__(params) self._has_trail = has_trail @property def has_trail(self) -> bool: - """Does the parameter list end with a trailing parameter""" + """Does the parameter list end with a trailing parameter.""" return self._has_trail @classmethod @@ -464,11 +494,13 @@ def from_list(cls, data: Sequence[str]) -> Self: @classmethod def parse(cls, text: str) -> Self: - """ - Parse a list of parameters + """Parse a list of parameters. - :param text: The list of parameters - :return: The parsed object + Arguments: + text: The list of parameters + + Returns: + The parsed object """ args = [] has_trail = False @@ -485,6 +517,7 @@ def parse(cls, text: str) -> Self: return cls(*args, has_trail=has_trail) def __eq__(self, other: object) -> bool: + """Compare to string or list of parameters.""" if isinstance(other, str): return self == self.parse(other) @@ -494,6 +527,7 @@ def __eq__(self, other: object) -> bool: return NotImplemented def __ne__(self, other: object) -> bool: + """Compare to string or list of parameters.""" if isinstance(other, str): return self != self.parse(other) @@ -503,6 +537,7 @@ def __ne__(self, other: object) -> bool: return NotImplemented def __str__(self) -> str: + """Represent this parameter list as a string.""" if not self: return "" @@ -563,9 +598,7 @@ def _parse_params( class Message(Parseable): - """ - An object representing a parsed IRC line - """ + """An object representing a parsed IRC line.""" def __init__( self, @@ -574,6 +607,7 @@ def __init__( command: str, *parameters: Union[str, List[str], ParamList], ) -> None: + """Construct message object.""" self._tags = _parse_tags(tags) self._prefix = _parse_prefix(prefix) self._command = command @@ -581,31 +615,31 @@ def __init__( @property def tags(self) -> MsgTagList: - """IRC tag list""" + """IRC tag list.""" return self._tags @property def prefix(self) -> MsgPrefix: - """IRC prefix""" + """IRC prefix.""" return self._prefix @property def command(self) -> str: - """IRC command""" + """IRC command.""" return self._command @property def parameters(self) -> ParamList: - """Command parameter list""" + """Command parameter list.""" return self._parameters def as_tuple(self) -> MessageTuple: - """Get the message object as a tuple of values""" + """Get the message object as a tuple of values.""" return self.tags, self.prefix, self.command, self.parameters @classmethod def parse(cls, text: Union[str, bytes]) -> Self: - """Parse an IRC message in to objects""" + """Parse an IRC message in to objects.""" if isinstance(text, memoryview): text = text.tobytes().decode(errors="ignore") @@ -630,6 +664,7 @@ def parse(cls, text: Union[str, bytes]) -> Self: return cls(tags_obj, prefix_obj, command, param_obj) def __eq__(self, other: object) -> bool: + """Compare to another message which can be str, bytes, or a Message object.""" if isinstance(other, (str, bytes)): return self == Message.parse(other) @@ -639,6 +674,7 @@ def __eq__(self, other: object) -> bool: return NotImplemented def __ne__(self, other: object) -> bool: + """Compare to another message which can be str, bytes, or a Message object.""" if isinstance(other, (str, bytes)): return self != Message.parse(other) @@ -648,9 +684,11 @@ def __ne__(self, other: object) -> bool: return NotImplemented def __bool__(self) -> bool: + """Check if the line is empty or not.""" return any(self.as_tuple()) def __str__(self) -> str: + """Represent this Message as a properly formatted IRC line.""" tag_str = "" if self.tags is None else TAGS_SENTINEL + str(self.tags) prefix_str = ( "" if self.prefix is None else PREFIX_SENTINEL + str(self.prefix) diff --git a/irclib/util/__init__.py b/irclib/util/__init__.py index 61c8b65..83c7430 100644 --- a/irclib/util/__init__.py +++ b/irclib/util/__init__.py @@ -1,3 +1,3 @@ -"""IRC utils""" +"""IRC utils.""" __all__ = ("commands", "compare", "frozendict", "numerics", "string") diff --git a/irclib/util/commands.py b/irclib/util/commands.py index 0343557..a231faa 100644 --- a/irclib/util/commands.py +++ b/irclib/util/commands.py @@ -1,4 +1,4 @@ -"""IRC command data and utilities""" +"""IRC command data and utilities.""" from typing import Iterator, List, Mapping, Optional, cast @@ -9,15 +9,14 @@ @attr.s(frozen=True, hash=True, auto_attribs=True) class CommandArgument: - """A single IRC command argument""" + """A single IRC command argument.""" name: str required: Optional[bool] = True @classmethod def parse(cls, s: str) -> "CommandArgument": - """ - Parse a CommandArgument from an argument string + """Parse a CommandArgument from an argument string. >>> CommandArgument.parse("[foo]") CommandArgument(name='foo', required=False) @@ -42,7 +41,7 @@ def parse(cls, s: str) -> "CommandArgument": @attr.s(frozen=True, hash=True, auto_attribs=True) class Command: - """A single IRC command""" + """A single IRC command.""" name: str args: List[CommandArgument] @@ -51,7 +50,7 @@ class Command: class LookupDict(Mapping[str, Command]): - """Command lookup dictionary""" + """Command lookup dictionary.""" def __init__(self, *commands: Command) -> None: for command in commands: diff --git a/irclib/util/compare.py b/irclib/util/compare.py index c934f33..9f6462a 100644 --- a/irclib/util/compare.py +++ b/irclib/util/compare.py @@ -1,6 +1,4 @@ -""" -IRC string comparison utilities -""" +"""IRC string comparison utilities.""" import re from typing import Final @@ -11,9 +9,7 @@ def match_mask(mask: str, pattern: str) -> bool: - """ - Match hostmask patterns in the standard banmask syntax (eg '*!*@host') - """ + """Match hostmask patterns in the standard banmask syntax (eg '*!*@host').""" re_pattern = "" for c in pattern: re_pattern += GLOB_MAP.get(c, re.escape(c)) diff --git a/irclib/util/frozendict.py b/irclib/util/frozendict.py index f3aafd1..ff0a557 100644 --- a/irclib/util/frozendict.py +++ b/irclib/util/frozendict.py @@ -1,7 +1,6 @@ -"""Frozen Dict""" +"""Frozen Dict.""" from typing import ( - Any, Dict, Iterable, Iterator, @@ -12,13 +11,14 @@ Union, ) +from typing_extensions import Self + __all__ = ("FrozenDict",) -V = TypeVar("V", bound=Any) -SelfT = TypeVar("SelfT", bound="FrozenDict[Any]") +_V = TypeVar("_V") -class FrozenDict(Mapping[str, V]): +class FrozenDict(Mapping[str, _V]): """Frozen Mapping. An immutable mapping of string -> Any type @@ -28,15 +28,16 @@ class FrozenDict(Mapping[str, V]): def __init__( self, - seq: Union[Mapping[str, V], Iterable[Tuple[str, V]], None] = None, - **kwargs: V, + seq: Union[Mapping[str, _V], Iterable[Tuple[str, _V]], None] = None, + **kwargs: _V, ) -> None: + """Construct a FrozenDict.""" d = dict(seq, **kwargs) if seq is not None else dict(**kwargs) - self.__data: Dict[str, V] = d + self.__data: Dict[str, _V] = d self.__hash: Optional[int] = None - def copy(self: SelfT, **kwargs: V) -> SelfT: + def copy(self, **kwargs: _V) -> Self: """Copy dict, replacing values according to kwargs. >>> fd = FrozenDict(a=1) @@ -50,16 +51,20 @@ def copy(self: SelfT, **kwargs: V) -> SelfT: """ return self.__class__(self.__data, **kwargs) - def __getitem__(self, k: str) -> V: + def __getitem__(self, k: str) -> _V: + """Retrieve an item from the dict by key.""" return self.__data[k] def __iter__(self) -> Iterator[str]: + """Iterate the keys of the dict.""" return iter(self.__data) def __len__(self) -> int: + """Number of keys in the dict.""" return len(self.__data) def __hash__(self) -> int: + """Get hash for the dict.""" if self.__hash is None: self.__hash = hash(tuple(self.items())) diff --git a/irclib/util/numerics.py b/irclib/util/numerics.py index ce44b07..3b557f6 100644 --- a/irclib/util/numerics.py +++ b/irclib/util/numerics.py @@ -1,4 +1,4 @@ -"""IRC numeric mapping""" +"""IRC numeric mapping.""" from dataclasses import dataclass from typing import Iterator, Mapping @@ -8,12 +8,14 @@ @dataclass class Numeric: + """IRC numeric information.""" + name: str numeric: int class NumericsDict(Mapping[str, Numeric]): - """Mapping of IRC numerics""" + """Mapping of IRC numerics.""" def __init__(self, *args: Numeric) -> None: super().__init__() @@ -23,7 +25,7 @@ def __init__(self, *args: Numeric) -> None: self.num_strs = {str(num.numeric).zfill(3): num for num in args} def from_int(self, n: int) -> Numeric: - """Get a numeric by its number""" + """Get a numeric by its number.""" return self.nums[n] def from_name(self, name: str) -> Numeric: diff --git a/irclib/util/string.py b/irclib/util/string.py index dc49ee5..9e2a5cc 100644 --- a/irclib/util/string.py +++ b/irclib/util/string.py @@ -1,6 +1,4 @@ -""" -IRC string utils -""" +"""IRC string utils.""" import operator import string @@ -21,7 +19,7 @@ class Casemap(NamedTuple): - """String case-map + """String case-map. Represents a mapping of lower to upper case letters """ @@ -31,12 +29,12 @@ class Casemap(NamedTuple): @property def lower_table(self) -> Dict[int, int]: - """The lower->upper translation table""" + """The lower->upper translation table.""" return str.maketrans(self.lower, self.upper) @property def upper_table(self) -> Dict[int, int]: - """The upper->lower table""" + """The upper->lower table.""" return str.maketrans(self.upper, self.lower) @@ -57,7 +55,7 @@ def __getitem__(self, item: int, /) -> Union[str, int, None]: class String(str): - """Case-insensitive string""" + """Case-insensitive string.""" __slots__ = ("_casemap",) @@ -66,11 +64,13 @@ class String(str): def __new__( cls, value: str = "", casemap: Optional[Casemap] = None ) -> "String": + """Construct new String and set casemap.""" o = str.__new__(cls, value) o._casemap = casemap or ASCII # noqa: SLF001 return o def _wrap(self, value: str) -> "String": + """Convert value to String with matching casemap.""" return self.__class__.__new__(self.__class__, value, self.casemap) def __internal_cmp( @@ -85,6 +85,7 @@ def __internal_cmp( return NotImplemented def translate(self, table: TranslateTable) -> "String": + """Apply translation table to string.""" return self._wrap(super().translate(table)) @property @@ -92,6 +93,10 @@ def _capitalize(self) -> "String": return self[:1].upper() + self[1:].lower() def capitalize(self) -> "String": + """Return a capitalized version of the string. + + More specifically, make the first character have upper case and the rest lower case. + """ return self._capitalize @property @@ -99,6 +104,7 @@ def _casefold(self) -> "String": return self.lower() def casefold(self) -> "String": + """Casefold the string according to the casemap.""" return self._casefold @property @@ -106,6 +112,7 @@ def _lower(self) -> "String": return self.translate(self.casemap.lower_table) def lower(self) -> "String": + """Lowercase the string according to the casemap.""" return self._lower @property @@ -113,6 +120,7 @@ def _upper(self) -> "String": return self.translate(self.casemap.upper_table) def upper(self) -> "String": + """Uppercase the string according to the casemap.""" return self._upper def count( @@ -121,6 +129,7 @@ def count( start: Optional[SupportsIndex] = None, end: Optional[SupportsIndex] = None, ) -> int: + """Count substring occurrences.""" return str(self.casefold()).count( self._wrap(sub).casefold(), start, end ) @@ -131,6 +140,7 @@ def startswith( start: Optional[SupportsIndex] = None, end: Optional[SupportsIndex] = None, ) -> bool: + """Check if string starts with a prefix.""" prefix_list: Tuple[str, ...] prefix_list = (prefix,) if isinstance(prefix, str) else prefix @@ -144,6 +154,7 @@ def endswith( start: Optional[SupportsIndex] = None, end: Optional[SupportsIndex] = None, ) -> bool: + """Check if string ends with a suffix.""" suffix_list: Tuple[str, ...] suffix_list = (suffix,) if isinstance(suffix, str) else suffix @@ -157,6 +168,7 @@ def find( start: Optional[SupportsIndex] = None, end: Optional[SupportsIndex] = None, ) -> int: + """Find the substring in string.""" return str(self.casefold()).find(self._wrap(sub).casefold(), start, end) def rfind( @@ -165,6 +177,7 @@ def rfind( start: Optional[SupportsIndex] = None, end: Optional[SupportsIndex] = None, ) -> int: + """Perform a reverse find.""" return str(self.casefold()).rfind( self._wrap(sub).casefold(), start, end ) @@ -175,6 +188,7 @@ def index( start: Optional[SupportsIndex] = None, end: Optional[SupportsIndex] = None, ) -> int: + """Find the index of the substring.""" return str(self.casefold()).index( self._wrap(sub).casefold(), start, end ) @@ -185,11 +199,13 @@ def rindex( start: Optional[SupportsIndex] = None, end: Optional[SupportsIndex] = None, ) -> int: + """Perform a reverse index.""" return str(self.casefold()).rindex( self._wrap(sub).casefold(), start, end ) def partition(self, sep: str) -> Tuple["String", "String", "String"]: + """Partition string on a separator.""" pos = self.find(sep) if pos < 0: return self, self._wrap(""), self._wrap("") @@ -197,6 +213,7 @@ def partition(self, sep: str) -> Tuple["String", "String", "String"]: return self[:pos], self[pos : pos + len(sep)], self[pos + len(sep) :] def rpartition(self, sep: str) -> Tuple["String", "String", "String"]: + """Reverse partition a string on a separator.""" pos = self.rfind(sep) if pos < 0: return self._wrap(""), self._wrap(""), self @@ -206,12 +223,15 @@ def rpartition(self, sep: str) -> Tuple["String", "String", "String"]: def replace( self, old: str, new: str, count: SupportsIndex = -1 ) -> "String": + """Not currently implemented.""" raise NotImplementedError def strip(self, chars: Optional[str] = None) -> "String": + """Remove characters from the beginning and end of the string.""" return self.lstrip(chars).rstrip(chars) def lstrip(self, chars: Optional[str] = None) -> "String": + """Remove characters from the beginning of the string.""" if chars is None: chars = string.whitespace @@ -224,6 +244,7 @@ def lstrip(self, chars: Optional[str] = None) -> "String": return self[start:] def rstrip(self, chars: Optional[str] = None) -> "String": + """Remove characters from the end of the string.""" if chars is None: chars = string.whitespace @@ -243,55 +264,69 @@ def _swapcase(self) -> "String": return self.translate(trans) def swapcase(self) -> "String": + """Swap lower-case and upper-case characters.""" return self._swapcase def title(self) -> str: + """Not currently implemented.""" raise NotImplementedError def split( self, sep: Optional[str] = None, maxsplit: SupportsIndex = -1 ) -> List[str]: + """Not currently implemented.""" raise NotImplementedError def rsplit( self, sep: Optional[str] = None, maxsplit: SupportsIndex = -1 ) -> List[str]: + """Not currently implemented.""" raise NotImplementedError @property def casemap(self) -> Casemap: - """Casemap associated with this string""" + """Casemap associated with this string.""" return self._casemap def __getitem__(self, item: Union[SupportsIndex, slice]) -> "String": + """Get substring.""" return self._wrap(super().__getitem__(item)) def __contains__(self, item: object) -> bool: + """Check if `item` is in string case-insensitively.""" if not isinstance(item, str): return False return self._wrap(item).casefold() in str(self.casefold()) def __add__(self, other: str) -> "String": + """Concat a string to this one.""" return self._wrap(str(self) + other) def __lt__(self, other: str) -> bool: + """Compare another string to this one case-insensitively.""" return self.__internal_cmp(other, operator.lt) def __le__(self, other: str) -> bool: + """Compare another string to this one case-insensitively.""" return self.__internal_cmp(other, operator.le) def __eq__(self, other: object) -> bool: + """Compare another string to this one case-insensitively.""" return self.__internal_cmp(other, operator.eq) def __ne__(self, other: object) -> bool: + """Compare another string to this one case-insensitively.""" return self.__internal_cmp(other, operator.ne) def __gt__(self, other: str) -> bool: + """Compare another string to this one case-insensitively.""" return self.__internal_cmp(other, operator.gt) def __ge__(self, other: str) -> bool: + """Compare another string to this one case-insensitively.""" return self.__internal_cmp(other, operator.ge) def __hash__(self) -> int: + """Hash the lowercase string.""" return hash(str(self.lower())) diff --git a/pyproject.toml b/pyproject.toml index 48a683f..26a18fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,9 +108,10 @@ extend-safe-fixes = [ "SIM117", "SIM108", "ANN201", + "D415", + "D200", ] ignore = [ - "D", "TRY003", # TODO(aspen): Switch to custom exceptions "ANN101", # Due to be deprecated in ruff "ANN102", # Due to be deprecated in ruff @@ -138,7 +139,7 @@ max-line-length = 100 split-on-trailing-comma = false [tool.ruff.lint.pydocstyle] -convention = "pep257" +convention = "google" [tool.flynt] aggressive = true diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..532b006 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Library tests.""" diff --git a/tests/commands_test.py b/tests/commands_test.py index 8a36de0..a24e491 100644 --- a/tests/commands_test.py +++ b/tests/commands_test.py @@ -1,4 +1,4 @@ -"""Test commands util""" +"""Test commands util.""" import pytest @@ -7,7 +7,7 @@ def test_command_lookup() -> None: - """Test looking up a command""" + """Test looking up a command.""" pm = commands.client_commands["privmsg"] assert pm is commands.client_commands["PRIVMSG"] assert pm is commands.client_commands.privmsg @@ -22,13 +22,13 @@ def test_command_lookup() -> None: [("", "foo", True), ("[foo]", "foo", False)], ) def test_arg_parse(text: str, name: str, required: bool) -> None: - """Test parsing an argument string""" + """Test parsing an argument string.""" arg = CommandArgument.parse(text) assert arg.name == name assert arg.required is required def test_parse_error() -> None: - """Test errors from CommandArgument.parse""" + """Test errors from CommandArgument.parse.""" with pytest.raises(ValueError, match=r"Unable to parse argument: .*"): CommandArgument.parse("{a|b}") diff --git a/tests/frozendict_test.py b/tests/frozendict_test.py index bf0716a..93034b0 100644 --- a/tests/frozendict_test.py +++ b/tests/frozendict_test.py @@ -1,10 +1,10 @@ -"""Test frozendict util""" +"""Test frozendict util.""" from irclib.util.frozendict import FrozenDict def test_dict() -> None: - """Test basic dict functions""" + """Test basic dict functions.""" fd = FrozenDict(a=1, b=2) assert fd["a"] == 1 assert fd["b"] == 2 @@ -14,7 +14,7 @@ def test_dict() -> None: def test_init_literal() -> None: - """Test initializing with a dict literal""" + """Test initializing with a dict literal.""" fd = FrozenDict({"a": 1, "b": 2}) assert fd["a"] == 1 assert fd["b"] == 2 @@ -24,7 +24,7 @@ def test_init_literal() -> None: def test_copy() -> None: - """Test dict copy""" + """Test dict copy.""" fd = FrozenDict([("a", 1), ("b", 2)]) fd1 = fd.copy() assert len(fd1) == 2 @@ -43,7 +43,7 @@ def test_copy() -> None: def test_hash() -> None: - """Test dict hashes""" + """Test dict hashes.""" fd = FrozenDict(a=1, b=2) fd1 = FrozenDict({"a": 1, "b": 2}) assert fd == fd1 diff --git a/tests/numerics_test.py b/tests/numerics_test.py index 094f91f..f800fea 100644 --- a/tests/numerics_test.py +++ b/tests/numerics_test.py @@ -1,20 +1,20 @@ -"""Test numerics util""" +"""Test numerics util.""" from irclib.util import numerics def test_lookup() -> None: - """Test looking up a numeric""" + """Test looking up a numeric.""" n = numerics.numerics["001"] assert n is numerics.numerics.from_int(1) assert n is numerics.numerics.from_name("RPL_WELCOME") def test_iter() -> None: - """Test iterating the numerics list""" + """Test iterating the numerics list.""" assert next(iter(numerics.numerics)) == "001" def test_len() -> None: - """Test checking the length of the numerics list""" + """Test checking the length of the numerics list.""" assert len(numerics.numerics) > 0 diff --git a/tests/parser_test.py b/tests/parser_test.py index 73542d6..0ca2f2e 100644 --- a/tests/parser_test.py +++ b/tests/parser_test.py @@ -1,4 +1,4 @@ -"""Test IRC parser""" +"""Test IRC parser.""" from typing import Dict, List, Optional, Tuple, Type, TypedDict @@ -19,6 +19,8 @@ class MsgAtoms(TypedDict): + """Message components for test cases.""" + tags: Dict[str, str] source: str verb: str @@ -26,28 +28,36 @@ class MsgAtoms(TypedDict): class MsgSplitCase(TypedDict): + """Message parsing test case data.""" + input: str atoms: MsgAtoms class UserHostAtoms(TypedDict): + """Prefix components for test cases.""" + nick: str user: str host: str class UserHostSplitCase(TypedDict): + """Prefix parsing test case data.""" + source: str atoms: UserHostAtoms class MsgJoinCase(TypedDict): + """Message construction test case data.""" + atoms: MsgAtoms matches: List[str] def test_line() -> None: - """Test parsing a single IRC message""" + """Test parsing a single IRC message.""" assert Message.parse("COMMAND") == "COMMAND" assert Message.parse("command") == "COMMAND" @@ -64,7 +74,7 @@ def test_line() -> None: class TestCap: - """Test Cap class""" + """Test Cap class.""" @pytest.mark.parametrize( ("name", "value", "expected"), @@ -74,13 +84,13 @@ class TestCap: ], ) def test_str(self, name: str, value: Optional[str], expected: str) -> None: - """Test string conversion""" + """Test string conversion.""" c = Cap(name, value) assert str(c) == expected @pytest.mark.parametrize(("name", "value"), [("a", "b"), ("foo", "bar")]) def test_eq(self, name: str, value: str) -> None: - """Test equals""" + """Test equals.""" assert Cap(name, value) == Cap(name, value) @pytest.mark.parametrize( @@ -88,7 +98,7 @@ def test_eq(self, name: str, value: str) -> None: [("a", "b", "a=b"), ("foo", "bar", "foo=bar")], ) def test_eq_str(self, name: str, value: str, string: str) -> None: - """Test equals string""" + """Test equals string.""" assert Cap(name, value) == string assert string == Cap(name, value) @@ -114,7 +124,7 @@ def test_ne( other_name: str, other_value: Optional[str], ) -> None: - """Test not-equals""" + """Test not-equals.""" assert Cap(name, value) != Cap(other_name, other_value) assert Cap(other_name, other_value) != Cap(name, value) @@ -133,13 +143,13 @@ def test_ne( ], ) def test_ne_str(self, name: str, value: Optional[str], string: str) -> None: - """Test not-equals string""" + """Test not-equals string.""" assert Cap(name, value) != string assert string != Cap(name, value) @pytest.mark.parametrize(("obj", "other"), [(Cap("foo"), 1)]) def test_no_cmp(self, obj: Cap, other: object) -> None: - """Test not-equals""" + """Test not-equals.""" assert obj != other assert other != obj @@ -151,14 +161,14 @@ def test_no_cmp(self, obj: Cap, other: object) -> None: [("vendor.example.org/cap-name", "vendor.example.org/cap-name", None)], ) def test_parse(self, text: str, name: str, value: Optional[str]) -> None: - """Test parsing string""" + """Test parsing string.""" cap = Cap.parse(text) assert cap.name == name assert cap.value == value class TestCapList: - """Test parsing a list of CAPs""" + """Test parsing a list of CAPs.""" @pytest.mark.parametrize( ("text", "expected"), @@ -205,7 +215,7 @@ class TestCapList: def test_parse( self, text: str, expected: Tuple[Tuple[str, Optional[str]], ...] ) -> None: - """Test string parsing""" + """Test string parsing.""" parsed = CapList.parse(text) assert len(parsed) == len(expected) for (name, value), actual in zip(expected, parsed): @@ -214,13 +224,13 @@ def test_parse( @pytest.mark.parametrize("caps", [[], [Cap("a"), Cap("b", "c")]]) def test_eq_list(self, caps: List[Cap]) -> None: - """Test equals list""" + """Test equals list.""" assert CapList(caps) == caps assert caps == CapList(caps) @pytest.mark.parametrize("caps", [[], [Cap("a"), Cap("b", "c")]]) def test_ne_list(self, caps: List[Cap]) -> None: - """Test not-equals list""" + """Test not-equals list.""" b = CapList(caps) != caps assert not b b1 = caps != CapList(caps) @@ -235,7 +245,7 @@ def test_ne_list(self, caps: List[Cap]) -> None: ], ) def test_eq_str(self, caps: List[Cap], text: str) -> None: - """Test equals strings""" + """Test equals strings.""" assert CapList(caps) == text assert text == CapList(caps) @@ -248,7 +258,7 @@ def test_eq_str(self, caps: List[Cap], text: str) -> None: ], ) def test_ne_str(self, caps: List[Cap], text: str) -> None: - """Test not-equals strings""" + """Test not-equals strings.""" b = CapList(caps) != text assert not b b1 = text != CapList(caps) @@ -258,7 +268,7 @@ def test_ne_str(self, caps: List[Cap], text: str) -> None: ("obj", "other"), [(CapList(), None), (CapList(), 0)] ) def test_no_cmp(self, obj: CapList, other: object) -> None: - """Test not-equals""" + """Test not-equals.""" assert obj != other assert other != obj @@ -274,13 +284,13 @@ def test_no_cmp(self, obj: CapList, other: object) -> None: ], ) def test_str(self, obj: CapList, text: str) -> None: - """Test string conversion""" + """Test string conversion.""" assert str(obj) == text assert text == str(obj) class TestMessageTag: - """Test parsing a single message tag""" + """Test parsing a single message tag.""" @pytest.mark.parametrize( ("text", "name", "value"), @@ -294,19 +304,19 @@ class TestMessageTag: ], ) def test_parse(self, text: str, name: str, value: Optional[str]) -> None: - """Test parsing a string""" + """Test parsing a string.""" tag = MessageTag.parse(text) assert tag.name == name assert tag.value == value @pytest.mark.parametrize(("name", "value"), [("a", None), ("a", "b")]) def test_eq(self, name: str, value: Optional[str]) -> None: - """Test equals""" + """Test equals.""" assert MessageTag(name, value) == MessageTag(name, value) @pytest.mark.parametrize(("name", "value"), [("a", None), ("a", "b")]) def test_ne(self, name: str, value: Optional[str]) -> None: - """Test not-equals""" + """Test not-equals.""" b = MessageTag(name, value) != MessageTag(name, value) assert not b @@ -315,7 +325,7 @@ def test_ne(self, name: str, value: Optional[str]) -> None: [("foo", None, "foo"), ("foo", "bar", "foo=bar")], ) def test_eq_str(self, name: str, value: Optional[str], text: str) -> None: - """Test equals string""" + """Test equals string.""" assert MessageTag(name, value) == text assert text == MessageTag(name, value) @@ -324,7 +334,7 @@ def test_eq_str(self, name: str, value: Optional[str], text: str) -> None: [("foo", None, "foo"), ("foo", "bar", "foo=bar")], ) def test_ne_str(self, name: str, value: Optional[str], text: str) -> None: - """Test not-equals string""" + """Test not-equals string.""" b = MessageTag(name, value) != text assert not b b1 = text != MessageTag(name, value) @@ -334,7 +344,7 @@ def test_ne_str(self, name: str, value: Optional[str], text: str) -> None: ("obj", "other"), [(MessageTag(""), None), (MessageTag(""), 1)] ) def test_no_cmp(self, obj: MessageTag, other: object) -> None: - """Test not-equals other types""" + """Test not-equals other types.""" assert obj != other assert other != obj @@ -343,7 +353,7 @@ def test_no_cmp(self, obj: MessageTag, other: object) -> None: class TestTagList: - """Test parsing a tag list""" + """Test parsing a tag list.""" @pytest.mark.parametrize( ("text", "tags"), @@ -358,7 +368,7 @@ class TestTagList: ], ) def test_parse(self, text: str, tags: List[Tuple[str, str]]) -> None: - """Test parsing from string""" + """Test parsing from string.""" tag_list = TagList.parse(text) assert len(tag_list) == len(tags) @@ -372,14 +382,14 @@ def test_parse(self, text: str, tags: List[Tuple[str, str]]) -> None: "tags", [[], [MessageTag("a")], [MessageTag("b", "c")]] ) def test_eq(self, tags: List[MessageTag]) -> None: - """Test equals""" + """Test equals.""" assert TagList(tags) == TagList(tags) @pytest.mark.parametrize( "tags", [[], [MessageTag("a")], [MessageTag("b", "c")]] ) def test_ne(self, tags: List[MessageTag]) -> None: - """Test not-equals""" + """Test not-equals.""" b = TagList(tags) != TagList(tags) assert not b @@ -387,7 +397,7 @@ def test_ne(self, tags: List[MessageTag]) -> None: "tags", [[], [MessageTag("a")], [MessageTag("b", "c")]] ) def test_eq_list(self, tags: List[MessageTag]) -> None: - """Test equals list""" + """Test equals list.""" assert TagList(tags) == tags assert tags == TagList(tags) @@ -395,7 +405,7 @@ def test_eq_list(self, tags: List[MessageTag]) -> None: "tags", [[], [MessageTag("a")], [MessageTag("b", "c")]] ) def test_ne_list(self, tags: List[MessageTag]) -> None: - """Test not-equals list""" + """Test not-equals list.""" b = TagList(tags) != tags assert not b b1 = tags != TagList(tags) @@ -412,7 +422,7 @@ def test_ne_list(self, tags: List[MessageTag]) -> None: def test_eq_dict( self, obj: TagList, other: Dict[str, Optional[str]] ) -> None: - """Test equals dict""" + """Test equals dict.""" assert obj == other assert other == obj @@ -427,7 +437,7 @@ def test_eq_dict( def test_ne_dict( self, obj: TagList, other: Dict[str, Optional[str]] ) -> None: - """Test not-equals dict""" + """Test not-equals dict.""" b = obj != other assert not b b1 = other != obj @@ -438,7 +448,7 @@ def test_ne_dict( [([], ""), ([MessageTag("a")], "a"), ([MessageTag("b", "c")], "b=c")], ) def test_eq_str(self, tags: List[MessageTag], text: str) -> None: - """Test equals strings""" + """Test equals strings.""" assert TagList(tags) == text assert text == TagList(tags) @@ -447,7 +457,7 @@ def test_eq_str(self, tags: List[MessageTag], text: str) -> None: [([], ""), ([MessageTag("a")], "a"), ([MessageTag("b", "c")], "b=c")], ) def test_ne_str(self, tags: List[MessageTag], text: str) -> None: - """Test not-equals strings""" + """Test not-equals strings.""" b = TagList(tags) != text assert not b b1 = text != TagList(tags) @@ -457,7 +467,7 @@ def test_ne_str(self, tags: List[MessageTag], text: str) -> None: ("obj", "other"), [(TagList(), 0), (TagList(), None)] ) def test_no_cmp(self, obj: TagList, other: object) -> None: - """Test not-equals other types""" + """Test not-equals other types.""" assert obj != other assert other != obj @@ -466,7 +476,7 @@ def test_no_cmp(self, obj: TagList, other: object) -> None: class TestPrefix: - """Test parsing a prefix""" + """Test parsing a prefix.""" @pytest.mark.parametrize( ("text", "nick", "user", "host"), @@ -491,7 +501,7 @@ class TestPrefix: ], ) def test_parse(self, text: str, nick: str, user: str, host: str) -> None: - """Test parsing a string""" + """Test parsing a string.""" p = Prefix.parse(text) assert p.nick == nick @@ -511,7 +521,7 @@ def test_parse(self, text: str, nick: str, user: str, host: str) -> None: def test_eq( self, nick: str, user: Optional[str], host: Optional[str] ) -> None: - """Test equals""" + """Test equals.""" assert Prefix(nick, user, host) == Prefix(nick, user, host) @pytest.mark.parametrize( @@ -526,7 +536,7 @@ def test_eq( def test_ne( self, nick: str, user: Optional[str], host: Optional[str] ) -> None: - """Test not-equals""" + """Test not-equals.""" b = Prefix(nick, user, host) != Prefix(nick, user, host) assert not b @@ -546,7 +556,7 @@ def test_eq_str( user: Optional[str], host: Optional[str], ) -> None: - """Test equals string""" + """Test equals string.""" assert Prefix(nick, user, host) == text assert text == Prefix(nick, user, host) @@ -566,7 +576,7 @@ def test_ne_str( user: Optional[str], host: Optional[str], ) -> None: - """Test not-equals string comparisons""" + """Test not-equals string comparisons.""" b = Prefix(nick, user, host) != text assert not b b1 = text != Prefix(nick, user, host) @@ -577,7 +587,7 @@ def test_ne_str( [(Prefix(""), 0), (Prefix(""), None), (Prefix(""), ())], ) def test_no_cmp(self, obj: Prefix, other: object) -> None: - """Test not-equals""" + """Test not-equals.""" assert obj != other assert other != obj @@ -600,7 +610,7 @@ def test_unpack( user: Optional[str], host: Optional[str], ) -> None: - """Test unpacking Prefix""" + """Test unpacking Prefix.""" n, u, h = obj assert (n, u, h) == (nick, user, host) @@ -625,7 +635,7 @@ def test_unpack( def test_bool( self, nick: Optional[str], user: Optional[str], host: Optional[str] ) -> None: - """Test cases where bool(Prefix) == True""" + """Test cases where bool(Prefix) == True.""" assert Prefix(nick, user, host) @pytest.mark.parametrize( @@ -644,12 +654,12 @@ def test_bool( def test_bool_false( self, nick: Optional[str], user: Optional[str], host: Optional[str] ) -> None: - """Test cases where bool(Prefix) == False""" + """Test cases where bool(Prefix) == False.""" assert not Prefix(nick, user, host) class TestParamList: - """Test parsing a parameter list""" + """Test parsing a parameter list.""" @pytest.mark.parametrize( ("obj", "text"), @@ -664,7 +674,7 @@ class TestParamList: ], ) def test_str(self, obj: ParamList, text: str) -> None: - """Test string conversion""" + """Test string conversion.""" assert str(obj) == text @pytest.mark.parametrize( @@ -672,7 +682,7 @@ def test_str(self, obj: ParamList, text: str) -> None: [[], [""], ["a"], ["a", "b"], ["a", ":b"], ["a", "b "], ["a", ""]], ) def test_eq(self, params: List[str]) -> None: - """Test equals""" + """Test equals.""" assert ParamList(*params) == ParamList(*params) assert ParamList(*params) == ParamList.from_list(params) assert ParamList.from_list(params) == ParamList(*params) @@ -682,7 +692,7 @@ def test_eq(self, params: List[str]) -> None: [[], [""], ["a"], ["a", "b"], ["a", ":b"], ["a", "b "], ["a", ""]], ) def test_ne(self, params: List[str]) -> None: - """Test not-equals""" + """Test not-equals.""" b = ParamList(*params) != ParamList(*params) assert not b @@ -690,7 +700,7 @@ def test_ne(self, params: List[str]) -> None: ("obj", "other"), [(ParamList(), 0), (ParamList(), None)] ) def test_no_cmp(self, obj: ParamList, other: object) -> None: - """Test not-equals""" + """Test not-equals.""" assert obj != other assert other != obj @@ -699,22 +709,22 @@ def test_no_cmp(self, obj: ParamList, other: object) -> None: class TestMessage: - """Test parsing an entire IRC message""" + """Test parsing an entire IRC message.""" def test_parse_bytes(self) -> None: - """Test parsing bytes""" + """Test parsing bytes.""" line = Message.parse(b"COMMAND some params :and stuff") assert line.command == "COMMAND" assert line.parameters == ["some", "params", "and stuff"] def test_parse_bytearray(self) -> None: - """Test parsing bytearray""" + """Test parsing bytearray.""" line = Message.parse(bytearray(b"COMMAND some params :and stuff")) assert line.command == "COMMAND" assert line.parameters == ["some", "params", "and stuff"] def test_parse_memoryview(self) -> None: - """Test parsing memoryview""" + """Test parsing memoryview.""" line = Message.parse(memoryview(b"COMMAND some params :and stuff")) assert line.command == "COMMAND" assert line.parameters == ["some", "params", "and stuff"] @@ -743,7 +753,7 @@ def test_parse_memoryview(self) -> None: ], ) def test_str(self, obj: Message, text: str) -> None: - """Test string conversion""" + """Test string conversion.""" assert str(obj) == text @pytest.mark.parametrize( @@ -757,7 +767,7 @@ def test_eq( command: str, params: List[str], ) -> None: - """Test equals""" + """Test equals.""" assert Message(tags, prefix, command, params) == Message( tags, prefix, command, params ) @@ -773,7 +783,7 @@ def test_ne( command: str, params: List[str], ) -> None: - """Test not-equals""" + """Test not-equals.""" b = Message(tags, prefix, command, params) != Message( tags, prefix, command, params ) @@ -788,7 +798,7 @@ def test_ne( ], ) def test_no_cmp(self, obj: Message, other: object) -> None: - """Test Message.__ne__""" + """Test Message.__ne__.""" assert obj != other assert other != obj @@ -797,7 +807,7 @@ def test_no_cmp(self, obj: Message, other: object) -> None: @pytest.mark.parametrize("obj", [Message(None, None, "COMMAND")]) def test_bool(self, obj: Message) -> None: - """Test the cases where bool(Message) should return True""" + """Test the cases where bool(Message) should return True.""" assert obj @pytest.mark.parametrize( @@ -813,15 +823,12 @@ def test_bool(self, obj: Message) -> None: ], ) def test_bool_false(self, obj: Message) -> None: - """Test all the cases where bool(Message) should return False""" + """Test all the cases where bool(Message) should return False.""" assert not obj def test_trail() -> None: - """ - Ensure this parser does not have the same - issue as https://github.com/hexchat/hexchat/issues/2271 - """ + """Ensure this parser does not have the same issue as https://github.com/hexchat/hexchat/issues/2271.""" text = "COMMAND thing thing :thing" parsed = Message.parse(text) @@ -853,14 +860,14 @@ def test_trail() -> None: ], ) def test_comparisons(parse_type: Type[Parseable], text: str) -> None: - """Test comparing parsed objects to strings""" + """Test comparing parsed objects to strings.""" assert text == parse_type.parse(text) assert not text != parse_type.parse(text) @pytest.mark.parametrize("data", parser_tests.data.msg_split["tests"]) def test_msg_split(data: MsgSplitCase) -> None: - """Test splitting a message against the irc-parser-tests data""" + """Test splitting a message against the irc-parser-tests data.""" msg = Message.parse(data["input"]) atoms = data["atoms"].copy() @@ -883,7 +890,7 @@ def test_msg_split(data: MsgSplitCase) -> None: @pytest.mark.parametrize("data", parser_tests.data.userhost_split["tests"]) def test_userhost_split(data: UserHostSplitCase) -> None: - """Ensure that user/host parsing passes against the irc-parser-tests data""" + """Ensure that user/host parsing passes against the irc-parser-tests data.""" source = Prefix.parse(data["source"]) atoms = data["atoms"].copy() @@ -893,17 +900,14 @@ def test_userhost_split(data: UserHostSplitCase) -> None: def test_message_tag_repr() -> None: - """Test repr(MessageTag)""" + """Test repr(MessageTag).""" m = MessageTag("foo", "bar") assert repr(m) == "MessageTag(name='foo', value='bar')" @pytest.mark.parametrize("data", parser_tests.data.msg_join["tests"]) def test_msg_join(data: MsgJoinCase) -> None: - """ - Ensure that message building passes all tests from - the irc-parser-tests library. - """ + """Ensure that message building passes all tests from the irc-parser-tests library.""" atoms = data["atoms"] msg = Message( atoms.get("tags", None), @@ -924,6 +928,6 @@ def test_msg_join(data: MsgJoinCase) -> None: ], ) def test_has_trail(text: str, has_trail: bool) -> None: - """Ensure that a message with trailing arguments is recorded as having a tril""" + """Ensure that a message with trailing arguments is recorded as having a tril.""" msg = Message.parse(text) assert msg.parameters.has_trail == has_trail diff --git a/tests/string_test.py b/tests/string_test.py index d836225..d8661f2 100644 --- a/tests/string_test.py +++ b/tests/string_test.py @@ -1,4 +1,4 @@ -"""Test IRC string class""" +"""Test IRC string class.""" import pytest @@ -6,7 +6,7 @@ def test_comparisons() -> None: - """Test comparison logic""" + """Test comparison logic.""" # pylint: disable=misplaced-comparison-constant, unneeded-not str1 = String("A", ASCII) str1_ = str1 @@ -57,7 +57,7 @@ def test_comparisons() -> None: def test_getitem() -> None: - """Test `getitem`""" + """Test `getitem`.""" s = String("abc") assert s[0] == "a" assert isinstance(s[0], String) @@ -65,19 +65,19 @@ def test_getitem() -> None: def test_capitalize() -> None: - """Test `capitalize`""" + """Test `capitalize`.""" s = String("abC") assert s.capitalize() == "Abc" def test_swapcase() -> None: - """Test `swapcase`""" + """Test `swapcase`.""" s = String("abC") assert s.swapcase() == "ABc" def test_startswith() -> None: - """Test `startswith`""" + """Test `startswith`.""" s = String("aBc") assert s.startswith("a") assert s.startswith("ab") @@ -86,7 +86,7 @@ def test_startswith() -> None: def test_endswith() -> None: - """Test `endswith`""" + """Test `endswith`.""" s = String("aBc") assert s.endswith("c") assert s.endswith("bC") @@ -95,7 +95,7 @@ def test_endswith() -> None: def test_find() -> None: - """Test `find`""" + """Test `find`.""" s = String("AbCB") assert s.find("B") == 1 assert s.find(String("B")) == 1 @@ -103,7 +103,7 @@ def test_find() -> None: def test_rfind() -> None: - """Test `rfind`""" + """Test `rfind`.""" s = String("AbCB") assert s.rfind("B") == 3 assert s.rfind(String("B")) == 3 @@ -111,7 +111,7 @@ def test_rfind() -> None: def test_index() -> None: - """Test `index`""" + """Test `index`.""" s = String("AbCB") assert s.index("B") == 1 assert s.index(String("B")) == 1 @@ -120,7 +120,7 @@ def test_index() -> None: def test_rindex() -> None: - """Test `rindex`""" + """Test `rindex`.""" s = String("AbCB") assert s.rindex("B") == 3 assert s.rindex(String("B")) == 3 @@ -129,30 +129,30 @@ def test_rindex() -> None: def test_count() -> None: - """Test `count`""" + """Test `count`.""" assert String("aabbcc").count("a") == 2 assert String("aabbcc").count(String("a")) == 2 def test_hash() -> None: - """Test hashing a string""" + """Test hashing a string.""" assert hash(String("ABC")) == hash(String("abc")) def test_contains() -> None: - """Test `in` operator""" + """Test `in` operator.""" assert "a" in String("abc") assert String("a") in String("abc") assert 445 not in String("abc") def test_instance() -> None: - """Ensure that basic strings aren't being counted as String""" + """Ensure that basic strings aren't being counted as String.""" assert not isinstance("a", String) def test_rfc1459() -> None: - """Test rfc1459 casemap""" + """Test rfc1459 casemap.""" str1 = String("ABC|{", RFC1459) str2 = String("abc\\[", RFC1459) @@ -162,21 +162,21 @@ def test_rfc1459() -> None: def test_partition() -> None: - """Test partition""" + """Test partition.""" s = String("ABc") assert s.partition("b") == ("A", "B", "c") assert s.partition("d") == ("ABc", "", "") def test_rpartition() -> None: - """Test rpartition""" + """Test rpartition.""" s = String("AbCbAcBa") assert s.rpartition("b") == ("AbCbAc", "B", "a") assert s.rpartition("d") == ("", "", "AbCbAcBa") def test_strip() -> None: - """Test stripping characters""" + """Test stripping characters.""" s = String("AbCbAcBa") assert s.strip("d") == "AbCbAcBa" assert s.strip("a") == "bCbAcB" diff --git a/tests/test_construct.py b/tests/test_construct.py index cfc0859..7aa1f83 100644 --- a/tests/test_construct.py +++ b/tests/test_construct.py @@ -1,12 +1,10 @@ -""" -Test constructing message objects -""" +"""Test constructing message objects.""" from irclib.parser import Message def test_param_construct() -> None: - """Test constructing Message objects""" + """Test constructing Message objects.""" msg = Message(None, None, "PRIVMSG", "#channel", "Message thing") assert str(msg) == "PRIVMSG #channel :Message thing" diff --git a/tests/test_masks.py b/tests/test_masks.py index b2478f1..a0d64c3 100644 --- a/tests/test_masks.py +++ b/tests/test_masks.py @@ -1,4 +1,4 @@ -"""Test IRC string comparisons""" +"""Test IRC string comparisons.""" from typing import List, TypedDict @@ -9,6 +9,8 @@ class MaskCase(TypedDict): + """Mask match test case data.""" + mask: str matches: List[str] fails: List[str] @@ -16,7 +18,7 @@ class MaskCase(TypedDict): @pytest.mark.parametrize("data", parser_tests.data.mask_match["tests"]) def test_mask_match(data: MaskCase) -> None: - """Test the mask matching logic""" + """Test the mask matching logic.""" pattern = data["mask"] for hostmask in data["matches"]: