Files
pyFun/utils_exp.py
2014-07-07 18:18:56 -07:00

248 lines
9.5 KiB
Python

# Utility functions
# ToDo: Put everything in a class as staticmethods when imported, if
# name=__main__, keep them local
from pygame import locals as pygame_consts
from functools import partial
import inspect
import logging
import types
def register(list_to_register=None):
''' Used as decorator: Adds the function (or anything else) to list_to_register '''
def temp_func(what_to_register):
list_to_register.append(what_to_register)
return what_to_register
return temp_func
KW_EVENTS = {
"QUIT": pygame_consts.QUIT,
"ACTIVE_EVENT": pygame_consts.ACTIVEEVENT,
"KEY_DOWN": pygame_consts.KEYDOWN,
"KEY_UP": pygame_consts.KEYUP,
"MOUSE_MOTION": pygame_consts.MOUSEMOTION,
"MOUSE_BUTTON_UP": pygame_consts.MOUSEBUTTONUP,
"MOUSE_BUTTON_DOWN": pygame_consts.MOUSEBUTTONDOWN,
"JOY_AXIS_MOTION": pygame_consts.JOYAXISMOTION,
"JOY_BALL_MOTION": pygame_consts.JOYBALLMOTION,
"JOY_HAT_MOTION": pygame_consts.JOYHATMOTION,
"JOY_BUTTON_UP": pygame_consts.JOYBUTTONUP,
"JOY_BUTTOND_OWN": pygame_consts.JOYBUTTONDOWN,
"VIDEO_RESIZE": pygame_consts.VIDEORESIZE,
"VIDEO_EXPOSE": pygame_consts.VIDEOEXPOSE,
"USER_EVENT": pygame_consts.USEREVENT,
# think of begin_step, step and end_step
"BEGIN_STEP": pygame_consts.USEREVENT + 1,
"STEP": pygame_consts.USEREVENT + 2,
"END_STEP": pygame_consts.USEREVENT + 3
}
def fuzzy_match_event_name(string):
# print(string)
str_normalized = string.upper()
for tmp_event in KW_EVENTS:
# tmp event is the event_name, keys of KW_EVENTS
tmp_event_normalized = tmp_event.replace("_", "")
if (tmp_event in str_normalized) or (tmp_event_normalized in str_normalized):
return tmp_event
class Action(object):
""" pygame.event.Action(type, dict): return Action
pygame.Action.Action(type, **attributes): return Action
"""
def __init__(self, type, *args, **kwargs):
super(Action, self).__init__()
self.type = type
_dict = {}
if len(args) == 1:
_dict.update(args[0])
elif len(args) > 1:
raise TypeError(
"Max one non-keyword argument allowed (which is set to dict)")
if kwargs:
_dict.update(kwargs)
if _dict:
self.dict = _dict
def copy_func(f, name=None):
return types.FunctionType(f.func_code, f.func_globals, name or f.func_name,
f.func_defaults, f.func_closure)
def selfie_depreceated(f):
"""Decorate function `f` to pass a reference to the function
as the first argument"""
return partial(f, f)
def selfie(f):
"""Decorate function `f` to pass a reference to the function
as the first argument"""
return f.__get__(f, type(f))
def sync_func(func1, func2):
'''Makes the 1st param (func) equivalent to 2nd param (func).'''
func1.func_code = func2.func_code
func1.func_defaults = func2.func_defaults
func1.func_dict = func2.func_dict
func1.func_doc = func2.func_doc
func1.func_name = func2.func_name
class meta_accessor_class(type):
''' This metaclass allows class access to any function (or as extended use, to metaclasses)'''
# Using metaclass and decorator to allow class access during class creation time
# No method defined within the class should have "_process_meta" as arg
# Potential problems: Using closures, function.func_globals is read-only
# Doesnt get appropiate globals, maybe pass it (or the reqd global vars)
# as param on func defn
def __new__(cls, name, base, clsdict):
temp_cls = type.__new__(cls, name, base, clsdict)
methods = inspect.getmembers(temp_cls, inspect.ismethod)
for (method_name, method_obj) in methods:
has_im_func = hasattr(method_obj, "im_func")
has_func = hasattr(method_obj, "func")
while has_im_func or has_func:
if has_im_func:
method_obj = method_obj.im_func
if has_func:
# cater for partials
method_obj = method_obj.func
has_im_func = hasattr(method_obj, "im_func")
has_func = hasattr(method_obj, "func")
if hasattr(method_obj, "_process_meta"):
method_obj._process_meta = True
# method_obj should return itself after setting main_func
if hasattr(method_obj,"selfie"):
method_obj = method_obj(method_obj)
else:
method_obj = method_obj()
what_to_do, main_func = method_obj.what_to_do, method_obj.main_func
# as we have resolved all im_funcs in
# partial,classmethod,boundmethods
f = method_obj
sync_func(f, main_func)
# func_to_register hardcoded
mod_func = what_to_do(f, cls=temp_cls)
sync_func(f, mod_func)
return temp_cls
@selfie
def run_dec_on_meta(self, what_to_do):
'''Runs decorator (what_to_do) passed to it during meta with args, cls=>class and func=>function which this is used on'''
# must be deepcopied bcoz multiple versions will edit main func
# syntax error to grab attention
# make copies of this func and instead of params use .vars. Should accept
# *args,*kwargs to main_func. Next step: multiple funcs, think.
def inter_run_dec_on_meta(*args,**kwargs):
# this intermediate func is used to produce func_process_meta for each time
def func_process_meta(self, *args, **kwargs):
if not hasattr(self, "_process_meta"):
# changing own's state, should be restored at last step before
# meta-return
self._process_meta = False
#(below) unneccesary here
if not hasattr(self,"__old_args") and not hasattr(self,"__old_kwargs"):
self.__old_args = None
self.__old_kwargs = None
if not self._process_meta:
# as long as not being meta-processed
# its not really main_func, really its just what_to_do's param
if (self.__old_args or self.__old_kwargs):
self.what_to_do = partial(self.what_to_do, *(self.__old_args), **(self.__old_kwargs))
self.__old_args = args
self.__old_kwargs = kwargs
return selfie(self)
else:
self.main_func = self.__old_args[0]
return self
func_process_meta.__old_args = args
func_process_meta.__old_kwargs = kwargs
func_process_meta.what_to_do = what_to_do
func_process_meta.selfie=True
func_process_meta = selfie(func_process_meta)
return func_process_meta
return inter_run_dec_on_meta
def run_func_on_meta(run_func):
'''Runs func (run_func) passed to it during meta with arg, cls.
run_func remains a method of the class.'''
def what_to_do(func, cls):
func(cls=cls)
return func
return run_dec_on_meta(what_to_do=what_to_do, main_func=run_func)
def register_event(event_name=None, func_to_register=None, cls=None):
'''
@fun_Class.register_event() # @fun_Class.register_event === @fun_Class.register_event()
def event_key_down(ev):
pass
OR
# event_name=KEYDOWN (or "key_down")
@fun_Class.register_event(event_name)
def whatever(ev):
pass
OR
fun_Class.register_event(event_name,func)
'''
if (event_name is None) and (func_to_register is None):
# i.e No param passed, used as decorator (1st step of ex. 1)
logging.info("Nothing passed")
return register_event
elif (not(event_name is None)) and (func_to_register is None):
# i.e. one param is passed, maybe func or (str or int)
logging.info("Event Name only passed")
if isinstance(event_name, int) or isinstance(event_name, str):
# i.e. 1st parm-> str or int, is event name, acts as decorator
# (2nd ex.)
# dont hardcode func name- use metaclass
logging.info("Event Name is event_name")
return partial(cls.register_event, event_name)
elif hasattr(event_name, "__call__"):
# first param is func, actually func_to_register.
# So get event_name from func_name (2nd step of 1st ex.)
logging.info("Event Name is actually func")
func_to_register = event_name
event_name = fuzzy_match_event_name(func_to_register.func_name)
return register_event(event_name=event_name, func_to_register=func_to_register, cls=cls)
else:
raise TypeError("event_name must be (str or int) or func")
else:
# Both params present
logging.info("Both param passed")
if isinstance(event_name, str):
event_name = fuzzy_match_event_name(event_name)
event_numb = KW_EVENTS[event_name]
elif isinstance(event_name, int):
event_numb = event_name
else:
raise TypeError("event_name should be str or int")
logging.info("Registering func")
if not (event_numb in cls.dict_action_func):
cls.dict_action_func[event_numb] = []
cls.dict_action_func[event_numb].append(func_to_register)
return func_to_register
##_register_event_ = register_event
##del register_event
# so that register event is actually delegated register
##register_event = run_dec_on_meta(_register_event_)