Replacement for types.py

class_based_refactor
Nicholas Hope 2022-08-25 21:28:12 -04:00
parent 70733b1aad
commit 1fbe47943b
1 changed files with 278 additions and 0 deletions

278
src/tfscript/tftypes.py Normal file
View File

@ -0,0 +1,278 @@
validKeyList = [
# top row
'escape', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12',
# keyboard
'`', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '=', 'backspace',
'tab', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\',
'capslock', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'semicolon', '\'', 'enter',
'shift', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 'rshift',
'ctrl', 'lwin', 'alt', 'space', 'rwin', 'ralt', 'rctrl',
# mouse
'mouse1', 'mouse2', 'mouse3', 'mouse4', 'mouse5', 'mwheelup', 'mwheeldown',
# 8 of the 9 keys to the upper-right (PrtScn can't be bound)
'scrolllock', 'numlock',
'ins', 'home', 'pgup',
'del', 'end', 'pgdn',
# arrows
'uparrow', 'downarrow',
'leftarrow', 'rightarrow'
]
bindTypes = [
'impulse',
'hold',
'toggle',
'double',
'repeat'
]
class bind:
'''
Parent class for all bind types.
Verifies key, creates local variables
'''
def __init__(self, key, fields):
self.key = key
self.fields = fields
self.errors = []
self.targetType = None
# redefined for each unique type, default just verifies key
# and some other universal fields like alias and finds targetType
self.verify()
if type(self) == bind or len(self.fields) == 0:
return
# verify function should remove all fields relavent to the bind.
# Any extras are errors
self.errors.append(f'extra fields in "{self.key}":')
if isinstance(self.fields, str):
# iterating over a str returns each character,
# making meaningless error messages
self.errors.append(f' "{self.fields}"')
else:
for field in self.fields:
self.errors.append(f' "{field}"')
def verify(self):
try:
typeName, self.key = self.key.split(' ', 1)
self.key = self.key.lower()
except ValueError:
# catastrophic error: no type
self.errors.append(f'could not find type in "{self.key}"')
return
try:
isalias = self.fields.pop('alias')
if not isinstance(isalias, bool):
self.errors.append(f'alias should be yes or no, not "{isalias}"')
isalias = False
except (KeyError, AttributeError, TypeError):
isalias = False
if (not isalias) and (self.key not in validKeyList):
self.errors.append(f'invalid key name: "{self.key}"')
types = {
'impulse': impulse,
'hold': hold,
'toggle': toggle,
'double': double,
'repeat': repeat
}
for loopName, typeClass in types.items():
if loopName == typeName:
self.targetType = typeClass
break
if self.targetType is None:
self.errors.append(f'could not find type in "{self.key}"')
def toTargetType(self):
if self.targetType is None:
# do nothing
bind = self
else:
# cast to targetType, extend errors
bind = self.targetType(self.key, self.fields)
bind.errors.extend(self.errors)
return bind
def err(self, message):
self.errors.append(f'{type(self).__name__} "{self.key}" {message}')
class impulse(bind):
def verify(self):
self.command = None
try:
self.command = self.fields.pop('command')
if not isinstance(self.command, (str, list)):
self.err('command must be string or list')
self.command = None
except (KeyError, AttributeError, TypeError):
self.fields = {'command': self.fields}
self.command = self.fields.pop('command')
if not isinstance(self.command, (str, list)):
self.err('must be command or argument of string or list')
self.command = None
class hold(bind):
def verify(self):
self.press = None
self.release = None
if isinstance(self.fields, dict):
# verify press
try:
self.press = self.fields.pop('press')
if not isinstance(self.press, (str, list)):
self.err('press must be string or list')
self.press = None
except KeyError:
self.err('requires press field')
# verify release
try:
self.release = self.fields.pop('release')
if not isinstance(self.release, (str, list)):
self.err('release must be string or list')
self.release = None
except KeyError:
self.err('requires release field')
elif isinstance(self.fields, str):
self.fields = {'press': self.fields}
self.press = self.fields.pop('press')
else:
self.err('must be press and release, or argument of string')
class toggle(bind):
def verify(self):
self.on = None
self.off = None
if isinstance(self.fields, dict):
# verify on
try:
self.on = self.fields.pop('on')
if not isinstance(self.on, (str, list)):
self.err('on must be string or list')
self.on = None
except KeyError:
self.err('requires on field')
# verify off
try:
self.off = self.fields.pop('off')
if not isinstance(self.off, (str, list)):
self.err('off must be string or list')
self.off = None
except KeyError:
self.err('requires end field')
elif isinstance(self.fields, str):
self.fields = {'on': self.fields}
self.on = self.fields.pop('on')
else:
self.err('must be on and end, or argument of string')
class double(bind):
defaultDict = {}
condDict = {}
def verify(self):
self.primary = None
self.secondary = None
self.condition = None
# name of a bind type
self.type = None
# either "released" (default) or "both"
self.cancel = 'released'
# toggler
try:
self.condition = self.fields.pop('condition')
if self.condition not in validKeyList:
self.err(f'has invalid condition to toggle: "{self.condition}"')
except KeyError:
self.err('requires condition to toggle')
# cancel mode
try:
self.cancel = self.fields.pop('cancel')
if self.cancel not in ['released', 'both']:
self.err(f'cancel must be either "released" or "both", not "{self.cancel}"')
except KeyError:
# maintain default
pass
# type
try:
self.type = self.fields.pop('type')
if self.type not in bindTypes:
# catastrophic: invalid type
self.err(f'has invalid type: "{self.type}"')
return
except KeyError:
# catastrophic: no type given
self.err('requires type')
return
# primary action
try:
mainSection = self.fields.pop('primary')
# mainSection.update({'alias': True})
mainAction = bind(f'{self.type} primary', mainSection)
if mainAction.targetType is not None:
mainAction = mainAction.targetType(mainAction.key, mainAction.fields)
self.errors.extend(mainAction.errors)
except KeyError:
self.err('requires primary action')
# secondary action
try:
altSection = self.fields.pop('secondary')
# altSection.update({'alias': True})
altBind = bind(f'{self.type} secondary', altSection)
if altBind.targetType is not None:
altBind = altBind.targetType(altBind.key, altBind.fields)
self.errors.extend(altBind.errors)
except KeyError:
self.err('requires secondary action')
class repeat(bind):
def verify(self):
self.interval = None
self.command = None
try:
intervalStr = str(self.fields.pop('interval'))
self.interval = int(intervalStr)
if self.interval <= 0:
self.err('interval must be greater than 0')
except KeyError:
self.err('requires interval')
except ValueError:
self.err(f'has invalid number of ticks: "{self.interval}"')
try:
self.command = self.fields.pop('command')
if not isinstance(self.command, (str, list)):
self.err('command must be string or list')
self.command = None
except KeyError:
self.err('requires command')