aboutsummaryrefslogtreecommitdiff
path: root/src/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/__init__.py184
-rw-r--r--src/utils/decorators.py50
-rw-r--r--src/utils/parse.py28
-rw-r--r--src/utils/settings.py110
4 files changed, 191 insertions, 181 deletions
diff --git a/src/utils/__init__.py b/src/utils/__init__.py
index 33ad449b..1013b2ac 100644
--- a/src/utils/__init__.py
+++ b/src/utils/__init__.py
@@ -2,6 +2,10 @@ import contextlib, datetime, decimal, enum, io, ipaddress, multiprocessing
import queue, re, signal, threading, typing
from . import cli, consts, irc, http, parse, security
+from .decorators import export, get_magic, has_magic, hook, kwarg
+from .settings import (BoolSetting, FunctionSetting, IntRangeSetting,
+ IntSetting, OptionsSetting, sensitive_format, SensitiveSetting, Setting)
+
class Direction(enum.Enum):
Send = 0
Recv = 1
@@ -129,31 +133,6 @@ def to_pretty_time(total_seconds: int, minimum_unit: int=UNIT_SECOND,
units += 1
return " ".join(out)
-def parse_number(s: str) -> str:
- try:
- decimal.Decimal(s)
- return s
- except:
- pass
-
- unit = s[-1].lower()
- number_str = s[:-1]
- try:
- number = decimal.Decimal(number_str)
- except:
- raise ValueError("Invalid format '%s' passed to parse_number" %
- number_str)
-
- if unit == "k":
- number *= decimal.Decimal("1_000")
- elif unit == "m":
- number *= decimal.Decimal("1_000_000")
- elif unit == "b":
- number *= decimal.Decimal("1_000_000_000")
- else:
- raise ValueError("Unknown unit '%s' given to parse_number" % unit)
- return str(number)
-
def prevent_highlight(nickname: str) -> str:
return nickname[0]+"\u200c"+nickname[1:]
@@ -169,53 +148,6 @@ class EventsUsageError(EventError):
def __init__(self, usage):
EventError.__init__(self, "Not enough arguments, usage: %s" % usage)
-class BitBotMagic(object):
- def __init__(self):
- self._hooks: typing.List[typing.Tuple[str, dict]] = []
- self._kwargs: typing.List[typing.Tuple[str, typing.Any]] = []
- self._exports: typing.List[typing.Tuple[str, typing.Any]] = []
- def add_hook(self, hook: str, kwargs: dict):
- self._hooks.append((hook, kwargs))
- def add_kwarg(self, key: str, value: typing.Any):
- self._kwargs.append((key, value))
-
- def get_hooks(self):
- hooks: typing.List[typing.Tuple[str, typing.List[Tuple[str, typing.Any]]]] = []
- for hook, kwargs in self._hooks:
- hooks.append((hook, self._kwargs.copy()+list(kwargs.items())))
- return hooks
-
- def add_export(self, key: str, value: typing.Any):
- self._exports.append((key, value))
- def get_exports(self):
- return self._exports.copy()
-
-def get_magic(obj: typing.Any):
- if not has_magic(obj):
- setattr(obj, consts.BITBOT_MAGIC, BitBotMagic())
- return getattr(obj, consts.BITBOT_MAGIC)
-def has_magic(obj: typing.Any):
- return hasattr(obj, consts.BITBOT_MAGIC)
-
-def hook(event: str, **kwargs):
- def _hook_func(func):
- magic = get_magic(func)
- magic.add_hook(event, kwargs)
- return func
- return _hook_func
-def export(setting: str, value: typing.Any):
- def _export_func(module):
- magic = get_magic(module)
- magic.add_export(setting, value)
- return module
- return _export_func
-def kwarg(key: str, value: typing.Any):
- def _kwarg_func(func):
- magic = get_magic(func)
- magic.add_kwarg(key, value)
- return func
- return _kwarg_func
-
class MultiCheck(object):
def __init__(self,
requests: typing.List[typing.Tuple[str, typing.List[str]]]):
@@ -275,114 +207,6 @@ def is_ip(s: str) -> bool:
def is_main_thread() -> bool:
return threading.current_thread() is threading.main_thread()
-class SettingParseException(Exception):
- pass
-
-class Setting(object):
- example: typing.Optional[str] = None
- def __init__(self, name: str, help: str=None, example: str=None):
- self.name = name
- self.help = help
- if not example == None:
- self.example = example
- def parse(self, value: str) -> typing.Any:
- return value
-
- def get_example(self):
- if not self.example == None:
- return "Example: %s" % self.example
- else:
- return self._format_example()
- def _format_example(self):
- return None
-
- def format(self, value: typing.Any):
- return repr(value)
-
-SETTING_TRUE = ["true", "yes", "on", "y"]
-SETTING_FALSE = ["false", "no", "off", "n"]
-class BoolSetting(Setting):
- example: typing.Optional[str] = "on"
- def parse(self, value: str) -> typing.Any:
- value_lower = value.lower()
- if value_lower in SETTING_TRUE:
- return True
- elif value_lower in SETTING_FALSE:
- return False
- return None
-
-class IntSetting(Setting):
- example: typing.Optional[str] = "10"
- def parse(self, value: str) -> typing.Any:
- if value == "0":
- return 0
- else:
- stripped = value.lstrip("0")
- if stripped.isdigit():
- return int(stripped)
- return None
-
-class IntRangeSetting(IntSetting):
- example: typing.Optional[str] = None
- def __init__(self, n_min: int, n_max: int, name: str, help: str=None,
- example: str=None):
- self._n_min = n_min
- self._n_max = n_max
- Setting.__init__(self, name, help, example)
-
- def parse(self, value: str) -> typing.Any:
- out = IntSetting.parse(self, value)
- if not out == None and self._n_min <= out <= self._n_max:
- return out
- return None
-
- def _format_example(self):
- return "Must be between %d and %d" % (self._n_min, self._n_max)
-
-class OptionsSetting(Setting):
- def __init__(self, options: typing.List[str], name: str, help: str=None,
- example: str=None,
- options_factory: typing.Callable[[], typing.List[str]]=None):
- self._options = options
- self._options_factory = options_factory
- Setting.__init__(self, name, help, example)
-
- def _get_options(self):
- if not self._options_factory == None:
- return self._options_factory()
- else:
- return self._options
-
- def parse(self, value: str) -> typing.Any:
- value_lower = value.lower()
- for option in self._get_options():
- if option.lower() == value_lower:
- return option
- return None
-
- def _format_example(self):
- options = self._get_options()
- options_str = ["'%s'" % option for option in options]
- return "Options: %s" % ", ".join(options_str)
-
-class FunctionSetting(Setting):
- def __init__(self, func: typing.Callable[[str], bool], name: str,
- help: str=None, example: str=None, format=None):
- self._func = func
- Setting.__init__(self, name, help, example)
- if not format == None:
- self.format = format # type: ignore
-
- def parse(self, value: str) -> typing.Any:
- return self._func(value)
-
-def sensitive_format(value: typing.Any):
- return "*"*16
-
-class SensitiveSetting(Setting):
- def format(self, value: typing.Any):
- return sensitive_format(value)
-
class DeadlineExceededException(Exception):
pass
def _raise_deadline():
diff --git a/src/utils/decorators.py b/src/utils/decorators.py
new file mode 100644
index 00000000..f6897e17
--- /dev/null
+++ b/src/utils/decorators.py
@@ -0,0 +1,50 @@
+import typing
+from . import consts
+
+class BitBotMagic(object):
+ def __init__(self):
+ self._hooks: typing.List[typing.Tuple[str, dict]] = []
+ self._kwargs: typing.List[typing.Tuple[str, typing.Any]] = []
+ self._exports: typing.List[typing.Tuple[str, typing.Any]] = []
+ def add_hook(self, hook: str, kwargs: dict):
+ self._hooks.append((hook, kwargs))
+ def add_kwarg(self, key: str, value: typing.Any):
+ self._kwargs.append((key, value))
+
+ def get_hooks(self):
+ hooks: typing.List[typing.Tuple[str, typing.List[Tuple[str, typing.Any]]]] = []
+ for hook, kwargs in self._hooks:
+ hooks.append((hook, self._kwargs.copy()+list(kwargs.items())))
+ return hooks
+
+ def add_export(self, key: str, value: typing.Any):
+ self._exports.append((key, value))
+ def get_exports(self):
+ return self._exports.copy()
+
+def get_magic(obj: typing.Any):
+ if not has_magic(obj):
+ setattr(obj, consts.BITBOT_MAGIC, BitBotMagic())
+ return getattr(obj, consts.BITBOT_MAGIC)
+def has_magic(obj: typing.Any):
+ return hasattr(obj, consts.BITBOT_MAGIC)
+
+def hook(event: str, **kwargs):
+ def _hook_func(func):
+ magic = get_magic(func)
+ magic.add_hook(event, kwargs)
+ return func
+ return _hook_func
+def export(setting: str, value: typing.Any):
+ def _export_func(module):
+ magic = get_magic(module)
+ magic.add_export(setting, value)
+ return module
+ return _export_func
+def kwarg(key: str, value: typing.Any):
+ def _kwarg_func(func):
+ magic = get_magic(func)
+ magic.add_kwarg(key, value)
+ return func
+ return _kwarg_func
+
diff --git a/src/utils/parse.py b/src/utils/parse.py
index 7699bd19..d5018441 100644
--- a/src/utils/parse.py
+++ b/src/utils/parse.py
@@ -1,4 +1,4 @@
-import io, typing
+import decimal, io, typing
COMMENT_TYPES = ["#", "//"]
def hashflags(filename: str
@@ -83,3 +83,29 @@ def try_int(s: str) -> typing.Optional[int]:
def line_normalise(s: str) -> str:
lines = list(filter(None, [line.strip() for line in s.split("\n")]))
return " ".join(line.replace(" ", " ") for line in lines)
+
+def parse_number(s: str) -> str:
+ try:
+ decimal.Decimal(s)
+ return s
+ except:
+ pass
+
+ unit = s[-1].lower()
+ number_str = s[:-1]
+ try:
+ number = decimal.Decimal(number_str)
+ except:
+ raise ValueError("Invalid format '%s' passed to parse_number" %
+ number_str)
+
+ if unit == "k":
+ number *= decimal.Decimal("1_000")
+ elif unit == "m":
+ number *= decimal.Decimal("1_000_000")
+ elif unit == "b":
+ number *= decimal.Decimal("1_000_000_000")
+ else:
+ raise ValueError("Unknown unit '%s' given to parse_number" % unit)
+ return str(number)
+
diff --git a/src/utils/settings.py b/src/utils/settings.py
new file mode 100644
index 00000000..8040c2e8
--- /dev/null
+++ b/src/utils/settings.py
@@ -0,0 +1,110 @@
+import typing
+
+class SettingParseException(Exception):
+ pass
+
+class Setting(object):
+ example: typing.Optional[str] = None
+ def __init__(self, name: str, help: str=None, example: str=None):
+ self.name = name
+ self.help = help
+ if not example == None:
+ self.example = example
+ def parse(self, value: str) -> typing.Any:
+ return value
+
+ def get_example(self):
+ if not self.example == None:
+ return "Example: %s" % self.example
+ else:
+ return self._format_example()
+ def _format_example(self):
+ return None
+
+ def format(self, value: typing.Any):
+ return repr(value)
+
+SETTING_TRUE = ["true", "yes", "on", "y"]
+SETTING_FALSE = ["false", "no", "off", "n"]
+class BoolSetting(Setting):
+ example: typing.Optional[str] = "on"
+ def parse(self, value: str) -> typing.Any:
+ value_lower = value.lower()
+ if value_lower in SETTING_TRUE:
+ return True
+ elif value_lower in SETTING_FALSE:
+ return False
+ return None
+
+class IntSetting(Setting):
+ example: typing.Optional[str] = "10"
+ def parse(self, value: str) -> typing.Any:
+ if value == "0":
+ return 0
+ else:
+ stripped = value.lstrip("0")
+ if stripped.isdigit():
+ return int(stripped)
+ return None
+
+class IntRangeSetting(IntSetting):
+ example: typing.Optional[str] = None
+ def __init__(self, n_min: int, n_max: int, name: str, help: str=None,
+ example: str=None):
+ self._n_min = n_min
+ self._n_max = n_max
+ Setting.__init__(self, name, help, example)
+
+ def parse(self, value: str) -> typing.Any:
+ out = IntSetting.parse(self, value)
+ if not out == None and self._n_min <= out <= self._n_max:
+ return out
+ return None
+
+ def _format_example(self):
+ return "Must be between %d and %d" % (self._n_min, self._n_max)
+
+class OptionsSetting(Setting):
+ def __init__(self, options: typing.List[str], name: str, help: str=None,
+ example: str=None,
+ options_factory: typing.Callable[[], typing.List[str]]=None):
+ self._options = options
+ self._options_factory = options_factory
+ Setting.__init__(self, name, help, example)
+
+ def _get_options(self):
+ if not self._options_factory == None:
+ return self._options_factory()
+ else:
+ return self._options
+
+ def parse(self, value: str) -> typing.Any:
+ value_lower = value.lower()
+ for option in self._get_options():
+ if option.lower() == value_lower:
+ return option
+ return None
+
+ def _format_example(self):
+ options = self._get_options()
+ options_str = ["'%s'" % option for option in options]
+ return "Options: %s" % ", ".join(options_str)
+
+class FunctionSetting(Setting):
+ def __init__(self, func: typing.Callable[[str], bool], name: str,
+ help: str=None, example: str=None, format=None):
+ self._func = func
+ Setting.__init__(self, name, help, example)
+ if not format == None:
+ self.format = format # type: ignore
+
+ def parse(self, value: str) -> typing.Any:
+ return self._func(value)
+
+def sensitive_format(value: typing.Any):
+ return "*"*16
+
+class SensitiveSetting(Setting):
+ def format(self, value: typing.Any):
+ return sensitive_format(value)
+