hikari-risa 1.0.0__tar.gz

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.
Files changed (43) hide show
  1. hikari_risa-1.0.0/.gitignore +27 -0
  2. hikari_risa-1.0.0/LICENSE +21 -0
  3. hikari_risa-1.0.0/PKG-INFO +83 -0
  4. hikari_risa-1.0.0/README.md +49 -0
  5. hikari_risa-1.0.0/pyproject.toml +161 -0
  6. hikari_risa-1.0.0/risa/__init__.py +49 -0
  7. hikari_risa-1.0.0/risa/__main__.py +145 -0
  8. hikari_risa-1.0.0/risa/_about.py +31 -0
  9. hikari_risa-1.0.0/risa/binding.py +251 -0
  10. hikari_risa-1.0.0/risa/client.py +657 -0
  11. hikari_risa-1.0.0/risa/codecs.py +204 -0
  12. hikari_risa-1.0.0/risa/context.py +693 -0
  13. hikari_risa-1.0.0/risa/di.py +75 -0
  14. hikari_risa-1.0.0/risa/dispatch.py +828 -0
  15. hikari_risa-1.0.0/risa/errors.py +603 -0
  16. hikari_risa-1.0.0/risa/internal/__init__.py +22 -0
  17. hikari_risa-1.0.0/risa/internal/codec.py +547 -0
  18. hikari_risa-1.0.0/risa/internal/constants.py +36 -0
  19. hikari_risa-1.0.0/risa/internal/leases.py +197 -0
  20. hikari_risa-1.0.0/risa/internal/locks.py +87 -0
  21. hikari_risa-1.0.0/risa/internal/reader.py +88 -0
  22. hikari_risa-1.0.0/risa/internal/registry.py +208 -0
  23. hikari_risa-1.0.0/risa/internal/wire.py +118 -0
  24. hikari_risa-1.0.0/risa/py.typed +0 -0
  25. hikari_risa-1.0.0/risa/state/__init__.py +25 -0
  26. hikari_risa-1.0.0/risa/state/anchor.py +279 -0
  27. hikari_risa-1.0.0/risa/state/backend.py +155 -0
  28. hikari_risa-1.0.0/risa/state/message.py +278 -0
  29. hikari_risa-1.0.0/risa/state/placement.py +104 -0
  30. hikari_risa-1.0.0/risa/state/schema.py +447 -0
  31. hikari_risa-1.0.0/risa/state/serde.py +69 -0
  32. hikari_risa-1.0.0/risa/state/stateless.py +94 -0
  33. hikari_risa-1.0.0/risa/state/stored.py +444 -0
  34. hikari_risa-1.0.0/risa/store/__init__.py +30 -0
  35. hikari_risa-1.0.0/risa/store/memory.py +152 -0
  36. hikari_risa-1.0.0/risa/store/postgres.py +311 -0
  37. hikari_risa-1.0.0/risa/store/protocol.py +172 -0
  38. hikari_risa-1.0.0/risa/store/redis.py +254 -0
  39. hikari_risa-1.0.0/risa/store/testing.py +341 -0
  40. hikari_risa-1.0.0/risa/ui/__init__.py +26 -0
  41. hikari_risa-1.0.0/risa/ui/build.py +353 -0
  42. hikari_risa-1.0.0/risa/ui/nodes.py +918 -0
  43. hikari_risa-1.0.0/risa/view.py +466 -0
@@ -0,0 +1,27 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Tool caches
13
+ .nox/
14
+ .ruff_cache/
15
+ .pytest_cache/
16
+
17
+ # IDEs
18
+ .idea/
19
+ .vscode/
20
+
21
+ # Secrets
22
+ .env
23
+
24
+ # Documentation site
25
+ site/
26
+ docs/assets/images/
27
+ docs/generated/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ray
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.5
2
+ Name: hikari-risa
3
+ Version: 1.0.0
4
+ Summary: A component handler for hikari with first-class Components V2 support.
5
+ Project-URL: Documentation, https://rayakame.github.io/hikari-risa/
6
+ Project-URL: Repository, https://github.com/rayakame/hikari-risa
7
+ Project-URL: Issues, https://github.com/rayakame/hikari-risa/issues
8
+ Project-URL: Changelog, https://rayakame.github.io/hikari-risa/stable/changelog/
9
+ Author-email: Rayakame <github.android206@passmail.net>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: components,discord,hikari,ui
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Programming Language :: Python :: 3.15
21
+ Classifier: Topic :: Communications :: Chat
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: <3.16,>=3.12
24
+ Requires-Dist: hikari~=2.5
25
+ Requires-Dist: linkd~=0.6
26
+ Requires-Dist: msgspec~=0.22
27
+ Provides-Extra: postgres
28
+ Requires-Dist: asyncpg~=0.32; extra == 'postgres'
29
+ Provides-Extra: radixly
30
+ Requires-Dist: radixly~=1.0; extra == 'radixly'
31
+ Provides-Extra: redis
32
+ Requires-Dist: redis~=8.0; extra == 'redis'
33
+ Description-Content-Type: text/markdown
34
+
35
+ <p align="center">
36
+ <img src="https://raw.githubusercontent.com/rayakame/hikari-risa/a14722c65c6df514d463402cde7f7f48d133bd66/assets/web/banner.webp" alt="hikari-risa" width="100%">
37
+ </p>
38
+
39
+ # Components V2 views for hikari, with state that survives restarts.
40
+
41
+ risa is a component handler for [hikari](https://github.com/hikari-py/hikari), built for Components V2 first.
42
+ A view keeps its state in the message it renders, or in a store you choose, so a click still lands where it should
43
+ after a restart.
44
+
45
+ ```python
46
+ import risa
47
+ from risa import ui
48
+
49
+
50
+ @risa.register(name="counter")
51
+ class Counter(risa.View):
52
+ count: int = 0
53
+
54
+ def render(self) -> ui.Layout:
55
+ return ui.Container(
56
+ ui.TextDisplay(f"Clicks: **{self.count}**"),
57
+ ui.Row(ui.Button(self.click, label="+1")),
58
+ )
59
+
60
+ @risa.handler
61
+ async def click(self, ctx: risa.ComponentContext) -> None:
62
+ self.count += 1
63
+ await ctx.rerender()
64
+ ```
65
+
66
+ ## Install
67
+
68
+ risa runs on Python 3.12 to 3.15.
69
+
70
+ ```sh
71
+ uv add hikari-risa
72
+ pip install hikari-risa
73
+ ```
74
+
75
+ Extras: `hikari-risa[redis]`, `hikari-risa[postgres]` and `hikari-risa[radixly]`.
76
+
77
+ ## Documentation
78
+
79
+ <https://rayakame.github.io/hikari-risa/>
80
+
81
+ ## License
82
+
83
+ MIT, see [LICENSE](https://github.com/rayakame/hikari-risa/blob/main/LICENSE).
@@ -0,0 +1,49 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/rayakame/hikari-risa/a14722c65c6df514d463402cde7f7f48d133bd66/assets/web/banner.webp" alt="hikari-risa" width="100%">
3
+ </p>
4
+
5
+ # Components V2 views for hikari, with state that survives restarts.
6
+
7
+ risa is a component handler for [hikari](https://github.com/hikari-py/hikari), built for Components V2 first.
8
+ A view keeps its state in the message it renders, or in a store you choose, so a click still lands where it should
9
+ after a restart.
10
+
11
+ ```python
12
+ import risa
13
+ from risa import ui
14
+
15
+
16
+ @risa.register(name="counter")
17
+ class Counter(risa.View):
18
+ count: int = 0
19
+
20
+ def render(self) -> ui.Layout:
21
+ return ui.Container(
22
+ ui.TextDisplay(f"Clicks: **{self.count}**"),
23
+ ui.Row(ui.Button(self.click, label="+1")),
24
+ )
25
+
26
+ @risa.handler
27
+ async def click(self, ctx: risa.ComponentContext) -> None:
28
+ self.count += 1
29
+ await ctx.rerender()
30
+ ```
31
+
32
+ ## Install
33
+
34
+ risa runs on Python 3.12 to 3.15.
35
+
36
+ ```sh
37
+ uv add hikari-risa
38
+ pip install hikari-risa
39
+ ```
40
+
41
+ Extras: `hikari-risa[redis]`, `hikari-risa[postgres]` and `hikari-risa[radixly]`.
42
+
43
+ ## Documentation
44
+
45
+ <https://rayakame.github.io/hikari-risa/>
46
+
47
+ ## License
48
+
49
+ MIT, see [LICENSE](https://github.com/rayakame/hikari-risa/blob/main/LICENSE).
@@ -0,0 +1,161 @@
1
+ [project]
2
+ name = "hikari-risa"
3
+ version = "1.0.0"
4
+ description = "A component handler for hikari with first-class Components V2 support."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Rayakame", email = "github.android206@passmail.net" }
8
+ ]
9
+ license = "MIT"
10
+ license-files = ["LICENSE"]
11
+ requires-python = ">=3.12,<3.16"
12
+ keywords = ["hikari", "discord", "components", "ui"]
13
+ classifiers = [
14
+ "Development Status :: 5 - Production/Stable",
15
+ "Framework :: AsyncIO",
16
+ "Intended Audience :: Developers",
17
+ "Operating System :: OS Independent",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Programming Language :: Python :: 3.15",
22
+ "Topic :: Communications :: Chat",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = [
26
+ "hikari~=2.5",
27
+ "linkd~=0.6",
28
+ "msgspec~=0.22",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ postgres = [
33
+ "asyncpg~=0.32",
34
+ ]
35
+ radixly = [
36
+ "radixly~=1.0",
37
+ ]
38
+ redis = [
39
+ "redis~=8.0",
40
+ ]
41
+
42
+ [project.scripts]
43
+ hikari-risa = "risa.__main__:main"
44
+
45
+ [project.urls]
46
+ Documentation = "https://rayakame.github.io/hikari-risa/"
47
+ Repository = "https://github.com/rayakame/hikari-risa"
48
+ Issues = "https://github.com/rayakame/hikari-risa/issues"
49
+ Changelog = "https://rayakame.github.io/hikari-risa/stable/changelog/"
50
+
51
+ [build-system]
52
+ requires = ["hatchling>=1.27"]
53
+ build-backend = "hatchling.build"
54
+
55
+ [tool.hatch.build.targets.wheel]
56
+ packages = ["risa"]
57
+
58
+ [tool.hatch.build.targets.sdist]
59
+ only-include = ["risa"]
60
+
61
+ [dependency-groups]
62
+ changelog = [
63
+ "towncrier==26.9.0",
64
+ ]
65
+ docs = [
66
+ {include-group = "ruff"},
67
+ "griffe-inherited-docstrings==1.1.4",
68
+ "griffelib==2.4.1",
69
+ "mkdocstrings-python==2.0.10",
70
+ "pygments==2.21.0",
71
+ "radixly~=1.0",
72
+ "redis~=8.0",
73
+ "zensical==0.1.0",
74
+ ]
75
+ nox = [
76
+ "nox[uv]==2026.10.20",
77
+ ]
78
+ images = [
79
+ "pillow==13.0.0",
80
+ ]
81
+ lightbulb = [
82
+ "hikari-lightbulb==3.2.6",
83
+ ]
84
+ pyright = [
85
+ "asyncpg-stubs==0.32.0",
86
+ "pyright==1.1.414",
87
+ {include-group = "lightbulb"},
88
+ ]
89
+ ruff = [
90
+ "ruff==0.17.4",
91
+ ]
92
+ slotscheck = [
93
+ "asyncpg~=0.32",
94
+ "redis~=8.0",
95
+ "slotscheck==0.21.0",
96
+ ]
97
+ pytest = [
98
+ {include-group = "images"},
99
+ "asyncpg~=0.32",
100
+ "fakeredis[lua]==2.40.0",
101
+ "griffe-inherited-docstrings==1.1.4",
102
+ "griffelib==2.4.1",
103
+ "pygments==2.21.0",
104
+ "pytest==9.2.0",
105
+ "pytest-asyncio==1.4.1.post1",
106
+ "radixly~=1.0",
107
+ "redis~=8.0",
108
+ ]
109
+
110
+ [tool.towncrier]
111
+ directory = "changes"
112
+ filename = "CHANGELOG.md"
113
+ start_string = "<!-- towncrier release notes start -->\n"
114
+ title_format = "## {version} ({project_date})"
115
+ issue_format = "[#{issue}](https://github.com/rayakame/hikari-risa/pull/{issue})"
116
+
117
+ [[tool.towncrier.type]]
118
+ directory = "breaking"
119
+ name = "Breaking changes"
120
+ showcontent = true
121
+
122
+ [[tool.towncrier.type]]
123
+ directory = "deprecation"
124
+ name = "Deprecations"
125
+ showcontent = true
126
+
127
+ [[tool.towncrier.type]]
128
+ directory = "feature"
129
+ name = "Features"
130
+ showcontent = true
131
+
132
+ [[tool.towncrier.type]]
133
+ directory = "bugfix"
134
+ name = "Bug fixes"
135
+ showcontent = true
136
+
137
+ [tool.slotscheck]
138
+ require-subclass = true
139
+ strict-imports = true
140
+ # detect-unused-slots stays off: it cannot see msgspec Structs use the slots it
141
+ # generates for them, so every Struct in the package reports a false positive.
142
+
143
+ [tool.pyright]
144
+ include = ["risa", "tests", "tools", "docs/examples"]
145
+ exclude = [
146
+ "**/__init__.py"
147
+ ]
148
+ pythonVersion = "3.12"
149
+ typeCheckingMode = "strict"
150
+
151
+ reportUnnecessaryTypeIgnoreComment = "error"
152
+
153
+ [tool.pytest.ini_options]
154
+ testpaths = ["tests"]
155
+ pythonpath = ["."]
156
+ asyncio_mode = "auto"
157
+ asyncio_default_fixture_loop_scope = "function"
158
+ xfail_strict = true
159
+ filterwarnings = [
160
+ "error",
161
+ ]
@@ -0,0 +1,49 @@
1
+ # Copyright (c) 2025 Rayakame
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+ """A component handler for hikari, built on Discord's Components V2.
21
+
22
+ A view is a class whose fields are its state, whose `render` returns its layout, and
23
+ whose handlers answer the clicks on it. The state travels inside the message by
24
+ default, so views survive restarts with no infrastructure. A view that outgrows the
25
+ message can keep its state in a store.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ from risa import codecs as codecs
31
+ from risa import di as di
32
+ from risa import errors as errors
33
+ from risa import state as state
34
+ from risa import store as store
35
+ from risa import ui as ui
36
+ from risa._about import __author__ as __author__
37
+ from risa._about import __copyright__ as __copyright__
38
+ from risa._about import __discord_invite__ as __discord_invite__
39
+ from risa._about import __license__ as __license__
40
+ from risa._about import __url__ as __url__
41
+ from risa._about import __version__ as __version__
42
+ from risa.binding import *
43
+ from risa.client import *
44
+ from risa.context import *
45
+ from risa.di import *
46
+ from risa.errors import *
47
+ from risa.state import *
48
+ from risa.store import *
49
+ from risa.view import *
@@ -0,0 +1,145 @@
1
+ # Copyright (c) 2025 Rayakame
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+ """The ``hikari-risa`` command, also ``python -m risa``: the versions a bug report needs."""
21
+
22
+ from __future__ import annotations
23
+
24
+ import argparse
25
+ import ctypes
26
+ import importlib.metadata
27
+ import os
28
+ import pathlib
29
+ import platform
30
+ import re
31
+ import sys
32
+ import typing
33
+
34
+ import risa
35
+
36
+ if typing.TYPE_CHECKING:
37
+ import collections.abc
38
+
39
+ __all__ = ("main",)
40
+
41
+ _ART: typing.Final[collections.abc.Sequence[str]] = (
42
+ r" _ ",
43
+ r" _____(_)________ _",
44
+ r" / ___/ / ___/ __ `/",
45
+ r" / / / (__ ) /_/ / ",
46
+ r"/_/ /_/____/\__,_/ ",
47
+ )
48
+ # 256-colour codes that read on dark and light terminals.
49
+ _ART_COLOURS: typing.Final[collections.abc.Sequence[str]] = ("38;5;205", "38;5;206", "38;5;170", "38;5;134", "38;5;98")
50
+ _PINK: typing.Final[str] = "38;5;205"
51
+ _BOLD: typing.Final[str] = "1"
52
+ _DIM: typing.Final[str] = "2"
53
+ _LABEL_WIDTH: typing.Final[int] = 10
54
+ _EXTRA: typing.Final[re.Pattern[str]] = re.compile(r"""extra\s*==\s*["']([^"']+)["']""")
55
+ _STD_OUTPUT_HANDLE: typing.Final[int] = -11
56
+ _ENABLE_VIRTUAL_TERMINAL_PROCESSING: typing.Final[int] = 0x0004
57
+
58
+ type _Paint = collections.abc.Callable[[str, str], str]
59
+
60
+
61
+ def _escape_codes_work() -> bool:
62
+ # A Windows console reads escape codes only once the program turns on virtual terminal processing.
63
+ if sys.platform != "win32":
64
+ return True
65
+ kernel32 = ctypes.WinDLL("kernel32")
66
+ kernel32.GetStdHandle.restype = ctypes.c_void_p
67
+ kernel32.GetConsoleMode.argtypes = (ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint32))
68
+ kernel32.SetConsoleMode.argtypes = (ctypes.c_void_p, ctypes.c_uint32)
69
+ handle = kernel32.GetStdHandle(_STD_OUTPUT_HANDLE)
70
+ mode = ctypes.c_uint32()
71
+ if not kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
72
+ return False
73
+ return bool(kernel32.SetConsoleMode(handle, mode.value | _ENABLE_VIRTUAL_TERMINAL_PROCESSING))
74
+
75
+
76
+ def _section(
77
+ paint: _Paint, label: str, rows: collections.abc.Sequence[collections.abc.Sequence[str]]
78
+ ) -> collections.abc.Iterator[str]:
79
+ # The last column goes unpadded, so it may carry colour codes without breaking the alignment.
80
+ widths = [max(len(row[column]) for row in rows) for column in range(len(rows[0]) - 1)] if rows else []
81
+ for index, row in enumerate(rows):
82
+ cells = [cell.ljust(width) for cell, width in zip(row, widths, strict=False)]
83
+ heading = paint((label if index == 0 else "").ljust(_LABEL_WIDTH), _PINK)
84
+ yield f" {heading}{' '.join([*cells, row[-1]])}"
85
+
86
+
87
+ def _report(*, colour: bool) -> collections.abc.Iterator[str]:
88
+ def paint(text: str, style: str) -> str:
89
+ return f"\x1b[{style}m{text}\x1b[0m" if colour else text
90
+
91
+ for line, style in zip(_ART[:-1], _ART_COLOURS, strict=False):
92
+ yield paint(line.rstrip(), style)
93
+ yield paint(_ART[-1], _ART_COLOURS[-1]) + paint(f"hikari-risa {risa.__version__}", _BOLD)
94
+
95
+ metadata = importlib.metadata.metadata("hikari-risa")
96
+ urls = dict(url.split(", ", 1) for url in metadata.get_all("Project-URL") or ())
97
+ required: list[collections.abc.Sequence[str]] = []
98
+ extras: list[collections.abc.Sequence[str]] = []
99
+ for requirement in importlib.metadata.requires("hikari-risa") or ():
100
+ name = re.split(r"[^\w.-]", requirement, maxsplit=1)[0]
101
+ try:
102
+ version = importlib.metadata.version(name)
103
+ except importlib.metadata.PackageNotFoundError:
104
+ version = paint("not installed", _DIM)
105
+ if extra := _EXTRA.search(requirement):
106
+ extras.append((extra[1], name, version))
107
+ else:
108
+ required.append((name, version))
109
+
110
+ yield ""
111
+ yield paint(metadata["Summary"], _DIM)
112
+ yield ""
113
+ yield from _section(paint, "python", [(f"{platform.python_implementation()} {platform.python_version()}",)])
114
+ yield from _section(paint, "platform", [(platform.platform(),)])
115
+ yield from _section(paint, "location", [(str(pathlib.Path(risa.__file__).parent),)])
116
+ yield ""
117
+ yield from _section(paint, "requires", required)
118
+ yield ""
119
+ yield from _section(paint, "extras", extras)
120
+ yield ""
121
+ yield from _section(paint, "docs", [(urls["Documentation"],)])
122
+ yield from _section(paint, "issues", [(urls["Issues"],)])
123
+
124
+
125
+ def main() -> None:
126
+ """Print the versions of risa, of Python and of the packages risa installs."""
127
+ parser = argparse.ArgumentParser(
128
+ prog="hikari-risa", description="Print the versions of risa, Python and what risa installs, for a bug report."
129
+ )
130
+ parser.add_argument("--version", action="version", version=f"hikari-risa {risa.__version__}")
131
+ parser.parse_args()
132
+ colour = (
133
+ sys.stdout.isatty()
134
+ and not os.environ.get("NO_COLOR")
135
+ and os.environ.get("TERM") != "dumb"
136
+ and _escape_codes_work()
137
+ )
138
+ report = "\n".join(_report(colour=colour)) + "\n"
139
+ # Before 3.15 a Windows pipe writes in the ANSI code page, which may lack a character of the path.
140
+ encoding = sys.stdout.encoding or "utf-8"
141
+ sys.stdout.write(report.encode(encoding, "backslashreplace").decode(encoding))
142
+
143
+
144
+ if __name__ == "__main__":
145
+ main()
@@ -0,0 +1,31 @@
1
+ # Copyright (c) 2025 Rayakame
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+ """Package metadata, re-exported by `risa`."""
21
+
22
+ from __future__ import annotations
23
+
24
+ import typing
25
+
26
+ __author__: typing.Final[str] = "Rayakame"
27
+ __copyright__: typing.Final[str] = "2025, Rayakame"
28
+ __discord_invite__: typing.Final[str] = "https://discord.gg/Jx4cNGG"
29
+ __url__: typing.Final[str] = "https://github.com/rayakame/hikari-risa"
30
+ __version__: typing.Final[str] = "1.0.0"
31
+ __license__: typing.Final[str] = "MIT"