Skip to content

Commit 56c0d94

Browse files
committed
Document things
1 parent ce39367 commit 56c0d94

7 files changed

Lines changed: 366 additions & 12 deletions

File tree

docs/api/autopost.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
Auto-post API Reference
33
#######################
44

5-
.. automodule:; topgg.autopost
5+
.. automodule:: topgg.autopost
66
:members:
77
:inherited-members:

docs/api/types.rst

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,39 @@ Models API Reference
44

55
.. automodule:: topgg.types
66
:members:
7-
:inherited-members:
7+
8+
.. autoclass:: topgg.types.DataDict
9+
:members:
10+
:inherited-members:
11+
12+
.. autoclass:: topgg.types.BotData
13+
:members:
14+
:show-inheritance:
15+
16+
.. autoclass:: topgg.types.BotStatsData
17+
:members:
18+
:show-inheritance:
19+
20+
.. autoclass:: topgg.types.BriefUserData
21+
:members:
22+
:show-inheritance:
23+
24+
.. autoclass:: topgg.types.UserData
25+
:members:
26+
:show-inheritance:
27+
28+
.. autoclass:: topgg.types.SocialData
29+
:members:
30+
:show-inheritance:
31+
32+
.. autoclass:: topgg.types.VoteDataDict
33+
:members:
34+
:show-inheritance:
35+
36+
.. autoclass:: topgg.types.BotVoteData
37+
:members:
38+
:show-inheritance:
39+
40+
.. autoclass:: topgg.types.GuildVoteData
41+
:members:
42+
:show-inheritance:

tests/test_type.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ def bot_vote_data() -> types.BotVoteData:
104104

105105

106106
@pytest.fixture
107-
def server_vote_data() -> types.ServerVoteData:
108-
return types.ServerVoteData(**server_vote_dict)
107+
def server_vote_data() -> types.GuildVoteData:
108+
return types.GuildVoteData(**server_vote_dict)
109109

110110

111111
@pytest.fixture

topgg/autopost.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@
4141

4242

4343
class AutoPoster(DataContainerMixin):
44+
"""
45+
A helper class for autoposting. Takes in a :obj:`~.client.DBLClient` to instantiate.
46+
47+
Parameters
48+
----------
49+
client: :obj:`~.client.DBLClient`
50+
An instance of DBLClient.
51+
"""
52+
4453
__slots__ = (
4554
"_error",
4655
"_success",
@@ -79,6 +88,27 @@ def on_success(self, callback: CallbackT) -> "AutoPoster":
7988
...
8089

8190
def on_success(self, callback: t.Any = None) -> t.Any:
91+
"""
92+
Registers an autopost success callback. The callback can be either sync or async.
93+
This method can be used as a decorator or a decorator factory.
94+
95+
Example
96+
-------
97+
.. code-block:: python
98+
99+
# The following are valid.
100+
autopost = dblclient.autopost().on_success(lambda: print("Success!"))
101+
102+
# Used as decorator, the decorated function will become the AutoPoster object.
103+
@autopost.on_success
104+
def autopost():
105+
...
106+
107+
# Used as decorator factory, the decorated function will still be the function itself.
108+
@autopost.on_success()
109+
def on_success():
110+
...
111+
"""
82112
if callback is not None:
83113
self._success = callback
84114
return self
@@ -94,6 +124,30 @@ def on_error(self, callback: CallbackT) -> "AutoPoster":
94124
...
95125

96126
def on_error(self, callback: t.Any = None) -> t.Any:
127+
"""
128+
Registers an autopost error callback. The callback can be either sync or async.
129+
This method can be used as a decorator or a decorator factory.
130+
131+
.. note::
132+
If you don't provide an error callback, the default error handler will be called.
133+
134+
Example
135+
-------
136+
.. code-block:: python
137+
138+
# The following are valid.
139+
autopost = dblclient.autopost().on_error(lambda exc: print("Failed posting stats!", exc))
140+
141+
# Used as decorator, the decorated function will become the AutoPoster object.
142+
@autopost.on_error
143+
def autopost(exc: Exception):
144+
...
145+
146+
# Used as decorator factory, the decorated function will still be the function itself.
147+
@autopost.on_error()
148+
def on_error(exc: Exception):
149+
...
150+
"""
97151
if callback is not None:
98152
self._error = callback # type: ignore
99153
return self
@@ -109,6 +163,32 @@ def stats(self, callback: StatsCallbackT) -> "AutoPoster":
109163
...
110164

111165
def stats(self, callback: t.Any = None) -> t.Any:
166+
"""
167+
Registers a function that returns an instance of :obj:`~.types.StatsWrapper`. The callback can be either sync or async.
168+
This method can be used as a decorator or a decorator factory.
169+
170+
Example
171+
-------
172+
.. code-block:: python
173+
174+
import topgg
175+
176+
# In this example, we fetch the stats from a Discord client instance.
177+
client = Client(...)
178+
dblclient = topgg.DBLClient(TOKEN).set_data(client)
179+
autopost = (
180+
dblclient
181+
.autopost()
182+
.on_success(lambda: print("Successfully posted the stats!")
183+
)
184+
185+
@autopost.stats()
186+
def get_stats(client: Client = topgg.data(Client)):
187+
return topgg.StatsWrapper(guild_count=len(client.guilds), shard_count=len(client.shards))
188+
189+
# somewhere after the event loop has started
190+
autopost.start()
191+
"""
112192
if callback is not None:
113193
self._stats = callback
114194
return self
@@ -117,13 +197,27 @@ def stats(self, callback: t.Any = None) -> t.Any:
117197

118198
@property
119199
def interval(self) -> float:
200+
"""The interval between posting stats."""
120201
return self._interval
121202

122203
@interval.setter
123204
def interval(self, seconds: t.Union[float, datetime.timedelta]) -> None:
205+
"""Alias to :meth:`~.autopost.AutoPoster.set_interval`"""
124206
self.set_interval(seconds)
125207

126208
def set_interval(self, seconds: t.Union[float, datetime.timedelta]) -> "AutoPoster":
209+
"""
210+
Sets the interval between posting stats.
211+
212+
Parameters
213+
----------
214+
seconds: :obj:`float` or :obj:`datetime.timedelta`
215+
216+
Raises
217+
------
218+
:obj:`ValueError`:
219+
If the provided interval is less than 900 seconds.
220+
"""
127221
if isinstance(seconds, datetime.timedelta):
128222
seconds = seconds.total_seconds()
129223

@@ -135,6 +229,7 @@ def set_interval(self, seconds: t.Union[float, datetime.timedelta]) -> "AutoPost
135229

136230
@property
137231
def is_running(self) -> bool:
232+
"""Whether or not the autopost is running."""
138233
return self._task is not None and self._task.done()
139234

140235
async def _internal_loop(self) -> None:
@@ -161,6 +256,17 @@ async def _internal_loop(self) -> None:
161256
self._task = None
162257

163258
def start(self) -> "asyncio.Task[None]":
259+
"""
260+
Starts the autoposting loop.
261+
262+
.. note::
263+
This method must be called when the event loop has already running!
264+
265+
Raises
266+
------
267+
:obj:`~.errors.TopGGException`:
268+
If there's no callback provided or the autopost is already running.
269+
"""
164270
if not hasattr(self, "_stats"):
165271
raise errors.TopGGException(
166272
"you must provide a callback that returns the stats."
@@ -173,10 +279,24 @@ def start(self) -> "asyncio.Task[None]":
173279
return task
174280

175281
def stop(self) -> None:
282+
"""
283+
Stops the autoposting loop.
284+
285+
.. note::
286+
This differs from :meth:`~.autopost.AutoPoster.cancel`
287+
because this will stop after posting as opposed to cancel immediately.
288+
"""
176289
self._stopping = True
177290
return None
178291

179292
def cancel(self) -> None:
293+
"""
294+
Cancels the autoposting loop.
295+
296+
.. note::
297+
This differs from :meth:`~.autopost.AutoPoster.stop`
298+
because this will stop the loop right away.
299+
"""
180300
if self._task is None:
181301
return
182302

topgg/data.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2121
# DEALINGS IN THE SOFTWARE.
2222

23-
__all__ = ["Data", "data"]
23+
__all__ = ["data", "DataContainerMixin"]
2424

2525
import inspect
2626
import typing as t
@@ -32,6 +32,33 @@
3232

3333

3434
def data(type_: t.Type[T]) -> T:
35+
"""
36+
Represents the injected data. This should be set as the parameter's default value.
37+
38+
Parameters
39+
----------
40+
type_: :obj:`Type[T]`
41+
The type of the injected data.
42+
43+
Returns
44+
-------
45+
data: The injected data of type T.
46+
47+
Example
48+
-------
49+
.. code-block:: python
50+
51+
import topgg
52+
53+
# In this example, we fetch the stats from a Discord client instance.
54+
client = Client(...)
55+
dblclient = topgg.DBLClient(TOKEN).set_data(client)
56+
autopost: topgg.AutoPoster = dblclient.autopost()
57+
58+
@autopost.stats()
59+
def get_stats(client: Client = topgg.data(Client)):
60+
return topgg.StatsWrapper(guild_count=len(client.guilds), shard_count=len(client.shards))
61+
"""
3562
return t.cast(T, Data(type_))
3663

3764

@@ -43,6 +70,13 @@ def __init__(self, type_: t.Type[T]) -> None:
4370

4471

4572
class DataContainerMixin:
73+
"""
74+
A class that holds data.
75+
76+
This is useful for injecting some data so that they're available
77+
as arguments in your functions.
78+
"""
79+
4680
__slots__ = ("_data",)
4781

4882
def __init__(self) -> None:
@@ -51,6 +85,21 @@ def __init__(self) -> None:
5185
def set_data(
5286
self: DataContainerT, data_: t.Any, *, override: bool = False
5387
) -> DataContainerT:
88+
"""
89+
Sets data to be available in your functions.
90+
91+
Parameters
92+
----------
93+
data_: :obj:`Any`
94+
The data to be injected.
95+
override: :obj:`bool`
96+
Whether or not to override another instance that already exists.
97+
98+
Raises
99+
------
100+
:obj:`~.errors.TopGGException`
101+
If override is False and another instance of the same type exists.
102+
"""
54103
type_ = type(data_)
55104
if not override and type_ in self._data:
56105
raise TopGGException(
@@ -60,7 +109,16 @@ def set_data(
60109
self._data[type_] = data_
61110
return self
62111

63-
def get_data(self, type_: t.Type[T], default: t.Any = None) -> T:
112+
@t.overload
113+
def get_data(self, type_: t.Type[T]) -> t.Optional[T]:
114+
...
115+
116+
@t.overload
117+
def get_data(self, type_: t.Type[T], default: t.Any = None) -> t.Any:
118+
...
119+
120+
def get_data(self, type_: t.Any, default: t.Any = None) -> t.Any:
121+
"""Gets the injected data."""
64122
return self._data.get(type_, default)
65123

66124
async def _invoke_callback(

0 commit comments

Comments
 (0)