Skip to content

Commit

Permalink
codebase: typeshi integration + gh ctx attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
seven7ty committed Dec 20, 2024
1 parent 055352f commit 399562d
Show file tree
Hide file tree
Showing 19 changed files with 88 additions and 33 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
*.last.json
*.locale.last.json

# GitBot dev flow
resources/gen/*

# PyCharm Stuff
.idea/

Expand Down
3 changes: 2 additions & 1 deletion bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from lib.structs.discord.context import GitBotContext
from lib.structs.discord.bot import GitBot

# all of the configuration is handled inside the class, there is no real need to pass anything here
# all the configuration is handled inside the class, there is no real need to pass anything here
bot = GitBot()


Expand Down Expand Up @@ -72,6 +72,7 @@ async def global_check(ctx: GitBotContext) -> bool:
async def before_invoke(ctx: GitBotContext):
if str(ctx.command) not in bot.mgr.env.no_typing_commands:
await ctx.channel.typing()
# ctx.gh overwrites will be handled here


if __name__ == '__main__':
Expand Down
13 changes: 13 additions & 0 deletions cli/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os
import sys
import click
import typeshi
import subprocess
from lib.structs.proxies.dict_proxy import DictProxy
from .config import PYTHON_COMMAND_LINE, APP_ROOT_DIR
from .scripts import run_help_helper

Expand Down Expand Up @@ -31,6 +33,17 @@ def dev():
pass


@dev.command('generate-locale-defs')
def generate_locale_defs():
if not os.path.exists('resources/gen/'):
os.makedirs('resources/gen/')
typeshi.save_declaration_module_from_json(
'Locale', 'resources/locale/en.locale.json', 'resources/gen/locale_schema.py',
inherit_cls=DictProxy
)
print(f'Wrote locale schema to resources/gen/locale_schema.py')


@dev.command('update', help='Update the local code using git')
def update():
if sys.platform == 'win32':
Expand Down
3 changes: 2 additions & 1 deletion cli/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
click==8.1.6
click==8.1.6
typeshi==2.0.1
4 changes: 2 additions & 2 deletions cogs/backend/handle/events/_event_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async def silent_snippet_command(ctx: GitBotContext) -> Optional[discord.Message
codeblock: Optional[str] = None
config: AutomaticConversionSettings = await ctx.bot.db.guilds.get_autoconv_config(ctx) # noqa
match_ = None # put the match_ name in the namespace
if (attachment_url := ctx.bot.get_cache_v('carbon', ctx.message.content)) and (config['gh_lines'] == 2 or config.get('codeblock', False)):
if (attachment_url := ctx.bot.get_cache_value('carbon', ctx.message.content)) and (config['gh_lines'] == 2 or config.get('codeblock', False)):
ctx.bot.logger.debug(f'Responding with cached asset URL to MID %d - %s', ctx.message.id, attachment_url)
return await ctx.reply(attachment_url, mention_author=False)
elif (result := ctx.bot.mgr.extract_content_from_codeblock(ctx.message.content)) and config.get('codeblock', False):
Expand Down Expand Up @@ -57,7 +57,7 @@ async def silent_snippet_command(ctx: GitBotContext) -> Optional[discord.Message
codeblock, _1st_lineno)),
mention_author=False)
ctx.bot.logger.debug('Carbon asset generation elapsed: %ds', time.time() - start)
ctx.bot.set_cache_v('carbon', ctx.message.content, reply.attachments[0].url)
ctx.bot.set_cache_value('carbon', ctx.message.content, reply.attachments[0].url)
return reply


Expand Down
2 changes: 1 addition & 1 deletion cogs/backend/handle/events/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async def on_guild_join(self, guild: discord.Guild) -> None:
async def on_guild_remove(self, guild: discord.Guild) -> None:
await self.bot.db.guilds.find_one_and_delete({'_id': guild.id})
try:
self.bot.del_cache_v('autoconv', guild.id)
self.bot.del_cache_value('autoconv', guild.id)
except KeyError:
pass
embed_l: GitBotEmbed = await build_guild_embed(self.bot, guild, False)
Expand Down
10 changes: 5 additions & 5 deletions cogs/ecosystem/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async def toggle_autoconv_item(ctx: GitBotContext,
await ctx.bot.db.guilds.update_one({'_id': guild['_id']}, {'$set': {f'autoconv.{item}': state}})
else:
await ctx.bot.db.guilds.insert_one(GitBotGuild(_id=ctx.guild.id, autoconv=config)) # noqa _id is int
ctx.bot.set_cache_v('autoconv', ctx.guild.id, config)
ctx.bot.set_cache_value('autoconv', ctx.guild.id, config)
await ctx.success(ctx.l.config.autoconv.toggles.get(item).get(str(state)))
if not ctx.bot_permissions.read_message_history:
await ctx.hint(ctx.l.generic.hints.read_message_history_permission)
Expand Down Expand Up @@ -73,8 +73,8 @@ async def config_show_command_group(self, ctx: GitBotContext) -> None:
lang: str = ctx.fmt('accessibility list locale', f'`{ctx.l.meta.localized_name.capitalize()}`')
user_str, org, repo = (ctx.fmt(f'qa list {item}', self.bot.mgr.to_github_hyperlink(user[item], True) if item in user else
f'`{ctx.l.config.show.base.item_not_set}`') for item in ('user', 'org', 'repo'))
accessibility: list = ctx.l.config.show.base.accessibility.heading + '\n' + '\n'.join([lang])
qa: list = ctx.l.config.show.base.qa.heading + '\n' + '\n'.join([user_str, org, repo])
accessibility: str = ctx.l.config.show.base.accessibility.heading + '\n' + '\n'.join([lang])
qa: str = ctx.l.config.show.base.qa.heading + '\n' + '\n'.join([user_str, org, repo])
guild_str: str = ''
if not isinstance(ctx.channel, discord.DMChannel):
feed: str = ctx.l.config.show.base.guild.list.feed + '\n' + '\n'.join([f'{self.bot.mgr.e.square} <#{rfi["cid"]}>'
Expand Down Expand Up @@ -374,7 +374,7 @@ async def config_locale_command(self, ctx: GitBotContext, locale: Optional[str]
return
await self.bot.db.users.setitem(ctx, 'locale', l_[0]['name'])
setattr(ctx, 'l', await self.bot.db.users.get_locale(ctx))
self.bot.set_cache_v('locale', ctx.author.id, l_[0]['name']) # update the cache with the new locale
self.bot.set_cache_value('locale', ctx.author.id, l_[0]['name']) # update the cache with the new locale
await ctx.success_embed(ctx.fmt('success', l_[0]['localized_name'].capitalize()))
return
to_followup = await ctx.error(ctx.fmt('failure', locale))
Expand Down Expand Up @@ -472,7 +472,7 @@ async def _callback(_, res: discord.Message):
config: AutomaticConversionSettings = self.bot.mgr.env.autoconv_default
config['gh_lines'] = actual_state
await self.bot.db.guilds.insert_one({'_id': ctx.guild.id, 'autoconv': config})
self.bot.set_cache_v('autoconv', ctx.guild.id, config)
self.bot.set_cache_value('autoconv', ctx.guild.id, config)
await ctx.success(ctx.lp.results[_str])
if not ctx.bot_permissions.read_message_history:
await ctx.hint(ctx.l.generic.hints.read_message_history_permission)
Expand Down
2 changes: 1 addition & 1 deletion cogs/github/base/repo/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ async def repo_info_command(self, ctx: GitBotContext, repo: Optional[GitHubRepos
if ctx.data:
r: dict = getattr(ctx, 'data')
else:
r: Optional[dict] = await self.bot.github.get_repo(repo)
r: Optional[dict] = await ctx.gh.get_repo(repo)
if not r:
if ctx.invoked_with_stored:
await self.bot.db.users.delitem(ctx, 'repo')
Expand Down
4 changes: 2 additions & 2 deletions cogs/github/other/loc.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def remove_matches(self, directory: str, pattern: str) -> int:
return c_removed

async def process_repo(self, ctx: GitBotContext, repo: GitHubRepository) -> Optional[tuple[dict, int | None]]:
if (not ctx.__nocache__) and (cached := self.bot.get_cache_v('loc', repo := repo.lower())):
if (not ctx.__nocache__) and (cached := self.bot.get_cache_value('loc', repo := repo.lower())):
return cached
tmp_zip_path: str = f'./tmp/{ctx.message.id}.zip'
tmp_dir_path: str = tmp_zip_path[:-4]
Expand All @@ -112,7 +112,7 @@ async def process_repo(self, ctx: GitBotContext, repo: GitHubRepository) -> Opti
except subprocess.CalledProcessError as e:
self.bot.logger.error('the CLOC script failed with exit code %d', e.returncode)
else:
self.bot.set_cache_v('loc', repo, (output, c_removed))
self.bot.set_cache_value('loc', repo, (output, c_removed))
return output, c_removed
finally:
try:
Expand Down
11 changes: 7 additions & 4 deletions lib/api/github/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ async def wrapper(*args: tuple, **kwargs: dict) -> Any:

return wrapper


def _flatten_nodes(func: Callable) -> Callable[..., DictProxy | SnakeCaseDictProxy | None]:
"""
Flattens the nodes field in the response dict by copying the value up one level and removing the old key
Expand Down Expand Up @@ -223,10 +224,10 @@ def _sanitize_graphql_variables(variables: dict[str, ...]) -> dict[str, ...]:

async def query(self,
query_or_path: str,
transformer: tuple[LiteralString, ...] | str | Callable[[dict], dict] | None = None,
transformer: tuple[str, ...] | str | Callable[[dict], dict] | None = None,
on_fail_return: _GitHubAPIQueryWrapOnFailReturnDefaultConditionDict |
_GitHubAPIQueryWrapOnFailReturnDefaultNotSet | bool | list | None = 'default_not_set',
**graphql_variables) -> _ReturnDict | list[_ReturnDict] | str | None:
**graphql_variables) -> _ReturnDict | list[_ReturnDict] | str | bool | None:
"""
Wraps a GitHub API query call, handling errors and returning the result.
The request method is chosen between REST and GraphQL based on the query_or_path parameter -
Expand Down Expand Up @@ -393,7 +394,8 @@ async def get_repo_zip(self,
@_wrap_proxy
@normalize_repository
async def get_latest_release(self, repo: GitHubRepository) -> Optional[_ReturnDict]:
return await self.query(self.queries.latest_release, _Repo=repo, transformer=transform_latest_release, on_fail_return=None)
return await self.query(self.queries.latest_release, _Repo=repo, transformer=transform_latest_release,
on_fail_return=None)

@_wrap_proxy
@normalize_repository
Expand Down Expand Up @@ -471,4 +473,5 @@ async def get_latest_n_releases(self, repo: GitHubRepository, n: int = 5) -> lis
@_flatten_nodes
@normalize_repository
async def get_latest_n_releases_with_repo(self, repo: GitHubRepository, n: int = 5) -> list[_ReturnDict]:
return await self.query(self.queries.latest_releases, _Repo=repo, N=n, on_fail_return=None, transformer=transform_latest_release)
return await self.query(self.queries.latest_releases, _Repo=repo, N=n, on_fail_return=None,
transformer=transform_latest_release)
4 changes: 2 additions & 2 deletions lib/structs/db/collections/guilds.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ async def get_autoconv_config(self,
"""
_did_exist: bool = False

if cached := self.bot.get_cache_v('autoconv', _id):
if cached := self.bot.get_cache_value('autoconv', _id):
_did_exist: bool = True
permission: AutomaticConversionSettings = cached
self.bot.logger.debug('Returning cached auto values for identity "%d"', _id)
Expand All @@ -53,5 +53,5 @@ async def get_autoconv_config(self,
_did_exist: bool = True
else:
permission: AutomaticConversionSettings = self.bot.mgr.env.autoconv_default
self.bot.set_cache_v('autoconv', _id, permission)
self.bot.set_cache_value('autoconv', _id, permission)
return permission if not did_exist else (permission, _did_exist)
8 changes: 4 additions & 4 deletions lib/structs/db/collections/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ async def delitem(self, _id: Identity, field: str | Iterable[str]) -> bool:
return False

@normalize_identity()
async def getitem(self, _id: Identity, item: str | Iterable[str]) -> Optional[
async def getitem(self, _id: Identity, item: str | Iterable[str], query_additional_kwargs: Optional[dict] = None) -> Optional[
str | dict | list | bool | int | float]:
query: Optional[dict] = await self.find_one({'_id': _id})
query: Optional[dict] = await self.find_one({'_id': _id, **(query_additional_kwargs or {})})
if query and (ret := get_nested_key(query, item)) is not None:
return ret
return None
Expand Down Expand Up @@ -75,14 +75,14 @@ async def get_locale(self, _id: Identity) -> DictProxy:
:return: The locale associated with the user
"""
locale: LocaleName = self.bot.mgr.locale.master.meta.name
if cached := self.bot.get_cache_v('locale', _id):
if cached := self.bot.get_cache_value('locale', _id):
locale: LocaleName = cached
self.bot.logger.debug('Returning cached locale for identity "%d"', _id)
else:
if stored := await self.getitem(_id, 'locale'):
locale: str = stored
try:
self.bot.set_cache_v('locale', _id, locale)
self.bot.set_cache_value('locale', _id, locale)
return getattr(self.bot.mgr.l, locale)
except AttributeError:
return self.bot.mgr.locale.master
13 changes: 8 additions & 5 deletions lib/structs/discord/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
load_dotenv()

__all__: tuple = ('GitBot',)
_CacheNameT = Literal['autoconv', 'locale', 'carbon', 'loc']


class GitBot(commands.AutoShardedBot):
Expand All @@ -56,7 +57,8 @@ class GitBot(commands.AutoShardedBot):
def __init__(self, **kwargs):
self.__init_start: float = perf_counter()
self.user_id_blacklist: set = set()
super().__init__(command_prefix=f'{os.getenv("PREFIX")} ', case_insensitive=True,
super().__init__(command_prefix=commands.when_mentioned_or(f'{os.getenv("PREFIX")} '),
case_insensitive=True,
intents=discord.Intents(messages=True, message_content=True, guilds=True,
guild_reactions=True),
help_command=None, guild_ready_timeout=1,
Expand Down Expand Up @@ -202,7 +204,8 @@ async def reload_extension(self, name: str, *, package=None):
await super().reload_extension(name, package=package)
self.logger.info('Reloaded extension: "%s"', name)

def get_cache(self, cache_name: Literal['autoconv', 'locale', 'carbon', 'loc']) -> TypedCache | SelfHashingCache | None:
def get_cache(self,
cache_name: _CacheNameT) -> TypedCache | SelfHashingCache | None:
"""
Get a cache by name
Expand All @@ -212,7 +215,7 @@ def get_cache(self, cache_name: Literal['autoconv', 'locale', 'carbon', 'loc'])

return self.__caches__.get(cache_name)

def get_cache_v(self, cache_name: Literal['autoconv', 'locale', 'carbon', 'loc'], key: Any) -> Any:
def get_cache_value(self, cache_name: _CacheNameT, key: Any) -> Any:
"""
Get a cache value by cache name and key
Expand All @@ -223,7 +226,7 @@ def get_cache_v(self, cache_name: Literal['autoconv', 'locale', 'carbon', 'loc']

return self.__caches__.get(cache_name, {}).get(key)

def set_cache_v(self, cache_name: Literal['autoconv', 'locale', 'carbon', 'loc'], key: Any, value: Any) -> None:
def set_cache_value(self, cache_name: _CacheNameT, key: Any, value: Any) -> None:
"""
Set a cache value
Expand All @@ -234,7 +237,7 @@ def set_cache_v(self, cache_name: Literal['autoconv', 'locale', 'carbon', 'loc']

self.__caches__[cache_name][key] = value

def del_cache_v(self, cache_name: Literal['autoconv', 'locale', 'carbon', 'loc'], key: Any) -> None:
def del_cache_value(self, cache_name: _CacheNameT, key: Any) -> None:
"""
Delete a cache value
Expand Down
8 changes: 5 additions & 3 deletions lib/structs/discord/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import enum
from discord.ext import commands
from typing import Optional, Any, Sequence
from lib.typehints import EmbedLike
from lib.typehints import EmbedLike, LocaleDictProxyDef
from lib.structs import DictProxy
from lib.structs.discord.embed import GitBotEmbed
from lib.structs.discord.commands import GitBotCommand, GitBotCommandGroup, GitBotHybridCommandGroup
Expand All @@ -21,7 +21,7 @@
if TYPE_CHECKING:
from aiohttp import ClientSession
from lib.structs import CheckFailureCode, GitBot
from lib.api.github import GitHubQueryDebugInfo
from lib.api.github import GitHubQueryDebugInfo, GitHubAPI

__all__: tuple = ('MessageFormattingStyle', 'GitBotContext')

Expand All @@ -38,23 +38,25 @@ class MessageFormattingStyle(enum.Enum):


class GitBotContext(commands.Context):
l: LocaleDictProxyDef | None
bot: 'GitBot'
command: GitBotCommand | GitBotCommandGroup
check_failure_code: Union[int, 'CheckFailureCode'] | None = None
gh_query_debug: Optional['GitHubQueryDebugInfo'] = None
lines_total: int | None = None
gh: 'GitHubAPI'
__nocache__: bool = False
__autoinvoked__: bool = False
__silence_error_calls__: bool = False

def __init__(self, **attrs):
self.command: GitBotCommand | GitBotCommandGroup
super().__init__(**attrs)
self.session: ClientSession = self.bot.session
self.fmt = self.bot.mgr.fmt(self)
self.l = None # noqa
self.data: Optional[dict] = None # field used by chained invocations and quick access
self.invoked_with_stored: bool = False
self.gh: 'GitHubAPI' = self.bot.github # overwritten in bot.py before_invoke hook

def _format_content(self, content: str, style: MessageFormattingStyle | str) -> str:
match MessageFormattingStyle(style):
Expand Down
4 changes: 3 additions & 1 deletion lib/typehints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from lib.typehints.db.guild.autoconv import AutomaticConversionSettings
from lib.typehints.generic import *
from lib.typehints.locale.help import *
from lib.typehints.locale.localedef_wfallback import LocaleDictProxyDef
from lib.typehints.db.guild.release_feed import *
from lib.typehints.gitbot_config import *

Expand Down Expand Up @@ -35,7 +36,8 @@
'CommandGroupHelp',
'CratesIOCrate',
'ReleaseFeedItemMention',
'GitbotRepoConfig'
'GitbotRepoConfig',
'LocaleDictProxyDef'
)

AnyDict = dict | DictProxy | CaseInsensitiveDict | MaxAgeDict | FixedSizeOrderedDict
Expand Down
9 changes: 9 additions & 0 deletions lib/typehints/locale/localedef_wfallback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# coding: utf-8

try:
from resources.gen.locale_schema import Locale as LocaleDictProxyDef
except ImportError:
from lib.structs.proxies.dict_proxy import DictProxy
LocaleDictProxyDef = DictProxy

__all__: tuple = ('LocaleDictProxyDef',)
16 changes: 16 additions & 0 deletions lib/typehints/resource_defs_wfallbacks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# coding: utf-8

from lib.structs.proxies.dict_proxy import DictProxy

try:
from resources.gen.locale_schema import Locale as LocaleDictProxyDef
except ImportError:
LocaleDictProxyDef = DictProxy

try:
from resources.gen.env_defaults_schema import EnvDefaults as EnvDefaultsProxyDef
except ImportError:
EnvDefaultsProxyDef = DictProxy


__all__: tuple = ('LocaleDictProxyDef', 'EnvDefaultsProxyDef')
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
typeshi==2.0.1
3 changes: 2 additions & 1 deletion resources/env_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"github": {
"scopes": [
"user:email",
"read:user"
"read:user",
"repo"
],
"auth_timeout": 150
}
Expand Down

0 comments on commit 399562d

Please sign in to comment.