This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
glasnost/shared/common/things.py

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