Added black formatter to pre-commit hook

develop
Jef Roosens 2020-09-01 09:17:09 +02:00
parent 6f2d9d1dde
commit 7a7bd6fed4
14 changed files with 127 additions and 70 deletions

View File

@ -3,8 +3,8 @@
# This hooks runs tests and checks if the linter doesn't hate you # This hooks runs tests and checks if the linter doesn't hate you
# Linting # Linting
printf "flake8....." printf "black......"
make lint > /dev/null 2>&1 && printf "OK!\n" || { printf "Fail." && exit 1; } make format > /dev/null 2>&1 && printf "OK!\n" || { printf "Fail." && exit 1; }
# Running tests # Running tests
printf "pytest....." printf "pytest....."

View File

@ -69,6 +69,11 @@ test: pytest.ini build-venv
lint: build-venv lint: build-venv
@ "$(VENV)/bin/flake8" "$(SRC)"/**/*.py @ "$(VENV)/bin/flake8" "$(SRC)"/**/*.py
# =====FORMATTING=====
# Run black
format: build-venv
@ "$(VENV)/bin/black" '$(SRC)'
# =====PACKAGING===== # =====PACKAGING=====
package: README.md LICENSE setup.py test clean-setup package: README.md LICENSE setup.py test clean-setup

View File

@ -1,18 +1,25 @@
from .frank import Frank from .frank import Frank
from .module import ( from .module import (
Module, command, Command, default, Default, daemon, Daemon, Module,
regex_command, RegexCommand, command,
Command,
default,
Default,
daemon,
Daemon,
regex_command,
RegexCommand,
) )
__all__ = [ __all__ = [
'Frank', "Frank",
'Module', "Module",
'command', "command",
'Command', "Command",
'default', "default",
'Default', "Default",
'daemon', "daemon",
'Daemon', "Daemon",
'regex_command', "regex_command",
'RegexCommand', "RegexCommand",
] ]

View File

@ -11,6 +11,7 @@ import discord
# Typing imports # Typing imports
from typing import TYPE_CHECKING, List from typing import TYPE_CHECKING, List
if TYPE_CHECKING: if TYPE_CHECKING:
# Own imports # Own imports
from .module import Module from .module import Module
@ -23,8 +24,12 @@ class Frank(discord.Client):
their own behavior. their own behavior.
""" """
def __init__(self, modules: List[Module], config_file: str = 'frank.yaml', def __init__(
prefix: str = 'fr'): self,
modules: List[Module],
config_file: str = "frank.yaml",
prefix: str = "fr",
):
""" """
Args: Args:
modules: modules to load modules: modules to load
@ -39,7 +44,7 @@ class Frank(discord.Client):
self.PREFIX = prefix self.PREFIX = prefix
try: try:
with open(config_file, 'r') as f: with open(config_file, "r") as f:
self._config = yaml.load(f, Loader=yaml.FullLoader) self._config = yaml.load(f, Loader=yaml.FullLoader)
except FileNotFoundError: except FileNotFoundError:
@ -50,7 +55,7 @@ class Frank(discord.Client):
Runs when the bot has succesfully connected to Discord Runs when the bot has succesfully connected to Discord
""" """
print('Connected') print("Connected")
# Startup all modules # Startup all modules
for module in self._modules: for module in self._modules:
@ -63,7 +68,7 @@ class Frank(discord.Client):
await loaded._start() await loaded._start()
self._loaded_modules.append(loaded) self._loaded_modules.append(loaded)
print('All modules loaded') print("All modules loaded")
async def stop(self): async def stop(self):
""" """
@ -92,9 +97,14 @@ class Frank(discord.Client):
if not (cmd and cmd[0] == self.PREFIX): if not (cmd and cmd[0] == self.PREFIX):
return return
module = next((mod for mod in self._loaded_modules module = next(
if mod.match(cmd[1])), None) (mod for mod in self._loaded_modules if mod.match(cmd[1])), None
)
if module: if module:
await module(cmd=cmd[2:], author=message.author, await module(
channel=message.channel, mid=message.id) cmd=cmd[2:],
author=message.author,
channel=message.channel,
mid=message.id,
)

View File

@ -1,18 +1,24 @@
from .module import Module from .module import Module
from .decorators import ( from .decorators import (
command, Command, default, Default, daemon, Daemon, regex_command, command,
Command,
default,
Default,
daemon,
Daemon,
regex_command,
RegexCommand, RegexCommand,
) )
__all__ = [ __all__ = [
'Module', "Module",
'command', "command",
'Command', "Command",
'default', "default",
'Default', "Default",
'daemon', "daemon",
'Daemon', "Daemon",
'regex_command', "regex_command",
'RegexCommand', "RegexCommand",
] ]

View File

@ -2,12 +2,12 @@ from .classes import Command, RegexCommand, Daemon, Default
from .functions import command, regex_command, daemon, default from .functions import command, regex_command, daemon, default
__all__ = [ __all__ = [
'command', "command",
'Command', "Command",
'regex_command', "regex_command",
'RegexCommand', "RegexCommand",
'default', "default",
'Default', "Default",
'daemon', "daemon",
'Daemon', "Daemon",
] ]

View File

@ -8,6 +8,7 @@ import asyncio
# Typing imports # Typing imports
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
# Built-in imports # Built-in imports
from typing import Union from typing import Union
@ -120,6 +121,7 @@ class Daemon(Simple):
# If an interval > 0 is given, it wraps the function inside an infinite # If an interval > 0 is given, it wraps the function inside an infinite
# loop with the desired delay # loop with the desired delay
if interval > 0: if interval > 0:
async def loop(self, *args, **kwargs): async def loop(self, *args, **kwargs):
while True: while True:
# TODO: does this make func and sleep run at the same time? # TODO: does this make func and sleep run at the same time?

View File

@ -7,6 +7,7 @@ from .classes import Command, RegexCommand, Daemon, Default
# Typing imports # Typing imports
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
# Built-in imports # Built-in imports
from typing import Union from typing import Union

View File

@ -7,4 +7,4 @@ class DuplicateCommand(Exception):
class MultipleDefaults(Exception): class MultipleDefaults(Exception):
message = 'Multiple default commands detected' message = "Multiple default commands detected"

View File

@ -10,6 +10,7 @@ from .decorators import Command, Daemon, Default, RegexCommand
# Typing imports # Typing imports
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
# Built-in imports # Built-in imports
from typing import List, Any from typing import List, Any
@ -18,7 +19,7 @@ if TYPE_CHECKING:
class ModuleMeta: class ModuleMeta:
def _filter_attrs(self, condition: callable[[Any], bool]) -> List[Any]: def _filter_attrs(self, condition: callable[[Any], bool]) -> List[Any]:
# This prevents an infinite loop of getting the attribute # This prevents an infinite loop of getting the attribute
illegal_names = ['commands', 'daemons', 'default'] illegal_names = ["commands", "daemons", "default"]
output = [] output = []
@ -36,8 +37,10 @@ class ModuleMeta:
# The sort puts all the RegexCommand objects at the back, making them # The sort puts all the RegexCommand objects at the back, making them
# be matched last # be matched last
return sorted(self._filter_attrs(lambda val: isinstance(val, Command)), return sorted(
key=lambda x: isinstance(x, RegexCommand)) self._filter_attrs(lambda val: isinstance(val, Command)),
key=lambda x: isinstance(x, RegexCommand),
)
@cached_property @cached_property
def daemons(self) -> List[Daemon]: def daemons(self) -> List[Daemon]:
@ -45,5 +48,6 @@ class ModuleMeta:
@cached_property @cached_property
def default(self) -> Default: def default(self) -> Default:
return next(iter(self._filter_attrs( return next(
lambda val: isinstance(val, Default))), None) iter(self._filter_attrs(lambda val: isinstance(val, Default))), None
)

View File

@ -12,6 +12,7 @@ from .decorators import RegexCommand
# Typing imports # Typing imports
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
# Built-in imports # Built-in imports
from typing import List, Dict from typing import List, Dict
@ -33,13 +34,13 @@ class Module(ModuleMeta):
Prefix to activate this module. Prefix to activate this module.
""" """
NAME = '' NAME = ""
""" """
The name is used in various places, such as the config file and the The name is used in various places, such as the config file and the
help function. help function.
""" """
HELP = '' HELP = ""
""" """
Short description of the module to use in the help function. Short description of the module to use in the help function.
""" """
@ -84,8 +85,9 @@ class Module(ModuleMeta):
for task in self._tasks: for task in self._tasks:
task.cancel() task.cancel()
async def __call__(self, cmd: List[str], author: User, async def __call__(
channel: Messageable, mid: int): self, cmd: List[str], author: User, channel: Messageable, mid: int
):
""" """
Execute the command, if found. Execute the command, if found.
@ -97,21 +99,26 @@ class Module(ModuleMeta):
""" """
if cmd: if cmd:
func = next((func for func in self.commands func = next(
if func.match(cmd[0])), None) (func for func in self.commands if func.match(cmd[0])), None
)
# Throw error if no function is found # Throw error if no function is found
if not func: if not func:
raise InvalidCommand(f'Unknown command: {cmd}') raise InvalidCommand(f"Unknown command: {cmd}")
# A RegexCommand can use the prefix, as it's not a fixed string # A RegexCommand can use the prefix, as it's not a fixed string
if isinstance(func, RegexCommand): if isinstance(func, RegexCommand):
await func(prefix=cmd[0], cmd=cmd[1:], author=author, await func(
channel=channel, mid=mid) prefix=cmd[0],
cmd=cmd[1:],
author=author,
channel=channel,
mid=mid,
)
else: else:
await func(cmd=cmd[1:], author=author, channel=channel, await func(cmd=cmd[1:], author=author, channel=channel, mid=mid)
mid=mid)
elif self.default: elif self.default:
await self.default(author=author, channel=channel, mid=mid) await self.default(author=author, channel=channel, mid=mid)

View File

@ -2,5 +2,5 @@ from .help import HelpMod
__all__ = [ __all__ = [
'HelpMod', "HelpMod",
] ]

View File

@ -10,6 +10,7 @@ from .. import Module, default, regex_command
# Typing imports # Typing imports
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
# Built-in imports # Built-in imports
from typing import List from typing import List
@ -24,11 +25,11 @@ class HelpMod(Module):
other modules. other modules.
""" """
PREFIX = 'help' PREFIX = "help"
NAME = 'help' NAME = "help"
HELP = 'Shows help info about all modules' HELP = "Shows help info about all modules"
@default(help_str='Show help about all modules.') @default(help_str="Show help about all modules.")
async def send_all(self, author: User, channel: Messageable, mid: int): async def send_all(self, author: User, channel: Messageable, mid: int):
embed = Embed() embed = Embed()
@ -37,20 +38,33 @@ class HelpMod(Module):
await channel.send(embed=embed) await channel.send(embed=embed)
@regex_command(pattern='.+', help_str='Show help about a certain module.') @regex_command(pattern=".+", help_str="Show help about a certain module.")
async def show_module_help(self, prefix: str, cmd: List[str], author: User, async def show_module_help(
channel: Messageable, mid: int): self,
prefix: str,
cmd: List[str],
author: User,
channel: Messageable,
mid: int,
):
# Yes, this command just ignores cmd at the moment # Yes, this command just ignores cmd at the moment
mod_name = prefix.lower() mod_name = prefix.lower()
mod = next((mod for mod in self._client._modules mod = next(
if mod.NAME.lower() == mod_name), None) (
mod
for mod in self._client._modules
if mod.NAME.lower() == mod_name
),
None,
)
if mod: if mod:
embed = Embed() embed = Embed()
if mod.default: if mod.default:
embed.add_field(name='default', value=mod.default.help_str, embed.add_field(
inline=False) name="default", value=mod.default.help_str, inline=False
)
for cmd in mod._COMMANDS: for cmd in mod._COMMANDS:
embed.add_field(name=cmd.cmd, value=mod.help_str, inline=False) embed.add_field(name=cmd.cmd, value=mod.help_str, inline=False)

View File

@ -13,3 +13,4 @@ pytest-asyncio>=0.14.0,<1.0.0
twine>=3.2.0,<4.0.0 twine>=3.2.0,<4.0.0
Sphinx==3.2.1 Sphinx==3.2.1
sphinx-rtd-theme==0.5.0 sphinx-rtd-theme==0.5.0
black