Remove validator container, allow direct setting of options

This commit is contained in:
Griatch 2019-04-14 21:06:43 +02:00
parent 10b3657ffb
commit 7ae790a12f
5 changed files with 102 additions and 67 deletions

View file

@ -109,10 +109,9 @@ TASK_HANDLER = None
TICKER_HANDLER = None TICKER_HANDLER = None
MONITOR_HANDLER = None MONITOR_HANDLER = None
CHANNEL_HANDLER = None CHANNEL_HANDLER = None
GLOBAL_SCRIPTS = None
# Containers # Containers
VALIDATOR_FUNCTIONS = None GLOBAL_SCRIPTS = None
OPTION_CLASSES = None OPTION_CLASSES = None
@ -161,8 +160,8 @@ def _init():
global create_message, create_help_entry global create_message, create_help_entry
global settings, lockfuncs, logger, utils, gametime, ansi, spawn, managers global settings, lockfuncs, logger, utils, gametime, ansi, spawn, managers
global contrib, TICKER_HANDLER, MONITOR_HANDLER, SESSION_HANDLER global contrib, TICKER_HANDLER, MONITOR_HANDLER, SESSION_HANDLER
global CHANNEL_HANDLER, TASK_HANDLER, GLOBAL_SCRIPTS global CHANNEL_HANDLER, TASK_HANDLER
global VALIDATOR_FUNCS, OPTION_CLASSES global GLOBAL_SCRIPTS, OPTION_CLASSES
global EvMenu, EvTable, EvForm, EvMore, EvEditor global EvMenu, EvTable, EvForm, EvMore, EvEditor
global ANSIString global ANSIString
@ -226,7 +225,6 @@ def _init():
# containers # containers
from .utils.containers import GLOBAL_SCRIPTS from .utils.containers import GLOBAL_SCRIPTS
from .utils.containers import VALIDATOR_FUNCS
from .utils.containers import OPTION_CLASSES from .utils.containers import OPTION_CLASSES
# initialize the doc string # initialize the doc string

View file

@ -76,15 +76,6 @@ class Container(object):
return list(self.loaded_data.values()) return list(self.loaded_data.values())
class ValidatorContainer(Container):
"""
Loads and stores the final list of VALIDATOR FUNCTIONS.
Can access these as properties or dictionary-contents.
"""
storage_modules = settings.VALIDATOR_FUNC_MODULES
class OptionContainer(Container): class OptionContainer(Container):
""" """
Loads and stores the final list of OPTION CLASSES. Loads and stores the final list of OPTION CLASSES.
@ -203,5 +194,4 @@ class GlobalScriptContainer(Container):
# Create all singletons # Create all singletons
GLOBAL_SCRIPTS = GlobalScriptContainer() GLOBAL_SCRIPTS = GlobalScriptContainer()
VALIDATOR_FUNCS = ValidatorContainer()
OPTION_CLASSES = OptionContainer() OPTION_CLASSES = OptionContainer()

View file

@ -2,8 +2,8 @@ import datetime
from evennia import logger from evennia import logger
from evennia.utils.ansi import strip_ansi from evennia.utils.ansi import strip_ansi
from evennia.utils.validatorfuncs import _TZ_DICT from evennia.utils.validatorfuncs import _TZ_DICT
from evennia.utils.containers import VALIDATOR_FUNCS
from evennia.utils.utils import crop from evennia.utils.utils import crop
from evennia.utils import validatorfuncs
class BaseOption(object): class BaseOption(object):
@ -20,8 +20,6 @@ class BaseOption(object):
validator_key (str): The key of the Validator this uses. validator_key (str): The key of the Validator this uses.
""" """
validator_key = ''
def __str__(self): def __str__(self):
return "<Option {key}: {value}>".format( return "<Option {key}: {value}>".format(
key=self.key, value=crop(str(self.value), width=10)) key=self.key, value=crop(str(self.value), width=10))
@ -99,7 +97,6 @@ class BaseOption(object):
loadfunc = self.handler.loadfunc loadfunc = self.handler.loadfunc
load_kwargs = self.handler.load_kwargs load_kwargs = self.handler.load_kwargs
print("load", self.key, loadfunc, load_kwargs)
try: try:
self.value_storage = self.deserialize( self.value_storage = self.deserialize(
loadfunc(self.key, default=self.default_value, **load_kwargs)) loadfunc(self.key, default=self.default_value, **load_kwargs))
@ -124,7 +121,6 @@ class BaseOption(object):
value = self.serialize() value = self.serialize()
save_kwargs = {**self.handler.save_kwargs, **kwargs} save_kwargs = {**self.handler.save_kwargs, **kwargs}
savefunc = self.handler.savefunc savefunc = self.handler.savefunc
print("save:", self.key, value, savefunc, save_kwargs)
savefunc(self.key, value=value, **save_kwargs) savefunc(self.key, value=value, **save_kwargs)
def deserialize(self, save_data): def deserialize(self, save_data):
@ -164,12 +160,13 @@ class BaseOption(object):
This is necessary because of other settings which may affect the This is necessary because of other settings which may affect the
check, such as an Account's timezone affecting how their datetime check, such as an Account's timezone affecting how their datetime
entries are processed. entries are processed.
Returns: Returns:
any (any): The results of the validation. any (any): The results of the validation.
Raises:
ValidationError: If input value failed validation.
""" """
return VALIDATOR_FUNCS.get(self.validator_key)(value, thing_name=self.key, **kwargs) return validatorfuncs.text(value, option_key=self.key, **kwargs)
def display(self, **kwargs): def display(self, **kwargs):
""" """
@ -191,7 +188,6 @@ class BaseOption(object):
class Text(BaseOption): class Text(BaseOption):
validator_key = 'text'
def deserialize(self, save_data): def deserialize(self, save_data):
got_data = str(save_data) got_data = str(save_data)
@ -201,7 +197,9 @@ class Text(BaseOption):
class Email(BaseOption): class Email(BaseOption):
validator_key = 'email'
def validate(self, value, **kwargs):
return validatorfuncs.email(value, option_key=self.key, **kwargs)
def deserialize(self, save_data): def deserialize(self, save_data):
got_data = str(save_data) got_data = str(save_data)
@ -211,7 +209,9 @@ class Email(BaseOption):
class Boolean(BaseOption): class Boolean(BaseOption):
validator_key = 'boolean'
def validate(self, value, **kwargs):
return validatorfuncs.boolean(value, option_key=self.key, **kwargs)
def display(self, **kwargs): def display(self, **kwargs):
if self.value: if self.value:
@ -228,7 +228,9 @@ class Boolean(BaseOption):
class Color(BaseOption): class Color(BaseOption):
validator_key = 'color'
def validate(self, value, **kwargs):
return validatorfuncs.color(value, option_key=self.key, **kwargs)
def display(self, **kwargs): def display(self, **kwargs):
return f'{self.value} - |{self.value}this|n' return f'{self.value} - |{self.value}this|n'
@ -240,7 +242,9 @@ class Color(BaseOption):
class Timezone(BaseOption): class Timezone(BaseOption):
validator_key = 'timezone'
def validate(self, value, **kwargs):
return validatorfuncs.timezone(value, option_key=self.key, **kwargs)
@property @property
def default(self): def default(self):
@ -258,6 +262,9 @@ class Timezone(BaseOption):
class UnsignedInteger(BaseOption): class UnsignedInteger(BaseOption):
validator_key = 'unsigned_integer' validator_key = 'unsigned_integer'
def validate(self, value, **kwargs):
return validatorfuncs.unsigned_integer(value, option_key=self.key, **kwargs)
def deserialize(self, save_data): def deserialize(self, save_data):
if isinstance(save_data, int) and save_data >= 0: if isinstance(save_data, int) and save_data >= 0:
return save_data return save_data
@ -265,7 +272,9 @@ class UnsignedInteger(BaseOption):
class SignedInteger(BaseOption): class SignedInteger(BaseOption):
validator_key = 'signed_integer'
def validate(self, value, **kwargs):
return validatorfuncs.signed_integer(value, option_key=self.key, **kwargs)
def deserialize(self, save_data): def deserialize(self, save_data):
if isinstance(save_data, int): if isinstance(save_data, int):
@ -274,7 +283,9 @@ class SignedInteger(BaseOption):
class PositiveInteger(BaseOption): class PositiveInteger(BaseOption):
validator_key = 'positive_integer'
def validate(self, value, **kwargs):
return validatorfuncs.positive_integer(value, option_key=self.key, **kwargs)
def deserialize(self, save_data): def deserialize(self, save_data):
if isinstance(save_data, int) and save_data > 0: if isinstance(save_data, int) and save_data > 0:
@ -283,7 +294,9 @@ class PositiveInteger(BaseOption):
class Duration(BaseOption): class Duration(BaseOption):
validator_key = 'duration'
def validate(self, value, **kwargs):
return validatorfuncs.duration(value, option_key=self.key, **kwargs)
def deserialize(self, save_data): def deserialize(self, save_data):
if isinstance(save_data, int): if isinstance(save_data, int):
@ -295,7 +308,9 @@ class Duration(BaseOption):
class Datetime(BaseOption): class Datetime(BaseOption):
validator_key = 'datetime'
def validate(self, value, **kwargs):
return validatorfuncs.datetime(value, option_key=self.key, **kwargs)
def deserialize(self, save_data): def deserialize(self, save_data):
if isinstance(save_data, int): if isinstance(save_data, int):
@ -307,8 +322,12 @@ class Datetime(BaseOption):
class Future(Datetime): class Future(Datetime):
validator_key = 'future'
def validate(self, value, **kwargs):
return validatorfuncs.future(value, option_key=self.key, **kwargs)
class Lock(Text): class Lock(Text):
validator_key = 'lock'
def validate(self, value, **kwargs):
return validatorfuncs.lock(value, option_key=self.key, **kwargs)

View file

@ -1,6 +1,9 @@
from evennia.utils.utils import string_partial_matching from evennia.utils.utils import string_partial_matching
from evennia.utils.containers import OPTION_CLASSES from evennia.utils.containers import OPTION_CLASSES
_GA = object.__getattribute__
_SA = object.__setattr__
class InMemorySaveHandler(object): class InMemorySaveHandler(object):
""" """
@ -69,8 +72,26 @@ class OptionHandler(object):
self.options = {} self.options = {}
def __getattr__(self, key): def __getattr__(self, key):
"""
Allow for obj.options.key
"""
return self.get(key) return self.get(key)
def __setattr__(self, key, value):
"""
Allow for obj.options.key = value
But we must be careful to avoid infinite loops!
"""
try:
if key in _GA(self, "options_dict"):
_GA(self, "set")(key, value)
except AttributeError:
pass
_SA(self, key, value)
def _load_option(self, key): def _load_option(self, key):
""" """
Loads option on-demand if it has not been loaded yet. Loads option on-demand if it has not been loaded yet.

View file

@ -19,25 +19,32 @@ from evennia.utils.utils import string_partial_matching as _partial
_TZ_DICT = {str(tz): _pytz.timezone(tz) for tz in _pytz.common_timezones} _TZ_DICT = {str(tz): _pytz.timezone(tz) for tz in _pytz.common_timezones}
def color(entry, thing_name='Color', **kwargs): def text(entry, option_key='Text', **kwargs):
try:
return str(entry)
except Exception as err:
raise ValueError(f"Input could not be converted to text ({err})")
def color(entry, option_key='Color', **kwargs):
""" """
The color should be just a color character, so 'r' if red color is desired. The color should be just a color character, so 'r' if red color is desired.
""" """
if not entry: if not entry:
raise ValueError(f"Nothing entered for a {thing_name}!") raise ValueError(f"Nothing entered for a {option_key}!")
test_str = strip_ansi(f'|{entry}|n') test_str = strip_ansi(f'|{entry}|n')
if test_str: if test_str:
raise ValueError(f"'{entry}' is not a valid {thing_name}.") raise ValueError(f"'{entry}' is not a valid {option_key}.")
return entry return entry
def datetime(entry, thing_name='Datetime', account=None, from_tz=None, **kwargs): def datetime(entry, option_key='Datetime', account=None, from_tz=None, **kwargs):
""" """
Process a datetime string in standard forms while accounting for the inputter's timezone. Process a datetime string in standard forms while accounting for the inputter's timezone.
Args: Args:
entry (str): A date string from a user. entry (str): A date string from a user.
thing_name (str): Name to display this datetime as. option_key (str): Name to display this datetime as.
account (AccountDB): The Account performing this lookup. Unless from_tz is provided, account (AccountDB): The Account performing this lookup. Unless from_tz is provided,
account's timezone will be used (if found) for local time and convert the results account's timezone will be used (if found) for local time and convert the results
to UTC. to UTC.
@ -48,7 +55,7 @@ def datetime(entry, thing_name='Datetime', account=None, from_tz=None, **kwargs)
datetime in utc. datetime in utc.
""" """
if not entry: if not entry:
raise ValueError(f"No {thing_name} entered!") raise ValueError(f"No {option_key} entered!")
if not from_tz: if not from_tz:
from_tz = _pytz.UTC from_tz = _pytz.UTC
utc = _pytz.UTC utc = _pytz.UTC
@ -60,23 +67,23 @@ def datetime(entry, thing_name='Datetime', account=None, from_tz=None, **kwargs)
elif len(split_time) == 4: elif len(split_time) == 4:
entry = f"{split_time[0]} {split_time[1]} {split_time[2]} {split_time[3]}" entry = f"{split_time[0]} {split_time[1]} {split_time[2]} {split_time[3]}"
else: else:
raise ValueError(f"{thing_name} must be entered in a 24-hour format such as: {now.strftime('%b %d %H:%H')}") raise ValueError(f"{option_key} must be entered in a 24-hour format such as: {now.strftime('%b %d %H:%H')}")
try: try:
local = _dt.datetime.strptime(input, '%b %d %H:%M %Y') local = _dt.datetime.strptime(input, '%b %d %H:%M %Y')
except ValueError: except ValueError:
raise ValueError(f"{thing_name} must be entered in a 24-hour format such as: {now.strftime('%b %d %H:%H')}") raise ValueError(f"{option_key} must be entered in a 24-hour format such as: {now.strftime('%b %d %H:%H')}")
local_tz = from_tz.localize(local) local_tz = from_tz.localize(local)
return local_tz.astimezone(utc) return local_tz.astimezone(utc)
def duration(entry, thing_name='Duration', **kwargs): def duration(entry, option_key='Duration', **kwargs):
""" """
Take a string and derive a datetime timedelta from it. Take a string and derive a datetime timedelta from it.
Args: Args:
entry (string): This is a string from user-input. The intended format is, for example: "5d 2w 90s" for entry (string): This is a string from user-input. The intended format is, for example: "5d 2w 90s" for
'five days, two weeks, and ninety seconds.' Invalid sections are ignored. 'five days, two weeks, and ninety seconds.' Invalid sections are ignored.
thing_name (str): Name to display this query as. option_key (str): Name to display this query as.
Returns: Returns:
timedelta timedelta
@ -103,54 +110,54 @@ def duration(entry, thing_name='Duration', **kwargs):
elif _re.match(r'^[\d]+y$', interval): elif _re.match(r'^[\d]+y$', interval):
days =+ int(interval.lower().rstrip("y")) * 365 days =+ int(interval.lower().rstrip("y")) * 365
else: else:
raise ValueError(f"Could not convert section '{interval}' to a {thing_name}.") raise ValueError(f"Could not convert section '{interval}' to a {option_key}.")
return _dt.timedelta(days, seconds, 0, 0, minutes, hours, weeks) return _dt.timedelta(days, seconds, 0, 0, minutes, hours, weeks)
def future(entry, thing_name="Future Datetime", from_tz=None, **kwargs): def future(entry, option_key="Future Datetime", from_tz=None, **kwargs):
time = datetime(entry, thing_name) time = datetime(entry, option_key)
if time < _dt.datetime.utcnow(): if time < _dt.datetime.utcnow():
raise ValueError(f"That {thing_name} is in the past! Must give a Future datetime!") raise ValueError(f"That {option_key} is in the past! Must give a Future datetime!")
return time return time
def signed_integer(entry, thing_name="Signed Integer", **kwargs): def signed_integer(entry, option_key="Signed Integer", **kwargs):
if not entry: if not entry:
raise ValueError(f"Must enter a whole number for {thing_name}!") raise ValueError(f"Must enter a whole number for {option_key}!")
try: try:
num = int(entry) num = int(entry)
except ValueError: except ValueError:
raise ValueError(f"Could not convert '{entry}' to a whole number for {thing_name}!") raise ValueError(f"Could not convert '{entry}' to a whole number for {option_key}!")
return num return num
def positive_integer(entry, thing_name="Positive Integer", **kwargs): def positive_integer(entry, option_key="Positive Integer", **kwargs):
num = signed_integer(entry, thing_name) num = signed_integer(entry, option_key)
if not num >= 1: if not num >= 1:
raise ValueError(f"Must enter a whole number greater than 0 for {thing_name}!") raise ValueError(f"Must enter a whole number greater than 0 for {option_key}!")
return num return num
def unsigned_integer(entry, thing_name="Unsigned Integer", **kwargs): def unsigned_integer(entry, option_key="Unsigned Integer", **kwargs):
num = signed_integer(entry, thing_name) num = signed_integer(entry, option_key)
if not num >= 0: if not num >= 0:
raise ValueError(f"{thing_name} must be a whole number greater than or equal to 0!") raise ValueError(f"{option_key} must be a whole number greater than or equal to 0!")
return num return num
def boolean(entry, thing_name="True/False", **kwargs): def boolean(entry, option_key="True/False", **kwargs):
""" """
Simplest check in computer logic, right? This will take user input to flick the switch on or off Simplest check in computer logic, right? This will take user input to flick the switch on or off
Args: Args:
entry (str): A value such as True, On, Enabled, Disabled, False, 0, or 1. entry (str): A value such as True, On, Enabled, Disabled, False, 0, or 1.
thing_name (str): What kind of Boolean we are setting. What Option is this for? option_key (str): What kind of Boolean we are setting. What Option is this for?
Returns: Returns:
Boolean Boolean
""" """
entry = entry.upper() entry = entry.upper()
error = f"Must enter 0 (false) or 1 (true) for {thing_name}. Also accepts True, False, On, Off, Yes, No, Enabled, and Disabled" error = f"Must enter 0 (false) or 1 (true) for {option_key}. Also accepts True, False, On, Off, Yes, No, Enabled, and Disabled"
if not entry: if not entry:
raise ValueError(error) raise ValueError(error)
if entry in ('1', 'TRUE', 'ON', 'ENABLED', 'ENABLE', 'YES'): if entry in ('1', 'TRUE', 'ON', 'ENABLED', 'ENABLE', 'YES'):
@ -160,41 +167,41 @@ def boolean(entry, thing_name="True/False", **kwargs):
raise ValueError(error) raise ValueError(error)
def timezone(entry, thing_name="Timezone", **kwargs): def timezone(entry, option_key="Timezone", **kwargs):
""" """
Takes user input as string, and partial matches a Timezone. Takes user input as string, and partial matches a Timezone.
Args: Args:
entry (str): The name of the Timezone. entry (str): The name of the Timezone.
thing_name (str): What this Timezone is used for. option_key (str): What this Timezone is used for.
Returns: Returns:
A PYTZ timezone. A PYTZ timezone.
""" """
if not entry: if not entry:
raise ValueError(f"No {thing_name} entered!") raise ValueError(f"No {option_key} entered!")
found = _partial(list(_TZ_DICT.keys()), entry, ret_index=False) found = _partial(list(_TZ_DICT.keys()), entry, ret_index=False)
if len(found) > 1: if len(found) > 1:
raise ValueError(f"That matched: {', '.join(str(t) for t in found)}. Please be more specific!") raise ValueError(f"That matched: {', '.join(str(t) for t in found)}. Please be more specific!")
if found: if found:
return _TZ_DICT[found[0]] return _TZ_DICT[found[0]]
raise ValueError(f"Could not find timezone '{entry}' for {thing_name}!") raise ValueError(f"Could not find timezone '{entry}' for {option_key}!")
def email(entry, thing_name="Email Address", **kwargs): def email(entry, option_key="Email Address", **kwargs):
if not entry: if not entry:
raise ValueError("Email address field empty!") raise ValueError("Email address field empty!")
try: try:
_val_email(entry) # offloading the hard work to Django! _val_email(entry) # offloading the hard work to Django!
except _error: except _error:
raise ValueError(f"That isn't a valid {thing_name}!") raise ValueError(f"That isn't a valid {option_key}!")
return entry return entry
def lock(entry, thing_name='locks', access_options=None, **kwargs): def lock(entry, option_key='locks', access_options=None, **kwargs):
entry = entry.strip() entry = entry.strip()
if not entry: if not entry:
raise ValueError(f"No {thing_name} entered to set!") raise ValueError(f"No {option_key} entered to set!")
for locksetting in entry.split(';'): for locksetting in entry.split(';'):
access_type, lockfunc = locksetting.split(':', 1) access_type, lockfunc = locksetting.split(':', 1)
if not access_type: if not access_type: