Skip to content

Commit

Permalink
Update code and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
FasterSpeeding committed Nov 17, 2024
1 parent cfcb6da commit 8173866
Show file tree
Hide file tree
Showing 65 changed files with 2,370 additions and 6,945 deletions.
9 changes: 4 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ and should also use styles which are specific to [pdoc](https://pdoc.dev/docs/pd
A few examples of pdoc style would be:

* Links: Unlike sphinx, regardless of whether you're linking to a module, class, function or variable the link will
always be in the style of `` `link.to.thing` `` with no type information included and relative links being supported
for types in the current module (e.g. `` `Class.attribute` ``.
always be in the style of `` [link.to.thing][] `` with no type information included.
* Documenting fluent methods: The return type for fluent methods should be given as `Self` with the description for it
following the lines of something like "the {x} instance to enable chained calls".
* Documented types (such as for parameters and return types) which are unions should be documented using `|` style
and `T | None`/`T | hikari.UndefinedType` are preferred over `typing.Optional[T]`/`hikari.UndefinedOr[T]`
* Union types sohuld be annotated in code and documentation using `|` style and `T | None`/`T | hikari.UndefinedType`
are preferred over `T | None`/`hikari.UndefinedOr[T]`

### CHANGELOG.md

Expand All @@ -65,7 +64,7 @@ good references for how projects should be type-hinted to be `type-complete`.
**NOTES**

* This project deviates from the common convention of importing types from the typing module and instead
imports the typing module itself to use generics and types in it like `typing.Union` and `typing.Optional`.
imports the typing module itself to use generics and types in it like `typing.Annotated`.
* Since this project supports python 3.9+, the `typing` types which were deprecated by
[PEP 585](https://www.python.org/dev/peps/pep-0585/) should be avoided in favour of their `collections.abc`,
builtin, `re` and `contextlib` equivalents.
Expand Down
2 changes: 0 additions & 2 deletions dev-requirements/tests.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
-r ./constraints.in

freezegun>=1.2.2
# unittest.mock doesn't work with inspect.signature on all covered versions.
mock>=5.0.2
pytest-asyncio>=0.20.1
pytest-timeout>=2.1
pytest-xdist>=3.1
Expand Down
10 changes: 5 additions & 5 deletions docs_src/usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def get_video(value: str) -> Video: ...


def annotations_example() -> None:
from typing import Annotated, Optional
from typing import Annotated

from tanjun.annotations import Bool, Converted, Int, Ranged, Str, User

Expand All @@ -173,7 +173,7 @@ async def command(
name: Annotated[Str, "description"],
age: Annotated[Int, Ranged(13, 130), "an int option with a min, max of 13, 130"],
video: Annotated[Video, Converted(get_video), "a required string option which is converted with get_video"],
user: Annotated[Optional[User], "a user option which defaults to None"] = None,
user: Annotated[User | None, "a user option which defaults to None"] = None,
enabled: Annotated[Bool, "a bool option which defaults to True"] = True,
) -> None: ...

Expand All @@ -195,7 +195,7 @@ def responding_to_commands_example() -> None:
@tanjun.as_message_command("name")
@tanjun.as_user_menu("name")
async def command(
ctx: tanjun.abc.Context, user: typing.Annotated[typing.Optional[annotations.User], "The user to target"] = None
ctx: tanjun.abc.Context, user: typing.Annotated[annotations.User | None, "The user to target"] = None
) -> None:
user = user or ctx.author
message = await ctx.respond(
Expand Down Expand Up @@ -226,7 +226,7 @@ def autocomplete_example(component: tanjun.Component) -> None:
@tanjun.with_str_slash_option("opt1", "description")
@tanjun.with_str_slash_option("opt2", "description", default=None)
@tanjun.as_slash_command("name", "description")
async def slash_command(ctx: tanjun.abc.SlashContext, opt1: str, opt2: typing.Optional[str]) -> None: ...
async def slash_command(ctx: tanjun.abc.SlashContext, opt1: str, opt2: str | None) -> None: ...

@slash_command.with_str_autocomplete("opt1")
async def opt1_autocomplete(ctx: tanjun.abc.AutocompleteContext, value: str) -> None:
Expand Down Expand Up @@ -323,7 +323,7 @@ async def success_hook(ctx: tanjun.abc.Context) -> None: ...

def error_hook_example(hooks: tanjun.abc.AnyHooks) -> None:
@hooks.with_on_error # hooks.add_on_error
async def error_hook(ctx: tanjun.abc.Context, error: Exception) -> typing.Optional[bool]: ...
async def error_hook(ctx: tanjun.abc.Context, error: Exception) -> bool | None: ...


def parser_error_hook_example(hooks: tanjun.abc.AnyHooks) -> None:
Expand Down
4 changes: 1 addition & 3 deletions examples/error_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
# You should have received a copy of the CC0 Public Domain Dedication along with this software.
# If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
"""Examples of how hooks may be used within a bot with a focus on error handling."""
import typing

import hikari

import tanjun
Expand All @@ -23,7 +21,7 @@


@hooks.with_on_error
async def on_error(ctx: tanjun.abc.Context, exc: Exception) -> typing.Optional[bool]:
async def on_error(ctx: tanjun.abc.Context, exc: Exception) -> bool | None:
"""General error handler.
This will be called on all errors raised during execution except errors
Expand Down
6 changes: 3 additions & 3 deletions examples/impls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ async def connect_to_database(*args: typing.Any, **kwargs: typing.Any) -> typing

class DatabaseImpl:
def __init__(self) -> None:
self._conn: typing.Optional[typing.Any] = None
self._conn: typing.Any | None = None

async def connect(self, config: examples.config.ExampleConfig = tanjun.inject(type=examples.config.ExampleConfig)):
self._conn = await connect_to_database(password=config.database_password, url=config.database_url)

async def get_guild_info(self, guild_id: int) -> typing.Optional[protos.GuildConfig]:
async def get_guild_info(self, guild_id: int) -> protos.GuildConfig | None:
raise NotImplementedError

async def get_user_info(self, user_id: int) -> typing.Optional[protos.UserInfo]:
async def get_user_info(self, user_id: int) -> protos.UserInfo | None:
raise NotImplementedError

async def remove_user(self, user_id: int) -> None:
Expand Down
4 changes: 2 additions & 2 deletions examples/protos.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class UserInfo(typing.Protocol): ...


class DatabaseProto(typing.Protocol):
async def get_guild_info(self, guild_id: int) -> typing.Optional[GuildConfig]:
async def get_guild_info(self, guild_id: int) -> GuildConfig | None:
raise NotImplementedError

async def get_user_info(self, user_id: int) -> typing.Optional[UserInfo]:
async def get_user_info(self, user_id: int) -> UserInfo | None:
raise NotImplementedError

async def remove_user(self, user_id: int) -> None:
Expand Down
3 changes: 1 addition & 2 deletions examples/slash_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
# `tanjun.abc.SlashContext` to allow state to still be tracked and ensure
# better compatibility.
import asyncio
import typing

import hikari

Expand Down Expand Up @@ -58,7 +57,7 @@ async def nsfw_command(ctx: tanjun.abc.Context) -> None:
# to the "name" argument as type str if it was provided else None.
@tanjun.with_str_slash_option("name", "Option description", default=None)
@nested_group.as_sub_command("hi", "command description")
async def hi_command(ctx: tanjun.abc.Context, name: typing.Optional[str], member: hikari.Member) -> None:
async def hi_command(ctx: tanjun.abc.Context, name: str | None, member: hikari.Member) -> None:
if name:
await ctx.respond(f"Hi, {name} and {member.username}")

Expand Down
10 changes: 0 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ Changelog = "https://tanjun.cursed.solutions/changelog"
name = "tanjun"

[tool.black]
extend-exclude = "^\\/tanjun\\/_internal\\/vendor\\/.*$"
include = ".*pyi?$"
line-length = 120
skip-magic-trailing-comma = true
Expand All @@ -64,14 +63,12 @@ exclude_lines = [
"^\\s*@abc.abstractmethod$",
"^if typing.TYPE_CHECKING:$"
]
omit = ["tanjun/_internal/vendor/*"]

[tool.flake8]
accept-encodings = "utf-8"
count = true
docstring-convention = "numpy"
eradicate-aggressive = true
exclude = ["tanjun/_internal/vendor/**"]
extend-select = ["TC100", "TC101"]
force-future-annotations = true
ignore-decorators = "overload"
Expand Down Expand Up @@ -124,7 +121,6 @@ per-file-ignores = [
[tool.isort]
profile = "black"
force_single_line = true
skip = ["tanjun/_internal/vendor"]

[tool.mypy]
# some good strict settings
Expand All @@ -143,9 +139,6 @@ warn_redundant_casts = false
allow_redefinition = true
disable_error_code = ["type-abstract"]

# Ignore vendored modules
exclude = ["tanjun/_internal/vendor/"]

[tool.piped]
default_sessions = [
"reformat",
Expand All @@ -158,7 +151,6 @@ default_sessions = [
"verify-types",
]
extra_test_installs = ["."]
path_ignore = "tanjun\\/_internal\\/vendor\\/"
project_name = "tanjun"
top_level_targets = ["./docs_src", "./examples", "./noxfile.py", "./tanjun", "./tests"]

Expand All @@ -183,7 +175,6 @@ python_versions = ["3.11", "3.12", "3.13", "3.14-dev"]
exclude = "docs_src"

[tool.pyright]
exclude = ["tanjun/_internal/vendor"]
include = ["docs_src", "examples", "tanjun", "noxfile.py", "tests"]

pythonVersion = "3.11"
Expand Down Expand Up @@ -233,4 +224,3 @@ tanjun.clients:(_LoaderDescriptor|_UnloaderDescriptor)
| tanjun.dependencies.reloaders:(_PathLoader|_ScanResult)
| .*Proto
"""
exclude-modules = "tanjun._internal.vendor"
52 changes: 20 additions & 32 deletions tanjun/_internal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,45 +37,35 @@
import copy as copy_
import enum
import functools
import inspect
import itertools
import logging
import operator
import sys
import types
import typing
from collections import abc as collections

import hikari

from .. import errors
from .vendor import inspect

if typing.TYPE_CHECKING:
import typing_extensions

from .. import abc as tanjun

_T = typing.TypeVar("_T")
_P = typing_extensions.ParamSpec("_P")
_P = typing.ParamSpec("_P")

_ContextT = typing.TypeVar("_ContextT", bound=tanjun.Context)
_CoroT = collections.Coroutine[typing.Any, typing.Any, _T]
_TreeT = dict[
typing.Union[str, "_IndexKeys"],
typing.Union["_TreeT", list[tuple[list[str], tanjun.MessageCommand[typing.Any]]]],
]
_TreeT = dict["str | _IndexKeys", "_TreeT | list[tuple[list[str], tanjun.MessageCommand[typing.Any]]]"]


_KeyT = typing.TypeVar("_KeyT")
_OtherT = typing.TypeVar("_OtherT")

_LOGGER = logging.getLogger("hikari.tanjun")

if sys.version_info >= (3, 10):
UnionTypes = frozenset((typing.Union, types.UnionType))

else:
UnionTypes = frozenset((typing.Union,))
UnionTypes = frozenset((typing.Union, types.UnionType))


class _DefaultEnum(enum.Enum):
Expand Down Expand Up @@ -158,7 +148,7 @@ def __len__(self) -> int:
_KEYWORD_TYPES = {inspect.Parameter.KEYWORD_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD}


def get_kwargs(callback: collections.Callable[..., typing.Any], /) -> typing.Union[list[str], None]:
def get_kwargs(callback: collections.Callable[..., typing.Any], /) -> list[str] | None:
"""Get a list of the keyword argument names for a callback.
Parameters
Expand Down Expand Up @@ -286,10 +276,10 @@ async def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _T:


class _WrappedProto(typing.Protocol):
wrapped_command: typing.Optional[tanjun.ExecutableCommand[typing.Any]]
wrapped_command: tanjun.ExecutableCommand[typing.Any] | None


def _has_wrapped(value: typing.Any, /) -> typing_extensions.TypeGuard[_WrappedProto]:
def _has_wrapped(value: typing.Any, /) -> typing.TypeGuard[_WrappedProto]:
try:
value.wrapped_command

Expand Down Expand Up @@ -359,7 +349,7 @@ def apply_to_wrapped(


def flatten_options(
name: str, options: typing.Optional[collections.Sequence[_OptionT]], /
name: str, options: collections.Sequence[_OptionT] | None, /
) -> tuple[str, collections.Sequence[_OptionT]]:
"""Flatten the options of a slash/autocomplete interaction.
Expand Down Expand Up @@ -411,7 +401,7 @@ def flatten_options(
CHANNEL_TYPES[hikari.InteractionChannel] = CHANNEL_TYPES[hikari.PartialChannel]


def parse_channel_types(*channel_types: typing.Union[type[hikari.PartialChannel], int]) -> list[hikari.ChannelType]:
def parse_channel_types(*channel_types: type[hikari.PartialChannel] | int) -> list[hikari.ChannelType]:
"""Parse a channel types collection to a list of channel type integers."""
types_iter = itertools.chain.from_iterable(
(hikari.ChannelType(type_),) if isinstance(type_, int) else CHANNEL_TYPES[type_] for type_ in channel_types
Expand Down Expand Up @@ -446,8 +436,8 @@ def repr_channel(channel_type: hikari.ChannelType, /) -> str:


def cmp_command(
cmd: typing.Union[hikari.PartialCommand, hikari.api.CommandBuilder],
other: typing.Union[hikari.PartialCommand, hikari.api.CommandBuilder, None],
cmd: hikari.PartialCommand | hikari.api.CommandBuilder,
other: hikari.PartialCommand | hikari.api.CommandBuilder | None,
/,
) -> bool:
"""Compare application command objects and command builders."""
Expand Down Expand Up @@ -479,10 +469,8 @@ def cmp_command(


def cmp_all_commands(
commands: collections.Collection[typing.Union[hikari.PartialCommand, hikari.api.CommandBuilder]],
other: collections.Mapping[
tuple[hikari.CommandType, str], typing.Union[hikari.PartialCommand, hikari.api.CommandBuilder]
],
commands: collections.Collection[hikari.PartialCommand | hikari.api.CommandBuilder],
other: collections.Mapping[tuple[hikari.CommandType, str], hikari.PartialCommand | hikari.api.CommandBuilder],
/,
) -> bool:
"""Compare two sets of command objects/builders."""
Expand All @@ -504,9 +492,9 @@ def __init__(
strict: bool,
/,
*,
commands: typing.Optional[list[tanjun.MessageCommand[typing.Any]]] = None,
names_to_commands: typing.Optional[dict[str, tuple[str, tanjun.MessageCommand[typing.Any]]]] = None,
search_tree: typing.Optional[_TreeT] = None,
commands: list[tanjun.MessageCommand[typing.Any]] | None = None,
names_to_commands: dict[str, tuple[str, tanjun.MessageCommand[typing.Any]]] | None = None,
search_tree: _TreeT | None = None,
) -> None:
"""Initialise a message command index.
Expand Down Expand Up @@ -564,7 +552,7 @@ def add(self, command: tanjun.MessageCommand[typing.Any], /) -> bool:

else: # strict indexes avoid using the search tree all together.
# This needs to be explicitly typed for MyPy.
node: typing.Union[_TreeT, list[tuple[list[str], tanjun.MessageCommand[typing.Any]]]]
node: _TreeT | list[tuple[list[str], tanjun.MessageCommand[typing.Any]]]
for name in filter(None, command.names):
node = self.search_tree
# The search tree is kept case-insensitive as a check against the actual name
Expand Down Expand Up @@ -593,7 +581,7 @@ def add(self, command: tanjun.MessageCommand[typing.Any], /) -> bool:
self.commands.append(command)
return True

def copy(self, *, parent: typing.Optional[tanjun.MessageCommandGroup[typing.Any]] = None) -> MessageCommandIndex:
def copy(self, *, parent: tanjun.MessageCommandGroup[typing.Any] | None = None) -> MessageCommandIndex:
"""In-place copy the index and its contained commands.
Parameters
Expand Down Expand Up @@ -646,7 +634,7 @@ def find(
return

# This needs to be explicitly typed for MyPy.
node: typing.Union[_TreeT, list[tuple[list[str], tanjun.MessageCommand[typing.Any]]]]
node: _TreeT | list[tuple[list[str], tanjun.MessageCommand[typing.Any]]]
node = self.search_tree
segments: list[tuple[int, list[tuple[list[str], tanjun.MessageCommand[typing.Any]]]]] = []
split = content.split(" ")
Expand Down Expand Up @@ -696,7 +684,7 @@ def remove(self, command: tanjun.MessageCommand[typing.Any], /) -> None:
return

# This needs to be explicitly typed for MyPy.
node: typing.Union[_TreeT, list[tuple[list[str], tanjun.MessageCommand[typing.Any]]]]
node: _TreeT | list[tuple[list[str], tanjun.MessageCommand[typing.Any]]]
for name in filter(None, command.names):
nodes: list[tuple[str, _TreeT]] = []
node = self.search_tree
Expand Down
2 changes: 1 addition & 1 deletion tanjun/_internal/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async def get_perm_channel(client: tanjun.Client, channel_id: hikari.Snowflake,
hikari.channels.PermissibleGuildChannel
The permissible guild channel.
"""
channel: typing.Optional[hikari.PartialChannel] # MyPy compat
channel: hikari.PartialChannel | None # MyPy compat
if client.cache and (channel := client.cache.get_guild_channel(channel_id)):
return channel

Expand Down
Loading

0 comments on commit 8173866

Please sign in to comment.