discordcn 0.0.1a1__py3-none-any.whl → 0.0.1a3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- discordcn/__init__.py +1 -4
- discordcn/_utils/__init__.py +3 -0
- discordcn/_utils/dependencies.py +194 -0
- discordcn/_version.py +2 -2
- discordcn/pycord/__init__.py +28 -0
- discordcn/pycord/_utils/__init__.py +8 -0
- discordcn/pycord/_utils/_asyncio.py +34 -0
- discordcn/pycord/interfaces/__init__.py +23 -0
- discordcn/pycord/interfaces/_types.py +14 -0
- discordcn/pycord/interfaces/accordion.py +497 -0
- discordcn/pycord/interfaces/confirm.py +195 -0
- discordcn/pycord/interfaces/paginator.py +286 -0
- discordcn-0.0.1a3.dist-info/METADATA +93 -0
- discordcn-0.0.1a3.dist-info/RECORD +17 -0
- discordcn-0.0.1a1.dist-info/METADATA +0 -23
- discordcn-0.0.1a1.dist-info/RECORD +0 -7
- {discordcn-0.0.1a1.dist-info → discordcn-0.0.1a3.dist-info}/WHEEL +0 -0
- {discordcn-0.0.1a1.dist-info → discordcn-0.0.1a3.dist-info}/licenses/LICENSE +0 -0
discordcn/__init__.py
CHANGED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""_utils.dependencies.py — dependency / backend guards for optional extras.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
from discordcn._utils import _require_extra
|
|
5
|
+
|
|
6
|
+
_require_extra(package="py-cord", import_name="discord", version=(2, 7, 0))
|
|
7
|
+
|
|
8
|
+
What it enforces:
|
|
9
|
+
- The top-level import namespace must be provided by exactly ONE installed
|
|
10
|
+
distribution, and it must be `package`.
|
|
11
|
+
- `package`'s installed version must be >= `version` (same major version).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import importlib
|
|
15
|
+
import importlib.metadata as md
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from collections.abc import Mapping
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ExtraRequirementError(RuntimeError):
|
|
23
|
+
"""Raised when an extra/backend requirement is not satisfied."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _norm_dist_name(name: str) -> str:
|
|
27
|
+
"""Normalize distribution name per PEP 503.
|
|
28
|
+
|
|
29
|
+
Converts to lowercase and replaces underscores/dots with hyphens.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
name: Distribution name to normalize.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Normalized distribution name.
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
>>> _norm_dist_name("Py_Cord")
|
|
39
|
+
'py-cord'
|
|
40
|
+
"""
|
|
41
|
+
return name.strip().casefold().replace("_", "-").replace(".", "-")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _parse_version(v: str) -> tuple[int, ...]:
|
|
45
|
+
"""Parse version string into numeric tuple.
|
|
46
|
+
|
|
47
|
+
Extracts numeric components and stops at first non-numeric part.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
v: Version string to parse (e.g., "2.7.0rc2").
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Tuple of version numbers (e.g., (2, 7, 0)).
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
>>> _parse_version("2.7.0rc2")
|
|
57
|
+
(2, 7, 0)
|
|
58
|
+
>>> _parse_version("3.11")
|
|
59
|
+
(3, 11)
|
|
60
|
+
"""
|
|
61
|
+
parts: list[int] = []
|
|
62
|
+
buf = ""
|
|
63
|
+
for ch in v:
|
|
64
|
+
if ch.isdigit():
|
|
65
|
+
buf += ch
|
|
66
|
+
elif buf:
|
|
67
|
+
parts.append(int(buf))
|
|
68
|
+
buf = ""
|
|
69
|
+
if buf:
|
|
70
|
+
parts.append(int(buf))
|
|
71
|
+
return tuple(parts) if parts else (0,)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _packages_providing(top_level: str) -> set[str]:
|
|
75
|
+
"""Get normalized distribution names providing a top-level module.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
top_level: Top-level module name (e.g., "discord").
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Set of normalized distribution names that provide the module.
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
>>> _packages_providing("discord")
|
|
85
|
+
{'py-cord'}
|
|
86
|
+
"""
|
|
87
|
+
mapping: Mapping[str, list[str]] = md.packages_distributions()
|
|
88
|
+
providers = mapping.get(top_level, [])
|
|
89
|
+
return {_norm_dist_name(p) for p in providers}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def require_extra(
|
|
93
|
+
*,
|
|
94
|
+
package: str,
|
|
95
|
+
import_name: str,
|
|
96
|
+
version: tuple[int, ...] | None = None,
|
|
97
|
+
) -> None:
|
|
98
|
+
"""Ensure import is provided exclusively by package with correct version.
|
|
99
|
+
|
|
100
|
+
Validates that:
|
|
101
|
+
1. The import name can be imported successfully.
|
|
102
|
+
2. Exactly one distribution provides it, and it's the specified package.
|
|
103
|
+
3. The package version is >= the required version (same major version).
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
package: Required package distribution name (e.g., "py-cord").
|
|
107
|
+
import_name: Top-level import name to check (e.g., "discord").
|
|
108
|
+
version: Minimum required version tuple (e.g., (2, 7, 0)).
|
|
109
|
+
Major version must match exactly. Defaults to None (no check).
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
ModuleNotFoundError: If import fails or package not found in metadata.
|
|
113
|
+
ExtraRequirementError: If wrong provider or version mismatch detected.
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
>>> require_extra(package="py-cord", import_name="discord", version=(2, 7, 0))
|
|
117
|
+
# Raises if discord.py is installed instead of py-cord
|
|
118
|
+
# Raises if py-cord version is 2.6.x or 3.x
|
|
119
|
+
"""
|
|
120
|
+
pkg_norm = _norm_dist_name(package)
|
|
121
|
+
|
|
122
|
+
# 1) Ensure the import works
|
|
123
|
+
try:
|
|
124
|
+
importlib.import_module(import_name)
|
|
125
|
+
except ModuleNotFoundError as e:
|
|
126
|
+
version_str = f" (need >= {'.'.join(map(str, version))})" if version else ""
|
|
127
|
+
msg = (
|
|
128
|
+
f"`{import_name}` is required but could not be imported.\n"
|
|
129
|
+
+ f"Install with: pip install '{package}'{version_str}"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
raise ModuleNotFoundError(msg) from e
|
|
133
|
+
|
|
134
|
+
# 2) Ensure ONLY the requested package provides it
|
|
135
|
+
providers = _packages_providing(import_name)
|
|
136
|
+
|
|
137
|
+
if not providers:
|
|
138
|
+
msg = (
|
|
139
|
+
f"Imported `{import_name}`, but no distribution claims it in metadata.\n"
|
|
140
|
+
+ f"Try: pip install -U --force-reinstall {package}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
raise ExtraRequirementError(msg)
|
|
144
|
+
|
|
145
|
+
if providers != {pkg_norm}:
|
|
146
|
+
other = sorted(p for p in providers if p != pkg_norm)
|
|
147
|
+
uninstall = f" pip uninstall -y {' '.join(other)}\n" if other else ""
|
|
148
|
+
msg = (
|
|
149
|
+
f"`{import_name}` must be provided exclusively by `{package}`, "
|
|
150
|
+
+ f"but found: {', '.join(sorted(providers))}.\n"
|
|
151
|
+
+ f"Fix by uninstalling conflicts:\n{uninstall}"
|
|
152
|
+
+ f" pip install -U '{package}'"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
raise ExtraRequirementError(msg)
|
|
156
|
+
|
|
157
|
+
# 3) Version check
|
|
158
|
+
if version is None:
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
installed_str = md.version(package)
|
|
163
|
+
except md.PackageNotFoundError as e:
|
|
164
|
+
msg = f"Distribution `{package}` not found in metadata.\nInstall with: pip install '{package}'"
|
|
165
|
+
|
|
166
|
+
raise ModuleNotFoundError(msg) from e
|
|
167
|
+
|
|
168
|
+
installed = _parse_version(installed_str)
|
|
169
|
+
required = tuple(version)
|
|
170
|
+
|
|
171
|
+
max_len = max(len(installed), len(required))
|
|
172
|
+
inst_padded = installed + (0,) * (max_len - len(installed))
|
|
173
|
+
req_padded = required + (0,) * (max_len - len(required))
|
|
174
|
+
|
|
175
|
+
if installed[0] != required[0]:
|
|
176
|
+
msg = (
|
|
177
|
+
f"`{package}` major version must be {required[0]}.x, "
|
|
178
|
+
+ f"but found {installed_str}.\n"
|
|
179
|
+
+ f"Install: pip install '{package}>={required[0]}.{required[1] if len(required) > 1 else 0}'"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
raise ExtraRequirementError(msg)
|
|
183
|
+
|
|
184
|
+
if inst_padded < req_padded:
|
|
185
|
+
msg = (
|
|
186
|
+
f"`{package}` >= {'.'.join(map(str, required))} required, "
|
|
187
|
+
+ f"found {installed_str}.\n"
|
|
188
|
+
+ f"Upgrade: pip install -U '{package}'"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
raise ExtraRequirementError(msg)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
__all__ = ("require_extra",)
|
discordcn/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0, 1, '
|
|
31
|
+
__version__ = version = '0.0.1a3'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 1, 'a3')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""discordcn for pycord."""
|
|
2
|
+
|
|
3
|
+
# ruff: noqa: E402
|
|
4
|
+
from discordcn._utils import require_extra
|
|
5
|
+
|
|
6
|
+
require_extra(package="py-cord", import_name="discord", version=(2, 7, 0))
|
|
7
|
+
|
|
8
|
+
from .interfaces import (
|
|
9
|
+
AccordionInterface,
|
|
10
|
+
ConfirmInterface,
|
|
11
|
+
PageButton,
|
|
12
|
+
PageIndicatorButton,
|
|
13
|
+
PaginatorControls,
|
|
14
|
+
PaginatorControlsBase,
|
|
15
|
+
PaginatorInterface,
|
|
16
|
+
PaginatorInterfaceBase,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = (
|
|
20
|
+
"AccordionInterface",
|
|
21
|
+
"ConfirmInterface",
|
|
22
|
+
"PageButton",
|
|
23
|
+
"PageIndicatorButton",
|
|
24
|
+
"PaginatorControls",
|
|
25
|
+
"PaginatorControlsBase",
|
|
26
|
+
"PaginatorInterface",
|
|
27
|
+
"PaginatorInterfaceBase",
|
|
28
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from asyncio import Future
|
|
2
|
+
from collections.abc import Awaitable
|
|
3
|
+
from inspect import isawaitable
|
|
4
|
+
from typing import Any, TypeAlias, TypeVar
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
MaybeAwaitable: TypeAlias = Awaitable[T] | T
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def maybe_awaitable(obj: MaybeAwaitable[T]) -> T:
|
|
12
|
+
if isawaitable(obj):
|
|
13
|
+
return await obj
|
|
14
|
+
return obj
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def safe_set_future_result(future: Future[T], value: T) -> bool:
|
|
18
|
+
if not future.done():
|
|
19
|
+
future.set_result(value)
|
|
20
|
+
return True
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def safe_set_future_exception(future: Future[Any], exc: BaseException) -> None:
|
|
25
|
+
if not future.done():
|
|
26
|
+
future.set_exception(exc)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
__all__ = (
|
|
30
|
+
"MaybeAwaitable",
|
|
31
|
+
"maybe_awaitable",
|
|
32
|
+
"safe_set_future_exception",
|
|
33
|
+
"safe_set_future_result",
|
|
34
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""discordcn interfaces for pycord."""
|
|
2
|
+
|
|
3
|
+
from .accordion import AccordionInterface
|
|
4
|
+
from .confirm import ConfirmInterface
|
|
5
|
+
from .paginator import (
|
|
6
|
+
PageButton,
|
|
7
|
+
PageIndicatorButton,
|
|
8
|
+
PaginatorControls,
|
|
9
|
+
PaginatorControlsBase,
|
|
10
|
+
PaginatorInterface,
|
|
11
|
+
PaginatorInterfaceBase,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = (
|
|
15
|
+
"AccordionInterface",
|
|
16
|
+
"ConfirmInterface",
|
|
17
|
+
"PageButton",
|
|
18
|
+
"PageIndicatorButton",
|
|
19
|
+
"PaginatorControls",
|
|
20
|
+
"PaginatorControlsBase",
|
|
21
|
+
"PaginatorInterface",
|
|
22
|
+
"PaginatorInterfaceBase",
|
|
23
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from collections.abc import Awaitable, Callable
|
|
2
|
+
from typing import Any, TypeAlias, TypeVar
|
|
3
|
+
|
|
4
|
+
import discord
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Callback: TypeAlias = Callable[[T], Awaitable[Any] | Any]
|
|
10
|
+
|
|
11
|
+
EmojiType: TypeAlias = discord.PartialEmoji | discord.AppEmoji | discord.GuildEmoji
|
|
12
|
+
"""Accepted emoji types for section accessory buttons."""
|
|
13
|
+
|
|
14
|
+
__all__ = ("Callback",)
|