488 lines
17 KiB
Python
488 lines
17 KiB
Python
# -*- coding: iso-8859-15 -*-
|
|
|
|
|
|
# Glasnost
|
|
# By: Odile Bénassy <obenassy@entrouvert.com>
|
|
# Romain Chantereau <rchantereau@entrouvert.com>
|
|
# Nicolas Clapiès <nclapies@easter-eggs.org>
|
|
# Pierre-Antoine Dejace <padejace@entrouvert.be>
|
|
# Thierry Dulieu <tdulieu@easter-eggs.com>
|
|
# Florent Monnier <monnier@codelutin.com>
|
|
# Cédric Musso <cmusso@easter-eggs.org>
|
|
# Frédéric Péters <fpeters@entrouvert.be>
|
|
# Benjamin Poussin <poussin@codelutin.com>
|
|
# Emmanuel Raviart <eraviart@entrouvert.com>
|
|
# Sébastien Régnier <regnier@codelutin.com>
|
|
# Emmanuel Saracco <esaracco@easter-eggs.com>
|
|
#
|
|
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
|
|
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
|
|
# Emmanuel Saracco & Théridion
|
|
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
|
|
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
|
|
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
|
|
# Régnier, Emmanuel Saracco, Théridion & Vecam
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
__doc__ = """Glasnost Common Things"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
import copy
|
|
import types
|
|
|
|
import context
|
|
import faults
|
|
import tools
|
|
import tools_new as commonTools
|
|
|
|
|
|
class ThingClasses:
|
|
name = None
|
|
prototype = None
|
|
classes = None
|
|
|
|
def __init__(self, name, prototype = None):
|
|
self.name = name
|
|
if prototype is not None:
|
|
self.prototype = prototype
|
|
self.classes = {}
|
|
|
|
def get(self, thingCategory, thingName):
|
|
key = '%s_%s' % (thingCategory, thingName)
|
|
if self.classes.has_key(key):
|
|
return self.classes[key]
|
|
elif thingCategory == 'object' and self.loadObjectModule(thingName) \
|
|
and self.classes.has_key(key):
|
|
return self.classes[key]
|
|
else:
|
|
if self.prototype is None:
|
|
raise Exception(
|
|
'Unknown Thing class (category = %s, name = %s) in'
|
|
' %s (= %s)' % (thingCategory, thingName, self.name,
|
|
self.classes.keys()))
|
|
else:
|
|
return self.prototype.get(thingCategory, thingName)
|
|
|
|
def getAll(self):
|
|
if self.prototype is None:
|
|
return self.classes.copy()
|
|
all = self.prototype.getAll()
|
|
all.update(self.classes)
|
|
return all
|
|
|
|
def loadObjectModule(self, serverRoleDotobjectName):
|
|
return 0
|
|
|
|
def register(self, thingClass):
|
|
thingCategory = thingClass.thingCategory
|
|
assert thingCategory
|
|
thingName = thingClass.getThingName.im_func(thingClass)
|
|
assert thingName
|
|
key = '%s_%s' % (thingCategory, thingName)
|
|
## assert not self.classes.has_key(key), \
|
|
## 'Thing class (category = %s, name = %s) already registered' \
|
|
## ' in %s(= %s)' % (
|
|
## thingCategory, thingName, self.name, self.classes.keys())
|
|
self.classes[key] = thingClass
|
|
|
|
|
|
thingClasses = ThingClasses('common')
|
|
context.setVar('thingClasses', thingClasses)
|
|
|
|
|
|
def register(thingClass):
|
|
thingClasses.register(thingClass)
|
|
|
|
|
|
class BaseThing:
|
|
"""The base class of all glasnost things.
|
|
|
|
This is THE super class of the most of the glasnost objects, class, etc...
|
|
|
|
Attributes
|
|
==========
|
|
|
|
Theses attributes are glasnost Slots. They define the kind class attributes
|
|
instanciations.
|
|
|
|
*thingCategory* (=> thingCategory_kind):
|
|
+ The category name of the thing is 'other'.
|
|
+ Not a property
|
|
+ kindName : String.
|
|
|
|
*thingName* (=> thingName_kind):
|
|
+ The name of the thing is None.
|
|
+ Not a property
|
|
+ kindName : String.
|
|
|
|
"""
|
|
|
|
thingCategory = 'other'
|
|
thingCategory_kind_importExport = 'private'
|
|
thingCategory_kindName = 'String'
|
|
|
|
thingName = None
|
|
thingName_kind_importExport = 'private'
|
|
thingName_kindName = 'String'
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
def acquireNonCore(self, objectDirectoryPath, parentSlot = None):
|
|
pass
|
|
|
|
def buildKinds(self):
|
|
"""Instanciate the r'.*_kind$' attributes."""
|
|
|
|
# Should be converted to a class method, when porting Glasnost to
|
|
# Python 2.2.
|
|
|
|
baseClasses = self.getC3ClassLinearization()
|
|
slotNames = [slotName
|
|
for slotName in self.__dict__.keys()
|
|
if slotName.endswith('_kindName')]
|
|
for baseClass in baseClasses:
|
|
for slotName in baseClass.__dict__.keys():
|
|
if slotName.endswith('_kindName') \
|
|
and not slotName in slotNames:
|
|
slotNames.append(slotName)
|
|
slotNames = [slotName[:-len('_kindName')]
|
|
for slotName in slotNames
|
|
if getattr(self, slotName) is not None]
|
|
slotNames = [slotName
|
|
for slotName in slotNames
|
|
if not '_' in slotName \
|
|
or not slotName.split('_', 1)[0] in slotNames]
|
|
for slotName in slotNames:
|
|
slotOptionHeader = slotName + '_kind_'
|
|
slotOptionHeaderLen = len(slotOptionHeader)
|
|
slotOptionNames = [name
|
|
for name in self.__dict__.keys()
|
|
if name.startswith(slotOptionHeader)]
|
|
for baseClass in baseClasses:
|
|
for name in baseClass.__dict__.keys():
|
|
if name.startswith(slotOptionHeader) \
|
|
and name not in slotOptionNames:
|
|
slotOptionNames.append(name)
|
|
slotOptions = {}
|
|
for slotOptionName in slotOptionNames:
|
|
slotOptions[slotOptionName[slotOptionHeaderLen:]] = getattr(
|
|
self, slotOptionName)
|
|
kindName = getattr(self, slotName + '_kindName')
|
|
if kindName is None or kindName == '__getter__':
|
|
kind = None
|
|
else:
|
|
kind = commonTools.newThing('kind', kindName)
|
|
kind.buildOptions(slotOptions)
|
|
setattr(self.__class__, slotName + '_kind', kind)
|
|
|
|
def convertIds(self, sourceDispatcherId, destinationDispatcherId,
|
|
parentSlot = None):
|
|
slotNames = self.getSlotNames(parentSlot = parentSlot)
|
|
for slotName, value in self.__dict__.items():
|
|
if not slotName in slotNames:
|
|
continue
|
|
slot = self.getSlot(slotName, parentSlot = parentSlot)
|
|
newValue = slot.getKind().convertValueIds(
|
|
slot, value, sourceDispatcherId,
|
|
destinationDispatcherId)
|
|
if newValue != value:
|
|
slot.setValue(newValue)
|
|
|
|
def exportToXmlRpc(self, requiredSlotNames = None, parentSlot = None):
|
|
"""Convert the current instance to a exportable XML RPC dictionnary.
|
|
|
|
Keyword arguments:
|
|
==================
|
|
|
|
*requiredSlotNames*:
|
|
Sequence of the wanted slot name to export. If defined, only the
|
|
slot with name in the sequence will be exported. Default = None.
|
|
|
|
*parentSlot*:
|
|
This is the slot where the object is (if applicable). Default =
|
|
None.
|
|
|
|
Return the dictionnary of the class designed to be send via XML RPC.
|
|
|
|
Exceptions:
|
|
===========
|
|
|
|
*AssertionError*:
|
|
- A required slot name is a private slot name.
|
|
|
|
- A element of the required slots names sequence is not a string.
|
|
|
|
"""
|
|
|
|
result = {
|
|
'__thingCategory__': self.thingCategory,
|
|
'__thingName__': self.getExportClassName(),
|
|
}
|
|
noneSlotNames = []
|
|
for slotName in self.getExportSlotNames(parentSlot = parentSlot):
|
|
if requiredSlotNames and not slotName in requiredSlotNames:
|
|
continue
|
|
slot = self.getSlot(slotName, parentSlot = parentSlot)
|
|
kind = slot.getKind()
|
|
if not kind.isExportable():
|
|
continue
|
|
# Key must be a string for xmlrpclib.
|
|
# A slotName must always be a valid Python Indentifier, so no
|
|
# encoding is needed
|
|
# slotName = tools.utf8(slotName)
|
|
assert type(slotName) == types.StringType
|
|
assert not slotName in [
|
|
'__noneSlotNames__', '__thingCategory__', '__thingName__']
|
|
if not slot.hasLocalValue():
|
|
continue
|
|
value = slot.getLocalValue()
|
|
exportedValue = kind.exportValueToXmlRpc(slot, value)
|
|
if exportedValue is None:
|
|
noneSlotNames.append(slotName)
|
|
else:
|
|
result[slotName] = exportedValue
|
|
if noneSlotNames:
|
|
result['__noneSlotNames__'] = noneSlotNames
|
|
return result
|
|
|
|
def getC3ClassLinearization(self):
|
|
import tools_new as commonTools
|
|
return commonTools.getC3ClassLinearization(self.__class__)
|
|
|
|
def getExportClassName(self):
|
|
return self.getThingName()
|
|
|
|
def getExportSlotNames(self, parentSlot = None):
|
|
"""Return the slot names sequence."""
|
|
|
|
return self.getSlotNames(parentSlot = parentSlot)
|
|
|
|
def getImportSlotNames(self, parentSlot = None):
|
|
return self.getSlotNames(parentSlot = parentSlot)
|
|
|
|
def getModifySlotNames(self, parentSlot = None):
|
|
return self.getSlotNames(parentSlot = parentSlot)
|
|
|
|
def getOrderedLayoutSlotNames(self, parentSlot = None):
|
|
return ['thingCategory', 'thingName']
|
|
|
|
def getPython22ClassLinearization(self):
|
|
import tools_new as commonTools
|
|
baseClasses = []
|
|
commonTools.buildPython22ClassLinearization(
|
|
self.__class__, baseClasses)
|
|
return baseClasses
|
|
|
|
def getSlot(self, attributeName, parentSlot = None):
|
|
"""Get the class attribute slot.
|
|
|
|
Keyword arguments:
|
|
==================
|
|
|
|
*attributeName*:
|
|
The class attribute name.
|
|
|
|
*parentSlot*:
|
|
This is the slot where the object is (if applicable).
|
|
|
|
"""
|
|
|
|
import slots
|
|
self.initClass()
|
|
if parentSlot is None:
|
|
container = self
|
|
else:
|
|
container = None
|
|
return slots.Attribute(
|
|
attributeName, container = container, parent = parentSlot)
|
|
|
|
def getSlotByPath(self, path, parentSlot = None):
|
|
if path.startswith('self'):
|
|
path = path[len('self'):]
|
|
if not path:
|
|
return parentSlot
|
|
if path[0] != '.':
|
|
raise faults.NonExistentSlotPath(path)
|
|
path = path[1:]
|
|
foundSlotName = None
|
|
for slotName in self.getSlotNames(parentSlot = parentSlot):
|
|
if path.startswith(slotName) and (
|
|
foundSlotName is None or len(slotName) > len(foundSlotName)):
|
|
foundSlotName = slotName
|
|
if foundSlotName is None:
|
|
raise faults.NonExistentSlotPath(path)
|
|
slot = self.getSlot(foundSlotName, parentSlot = parentSlot)
|
|
return slot.getKind().getModelSlotByPath(
|
|
slot, path[len(foundSlotName):])
|
|
|
|
def getSlotNames(self, parentSlot = None):
|
|
"""Return the slot names sequence of the current object instance.
|
|
|
|
Keyword argument:
|
|
=================
|
|
|
|
*parentSlot*:
|
|
This is the slot where the object is (if applicable). Default =
|
|
None.
|
|
|
|
Return the slot names sequence.
|
|
|
|
"""
|
|
|
|
self.initClass()
|
|
baseClasses = self.getC3ClassLinearization()
|
|
slotNames = [slotName
|
|
for slotName in self.__dict__.keys()
|
|
if slotName.endswith('_kind')]
|
|
for baseClass in baseClasses:
|
|
for slotName in baseClass.__dict__.keys():
|
|
if slotName.endswith('_kind') and slotName not in slotNames:
|
|
slotNames.append(slotName)
|
|
slotNames = [slotName[:-5]
|
|
for slotName in slotNames
|
|
if getattr(self, slotName) is not None \
|
|
or hasattr(self, slotName + 'Getter') \
|
|
and getattr(self, slotName + 'Getter') is not None]
|
|
slotNames = [slotName
|
|
for slotName in slotNames
|
|
if not '_' in slotName \
|
|
or not slotName.split('_', 1)[0] in slotNames]
|
|
return slotNames
|
|
|
|
def getSlotObject(self, parentSlot = None):
|
|
if parentSlot is None:
|
|
if self.thingCategory == 'object':
|
|
return self
|
|
else:
|
|
return None
|
|
else:
|
|
return parentSlot.getObject()
|
|
|
|
def getThingName(cls):
|
|
if type(cls) == types.InstanceType:
|
|
cls = cls.__class__
|
|
if cls.thingName:
|
|
thingName = cls.thingName
|
|
else:
|
|
thingName = cls.__name__
|
|
return thingName
|
|
|
|
def hasSlotName(self, attributeName, parentSlot = None):
|
|
return attributeName in self.getSlotNames(parentSlot = parentSlot)
|
|
|
|
def hasSlotPath(self, path, parentSlot = None):
|
|
try:
|
|
self.getSlotByPath(path, parentSlot = parentSlot)
|
|
except faults.NonExistentSlotPath:
|
|
return 0
|
|
else:
|
|
return 1
|
|
|
|
def importFromXmlRpc(self, dataImport, parentSlot = None):
|
|
"""Import object class instance from a XML RPC dictionnary.
|
|
|
|
This method mutates the current object instance in the corresponding
|
|
class and instance defined in the *dataImport* dictionnary.
|
|
|
|
Keyword attributes:
|
|
===================
|
|
|
|
*dataImport*:
|
|
The XML RPC dictionnary.
|
|
|
|
*parentSlot*:
|
|
This is the slot where the object is (if applicable). Default =
|
|
None.
|
|
|
|
"""
|
|
|
|
if dataImport.has_key('__noneSlotNames__'):
|
|
noneSlotNames = dataImport['__noneSlotNames__']
|
|
else:
|
|
noneSlotNames = []
|
|
for slotName in self.getImportSlotNames(parentSlot = parentSlot):
|
|
if slotName in [
|
|
'__noneSlotNames__', '__thingCategory__', '__thingName__']:
|
|
continue
|
|
# A slotName must always be a valid Python Indentifier, so no
|
|
# encoding is needed to for the dataImport keys.
|
|
if dataImport.has_key(slotName):
|
|
exportedValue = dataImport[slotName]
|
|
elif slotName in noneSlotNames:
|
|
exportedValue = None
|
|
else:
|
|
continue
|
|
slot = self.getSlot(slotName, parentSlot = parentSlot)
|
|
kind = slot.getKind()
|
|
if not kind.isImportable():
|
|
continue
|
|
slot.setValue(kind.importValueFromXmlRpc(slot, exportedValue))
|
|
|
|
def initClass(self):
|
|
# Should be converted to a class method, when porting Glasnost to
|
|
# Python 2.2.
|
|
|
|
if not self.__class__.__dict__.has_key('classIsInited') \
|
|
or not self.__class__.__dict__['classIsInited']:
|
|
# classIsInited should be set prior to calling buildKinds()
|
|
self.__class__.__dict__['classIsInited'] = 1
|
|
self.buildKinds()
|
|
|
|
def isEmpty(self, parentSlot = None):
|
|
return not self.__dict__
|
|
|
|
def newKind(self, parentSlot = None):
|
|
kind = commonTools.newThing('kind', 'Thing')
|
|
kind.valueThingCategory = self.thingCategory
|
|
kind.valueThingName = self.getThingName()
|
|
return kind
|
|
|
|
def releaseNonCore(self, parentSlot = None):
|
|
pass
|
|
|
|
def removeIds(self, rolesToKeep, parentSlot = None):
|
|
slotNames = self.getSlotNames(parentSlot = parentSlot)
|
|
for slotName, value in self.__dict__.items():
|
|
if not slotName in slotNames:
|
|
continue
|
|
slot = self.getSlot(slotName, parentSlot = parentSlot)
|
|
newValue = slot.getKind().removeValueIds(slot, value, rolesToKeep)
|
|
if newValue != value:
|
|
slot.setValue(newValue)
|
|
|
|
def repair(self, toVersion, parentSlot = None):
|
|
changed = 0
|
|
slotNames = self.getSlotNames(parentSlot = parentSlot)
|
|
for slotName, value in self.__dict__.items():
|
|
if not slotName in slotNames:
|
|
continue
|
|
slot = self.getSlot(slotName, parentSlot = parentSlot)
|
|
newValue = slot.getKind().repairValue(slot, value, toVersion)
|
|
if newValue is not None:
|
|
changed = 1
|
|
slot.setValue(newValue)
|
|
return changed
|
|
|
|
def saveNonCore(self, objectDirectoryPath, parentSlot = None):
|
|
pass
|
|
|