Added the ActiveGrid IDE as a sample application

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@33440 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2005-04-08 22:54:02 +00:00
parent 510bb7480c
commit 1f780e48af
42 changed files with 20049 additions and 9 deletions

View File

@@ -0,0 +1,72 @@
import logging
import cStringIO
import traceback
import sys
import string
import os
def classForName(className):
pathList = className.split('.')
moduleName = string.join(pathList[:-1], '.')
code = __import__(moduleName)
for name in pathList[1:]:
code = code.__dict__[name]
return code
def hasattrignorecase(object, name):
for attr in dir(object):
if attr.lower() == name.lower():
return True
for attr in dir(object):
if attr.lower() == '_' + name.lower():
return True
return False
def setattrignorecase(object, name, value):
for attr in object.__dict__:
if attr.lower() == name.lower():
object.__dict__[attr] = value
return
## for attr in dir(object):
## if attr.lower() == '_' + name.lower():
## object.__dict__[attr] = value
## return
object.__dict__[name] = value
def getattrignorecase(object, name):
for attr in object.__dict__:
if attr.lower() == name.lower():
return object.__dict__[attr]
## for attr in dir(object):
## if attr.lower() == '_' + name.lower():
## return object.__dict__[attr]
return object.__dict__[name]
def defaultLoad(fileObject):
xml = fileObject.read()
loadedObject = xmlmarshaller.unmarshal(xml)
if hasattr(fileObject, 'name'):
loadedObject.fileName = os.path.abspath(fileObject.name)
loadedObject.initialize()
return loadedObject
def defaultSave(fileObject, objectToSave):
xml = xmlmarshaller.marshal(objectToSave, prettyPrint=True)
fileObject.write(xml)
def clone(objectToClone):
xml = xmlmarshaller.marshal(objectToClone, prettyPrint=True)
clonedObject = xmlmarshaller.unmarshal(xml)
if hasattr(objectToClone, 'fileName'):
clonedObject.fileName = objectToClone.fileName
clonedObject.initialize()
return clonedObject
def exceptionToString(e):
sio = cStringIO.StringIO()
traceback.print_exception(e.__class__, e, sys.exc_traceback, file=sio)
return sio.getvalue()

View File

@@ -0,0 +1,84 @@
#----------------------------------------------------------------------------
# Name: aglogging.py
# Purpose: Utilities to help with logging
#
# Author: Jeff Norton
#
# Created: 01/04/05
# CVS-ID: $Id$
# Copyright: (c) 2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import sys
import os
import re
import traceback
global agTestMode
agTestMode = False
def setTestMode(mode):
global agTestMode
if (mode):
agTestMode = True
else:
agTestMode = False
def getTestMode():
global agTestMode
return agTestMode
def testMode(normalObj, testObj=None):
if getTestMode():
return testObj
return normalObj
def toDiffableString(value):
s = repr(value)
ds = ""
i = s.find(" at 0x")
start = 0
while (i >= 0):
j = s.find(">", i)
if (j < i):
break
ds += s[start:i]
start = j
i = s.find(" at 0x", start)
return ds + s[start:]
def removeFileRefs(str):
str = re.sub(r'(?<=File ")[^"]*(\\[^\\]*")(, line )[0-9]*', _fileNameReplacement, str)
return str
def _fileNameReplacement(match):
return "...%s" % match.group(1)
def getTraceback():
extype, val, tb = sys.exc_info()
tbs = "\n"
for s in traceback.format_tb(tb):
tbs += s
return tbs
def reportException(out=None, stacktrace=False, diffable=False):
extype, val, t = sys.exc_info()
if (diffable):
exstr = removeFileRefs(str(val))
else:
exstr = str(val)
if (out == None):
print "Got Exception = %s: %s" % (extype, exstr)
else:
print >> out, "Got Exception = %s: %s" % (extype, exstr)
if (stacktrace):
fmt = traceback.format_exception(extype, val, t)
for s in fmt:
if (diffable):
s = removeFileRefs(s)
if (out == None):
print s
else:
print >> out, s

View File

@@ -0,0 +1,87 @@
#----------------------------------------------------------------------------
# Name: cachedloader.py
# Purpose:
#
# Author: Joel Hare
#
# Created: 8/31/04
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import copy
import os.path
import string
import cStringIO
import time
# TODO: Instantiate the database and create a pool
class CachedLoader(object):
def __init__(self):
self.cache = {}
self.baseLoadDir = None
def fullPath(self, fileName):
if os.path.isabs(fileName):
absPath = fileName
elif self.baseLoadDir:
absPath = os.path.join(self.baseLoadDir, fileName)
else:
absPath = os.path.abspath(fileName)
return absPath
def setPrototype(self, fileName, loadedFile):
absPath = self.fullPath(fileName)
mtime = time.time() + 31536000.0 # Make sure prototypes aren't replaced by files on disk
self.cache[absPath] = (mtime, loadedFile)
def update(self, loader):
self.cache.update(loader.cache)
def clear(self):
self.cache.clear()
def delete(self, fileName):
absPath = self.fullPath(fileName)
del self.cache[absPath]
def needsLoad(self, fileName):
absPath = self.fullPath(fileName)
try:
cached = self.cache[absPath]
cachedTime = cached[0]
if cachedTime >= os.path.getmtime(absPath):
return False
except KeyError:
pass
return True
def load(self, fileName, loader):
absPath = self.fullPath(fileName)
loadedFile = None
try:
cached = self.cache[absPath]
except KeyError:
cached = None
if cached:
cachedTime = cached[0]
# ToDO We might need smarter logic for checking if a file needs to be reloaded
# ToDo We need a way to disable checking if this is a production server
if cachedTime >= os.path.getmtime(absPath):
loadedFile = cached[1]
if not loadedFile:
targetFile = file(absPath)
try:
mtime = os.path.getmtime(absPath)
loadedFile = loader(targetFile)
self.cache[absPath] = (mtime, loadedFile)
finally:
targetFile.close()
return loadedFile

View File

@@ -0,0 +1,145 @@
#----------------------------------------------------------------------------
# Name: dependencymgr.py
# Purpose: Dependency Manager
#
# Author: Jeff Norton
#
# Created: 01/28/05
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
#----------------------------------------------------------------------------
DM_NO_ID = 0
DM_ID_ATTR = "_DependencyMgr__ID"
##class ManageableObject(object):
##
## def __init__(self):
## self.__id = DM_NO_ID
##
## def __repr__(self):
## return "<ManageableObject id = %s>" % self.__id
##
## def __getID(self):
## return self.__id
##
## def __setID(self, value):
## if (self.__id != DM_NO_ID):
## raise DependencyMgrException("Cannot set the dependency ID on object %s to \"%s\" because it already has one (\"%s\")." % (repr(self), value, self.__id))
## self.__id = value
##
## _DependencyMgr__ID = property(__getID, __setID)
class DependencyMgr(object):
def __init__(self):
self.clear()
def clear(self):
self.__dependencies = {}
self.__lastID = DM_NO_ID
def addDependency(self, parent, child):
pid = self._initObjectID(parent)
try:
parentCollection = self.__dependencies[pid]
except KeyError:
parentCollection = self._newDependencyCollection()
self.__dependencies[pid] = parentCollection
if (child not in parentCollection):
parentCollection.append(child)
def removeDependency(self, parent, child):
pid = self._getObjectID(parent)
if (pid != DM_NO_ID):
try:
parentCollection = self.__dependencies[pid]
parentCollection.remove(child)
if (len(parentCollection) == 0):
del self.__dependencies[pid]
except KeyError, ValueError:
pass
def clearDependencies(self, parent):
"Returns a list of objects or an empty list if no dependencies exist as for getDependencies, and then removes the dependency list."
pid = self._getObjectID(parent)
try:
deps = self.__dependencies[pid]
del self.__dependencies[pid]
return deps
except KeyError:
return []
def hasDependency(self, parent):
"Returns a boolean"
return (self._getObjectID(parent) in self.__dependencies)
def getDependencies(self, parent):
"Returns a list of objects or an empty list if no dependencies exist."
try:
return self.__dependencies[self._getObjectID(parent)]
except KeyError:
return []
def dumpState(self, out):
"Writes the state of the dependency manager (as reported by getState) to out"
for line in self.getState():
print >> out, line
def getState(self):
"Returns the state of the dependency manager including all managed objects as a list of strings"
out = []
out.append("DependencyMgr %s has %i parent objects, last id assigned is %i" % (repr(self), len(self.__dependencies), self.__lastID))
for key, val in self.__dependencies.iteritems():
out.append("Object %s has dependents: %s " % (repr(key), ", ".join([repr(d) for d in val])))
return out
def _initObjectID(self, obj):
try:
id = getattr(obj, DM_ID_ATTR)
except AttributeError:
id = DM_NO_ID
if (id == DM_NO_ID):
id = self._newID()
setattr(obj, DM_ID_ATTR, id)
return id
def _getObjectID(self, obj):
try:
id = getattr(obj, DM_ID_ATTR)
except AttributeError:
id = DM_NO_ID
return id
def _newID(self):
self.__lastID += 1
return self.__lastID
def _newDependencyCollection(self):
return []
globalDM = DependencyMgr()
def addDependency(parent, child):
getGlobalDM().addDependency(parent, child)
def removeDependency(parent, child):
getGlobalDM().removeDependency(parent, child)
def clearDependencies(parent):
return getGlobalDM().clearDependencies(parent)
def hasDependency(parent):
return getGlobalDM().hasDependency(parent)
def getDependencies(parent):
return getGlobalDM().getDependencies(parent)
def getState():
return getGlobalDM().getState()
def dumpState(out):
getGlobalDM().dumpState(out)
def getGlobalDM():
return globalDM

View File

@@ -0,0 +1,39 @@
#----------------------------------------------------------------------------
# Name: fileutils.py
# Purpose: Active grid miscellaneous utilities
#
# Author: Jeff Norton
#
# Created: 12/10/04
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import os
def createFile(filename, mode='w'):
f = None
try:
f = file(filename, mode)
except:
os.makedirs(filename[:filename.rindex(os.sep)])
f = file(filename, mode)
return f
def compareFiles(file1, file2):
file1.seek(0)
file2.seek(0)
while True:
line1 = file1.readline()
line2 = file2.readline()
if (len(line1) == 0):
if (len(line2) == 0):
return 0
else:
return -1
elif (len(line2) == 0):
return -1
elif (line1 != line2):
return -1

View File

@@ -0,0 +1,36 @@
#----------------------------------------------------------------------------
# Name: gettersetter.py
# Purpose:
#
# Author: Peter Yared
#
# Created: 7/28/04
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
def gettersetter(list):
for attr in list:
lowercase = attr[0].lower() + attr[1:]
uppercase = attr[0].upper() + attr[1:]
print " def get%s(self):" % uppercase
print " return self._%s" % lowercase
print
print " def set%s(self, %s):" % (uppercase, lowercase)
print " self._%s = %s" % (lowercase, lowercase)
print
def listgettersetter(list):
for attr in list:
lowercase = attr[0].lower() + attr[1:]
uppercase = attr[0].upper() + attr[1:]
print " def get%s(self):" % uppercase
print " return self._%s" % lowercase
print
print " def add%s(self, %s):" % (uppercase[:-1], lowercase[:-1])
print " self._%s.append(%s)" % (lowercase, lowercase[:-1])
print
print " def remove%s(self, %s):" % (uppercase[:-1], lowercase[:-1])
print " self._%s.remove(%s)" % (lowercase, lowercase[:-1])
print

View File

@@ -0,0 +1,649 @@
#----------------------------------------------------------------------------
# Name: xmlmarshaller.py
# Purpose:
#
# Author: John Spurling
#
# Created: 7/28/04
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
from activegrid import util
import inspect
from types import *
import xml.sax
import xml.sax.handler
import __builtin__
from xml.sax import saxutils
### ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
"""
More documentation later, but here are some special Python attributes
that McLane recognizes:
name: __xmlname__
type: string
description: the name of the xml element for the marshalled object
name: __xmlattributes__
type: tuple or list
description: the name(s) of the Python string attribute(s) to be
marshalled as xml attributes instead of nested xml elements. currently
these can only be strings since there's not a way to get the type
information back when unmarshalling.
name: __xmlexclude__
type: tuple or list
description: the name(s) of the python attribute(s) to skip when
marshalling.
name: __xmlrename__
type: dict
description: describes an alternate Python <-> XML name mapping.
Normally the name mapping is the identity function. __xmlrename__
overrides that. The keys are the Python names, the values are their
associated XML names.
name: __xmlflattensequence__
type: dict, tuple, or list
description: the name(s) of the Python sequence attribute(s) whose
items are to be marshalled as a series of xml elements (with an
optional keyword argument that specifies the element name to use) as
opposed to containing them in a separate sequence element, e.g.:
myseq = (1, 2)
<!-- normal way of marshalling -->
<myseq>
<item objtype='int'>1</item>
<item objtype='int'>2</item>
</myseq>
<!-- with __xmlflattensequence__ set to {'myseq': 'squish'} -->
<squish objtype='int'>1</squish>
<squish objtype='int'>2</squish>
name: __xmlnamespaces__
type: dict
description: a dict of the namespaces that the object uses. Each item
in the dict should consist of a prefix,url combination where the key is
the prefix and url is the value, e.g.:
__xmlnamespaces__ = { "xsd":"http://www.w3c.org/foo.xsd" }
name: __xmldefaultnamespace__
type: String
description: the prefix of a namespace defined in __xmlnamespaces__ that
should be used as the default namespace for the object.
name: __xmlattrnamespaces__
type: dict
description: a dict assigning the Python object's attributes to the namespaces
defined in __xmlnamespaces__. Each item in the dict should consist of a
prefix,attributeList combination where the key is the prefix and the value is
a list of the Python attribute names. e.g.:
__xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] }
"""
################################################################################
#
# module exceptions
#
################################################################################
class Error(Exception):
"""Base class for errors in this module."""
pass
class UnhandledTypeException(Error):
"""Exception raised when attempting to marshal an unsupported
type.
"""
def __init__(self, typename):
self.typename = typename
def __str__(self):
return "%s is not supported for marshalling." % str(self.typename)
class XMLAttributeIsNotStringType(Error):
"""Exception raised when an object's attribute is specified to be
marshalled as an XML attribute of the enclosing object instead of
a nested element.
"""
def __init__(self, attrname, typename):
self.attrname = attrname
self.typename = typename
def __str__(self):
return """%s was set to be marshalled as an XML attribute
instead of a nested element, but the object's type is %s, not
string.""" % (self.attrname, self.typename)
################################################################################
#
# constants and such
#
################################################################################
XMLNS = 'xmlns'
XMLNS_PREFIX = XMLNS + ':'
XMLNS_PREFIX_LENGTH = len(XMLNS_PREFIX)
BASETYPE_ELEMENT_NAME = 'item'
MEMBERS_TO_SKIP = ('__module__', '__doc__', '__xmlname__', '__xmlattributes__',
'__xmlexclude__', '__xmlflattensequence__', '__xmlnamespaces__',
'__xmldefaultnamespace__', '__xmlattrnamespaces__')
WELL_KNOWN_OBJECTS = { "xs:element" : "activegrid.model.schema.XsdElement",
"xs:complexType" : "activegrid.model.schema.XsdComplexType",
"xs:complexType" : "activegrid.model.schema.XsdComplexType",
"xs:element" : "activegrid.model.schema.XsdElement",
"xs:key" : "activegrid.model.schema.XsdKey",
"xs:field" : "activegrid.model.schema.XsdKeyField",
"xs:keyref" : "activegrid.model.schema.XsdKeyRef",
"xs:selector" : "activegrid.model.schema.XsdKeySelector",
"xs:schema" : "activegrid.model.schema.Schema",
"ag:schemaOptions":"activegrid.model.schema.SchemaOptions",
"ag:debug" : "activegrid.model.processmodel.DebugOperation",
}
################################################################################
#
# classes and functions
#
################################################################################
def _objectfactory(objname, objargs=None, xsname=None):
try:
'''dynamically create an object based on the objname and return
it. look it up in the BASETYPE_ELEMENT_MAP first.
'''
## print "_objectfactory creating an object of type %s and value %s, xsname=%s" % (objname, objargs, xsname)
# split the objname into the typename and module path,
# importing the module if need be.
if not isinstance(objargs, list):
objargs = [objargs]
if (xsname):
try:
objname = WELL_KNOWN_OBJECTS[xsname]
except KeyError:
pass
objtype = objname.split('.')[-1]
pathlist = objname.split('.')
modulename = '.'.join(pathlist[0:-1])
## print "[objectfactory] objtype is %s" % objtype
## print "[objectfactory] objargs is %s" % `objargs`
## since the bool constructor will turn a string of non-zero
## length into True, we call it with no argument (yielding a
## False) if the string contains 'false'
if objtype == 'bool' and objargs[0].lower() == 'false':
objargs = None
## if objtype == 'str':
## print type(objargs)
## print "string we're unescaping: '%s'" % objargs[0]
## objargs = saxutils.unescape(objargs[0])
if objtype in ('float', 'int', 'str', 'long'):
objargs = [x.strip() for x in objargs]
if objtype == 'str':
objargs = [saxutils.unescape(x) for x in objargs]
if __builtin__.__dict__.has_key(objname):
module = __builtin__
else:
if modulename:
module = __import__(modulename)
for name in pathlist[1:-1]:
module = module.__dict__[name]
if objargs:
return module.__dict__[objtype](*objargs)
else:
if objtype == 'None':
return None
return module.__dict__[objtype]()
except KeyError:
raise KeyError("Could not find class %s" % objname)
class Element:
def __init__(self, name, attrs=None):
self.name = name
self.attrs = attrs
self.content = ''
self.children = []
def getobjtype(self):
if self.attrs.has_key('objtype'):
return self.attrs.getValue('objtype')
else:
return 'str'
class XMLObjectFactory(xml.sax.ContentHandler):
def __init__(self):
self.rootelement = None
self.elementstack = []
xml.sax.handler.ContentHandler.__init__(self)
## ContentHandler methods
def startElement(self, name, attrs):
if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
name = name[name.index(':') + 1:]
## for attrname in attrs.getNames():
## print "%s: %s" % (attrname, attrs.getValue(attrname))
element = Element(name, attrs.copy())
self.elementstack.append(element)
## print self.elementstack
def characters(self, content):
## print "got content: %s" % content
if content:
self.elementstack[-1].content += content
def endElement(self, name):
## print "[endElement] name of element we're at the end of: %s" % name
xsname = name
if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
name = name[name.index(':') + 1:]
element = self.elementstack.pop()
objtype = element.getobjtype()
constructorarglist = []
if element.content:
strippedElementContent = element.content.strip()
if strippedElementContent:
constructorarglist.append(element.content)
obj = _objectfactory(objtype, constructorarglist, xsname)
complexType = None
if hasattr(obj, '__xsdcomplextype__'):
complexType = getattr(obj, '__xsdcomplextype__')
if len(self.elementstack) > 0:
self.elementstack[-1].children.append((name, obj))
else:
self.rootelement = obj
if element.attrs and not isinstance(obj, list):
for attrname, attr in element.attrs.items():
if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
if attrname.startswith(XMLNS_PREFIX):
ns = attrname[XMLNS_PREFIX_LENGTH:]
else:
ns = ""
if not hasattr(obj, '__xmlnamespaces__'):
obj.__xmlnamespaces__ = {ns:attr}
elif ns not in obj.__xmlnamespaces__:
if (hasattr(obj.__class__, '__xmlnamespaces__')
and obj.__xmlnamespaces__ is obj.__class__.__xmlnamespaces__):
obj.__xmlnamespaces__ = dict(obj.__xmlnamespaces__)
obj.__xmlnamespaces__[ns] = attr
elif not attrname == 'objtype':
if attrname.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
attrname = attrname[attrname.index(':') + 1:]
if complexType:
xsdElement = complexType.findElement(attrname)
if xsdElement:
type = xsdElement.type
if type:
type = xsdToPythonType(type)
### ToDO remove maxOccurs hack after bug 177 is fixed
if attrname == "maxOccurs" and attr == "unbounded":
attr = "-1"
attr = _objectfactory(type, attr)
util.setattrignorecase(obj, _toAttrName(obj, attrname), attr)
## obj.__dict__[_toAttrName(obj, attrname)] = attr
# stuff any child attributes meant to be in a sequence via the __xmlflattensequence__
flattenDict = {}
if hasattr(obj, '__xmlflattensequence__'):
for sequencename, xmlnametuple in obj.__xmlflattensequence__.items():
for xmlname in xmlnametuple:
flattenDict[xmlname] = sequencename
# reattach an object's attributes to it
for childname, child in element.children:
if flattenDict.has_key(childname):
sequencename = _toAttrName(obj, flattenDict[childname])
try:
sequencevalue = obj.__dict__[sequencename]
except AttributeError:
sequencevalue = None
if sequencevalue == None:
sequencevalue = []
obj.__dict__[sequencename] = sequencevalue
sequencevalue.append(child)
elif isinstance(obj, list):
obj.append(child)
else:
## print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child))
util.setattrignorecase(obj, _toAttrName(obj, childname), child)
## obj.__dict__[_toAttrName(obj, childname)] = child
if complexType:
for element in complexType.elements:
if element.default:
elementName = _toAttrName(obj, element.name)
if ((elementName not in obj.__dict__) or (obj.__dict__[elementName] == None)):
pythonType = xsdToPythonType(element.type)
defaultValue = _objectfactory(pythonType, element.default)
obj.__dict__[elementName] = defaultValue
def getRootObject(self):
return self.rootelement
def _toAttrName(obj, name):
if (hasattr(obj, "__xmlrename__")):
for key, val in obj.__xmlrename__.iteritems():
if (name == val):
name = key
break
## if (name.startswith("__") and not name.endswith("__")):
## name = "_%s%s" % (obj.__class__.__name__, name)
return name
__typeMappingXsdToPython = {
"string": "str",
"char": "str",
"varchar": "str",
"date": "str", # ToDO Need to work out how to create python date types
"boolean": "bool",
"decimal": "float", # ToDO Does python have a better fixed point type?
"int": "int",
"long": "long",
"float": "float",
"bool": "bool",
"str": "str",
"unicode": "unicode",
}
def xsdToPythonType(xsdType):
try:
return __typeMappingXsdToPython[xsdType]
except KeyError:
raise Exception("Unknown xsd type %s" % xsdType)
def _getXmlValue(pythonValue):
if (isinstance(pythonValue, bool)):
return str(pythonValue).lower()
else:
return str(pythonValue)
def unmarshal(xmlstr):
objectfactory = XMLObjectFactory()
xml.sax.parseString(xmlstr, objectfactory)
return objectfactory.getRootObject()
def marshal(obj, elementName=None, nameSpacePrefix='', nameSpaces=None, prettyPrint=False, indent=0):
if prettyPrint or indent:
prefix = ' '*indent
newline = '\n'
increment = 4
else:
prefix = ''
newline = ''
increment = 0
## Determine the XML element name. If it isn't specified in the
## parameter list, look for it in the __xmlname__ Python
## attribute, else use the default generic BASETYPE_ELEMENT_NAME.
if not nameSpaces: nameSpaces = {} # Need to do this since if the {} is a default parameter it gets shared by all calls into the function
nameSpaceAttrs = ''
if hasattr(obj, '__xmlnamespaces__'):
for nameSpaceKey, nameSpaceUrl in getattr(obj, '__xmlnamespaces__').items():
if nameSpaceUrl in nameSpaces:
nameSpaceKey = nameSpaces[nameSpaceUrl]
else:
## # TODO: Wait to do this until there is shared state for use when going through the object graph
## origNameSpaceKey = nameSpaceKey # Make sure there is no key collision, ie: same key referencing two different URL's
## i = 1
## while nameSpaceKey in nameSpaces.values():
## nameSpaceKey = origNameSpaceKey + str(i)
## i += 1
nameSpaces[nameSpaceUrl] = nameSpaceKey
if nameSpaceKey == '':
nameSpaceAttrs += ' xmlns="%s" ' % (nameSpaceUrl)
else:
nameSpaceAttrs += ' xmlns:%s="%s" ' % (nameSpaceKey, nameSpaceUrl)
nameSpaceAttrs = nameSpaceAttrs.rstrip()
if hasattr(obj, '__xmldefaultnamespace__'):
nameSpacePrefix = getattr(obj, '__xmldefaultnamespace__') + ':'
if not elementName:
if hasattr(obj, '__xmlname__'):
elementName = nameSpacePrefix + obj.__xmlname__
else:
elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
else:
elementName = nameSpacePrefix + elementName
members_to_skip = []
## Add more members_to_skip based on ones the user has selected
## via the __xmlexclude__ attribute.
if hasattr(obj, '__xmlexclude__'):
members_to_skip += list(obj.__xmlexclude__)
# Marshal the attributes that are selected to be XML attributes.
objattrs = ''
className = obj.__class__.__name__
classNamePrefix = "_" + className
if hasattr(obj, '__xmlattributes__'):
xmlattributes = obj.__xmlattributes__
members_to_skip += xmlattributes
for attr in xmlattributes:
internalAttrName = attr
if (attr.startswith("__") and not attr.endswith("__")):
internalAttrName = classNamePrefix + attr
# Fail silently if a python attribute is specified to be
# an XML attribute but is missing.
try:
value = obj.__dict__[internalAttrName]
except KeyError:
continue
## # But, check and see if it is a property first:
## if (hasPropertyValue(obj, attr)):
## value = getattr(obj, attr)
## else:
## continue
xsdElement = None
if hasattr(obj, '__xsdcomplextype__'):
complexType = getattr(obj, '__xsdcomplextype__')
xsdElement = complexType.findElement(attr)
if xsdElement:
default = xsdElement.default
if default == value or default == _getXmlValue(value):
continue
elif value == None:
continue
# ToDO remove maxOccurs hack after bug 177 is fixed
if attr == "maxOccurs" and value == -1:
value = "unbounded"
if isinstance(value, bool):
if value == True:
value = "true"
else:
value = "false"
attrNameSpacePrefix = ''
if hasattr(obj, '__xmlattrnamespaces__'):
for nameSpaceKey, nameSpaceAttributes in getattr(obj, '__xmlattrnamespaces__').items():
if nameSpaceKey == nameSpacePrefix[:-1]: # Don't need to specify attribute namespace if it is the same as it selement
continue
if attr in nameSpaceAttributes:
attrNameSpacePrefix = nameSpaceKey + ':'
break
## if attr.startswith('_'):
## attr = attr[1:]
if (hasattr(obj, "__xmlrename__") and attr in obj.__xmlrename__):
attr = obj.__xmlrename__[attr]
objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, value)
objtype = type(obj)
if isinstance(obj, NoneType):
return ''
# return '%s<%s objtype="None"/>%s' % (prefix, elementName, newline)
elif isinstance(obj, bool):
return '%s<%s objtype="bool">%s</%s>%s' % (prefix, elementName, obj, elementName, newline)
elif isinstance(obj, int):
return '''%s<%s objtype="int">%s</%s>%s''' % (prefix, elementName, str(obj), elementName, newline)
elif isinstance(obj, long):
return '%s<%s objtype="long">%s</%s>%s' % (prefix, elementName, str(obj), elementName, newline)
elif isinstance(obj, float):
return '%s<%s objtype="float">%s</%s>%s' % (prefix, elementName, str(obj), elementName, newline)
elif isinstance(obj, basestring):
return '''%s<%s>%s</%s>%s''' % (prefix, elementName, saxutils.escape(obj), elementName, newline)
## elif isinstance(obj, unicode):
## return '''%s<%s>%s</%s>%s''' % (prefix, elementName, obj, elementName, newline)
elif isinstance(obj, list):
if len(obj) < 1:
return ''
xmlString = '%s<%s objtype="list">%s' % (prefix, elementName, newline)
for item in obj:
xmlString += marshal(item, nameSpaces=nameSpaces, indent=indent+increment)
xmlString += '%s</%s>%s' % (prefix, elementName, newline)
return xmlString
elif isinstance(obj, tuple):
if len(obj) < 1:
return ''
xmlString = '%s<%s objtype="list" mutable="false">%s' % (prefix, elementName, newline)
for item in obj:
xmlString += marshal(item, nameSpaces=nameSpaces, indent=indent+increment)
xmlString += '%s</%s>%s' % (prefix, elementName, newline)
return xmlString
elif isinstance(obj, dict):
xmlString = '%s<%s objtype="dict">%s' % (prefix, elementName, newline)
subprefix = prefix + ' '*increment
subindent = indent + 2*increment
for key, val in obj.iteritems():
xmlString += "%s<key>%s%s%s</key>%s%s<value>%s%s%s</value>%s" \
% (subprefix, newline, marshal(key, indent=subindent), subprefix, newline, subprefix, newline, marshal(val, nameSpaces=nameSpaces, indent=subindent), subprefix, newline)
xmlString += '%s</%s>%s' % (prefix, elementName, newline)
return xmlString
else:
moduleName = obj.__class__.__module__
if (moduleName == "activegrid.model.schema"):
xmlString = '%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)
else:
xmlString = '%s<%s%s%s objtype="%s.%s"' % (prefix, elementName, nameSpaceAttrs, objattrs, moduleName, className)
# get the member, value pairs for the object, filtering out
# the types we don't support.
xmlMemberString = ''
if hasattr(obj, '__xmlbody__'):
xmlMemberString = getattr(obj, obj.__xmlbody__)
else:
entryList = obj.__dict__.items()
## # Add in properties
## for key in obj.__class__.__dict__.iterkeys():
## if (key not in members_to_skip and key not in obj.__dict__
## and hasPropertyValue(obj, key)):
## value = getattr(obj, key)
## entryList.append((key, value))
entryList.sort()
for name, value in entryList:
## # special name handling for private "__*" attributes:
## # remove the _<class-name> added by Python
## if name.startswith(classNamePrefix): name = name[len(classNamePrefix):]
if name in members_to_skip: continue
if name.startswith('__') and name.endswith('__'): continue
## idx = name.find('__')
## if idx > 0:
## newName = name[idx+2:]
## if newName:
## name = newName
subElementNameSpacePrefix = nameSpacePrefix
if hasattr(obj, '__xmlattrnamespaces__'):
for nameSpaceKey, nameSpaceValues in getattr(obj, '__xmlattrnamespaces__').items():
if name in nameSpaceValues:
subElementNameSpacePrefix = nameSpaceKey + ':'
break
# handle sequences listed in __xmlflattensequence__
# specially: instead of listing the contained items inside
# of a separate list, as god intended, list them inside
# the object containing the sequence.
if hasattr(obj, '__xmlflattensequence__') and name in obj.__xmlflattensequence__ and value:
try:
xmlnametuple = obj.__xmlflattensequence__[name]
xmlname = None
if len(xmlnametuple) == 1:
xmlname = xmlnametuple[0]
except:
xmlname = name
## xmlname = name.lower()
for seqitem in value:
xmlMemberString += marshal(seqitem, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment)
else:
if (hasattr(obj, "__xmlrename__") and name in obj.__xmlrename__):
xmlname = obj.__xmlrename__[name]
else:
xmlname = name
## xmlname = name.lower()
## # skip
## if xmlname.startswith('_') and not xmlname.startswith('__'):
## xmlname = xmlname[1:]
## if (indent > 30):
## print "getting pretty deep, xmlname = ", xmlname
xmlMemberString += marshal(value, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment)
# if we have nested elements, add them here, otherwise close the element tag immediately.
if xmlMemberString:
xmlString += '>'
if hasattr(obj, '__xmlbody__'):
xmlString += xmlMemberString
xmlString += '</%s>%s' % (elementName, newline)
else:
xmlString += newline
xmlString += xmlMemberString
xmlString += '%s</%s>%s' % (prefix, elementName, newline)
else:
xmlString = xmlString + '/>%s' % newline
return xmlString
# We don't use this anymore but in case we want to get properties this is how
# you do it
def hasPropertyValue(obj, attr):
hasProp = False
try:
prop = obj.__class__.__dict__[attr]
if (isinstance(prop, property)):
hasProp = hasattr(obj, attr)
if (hasProp):
# It's a property and it has a value but sometimes we don't want it.
# If there is a _hasattr method execute it and the
# result will tell us whether to include this value
try:
hasProp = obj._hasattr(attr)
except:
pass
except KeyError:
pass
return hasProp
if __name__ == '__main__':
from xmlmarshallertests import Person, marshalledint, marshalledlist
l = [1, 2, 3]
d = {'1': 1, '2': 2}
outerlist = [l]
xmlstr = marshal(d, "d", prettyPrint=True)
print xmlstr
person = Person()
person.firstName = "Albert"
person.lastName = "Camus"
person.addressLine1 = "23 Absurd St."
person.city = "Ennui"
person.state = "MO"
person.zip = "54321"
person._phoneNumber = "808-303-2323"
person.favoriteWords = ['angst', 'ennui', 'existence']
person.weight = 150
xmlstring = marshal(person, 'person', prettyPrint=True)
print xmlstring
obj = unmarshal(marshalledlist)
print "obj has type %s and value %s" % (type(obj), str(obj))
for item in obj:
print "item: %s" % str(item)

View File

@@ -0,0 +1,183 @@
#----------------------------------------------------------------------------
# Name: xmlmarshallertests.py
# Purpose:
#
# Author: John Spurling
#
# Created: 8/16/04
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import unittest
import xmlmarshaller
from xmlprettyprinter import xmlprettyprint
marshalledPersonObject = """
<person objtype="Person">
<firstName>Albert</firstName>
<lastName>Camus</lastName>
<address>23 Absurd St.</address>
<city>Ennui</city>
<state>MO</state>
<zip>54321</zip>
<_phoneNumber>808-303-2323</_phoneNumber>
<favoriteWords objtype="list">
<item>angst</item>
<item>ennui</item>
<item>existence</item>
</favoriteWords>
<weight objtype="float">150</weight>
</person>
"""
marshalledint = '''
<item objtype="int">23</item>
'''
marshalledlist = '''
<mylist objtype="list">
<item>foo</item>
<item>bar</item>
</mylist>
'''
## a dummy class taken from the old XmlMarshaller module.
## class Person:
## def __init__(self):
## # These are not necessary but are nice if you want to tailor
## # the Python object <-> XML binding
## # The xml element name to use for this object, otherwise it
## # will use a fully qualified Python name like __main__.Person
## # which can be ugly.
## self.__xmlname__ = "person"
## self.firstName = None
## self.lastName = None
## self.addressLine1 = None
## self.addressLine2 = None
## self.city = None
## self.state = None
## self.zip = None
## self._phoneNumber = None
## self.favoriteWords = None
## self.weight = None
class Person:
__xmlflattensequence__ = {'asequence': ('the_earth_is_flat',)}
class XmlMarshallerTestFunctions(unittest.TestCase):
def setUp(self):
'''common setup code goes here.'''
pass
def testInt(self):
xml = xmlmarshaller.marshal(1)
print "\n#########################################"
print "# testString test case #"
print "#########################################"
print "marshalled int object:\n"
print xmlprettyprint(xml)
def testDict(self):
xml = xmlmarshaller.marshal({'one': 1,
'two': 2,
'three': 3})
print "\n#########################################"
print "# testString test case #"
print "#########################################"
print "marshalled dict object:\n"
print xmlprettyprint(xml)
def testBool(self):
xmltrue = xmlmarshaller.marshal(True)
xmlfalse = xmlmarshaller.marshal(False)
print "\n#########################################"
print "# testBool test case #"
print "#########################################"
print "marshalled boolean true object:\n"
print xmlprettyprint(xmltrue)
print "\nmarshalled boolean false object:\n"
print xmlprettyprint(xmlfalse)
pytrue = xmlmarshaller.unmarshal(xmltrue)
assert pytrue is True
pyfalse = xmlmarshaller.unmarshal(xmlfalse)
assert pyfalse is False
def testString(self):
xml = xmlmarshaller.marshal(
"all your marshalled objects are belong to us")
print "\n#########################################"
print "# testString test case #"
print "#########################################"
print xmlprettyprint(xml)
def testEmptyElement(self):
person = Person()
person.firstName = "Albert"
person.__xmlattributes__ = ('firstName',)
xml = xmlmarshaller.marshal(person, 'person')
print "\n#########################################"
print "# testEmptyElement test case #"
print "#########################################"
print xml
assert (xml == """<person objtype="__main__.Person" firstName="Albert"/>""")
def testXMLFlattenSequence(self):
person = Person()
person.asequence = ('one', 'two')
xml = xmlmarshaller.marshal(person, 'person')
print "\n#########################################"
print "# testXMLFlattenSequence test case #"
print "#########################################"
print xml
assert (xml == """<person objtype="__main__.Person"><the_earth_is_flat>one</the_earth_is_flat><the_earth_is_flat>two</the_earth_is_flat></person>""")
unmarshalledperson = xmlmarshaller.unmarshal(xml)
assert(hasattr(unmarshalledperson, 'asequence'))
assert(len(unmarshalledperson.asequence) == 2)
def testInstance(self):
print "\n#########################################"
print "# testInstance test case #"
print "#########################################"
class Foo:
def __init__(self):
self.alist = [1,2]
self.astring = 'f00'
f = Foo()
xml = xmlmarshaller.marshal(f, 'foo')
print xml
def testPerson(self):
person = Person()
person.firstName = "Albert"
person.lastName = "Camus"
person.addressLine1 = "23 Absurd St."
person.city = "Ennui"
person.state = "MO"
person.zip = "54321"
person._phoneNumber = "808-303-2323"
person.favoriteWords = ['angst', 'ennui', 'existence']
person.weight = 150
# __xmlattributes__ = ('fabulousness',)
person.fabulousness = "tres tres"
xml = xmlmarshaller.marshal(person)
print "\n#########################################"
print "# testPerson test case #"
print "#########################################"
print "Person object marshalled into XML:\n"
print xml
# When encountering a "person" element, use the Person class
## elementMappings = { "person" : Person }
## obj = unmarshal(xml, elementMappings = elementMappings)
## print "Person object recreated from XML with attribute types indicated:"
## print obj.person.__class__
## for (attr, value) in obj.person.__dict__.items():
## if not attr.startswith("__"):
## print attr, "=", value, type(value)
## print
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,63 @@
#----------------------------------------------------------------------------
# Name: xmlprettyprinter.py
# Purpose:
#
# Author: John Spurling
#
# Created: 9/21/04
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import xml.sax
import xml.sax.handler
class XMLPrettyPrinter(xml.sax.ContentHandler):
def __init__(self, indentationChar=' ', newlineChar='\n'):
self.xmlOutput = ''
self.indentationLevel = 0
self.indentationChar = indentationChar
self.elementStack = []
self.newlineChar = newlineChar
self.hitCharData = False
## ContentHandler methods
def startElement(self, name, attrs):
indentation = self.newlineChar + (self.indentationLevel * self.indentationChar)
# build attribute string
attrstring = ''
for attr in attrs.getNames():
value = attrs[attr]
attrstring += ' %s="%s"' % (attr, value)
self.xmlOutput += '%s<%s%s>' % (indentation, name, attrstring)
self.indentationLevel += 1
self.elementStack.append(name)
self.hitCharData = False
def characters(self, content):
self.xmlOutput += content
self.hitCharData = True
def endElement(self, name):
self.indentationLevel -= 1
indentation = ''
if not self.hitCharData:
## indentation += self.newlineChar + (self.indentationLevel * self.indentationChar)
indentation += self.indentationLevel * self.indentationChar
else:
self.hitCharData = False
self.xmlOutput += '%s</%s>%s' % (indentation, self.elementStack.pop(), self.newlineChar)
def getXMLString(self):
return self.xmlOutput[1:]
def xmlprettyprint(xmlstr, spaces=4):
xpp = XMLPrettyPrinter(indentationChar=' ' * spaces)
xml.sax.parseString(xmlstr, xpp)
return xpp.getXMLString()
if __name__ == '__main__':
simpleTestString = """<one>some text<two anattr="booga">two's data</two></one>"""
print prettyprint(simpleTestString)