1#-*-python-*- 2#GemRB - Infinity Engine Emulator 3#Copyright (C) 2009 The GemRB Project 4# 5#This program is free software; you can redistribute it and/or 6#modify it under the terms of the GNU General Public License 7#as published by the Free Software Foundation; either version 2 8#of the License, or (at your option) any later version. 9# 10#This program is distributed in the hope that it will be useful, 11#but WITHOUT ANY WARRANTY; without even the implied warranty of 12#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13#GNU General Public License for more details. 14# 15#You should have received a copy of the GNU General Public License 16#along with this program; if not, write to the Free Software 17#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 19 20# The metaclasses below are used to define objects that call 21# functions in the GemRB module. 22# 23# Example: 24# class GTable: 25# __metaclass__ = metaIDWrapper 26# methods = { 27# 'GetValue': GemRB.GetTableValue, 28# } 29# 30# x = GTable(5) 31# 32# Calling 33# x.GetValue("Row", "Col") 34# will then execute 35# GemRB.GetTableValue(5, "Row", "Col") 36 37from types import MethodType 38 39def add_metaclass(metaclass): 40 """Class decorator for creating a class with a metaclass.""" 41 def wrapper(cls): 42 orig_vars = cls.__dict__.copy() 43 slots = orig_vars.get('__slots__') 44 if slots is not None: 45 if isinstance(slots, str): 46 slots = [slots] 47 for slots_var in slots: 48 orig_vars.pop(slots_var, None) 49 orig_vars.pop('__dict__', None) 50 orig_vars.pop('__weakref__', None) 51 if hasattr(cls, '__qualname__'): 52 orig_vars['__qualname__'] = cls.__qualname__ 53 return metaclass(cls.__name__, cls.__bases__, orig_vars) 54 return wrapper 55 56def MethodAttributeError(f): 57 def handler(*args, **kwargs): 58 try: 59 return f(*args, **kwargs) 60 except Exception as e: 61 raise type(e)(str(e) + "\nMethod Docs:\n" + str(f.__doc__)) 62 return handler 63 64class metaIDWrapper(type): 65 @classmethod 66 def InitMethod(cls, f = None): 67 def __init__(self, *args, **kwargs): 68 for k,v in kwargs.items(): 69 setattr(self, k, v) 70 71 #required attributes for bridging to C++ 72 assert getattr(self, 'ID', None) is not None 73 74 if f: 75 f(self, *args) 76 return __init__ 77 78 def __new__(cls, classname, bases, classdict): 79 classdict['__slots__'] = classdict.get('__slots__', []) 80 classdict['__slots__'].append('ID') 81 82 classdict['__init__'] = classdict.get('__init__', None) 83 classdict['__init__'] = cls.InitMethod(classdict['__init__']) 84 85 methods = classdict.pop('methods', {}) 86 c = super(metaIDWrapper, cls).__new__(cls, classname, bases, classdict) 87 # we must bind the methods after the class is created (instead of adding to classdict) 88 # otherwise the methods would be class methods instead of instance methods 89 for key in methods: 90 e = MethodAttributeError(methods[key]) 91 try: 92 mtype = MethodType(e, None, c) 93 setattr(c, key, mtype) 94 except TypeError: # Python 3 95 setattr(c, key, e) # FIXME?: I dont actually know if this is correct, may have to circle back here after overcoming other errors 96 return c 97