Made the proxy typeclass system work in principle, using a wrapper of the __new__ method for the class.

This commit is contained in:
Griatch 2014-12-20 18:30:39 +01:00
parent 8314d8ba5e
commit 32e44dceab
7 changed files with 26 additions and 78 deletions

View file

@ -4,7 +4,6 @@ Default Typeclass for Comms.
See objects.objects for more information on Typeclassing. See objects.objects for more information on Typeclassing.
""" """
from src.comms.models import Msg, TempMsg, ChannelDB from src.comms.models import Msg, TempMsg, ChannelDB
from src.typeclasses.models import TypeclassBase
from src.utils import logger from src.utils import logger
from src.utils.utils import make_iter from src.utils.utils import make_iter
@ -14,19 +13,7 @@ class Channel(ChannelDB):
This is the base class for all Comms. Inherit from this to create different This is the base class for all Comms. Inherit from this to create different
types of communication channels. types of communication channels.
""" """
_is_typeclass = True
def __new__(cls, *args, **kwargs):
"""
We must define our Typeclasses as proxies. We also store the path
directly on the class, this is useful for managers.
"""
if hasattr(cls, "Meta"):
cls.Meta.proxy = True
else:
class Meta:
proxy = True
cls.Meta = Meta
return super(TypeclassBase, cls).__new__(*args, **kwargs)
# helper methods, for easy overloading # helper methods, for easy overloading

View file

@ -17,7 +17,6 @@ they control by simply linking to a new object's user property.
from django.conf import settings from django.conf import settings
from src.objects.models import ObjectDB from src.objects.models import ObjectDB
from src.typeclasses.models import TypeclassBase
from src.commands import cmdset, command from src.commands import cmdset, command
from src.utils.logger import log_depmsg from src.utils.logger import log_depmsg
@ -37,18 +36,7 @@ class Object(ObjectDB):
This is the base class for all in-game objects. Inherit from this This is the base class for all in-game objects. Inherit from this
to create different types of objects in the game. to create different types of objects in the game.
""" """
def __new__(cls, *args, **kwargs): _is_typeclass = True
"""
We must define our Typeclasses as proxies. We also store the path
directly on the class, this is useful for managers.
"""
if hasattr(cls, "Meta"):
cls.Meta.proxy = True
else:
class Meta:
proxy = True
cls.Meta = Meta
return super(Object, cls).__new__(*args, **kwargs)
# __init__ is only defined here in order to present docstring to API. # __init__ is only defined here in order to present docstring to API.
def __init__(self): def __init__(self):

View file

@ -22,23 +22,11 @@ _MULTISESSION_MODE = settings.MULTISESSION_MODE
_CMDSET_PLAYER = settings.CMDSET_PLAYER _CMDSET_PLAYER = settings.CMDSET_PLAYER
_CONNECT_CHANNEL = None _CONNECT_CHANNEL = None
class Player(PlayerDB): class Player(PlayerDB):
""" """
Base typeclass for all Players. Base typeclass for all Players.
""" """
def __new__(cls, *args, **kwargs): _is_typeclass = True
"""
We must define our Typeclasses as proxies. We also store the path
directly on the class, this is useful for managers.
"""
if hasattr(cls, "Meta"):
cls.Meta.proxy = True
else:
class Meta:
proxy = True
cls.Meta = Meta
return super(Player, cls).__new__(*args, **kwargs)
def __init__(self): def __init__(self):
""" """

View file

@ -9,7 +9,6 @@ from twisted.internet.defer import Deferred, maybeDeferred
from twisted.internet.task import LoopingCall from twisted.internet.task import LoopingCall
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from src.typeclasses.models import TypeclassBase
from src.scripts.models import ScriptDB from src.scripts.models import ScriptDB
from src.comms import channelhandler from src.comms import channelhandler
from src.utils import logger from src.utils import logger
@ -113,21 +112,7 @@ class ScriptBase(ScriptDB):
Base class for scripts. Don't inherit from this, inherit Base class for scripts. Don't inherit from this, inherit
from the class 'Script' instead. from the class 'Script' instead.
""" """
#__metaclass__ = TypeclassBase _is_typeclass = True
# private methods
def __new__(cls, *args, **kwargs):
"""
We must define our Typeclasses as proxies. We also store the path
directly on the class, this is useful for managers.
"""
if hasattr(cls, "Meta"):
cls.Meta.proxy = True
else:
class Meta:
proxy = True
cls.Meta = Meta
return super(ScriptBase, cls).__new__(*args, **kwargs)
def __eq__(self, other): def __eq__(self, other):
""" """

View file

@ -51,7 +51,6 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
Overload the standard get. This will limit itself to only Overload the standard get. This will limit itself to only
return the current typeclass. return the current typeclass.
""" """
print self.model
kwargs.update({"db_typeclass_path":self.model.path}) kwargs.update({"db_typeclass_path":self.model.path})
return super(TypedObjectManager, self).get(**kwargs) return super(TypedObjectManager, self).get(**kwargs)

View file

@ -740,7 +740,7 @@ class PermissionHandler(TagHandler):
#------------------------------------------------------------ #------------------------------------------------------------
# imported for access by other # imported for access by other
from src.utils.idmapper.base import SharedMemoryModelBase #from src.utils.idmapper.base import SharedMemoryModelBase
#class TypeclassBase(SharedMemoryModelBase): #class TypeclassBase(SharedMemoryModelBase):
# """ # """

View file

@ -30,8 +30,7 @@ from django.db.models.base import subclass_exception
import warnings import warnings
from django.db.models.options import Options from django.db.models.options import Options
from django.utils.deprecation import RemovedInDjango19Warning from django.utils.deprecation import RemovedInDjango19Warning
from django.core.exceptions import (ObjectDoesNotExist, from django.core.exceptions import MultipleObjectsReturned
MultipleObjectsReturned, FieldError)
from django.apps.config import MODELS_MODULE_NAME from django.apps.config import MODELS_MODULE_NAME
from django.db.models.fields.related import OneToOneField from django.db.models.fields.related import OneToOneField
#/ django patch imports #/ django patch imports
@ -95,6 +94,15 @@ class SharedMemoryModelBase(ModelBase):
already has a wrapper of the given name, the automatic creation is skipped. Note: Remember to already has a wrapper of the given name, the automatic creation is skipped. Note: Remember to
document this auto-wrapping in the class header, this could seem very much like magic to the user otherwise. document this auto-wrapping in the class header, this could seem very much like magic to the user otherwise.
""" """
# set up the typeclass handling only if a variable _is_typeclass is set on the class
if "_is_typeclass" in attrs:
if "Meta" in attrs:
attrs["Meta"].proxy = True
else:
class Meta:
proxy = True
attrs["Meta"] = Meta
def create_wrapper(cls, fieldname, wrappername, editable=True, foreignkey=False): def create_wrapper(cls, fieldname, wrappername, editable=True, foreignkey=False):
"Helper method to create property wrappers with unique names (must be in separate call)" "Helper method to create property wrappers with unique names (must be in separate call)"
def _get(cls, fname): def _get(cls, fname):
@ -189,9 +197,8 @@ class SharedMemoryModelBase(ModelBase):
#print "wrapping %s -> %s" % (fieldname, wrappername) #print "wrapping %s -> %s" % (fieldname, wrappername)
create_wrapper(cls, fieldname, wrappername, editable=field.editable, foreignkey=foreignkey) create_wrapper(cls, fieldname, wrappername, editable=field.editable, foreignkey=foreignkey)
# django patch # patch start
# Evennia mod, based on Django Ticket #11560: https://code.djangoproject.com/ticket/11560
# The actual patch is small and further down.
super_new = super(ModelBase, cls).__new__ super_new = super(ModelBase, cls).__new__
# Also ensure initialization is only performed for subclasses of Model # Also ensure initialization is only performed for subclasses of Model
@ -319,16 +326,14 @@ class SharedMemoryModelBase(ModelBase):
raise TypeError("Abstract base class containing model fields not permitted for proxy model '%s'." % name) raise TypeError("Abstract base class containing model fields not permitted for proxy model '%s'." % name)
else: else:
continue continue
# Evennia mod, based on Django Ticket #11560: https://code.djangoproject.com/ticket/11560 if base is not None:
# This allows multiple inheritance for proxy models
while parent._meta.proxy:
parent = parent._meta.proxy_for_model
if base is not None and base is not parent:
#if base is not None:
raise TypeError("Proxy model '%s' has more than one non-abstract model base class." % name) raise TypeError("Proxy model '%s' has more than one non-abstract model base class." % name)
else: else:
base = parent base = parent
if base is None: #if base is None: # patch
while parent._meta.proxy: # patch
parent = parent._meta.proxy_for_model # patch
if base is not None and base is not parent: # patch
raise TypeError("Proxy model '%s' has no non-abstract model base class." % name) raise TypeError("Proxy model '%s' has no non-abstract model base class." % name)
new_class._meta.setup_proxy(base) new_class._meta.setup_proxy(base)
new_class._meta.concrete_model = base._meta.concrete_model new_class._meta.concrete_model = base._meta.concrete_model
@ -423,14 +428,10 @@ class SharedMemoryModelBase(ModelBase):
new_class._meta.apps.register_model(new_class._meta.app_label, new_class) new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
return new_class return new_class
def __init__(cls, *args, **kwargs):
""" # /patch end
This is for the typeclass system. #return super(SharedMemoryModelBase, cls).__new__(cls, name, bases, attrs, *args, **kwargs)
"""
super(SharedMemoryModelBase, cls).__init__(*args, **kwargs)
cls.typename = cls.__name__
cls.path = "%s.%s" % (cls.__module__, cls.__name__)
print "shared __init__", cls
class SharedMemoryModel(Model): class SharedMemoryModel(Model):
# CL: setting abstract correctly to allow subclasses to inherit the default # CL: setting abstract correctly to allow subclasses to inherit the default