AdvancedTagscript 3.2.3__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.
Files changed (48) hide show
  1. TagScriptEngine/__init__.py +227 -0
  2. TagScriptEngine/_warnings.py +88 -0
  3. TagScriptEngine/adapter/__init__.py +50 -0
  4. TagScriptEngine/adapter/discordadapters.py +596 -0
  5. TagScriptEngine/adapter/functionadapter.py +23 -0
  6. TagScriptEngine/adapter/intadapter.py +22 -0
  7. TagScriptEngine/adapter/objectadapter.py +35 -0
  8. TagScriptEngine/adapter/redbotadapters.py +161 -0
  9. TagScriptEngine/adapter/stringadapter.py +47 -0
  10. TagScriptEngine/block/__init__.py +130 -0
  11. TagScriptEngine/block/allowedmentions.py +60 -0
  12. TagScriptEngine/block/assign.py +43 -0
  13. TagScriptEngine/block/breakblock.py +41 -0
  14. TagScriptEngine/block/case.py +63 -0
  15. TagScriptEngine/block/command.py +141 -0
  16. TagScriptEngine/block/comment.py +29 -0
  17. TagScriptEngine/block/control.py +149 -0
  18. TagScriptEngine/block/cooldown.py +95 -0
  19. TagScriptEngine/block/count.py +68 -0
  20. TagScriptEngine/block/embedblock.py +306 -0
  21. TagScriptEngine/block/fiftyfifty.py +34 -0
  22. TagScriptEngine/block/helpers.py +164 -0
  23. TagScriptEngine/block/loosevariablegetter.py +40 -0
  24. TagScriptEngine/block/mathblock.py +164 -0
  25. TagScriptEngine/block/randomblock.py +51 -0
  26. TagScriptEngine/block/range.py +56 -0
  27. TagScriptEngine/block/redirect.py +42 -0
  28. TagScriptEngine/block/replaceblock.py +110 -0
  29. TagScriptEngine/block/require_blacklist.py +79 -0
  30. TagScriptEngine/block/shortcutredirect.py +23 -0
  31. TagScriptEngine/block/stopblock.py +38 -0
  32. TagScriptEngine/block/strf.py +70 -0
  33. TagScriptEngine/block/strictvariablegetter.py +38 -0
  34. TagScriptEngine/block/substr.py +25 -0
  35. TagScriptEngine/block/urlencodeblock.py +41 -0
  36. TagScriptEngine/exceptions.py +105 -0
  37. TagScriptEngine/interface/__init__.py +14 -0
  38. TagScriptEngine/interface/adapter.py +75 -0
  39. TagScriptEngine/interface/block.py +124 -0
  40. TagScriptEngine/interpreter.py +502 -0
  41. TagScriptEngine/py.typed +0 -0
  42. TagScriptEngine/utils.py +71 -0
  43. TagScriptEngine/verb.py +160 -0
  44. advancedtagscript-3.2.3.dist-info/METADATA +99 -0
  45. advancedtagscript-3.2.3.dist-info/RECORD +48 -0
  46. advancedtagscript-3.2.3.dist-info/WHEEL +5 -0
  47. advancedtagscript-3.2.3.dist-info/licenses/LICENSE +1 -0
  48. advancedtagscript-3.2.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,596 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import datetime
5
+ from random import choice
6
+ from typing import Any, Dict, Union, cast, Tuple
7
+
8
+ import discord
9
+
10
+ from ..verb import Verb
11
+ from ..utils import escape_content
12
+ from .._warnings import deprecated
13
+ from ..interface import Adapter, SimpleAdapter
14
+
15
+
16
+ _log: logging.Logger = logging.getLogger(__name__)
17
+
18
+
19
+ __all__: Tuple[str, ...] = (
20
+ "AttributeAdapter",
21
+ "DiscordAttributeAdapter",
22
+ "UserAdapter",
23
+ "MemberAdapter",
24
+ "DMChannelAdapter",
25
+ "ChannelAdapter",
26
+ "GuildAdapter",
27
+ "RoleAdapter",
28
+ "DiscordObjectAdapter",
29
+ )
30
+
31
+
32
+ class AttributeAdapter(Adapter):
33
+ """
34
+ .. deprecated:: 3.2.0
35
+ AttributeAdapter has been deprecated and will be removed in favor of
36
+ ``TagScriptEngine.adapter.discordadpaters.DiscordAttributeAdapter`` or
37
+ consider using ``TagScriptEngine.interface.adapter.SimpleAdapter`` instead.
38
+ """
39
+
40
+ __slots__: Tuple[str, ...] = ("object", "_attributes", "_methods")
41
+
42
+ @deprecated(
43
+ name="TagScriptEngine.adapter.discordadpaters.AttributeAdapter",
44
+ reason=(
45
+ "AttributeAdapter has been deprecated and will be removed in favor of "
46
+ "``TagScriptEngine.adapter.discordadpaters.DiscordAttributeAdapter`` or "
47
+ "consider using ``TagScriptEngine.interface.adapter.SimpleAdapter`` instead."
48
+ ),
49
+ version="3.2.0",
50
+ )
51
+ def __init__(self, base: Union[discord.TextChannel, discord.Member, discord.Guild]) -> None:
52
+ self.object: Union[discord.TextChannel, discord.Member, discord.Guild] = base
53
+ created_at: datetime.datetime = getattr(
54
+ base, "created_at", None
55
+ ) or discord.utils.snowflake_time(base.id)
56
+ self._attributes: Dict[str, Any] = {
57
+ "id": base.id,
58
+ "created_at": created_at,
59
+ "timestamp": int(created_at.timestamp()),
60
+ "name": getattr(base, "name", str(base)),
61
+ }
62
+ self._methods: Dict[str, Any] = {}
63
+ self.update_attributes()
64
+ self.update_methods()
65
+
66
+ def __repr__(self) -> str:
67
+ return f"<{type(self).__qualname__} object={self.object!r}>"
68
+
69
+ def update_attributes(self) -> None:
70
+ pass
71
+
72
+ def update_methods(self) -> None:
73
+ pass
74
+
75
+ def get_value(self, ctx: Verb) -> str:
76
+ should_escape = False
77
+ if ctx.parameter is None:
78
+ return_value = str(self.object)
79
+ else:
80
+ try:
81
+ value = self._attributes[ctx.parameter]
82
+ except KeyError:
83
+ if method := self._methods.get(ctx.parameter):
84
+ value = method()
85
+ else:
86
+ return # type: ignore
87
+ if isinstance(value, tuple):
88
+ value, should_escape = value
89
+ return_value: str = str(value) if value is not None else None # type: ignore
90
+ return escape_content(return_value) if should_escape else return_value
91
+
92
+
93
+ class DiscordAttributeAdapter(
94
+ SimpleAdapter[
95
+ Union[
96
+ discord.TextChannel,
97
+ discord.DMChannel,
98
+ discord.User,
99
+ discord.Member,
100
+ discord.Guild,
101
+ discord.Role,
102
+ ]
103
+ ]
104
+ ):
105
+ """
106
+ .. versionadded:: 3.2.0
107
+ """
108
+
109
+ def __init__(
110
+ self,
111
+ base: Union[
112
+ discord.TextChannel,
113
+ discord.DMChannel,
114
+ discord.User,
115
+ discord.Member,
116
+ discord.Guild,
117
+ discord.Role,
118
+ ],
119
+ ) -> None:
120
+ super().__init__(base=base)
121
+ created_at: datetime.datetime = getattr(
122
+ base, "created_at", None
123
+ ) or discord.utils.snowflake_time(base.id)
124
+ self._attributes.update(
125
+ {
126
+ "id": base.id,
127
+ "created_at": created_at,
128
+ "timestamp": int(created_at.timestamp()),
129
+ "name": getattr(base, "name", str(base)),
130
+ }
131
+ )
132
+
133
+ def __repr__(self) -> str:
134
+ return "<{} object={}>".format(type(self).__qualname__, self.object)
135
+
136
+ def get_value(self, ctx: Verb) -> str: # type: ignore
137
+ should_escape = False
138
+ if ctx.parameter is None:
139
+ return_value = str(self.object)
140
+ else:
141
+ try:
142
+ value = self._attributes[ctx.parameter]
143
+ except KeyError:
144
+ if method := self._methods.get(ctx.parameter):
145
+ value = method()
146
+ else:
147
+ _log.debug(
148
+ "No parameter named `{}` found for the `{}` Adapter.".format(
149
+ ctx.parameter, self.__class__.__name__
150
+ )
151
+ )
152
+ return # type: ignore
153
+ if isinstance(value, tuple):
154
+ value, should_escape = value
155
+ return_value = str(value) if value is not None else None
156
+ return escape_content(return_value) if should_escape else return_value # type: ignore
157
+
158
+
159
+ class UserAdapter(DiscordAttributeAdapter):
160
+ """
161
+ The ``{user}`` block with no parameters returns the user's full username,
162
+ but passing the attributes listed below to the block payload will return
163
+ that attribute instead.
164
+
165
+ **Usage:** ``{user([attribute])}``
166
+
167
+ **Payload:** None
168
+
169
+ **Parameter:** attribute, None
170
+
171
+ Attributes
172
+ ----------
173
+ id
174
+ The user's Discord ID.
175
+ name
176
+ The user's username.
177
+ nick
178
+ The user's nickname, if they have one, else their username.
179
+ avatar
180
+ A link to the user's avatar, which can be used in embeds.
181
+ created_at
182
+ The user's account creation date.
183
+ timestamp
184
+ The user's account creation date as UTC timestamp.
185
+ mention
186
+ A formatted text that ping's the user.
187
+ bot
188
+ Wheather or not the user is a bot.
189
+ accent_color
190
+ The user's accent color if banner is not present.
191
+ avatar_decoration
192
+ A link to the user's avatar decoration.
193
+
194
+ .. versionadded:: 3.2.0
195
+ """
196
+
197
+ def update_attributes(self) -> None:
198
+ object: discord.User = cast(discord.User, self.object)
199
+ avatar_url: str = object.display_avatar.url
200
+ if asset := object.avatar_decoration:
201
+ decoration: Union[str, bool] = asset.with_format("png").url
202
+ else:
203
+ decoration: Union[str, bool] = False
204
+ additional_attributes: Dict[str, Any] = {
205
+ "nick": object.display_name,
206
+ "mention": object.mention,
207
+ "avatar": (avatar_url, False),
208
+ "bot": object.bot,
209
+ "accent_color": getattr(object, "accent_color", False),
210
+ "avatar_decoration": decoration,
211
+ }
212
+ self._attributes.update(additional_attributes)
213
+
214
+
215
+ class MemberAdapter(DiscordAttributeAdapter):
216
+ """
217
+ The ``{author}`` block with no parameters returns the tag invoker's full username
218
+ and discriminator, but passing the attributes listed below to the block payload
219
+ will return that attribute instead.
220
+
221
+ **Aliases:** ``user``
222
+
223
+ **Usage:** ``{author([attribute])``
224
+
225
+ **Payload:** None
226
+
227
+ **Parameter:** attribute, None
228
+
229
+ Attributes
230
+ ----------
231
+ id
232
+ The author's Discord ID.
233
+ name
234
+ The author's username.
235
+ nick
236
+ The author's nickname, if they have one, else their username.
237
+ avatar
238
+ A link to the author's avatar, which can be used in embeds.
239
+ discriminator
240
+ The author's discriminator.
241
+ created_at
242
+ The author's account creation date.
243
+ timestamp
244
+ The author's account creation date as a UTC timestamp.
245
+ joined_at
246
+ The date the author joined the server.
247
+ joinstamp
248
+ The author's join date as a UTC timestamp.
249
+ mention
250
+ A formatted text that pings the author.
251
+ bot
252
+ Whether or not the author is a bot.
253
+ color
254
+ The author's top role's color as a hex code.
255
+ top_role
256
+ The author's top role.
257
+ roleids
258
+ A list of the author's role IDs, split by spaces.
259
+ boost
260
+ If the user has boosted, this will be the UTC timestamp of when they did,
261
+ if not this will be empty.
262
+ timed_out
263
+ If the user is timed out, this will be the UTC timestamp of when they'll be untimed-out,
264
+ if not timed out this will be empty.
265
+ banner
266
+ The users banner url
267
+ """
268
+
269
+ def update_attributes(self) -> None:
270
+ object: discord.Member = cast(discord.Member, self.object)
271
+ avatar_url: str = object.display_avatar.url
272
+ joined_at: datetime.datetime = getattr(object, "joined_at", self.object.created_at)
273
+ additional_attributes: Dict[str, Any] = {
274
+ "color": object.color,
275
+ "colour": object.color,
276
+ "nick": object.display_name,
277
+ "avatar": (avatar_url, False),
278
+ "discriminator": object.discriminator,
279
+ "joined_at": joined_at,
280
+ "joinstamp": int(joined_at.timestamp()),
281
+ "mention": object.mention,
282
+ "bot": object.bot,
283
+ "top_role": getattr(object, "top_role", ""),
284
+ "boost": getattr(object, "premium_since", ""),
285
+ "timed_out": getattr(object, "timed_out_until", ""),
286
+ "banner": object.banner.url if object.banner else "",
287
+ }
288
+ if roleids := getattr(self.object, "_roles", None):
289
+ additional_attributes["roleids"] = " ".join(str(r) for r in roleids)
290
+ self._attributes.update(additional_attributes)
291
+
292
+
293
+ class DMChannelAdapter(DiscordAttributeAdapter):
294
+ """
295
+ The ``{channel}`` block with no parameters returns the channel's full name
296
+ but passing the attributes listed below to the block payload will return
297
+ the attribute instead.
298
+
299
+ **Usage:** ``{channel([attribute])``
300
+
301
+ **Payload:** None
302
+
303
+ **Parameter:** attribute, None
304
+
305
+ Attributes
306
+ ----------
307
+ id
308
+ The channel's ID.
309
+ name
310
+ The channel's name.
311
+ created_at
312
+ The channel's creation date.
313
+ timestamp
314
+ The channel's creation date as a UTC timestamp.
315
+ jump_url
316
+ A link to the channel.
317
+
318
+ .. versionadded:: 3.2.0
319
+ """
320
+
321
+ def update_attributes(self) -> None:
322
+ if isinstance(self.object, discord.DMChannel):
323
+ additional_attributes: Dict[str, Any] = {
324
+ "jump_url": getattr(self.object, "jump_url", None)
325
+ }
326
+ self._attributes.update(additional_attributes)
327
+
328
+
329
+ class ChannelAdapter(DiscordAttributeAdapter):
330
+ """
331
+ The ``{channel}`` block with no parameters returns the channel's full name
332
+ but passing the attributes listed below to the block payload
333
+ will return that attribute instead.
334
+
335
+ **Usage:** ``{channel([attribute])``
336
+
337
+ **Payload:** None
338
+
339
+ **Parameter:** attribute, None
340
+
341
+ Attributes
342
+ ----------
343
+ id
344
+ The channel's ID.
345
+ name
346
+ The channel's name.
347
+ created_at
348
+ The channel's creation date.
349
+ timestamp
350
+ The channel's creation date as a UTC timestamp.
351
+ nsfw
352
+ Whether the channel is nsfw.
353
+ mention
354
+ A formatted text that pings the channel.
355
+ topic
356
+ The channel's topic.
357
+ category_id
358
+ The category the channel is associated with.
359
+ If no category channel, this will return empty.
360
+ jump_url
361
+ A link to the channel.
362
+
363
+ .. versionchanged:: 3.2.0
364
+ Added ``jump_url`` as a parameter.
365
+ """
366
+
367
+ def update_attributes(self) -> None:
368
+ if isinstance(self.object, discord.TextChannel):
369
+ additional_attributes: Dict[str, Any] = {
370
+ "nsfw": self.object.nsfw,
371
+ "mention": self.object.mention,
372
+ "topic": self.object.topic or "",
373
+ "slowmode": self.object.slowmode_delay,
374
+ "category_id": self.object.category_id or "",
375
+ "jump_url": self.object.jump_url or None,
376
+ }
377
+ self._attributes.update(additional_attributes)
378
+
379
+
380
+ class GuildAdapter(DiscordAttributeAdapter):
381
+ """
382
+ The ``{server}`` block with no parameters returns the server's name
383
+ but passing the attributes listed below to the block payload
384
+ will return that attribute instead.
385
+
386
+ **Aliases:** ``guild``
387
+
388
+ **Usage:** ``{server([attribute])``
389
+
390
+ **Payload:** None
391
+
392
+ **Parameter:** attribute, None
393
+
394
+ Attributes
395
+ ----------
396
+ id
397
+ The server's ID.
398
+ name
399
+ The server's name.
400
+ icon
401
+ A link to the server's icon, which can be used in embeds.
402
+ created_at
403
+ The server's creation date.
404
+ timestamp
405
+ The server's creation date as a UTC timestamp.
406
+ member_count
407
+ The server's member count.
408
+ bots
409
+ The number of bots in the server.
410
+ humans
411
+ The number of humans in the server.
412
+ description
413
+ The server's description if one is set, or "No description".
414
+ random
415
+ A random member from the server.
416
+ vanity
417
+ If guild has a vanity, this returns the vanity else empty.
418
+ owner_id
419
+ The server owner's id.
420
+ mfa
421
+ The server's mfa level.
422
+ boosters
423
+ The server's active booster count.
424
+ boost_level
425
+ The server's current boost level/tier.
426
+ discovery_splash
427
+ A link to the server's discovery splash.
428
+ invite_splash
429
+ A link to the server's invite splash.
430
+ banner
431
+ A link to the server's banner.
432
+
433
+ .. versionchanged:: 3.2.0
434
+ Added ``mfa``, ``boosters``, ``boost_level``,
435
+ ``discovery_splash``, ``invite_splash`` & ``banner``.
436
+ """
437
+
438
+ def update_attributes(self) -> None:
439
+ object: discord.Guild = cast(discord.Guild, self.object)
440
+ guild: discord.Guild = object
441
+ bots: int = 0
442
+ humans: int = 0
443
+ for m in guild.members:
444
+ if m.bot:
445
+ bots += 1
446
+ else:
447
+ humans += 1
448
+ member_count: int = getattr(guild, "member_count", 0)
449
+ icon_url: str = getattr(guild.icon, "url", "")
450
+ additional_attributes: Dict[str, Any] = {
451
+ "icon": (icon_url, False),
452
+ "member_count": member_count,
453
+ "members": member_count,
454
+ "bots": bots,
455
+ "humans": humans,
456
+ "description": guild.description or "No description.",
457
+ "vanity": guild.vanity_url_code or "No Vanity URL.",
458
+ "owner_id": guild.owner_id or "",
459
+ "mfa": guild.mfa_level,
460
+ "boosters": guild.premium_subscription_count,
461
+ "boost_level": guild.premium_tier,
462
+ "discovery_splash": getattr(guild.discovery_splash, "url", False),
463
+ "invite_splash": getattr(guild.splash, "url", False),
464
+ "banner": getattr(guild.banner, "url", False),
465
+ }
466
+ self._attributes.update(additional_attributes)
467
+
468
+ def update_methods(self) -> None:
469
+ additional_methods: Dict[str, Any] = {"random": self.random_member}
470
+ self._methods.update(additional_methods)
471
+
472
+ def random_member(self) -> discord.Member:
473
+ object: discord.Guild = cast(discord.Guild, self.object)
474
+ return choice(object.members)
475
+
476
+
477
+ class RoleAdapter(DiscordAttributeAdapter):
478
+ """
479
+ The ``{role}`` block with no parameters returns the role's full name
480
+ but passing the attributes listed below to the block payload will
481
+ return that attribute instead.
482
+
483
+ **Usage:** ``{role([attribute])}``
484
+
485
+ **Payload:** None
486
+
487
+ **Parameter:** attribute, None
488
+
489
+ Attributes
490
+ ----------
491
+ id
492
+ The role's ID.
493
+ name
494
+ The role's name.
495
+ created_at
496
+ The role's creation date.
497
+ timestamp
498
+ The role's creation date as a UTC timestamp.
499
+ color
500
+ The role's color.
501
+ display_icon
502
+ The role's icon.
503
+ hoist
504
+ Wheather the role is hoisted or not.
505
+ managed
506
+ Wheather the role is managed or not.
507
+ mention
508
+ A formatted text that pings the role.
509
+ position
510
+ The role's position.
511
+
512
+ .. versionadded:: 3.2.0
513
+ """
514
+
515
+ def update_attributes(self) -> None:
516
+ object: discord.Role = cast(discord.Role, self.object)
517
+ additional_attributes: Dict[str, Any] = {
518
+ "color": object.color,
519
+ "display_icon": getattr(object.display_icon, "url", False),
520
+ "hoist": object.hoist,
521
+ "managed": object.managed,
522
+ "mention": object.mention,
523
+ "position": object.position,
524
+ }
525
+ self._attributes.update(additional_attributes)
526
+
527
+
528
+ class DiscordObjectAdapter(Adapter):
529
+ """
530
+ The ``{object}`` block with no parameters returs the discord object's ID,
531
+ but passing the attributes listed below to the block payload will return
532
+ that attribute instead.
533
+
534
+ **Usage:** ``{object([attribute])}``
535
+
536
+ **Payload:** None
537
+
538
+ **Parameter:** attribute, None
539
+
540
+ Attributes
541
+ ----------
542
+ id
543
+ The object's Discord ID.
544
+ created_at
545
+ The object's creation date.
546
+ timestamp
547
+ The object's creation date as a UTC timestamp.
548
+
549
+ .. versionadded:: 3.2.0
550
+ """
551
+
552
+ __slots__: Tuple[str, ...] = ("object", "_attributes", "_methods")
553
+
554
+ def __init__(self, base: discord.Object) -> None:
555
+ self.object: discord.Object = base
556
+ created_at: datetime.datetime = getattr(
557
+ base, "created_at", None
558
+ ) or discord.utils.snowflake_time(base.id)
559
+ self._attributes: Dict[str, Any] = {
560
+ "id": base.id,
561
+ "created_at": created_at,
562
+ "timestamp": int(created_at.timestamp()),
563
+ }
564
+ self._methods: Dict[str, Any] = {}
565
+ self.update_attributes()
566
+ self.update_methods()
567
+
568
+ def __repr__(self) -> str:
569
+ return f"<{type(self).__qualname__} object={self.object!r}>"
570
+
571
+ def update_attributes(self) -> None:
572
+ pass
573
+
574
+ def update_methods(self) -> None:
575
+ pass
576
+
577
+ def get_value(self, ctx: Verb) -> str: # type: ignore
578
+ should_escape = False
579
+
580
+ if ctx.parameter is None:
581
+ return_value = str(self.object.id)
582
+ else:
583
+ try:
584
+ value = self._attributes[ctx.parameter]
585
+ except KeyError:
586
+ if method := self._methods.get(ctx.parameter):
587
+ value = method()
588
+ else:
589
+ return # type: ignore
590
+
591
+ if isinstance(value, tuple):
592
+ value, should_escape = value
593
+
594
+ return_value = str(value) if value is not None else None
595
+
596
+ return escape_content(return_value) if should_escape else return_value # type: ignore
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable, Tuple
4
+
5
+ from ..interface import Adapter
6
+ from ..verb import Verb
7
+
8
+
9
+ __all__: Tuple[str, ...] = ("FunctionAdapter",)
10
+
11
+
12
+ class FunctionAdapter(Adapter):
13
+ __slots__: Tuple[str, ...] = ("fn",)
14
+
15
+ def __init__(self, function_pointer: Callable[[], str]) -> None:
16
+ self.fn = function_pointer
17
+ super().__init__()
18
+
19
+ def __repr__(self) -> str:
20
+ return f"<{type(self).__qualname__} fn={self.fn!r}>"
21
+
22
+ def get_value(self, ctx: Verb) -> str: # type: ignore
23
+ return str(self.fn())
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Tuple
4
+
5
+ from ..interface import Adapter
6
+ from ..verb import Verb
7
+
8
+
9
+ __all__: Tuple[str, ...] = ("IntAdapter",)
10
+
11
+
12
+ class IntAdapter(Adapter):
13
+ __slots__: Tuple[str, ...] = ("integer",)
14
+
15
+ def __init__(self, integer: int) -> None:
16
+ self.integer: int = int(integer)
17
+
18
+ def __repr__(self) -> str:
19
+ return f"<{type(self).__qualname__} integer={repr(self.integer)}>"
20
+
21
+ def get_value(self, ctx: Verb) -> str: # type: ignore
22
+ return str(self.integer)
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Tuple
4
+ from inspect import ismethod
5
+
6
+ from ..interface import Adapter
7
+ from ..verb import Verb
8
+
9
+
10
+ __all__: Tuple[str, ...] = ("SafeObjectAdapter",)
11
+
12
+
13
+ class SafeObjectAdapter(Adapter):
14
+ __slots__: Tuple[str, ...] = ("object",)
15
+
16
+ def __init__(self, base) -> None:
17
+ self.object = base
18
+
19
+ def __repr__(self) -> str:
20
+ return f"<{type(self).__qualname__} object={repr(self.object)}>"
21
+
22
+ def get_value(self, ctx: Verb) -> str:
23
+ if ctx.parameter is None:
24
+ return str(self.object)
25
+ if ctx.parameter.startswith("_") or "." in ctx.parameter:
26
+ return # type: ignore
27
+ try:
28
+ attribute = getattr(self.object, ctx.parameter)
29
+ except AttributeError:
30
+ return # type: ignore
31
+ if ismethod(attribute):
32
+ return # type: ignore
33
+ if isinstance(attribute, float):
34
+ attribute = int(attribute)
35
+ return str(attribute)