[config] Suppression des nones dans le toml, sauvegarde de la config au chargement pour ajouter les nouveaux parametres

[bot-base] Gestion des erreurs
This commit is contained in:
Louis Chauvet 2020-04-24 12:10:33 +02:00
parent 1966b69d53
commit 75f524c509
Signed by: fomys
GPG Key ID: 1ECA046A9615ABA0
4 changed files with 66 additions and 31 deletions

View File

@ -5,6 +5,7 @@ import inspect
import logging import logging
import os import os
import sys import sys
import traceback
import discord import discord
import toml import toml
@ -85,7 +86,8 @@ class BotBase(discord.Client):
# Check if module exists # Check if module exists
if not os.path.isdir(os.path.join(self.config["modules_folder"], module)): if not os.path.isdir(os.path.join(self.config["modules_folder"], module)):
self.warning(f"Attempt to load unknown module {module}.") self.warning(f"Attempt to load unknown module {module}.")
raise errors.ModuleNotFoundError(f"Module {module} not found in modules folder ({self.config['modules_folder']}.)") raise errors.ModuleNotFoundError(
f"Module {module} not found in modules folder ({self.config['modules_folder']}.)")
if not os.path.isfile(os.path.join(self.config["modules_folder"], module, "infos.toml")): if not os.path.isfile(os.path.join(self.config["modules_folder"], module, "infos.toml")):
self.warning(f"Attempt to load incompatible module {module}: no infos.toml found") self.warning(f"Attempt to load incompatible module {module}: no infos.toml found")
raise errors.IncompatibleModuleError(f"Module {module} is incompatible: no infos.toml found.") raise errors.IncompatibleModuleError(f"Module {module} is incompatible: no infos.toml found.")
@ -102,7 +104,7 @@ class BotBase(discord.Client):
self.warning(f"Attempt to load incompatible module {module}: need bot version {infos['bot_version']} " self.warning(f"Attempt to load incompatible module {module}: need bot version {infos['bot_version']} "
f"and you have {__version__}") f"and you have {__version__}")
raise errors.IncompatibleModuleError(f"Module {module} is not compatible with your current bot version " raise errors.IncompatibleModuleError(f"Module {module} is not compatible with your current bot version "
f"(need {infos['bot_version']} and you have {__version__}).") f"(need {infos['bot_version']} and you have {__version__}).")
# Check dependencies # Check dependencies
if infos.get("dependencies"): if infos.get("dependencies"):
for dep, version in infos["dependencies"].items(): for dep, version in infos["dependencies"].items():
@ -113,13 +115,17 @@ class BotBase(discord.Client):
self.warning(f"Attempt to load incompatible module {module}: (require {dep} ({version}) " self.warning(f"Attempt to load incompatible module {module}: (require {dep} ({version}) "
f"and you have {dep} ({self.modules[dep]['infos']['version']})") f"and you have {dep} ({self.modules[dep]['infos']['version']})")
raise errors.IncompatibleModuleError(f"Module {module} is not compatible with your current install " raise errors.IncompatibleModuleError(f"Module {module} is not compatible with your current install "
f"(require {dep} ({version}) and you have {dep} " f"(require {dep} ({version}) and you have {dep} "
f"({self.modules[dep]['infos']['version']})") f"({self.modules[dep]['infos']['version']})")
# Check if module is meta # Check if module is meta
if infos.get("metamodule", False) == False: if infos.get("metamodule", False) == False:
# Check if module have __main_class__ # Check if module have __main_class__
imported = importlib.import_module(module) try:
imported = importlib.import_module(module)
except Exception as e:
self.warning(f"Attempt to load incompatible module {module}: failed import")
raise e
try: try:
main_class = imported.__main_class__ main_class = imported.__main_class__
except AttributeError: except AttributeError:
@ -139,8 +145,9 @@ class BotBase(discord.Client):
dispatch = main_class.__dispatch__ dispatch = main_class.__dispatch__
except AttributeError: except AttributeError:
self.warning(f"Attempt to load incompatible module {module}: __dispatch_ not found") self.warning(f"Attempt to load incompatible module {module}: __dispatch_ not found")
raise errors.IncompatibleModuleError(f"Module {module} mainclass ({main_class}) does not provide __dispatch__" raise errors.IncompatibleModuleError(
f" attribute)") f"Module {module} mainclass ({main_class}) does not provide __dispatch__"
f" attribute)")
# Check if __dispatch__ is function # Check if __dispatch__ is function
if not inspect.isfunction(imported.__main_class__.__dispatch__): if not inspect.isfunction(imported.__main_class__.__dispatch__):
self.warning(f"Attempt to load incompatible module {module}: __dispatch__ is not a function") self.warning(f"Attempt to load incompatible module {module}: __dispatch__ is not a function")
@ -178,7 +185,7 @@ class BotBase(discord.Client):
"dispatch": dispatch, "dispatch": dispatch,
} }
}) })
else: # Module is metamodule else: # Module is metamodule
self.info(f"Add modules {module} to current modules") self.info(f"Add modules {module} to current modules")
self.modules.update({ self.modules.update({
module: { module: {
@ -196,16 +203,19 @@ class BotBase(discord.Client):
for module in self.modules.values(): for module in self.modules.values():
module["dispatch"](event, *args, **kwargs) module["dispatch"](event, *args, **kwargs)
async def on_error(self, event_method, exc, *args, **kwargs):
self.error(f"Error in {event_method}: \n{exc}")
# Logging # Logging
def info(self, *args, **kwargs): def info(self, *args, **kwargs):
if self.log: if self.log:
self.log.info(*args, **kwargs) self.log.info(*args, **kwargs)
self.dispatch("log_info", *args, **kwargs) self.dispatch("log_info", *args, **kwargs)
def error(self, *args, **kwargs): def error(self, e, *args, **kwargs):
if self.log: if self.log:
self.log.error(*args, **kwargs) self.log.error(e, *args, **kwargs)
self.dispatch("log_error", *args, **kwargs) self.dispatch("log_error", e, *args, **kwargs)
def warning(self, *args, **kwargs): def warning(self, *args, **kwargs):
if self.log: if self.log:

View File

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import os
import typing import typing
import toml import toml
@ -75,8 +76,9 @@ class Config:
>>> config = Config("doctest_config.toml") >>> config = Config("doctest_config.toml")
>>> config.register("my_parameter", factory(Int)) >>> config.register("my_parameter", factory(Int))
>>> config.set({"my_parameter": 3}) >>> config.set({"my_parameter": 3})
>>> config.save() >>> config.save() #doctest: +SKIP
""" """
os.makedirs(os.path.dirname(self.path), exist_ok=True)
with open(self.path, 'w') as file: with open(self.path, 'w') as file:
toml.dump({k: v.to_save() for k, v in self.fields.items()}, file) toml.dump({k: v.to_save() for k, v in self.fields.items()}, file)
@ -90,11 +92,11 @@ class Config:
>>> config = Config("doctest_config.toml") >>> config = Config("doctest_config.toml")
>>> config.register("my_parameter", factory(Int)) >>> config.register("my_parameter", factory(Int))
>>> config.set({"my_parameter": 3}) >>> config.set({"my_parameter": 3})
>>> config.save() >>> config.save() #doctest: +SKIP
>>> new_config = Config("doctest_config.toml") >>> new_config = Config("doctest_config.toml")
>>> new_config.register("my_parameter", factory(Int)) >>> new_config.register("my_parameter", factory(Int))
>>> new_config.load() >>> new_config.load() #doctest: +SKIP
>>> new_config["my_parameter"] >>> new_config["my_parameter"] #doctest: +SKIP
3 3
:return: None :return: None
@ -103,7 +105,8 @@ class Config:
with open(self.path, 'r') as file: with open(self.path, 'r') as file:
self.set(toml.load(file)) self.set(toml.load(file))
except FileNotFoundError: except FileNotFoundError:
self.save() pass
self.save()
def __getitem__(self, item: str) -> typing.Any: def __getitem__(self, item: str) -> typing.Any:
""" """

View File

@ -1,36 +1,57 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING import typing
import discord
from config.config_types.base_type import BaseType from config.config_types.base_type import BaseType
if TYPE_CHECKING: if typing.TYPE_CHECKING:
from main import LBI from bot_base import BotBase
class Channel(BaseType): class Channel(BaseType):
client: LBI client: BotBase
def __init__(self, client): def __init__(self, client):
self.value = None self.value = 0
self.channel_instance = None
self.client = client self.client = client
def check_value(self, value): def check_value(self, value):
id = value
if isinstance(value, discord.Guild):
id = value.id
if not self.client.is_ready():
self.client.warning(f"No check for channel {value} because client is not initialized!")
return True
if self.client.get_channel(id):
return True
return True return True
def set(self, value): def set(self, value):
if self.check_value(value): if not self.check_value(value):
self.value = value raise ValueError("Tentative de définir une valeur incompatible")
return self.value = value
raise ValueError("Tentative de définir une valeur incompatible") self._update()
def get(self): def get(self):
return self.value self._update()
return self.channel_instance or self.value
def to_save(self): def to_save(self):
return self.value return self.value or 0
def load(self, value): def load(self, value):
if self.check_value(value): if self.check_value(value):
raise ValueError("Tentative de charger une donnée incompatible.") raise ValueError("Tentative de charger une donnée incompatible.")
self.value = value self.set(value)
self._update()
def _update(self):
if self.client.is_ready() and self.channel_instance is None:
self.channel_instance = self.client.get_channel(self.value)
else:
self.channel_instance = None

View File

@ -29,7 +29,7 @@ class Guild(BaseType):
>>> Guild(client) #doctest: +SKIP >>> Guild(client) #doctest: +SKIP
<config_types.discord_type.Guild object with value None> <config_types.discord_type.Guild object with value None>
""" """
self.value = None self.value = 0
self.guild_instance = None self.guild_instance = None
self.client = client self.client = client
@ -56,7 +56,7 @@ class Guild(BaseType):
if isinstance(value, discord.Guild): if isinstance(value, discord.Guild):
id = value.id id = value.id
if not self.client.is_ready(): if not self.client.is_ready():
self.client.warning("No check for guild `value` because client is not initialized!") self.client.warning(f"No check for guild {value} because client is not initialized!")
return True return True
if self.client.get_guild(id): if self.client.get_guild(id):
return True return True
@ -119,7 +119,7 @@ class Guild(BaseType):
:return: Current id :return: Current id
:rtype: Optional[int] :rtype: Optional[int]
""" """
return self.value return self.value or 0
def load(self, value): def load(self, value):
""" """
@ -140,6 +140,7 @@ class Guild(BaseType):
if self.check_value(value): if self.check_value(value):
raise ValueError("Tentative de charger une donnée incompatible.") raise ValueError("Tentative de charger une donnée incompatible.")
self.set(value) self.set(value)
self._update()
def __repr__(self): def __repr__(self):
return f'<config_types.discord_types.guild object with value {self.value}>' return f'<config_types.discord_types.guild object with value {self.value}>'