Files
Sub-Zero.bundle/Contents/Code/interface/func.py
T

152 lines
5.0 KiB
Python

# coding=utf-8
import datetime
import operator
from support.config import config
from support.helpers import timestamp
def enable_channel_wrapper(func):
"""
returns the original wrapper :func: (route or handler) if applicable, else the plain to-be-wrapped function
:param func: original wrapper
:return: original wrapper or wrapped function
"""
def noop(*args, **kwargs):
def inner(*a, **k):
"""
:param a: args
:param k: kwargs
:return: originally to-be-wrapped function
"""
return a[0]
return inner
def wrap(*args, **kwargs):
enforce_route = kwargs.pop("enforce_route", None)
return (func if (config.enable_channel or enforce_route) else noop)(*args, **kwargs)
return wrap
ROUTE_REGISTRY = {}
def get_func_name(args):
return list(args).pop(0).__name__
def get_lookup_key(f, args, kwargs):
return tuple([f.__name__, tuple(args), tuple([(key, value) for key, value in kwargs.iteritems()])])
def should_debounce(f, key, **kw):
return getattr(f, "debounce", False) and "randomize" in kw and key in Dict["menu_history"]
def route_wrapper(*args, **kwargs):
def wrap(f):
already_wrapped = getattr(f, "orig_f", False)
if already_wrapped:
f = already_wrapped
fn = f.__name__
if fn != "ValidatePrefs" and fn not in ROUTE_REGISTRY:
ROUTE_REGISTRY[fn] = f
print f, getattr(f, "debounce", None)
def inner(*a, **kw):
if "menu_history" not in Dict:
Dict["menu_history"] = {}
key = get_lookup_key(f, list(a), kw)
ret_f = f
ret_a = a
ret_kw = kw
fallback_needed = False
fallback_found = False
if should_debounce(ret_f, key, **kw):
# special case for TriggerRestart
if ret_f.__name__ == "TriggerRestart":
Log.Debug("Don't trigger a re-restart, falling back to main menu")
return ROUTE_REGISTRY["fatality"](randomize=timestamp())
fallback_needed = True
# try to find a suitable fallback route in case we've encountered an already visited
# debounced route
fallbacks = []
current_last_visit = Dict["menu_history"][key]
# only consider items in menu history that have an older timestamp than the current
for key_, last_visit in sorted(Dict["menu_history"].items(), key=operator.itemgetter(1),
reverse=True):
if last_visit < current_last_visit:
fallbacks.append(key_)
for key_ in fallbacks:
# old data structure
if not len(key_) == 3 or not (isinstance(key_[1], tuple) and isinstance(key_[2], tuple)):
continue
old_f, old_a, old_kw = key_
if old_f == "ValidatePrefs":
continue
possible_fallback = ROUTE_REGISTRY[old_f]
# non-debounced function found
if not getattr(possible_fallback, "debounce", False):
ret_kw = dict(old_kw)
ret_a = old_a
if "randomize" in ret_kw:
ret_kw["randomize"] = timestamp()
ret_f = possible_fallback
key = get_lookup_key(ret_f, list(ret_a), ret_kw)
fallback_found = True
Log.Debug("not triggering %s twice with %s, %s, returning to %s, %s, %s" %
(f.__name__, a, kw, ret_f.__name__, ret_a, ret_kw))
break
if not fallback_found:
Log.Debug("No fallback found in menu history for %s, falling back to main menu", f)
return ROUTE_REGISTRY["fatality"](randomize=timestamp())
if not fallback_needed:
# add function to menu history
if key in Dict["menu_history"]:
del Dict["menu_history"][key]
Dict["menu_history"][key] = datetime.datetime.now() + datetime.timedelta(hours=6)
# limit to 25 items
Dict["menu_history"] = dict(sorted(Dict["menu_history"].items(), key=operator.itemgetter(1),
reverse=True)[:25])
try:
Dict.Save()
except TypeError:
Log.Error("Can't save menu history for: %r", key)
del Dict["menu_history"][key]
return ret_f(*ret_a, **ret_kw)
# @route may be used multiple times
if not already_wrapped:
inner.orig_f = f
return enable_channel_wrapper(route(*args, **kwargs))(inner)
return enable_channel_wrapper(route(*args, **kwargs))(f)
return wrap