2084 lines
64 KiB
Python
Executable File
2084 lines
64 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import re
|
|
import time
|
|
import getopt
|
|
import imp
|
|
import sys
|
|
import types
|
|
|
|
__version__ = 0.4
|
|
|
|
try:
|
|
from xml.etree import cElementTree as ElementTree
|
|
except ImportError:
|
|
try:
|
|
import cElementTree as ElementTree
|
|
except ImportError:
|
|
from elementtree import ElementTree
|
|
|
|
INDENT = 4*" "
|
|
DEBUG = False
|
|
|
|
XMLSCHEMA = "http://www.w3.org/2001/XMLSchema"
|
|
XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace'
|
|
|
|
CLASS_PROP = [("c_children", ".copy()"),
|
|
("c_attributes", ".copy()"),
|
|
("c_child_order", "[:]"),
|
|
("c_cardinality", ".copy()")]
|
|
|
|
BASE_ELEMENT = ["text", "extension_elements", "extension_attributes"]
|
|
|
|
class MissingPrerequisite(Exception):
|
|
pass
|
|
|
|
def sd_copy(arg):
|
|
try:
|
|
return arg.copy()
|
|
except AttributeError:
|
|
return {}
|
|
|
|
# ------------------------------------------------------------------------
|
|
|
|
def class_pyify(ref):
|
|
return ref.replace("-","_")
|
|
|
|
PROTECTED_KEYWORDS = ["import", "def", "if", "else", "return", "for",
|
|
"while", "not", "try", "except", "in"]
|
|
|
|
def def_init(imports, attributes):
|
|
indent = INDENT+INDENT
|
|
indent3 = INDENT+INDENT+INDENT
|
|
line = ["%sdef __init__(self," % INDENT]
|
|
|
|
for elem in attributes:
|
|
if elem[0] in PROTECTED_KEYWORDS:
|
|
_name = elem[0] +"_"
|
|
else:
|
|
_name = elem[0]
|
|
|
|
if elem[2]:
|
|
line.append("%s%s='%s'," % (indent3, _name, elem[2]))
|
|
else:
|
|
line.append("%s%s=%s," % (indent3, _name, elem[2]))
|
|
|
|
for _, elems in imports.items():
|
|
for elem in elems:
|
|
if elem in PROTECTED_KEYWORDS:
|
|
_name = elem +"_"
|
|
else:
|
|
_name = elem
|
|
line.append("%s%s=None," % (indent3, _name))
|
|
|
|
line.append("%stext=None," % indent3)
|
|
line.append("%sextension_elements=None," % indent3)
|
|
line.append("%sextension_attributes=None," % indent3)
|
|
line.append("%s):" % indent)
|
|
return line
|
|
|
|
def base_init(imports):
|
|
line = []
|
|
indent4 = INDENT+INDENT+INDENT+INDENT
|
|
if not imports:
|
|
line.append("%sSamlBase.__init__(self, " % (INDENT+INDENT))
|
|
for attr in BASE_ELEMENT:
|
|
if attr in PROTECTED_KEYWORDS:
|
|
_name = attr + "_"
|
|
else:
|
|
_name = attr
|
|
line.append("%s%s=%s," % (indent4, _name, _name))
|
|
line.append("%s)" % indent4)
|
|
else:
|
|
# TODO have to keep apart which properties comes from which superior
|
|
for sup, elems in imports.items():
|
|
line.append("%s%s.__init__(self, " % (INDENT+INDENT, sup))
|
|
lattr = elems[:]
|
|
lattr.extend(BASE_ELEMENT)
|
|
for attr in lattr:
|
|
if attr in PROTECTED_KEYWORDS:
|
|
_name = attr + "_"
|
|
else:
|
|
_name = attr
|
|
line.append("%s%s=%s," % (indent4, _name, _name))
|
|
line.append("%s)" % indent4)
|
|
return line
|
|
|
|
def initialize(attributes):
|
|
indent = INDENT+INDENT
|
|
line = []
|
|
for prop, val, _default in attributes:
|
|
if prop in PROTECTED_KEYWORDS:
|
|
_name = prop +"_"
|
|
else:
|
|
_name = prop
|
|
|
|
if val in PROTECTED_KEYWORDS:
|
|
_vname = val +"_"
|
|
else:
|
|
_vname = val
|
|
|
|
line.append("%sself.%s=%s" % (indent, _name, _vname))
|
|
return line
|
|
|
|
def _mod_typ(prop):
|
|
try:
|
|
(mod, typ) = prop.type
|
|
except ValueError:
|
|
typ = prop.type
|
|
mod = None
|
|
except TypeError: # No type property
|
|
try:
|
|
(mod, typ) = prop.ref
|
|
except ValueError:
|
|
if prop.class_name:
|
|
typ = prop.class_name
|
|
else:
|
|
typ = prop.ref
|
|
mod = None
|
|
|
|
return mod, typ
|
|
|
|
def _mod_cname(prop, cdict):
|
|
if hasattr(prop, "scoped"):
|
|
cname = prop.class_name
|
|
mod = None
|
|
else:
|
|
(mod, typ) = _mod_typ(prop)
|
|
if not mod:
|
|
try:
|
|
cname = cdict[typ].class_name
|
|
except KeyError:
|
|
cname = cdict[class_pyify(typ)].class_name
|
|
else:
|
|
cname = typ
|
|
|
|
return mod, cname
|
|
|
|
def leading_uppercase(string):
|
|
try:
|
|
return string[0].upper()+string[1:]
|
|
except IndexError:
|
|
return string
|
|
except TypeError:
|
|
return ""
|
|
|
|
def leading_lowercase(string):
|
|
try:
|
|
return string[0].lower()+string[1:]
|
|
except IndexError:
|
|
return string
|
|
except TypeError:
|
|
return ""
|
|
|
|
def rm_duplicates(properties):
|
|
keys = []
|
|
clist = []
|
|
for prop in properties:
|
|
if prop.name in keys:
|
|
continue
|
|
else:
|
|
clist.append(prop)
|
|
keys.append(prop.name)
|
|
return clist
|
|
|
|
# def rm_duplicates(lista):
|
|
# res = []
|
|
# for item in lista:
|
|
# if item not in res:
|
|
# res.append(item)
|
|
# return res
|
|
|
|
def klass_namn(obj):
|
|
if obj.class_name:
|
|
return obj.class_name
|
|
else:
|
|
return obj.name
|
|
|
|
class PyObj(object):
|
|
def __init__(self, name=None, pyname=None, root=None):
|
|
self.name = name
|
|
self.done = False
|
|
self.local = True
|
|
self.root = root
|
|
self.superior = []
|
|
self.value_type = ""
|
|
self.properties = ([], [])
|
|
self.abstract = False
|
|
self.class_name = ""
|
|
|
|
if pyname:
|
|
self.pyname = pyname
|
|
elif name:
|
|
self.pyname = pyify(name)
|
|
else:
|
|
self.pyname = name
|
|
|
|
self.type = None
|
|
|
|
def child_spec(self, target_namespace, prop, mod, typ, lista):
|
|
if mod:
|
|
namesp = external_namespace(self.root.modul[mod])
|
|
pkey = '{%s}%s' % (namesp, prop.name)
|
|
typ = "%s.%s" % (mod, typ)
|
|
else:
|
|
pkey = '{%s}%s' % (target_namespace, prop.name)
|
|
|
|
if lista:
|
|
return "c_children['%s'] = ('%s', [%s])" % (
|
|
pkey, prop.pyname, typ)
|
|
else:
|
|
return "c_children['%s'] = ('%s', %s)" % (
|
|
pkey, prop.pyname, typ)
|
|
|
|
def knamn(self, sup, cdict):
|
|
cname = cdict[sup].class_name
|
|
if not cname:
|
|
(namesp, tag) = cdict[sup].name.split('.')
|
|
if namesp:
|
|
ctag = self.root.modul[namesp].factory(tag).__class__.__name__
|
|
cname = '%s.%s' % (namesp, ctag)
|
|
else:
|
|
cname = tag + "_"
|
|
return cname
|
|
|
|
def _do_properties(self, line, cdict, ignore, target_namespace):
|
|
args = []
|
|
child = []
|
|
|
|
try:
|
|
(own, inh) = self.properties
|
|
except AttributeError:
|
|
(own, inh) = ([], [])
|
|
|
|
for prop in own:
|
|
if isinstance(prop, PyAttribute):
|
|
line.append("%sc_attributes['%s'] = %s" % (INDENT,
|
|
prop.name, prop.spec()))
|
|
if prop.fixed:
|
|
args.append((prop.pyname, prop.fixed, None))
|
|
else:
|
|
if prop.default:
|
|
args.append((prop.pyname, prop.pyname, prop.default))
|
|
else:
|
|
args.append((prop.pyname, prop.pyname, None))
|
|
|
|
elif isinstance(prop, PyElement):
|
|
|
|
(mod, cname) = _mod_cname(prop, cdict)
|
|
|
|
if prop.max == "unbounded":
|
|
lista = True
|
|
pmax = 0 # just has to be different from 1
|
|
else:
|
|
pmax = int(prop.max)
|
|
lista = False
|
|
|
|
if prop.name in ignore:
|
|
pass
|
|
else:
|
|
line.append("%s%s" % (INDENT, self.child_spec(
|
|
target_namespace, prop,
|
|
mod, cname,
|
|
lista)))
|
|
|
|
pmin = int(getattr(prop, 'min', 1))
|
|
|
|
if pmax == 1 and pmin == 1:
|
|
pass
|
|
elif prop.max == "unbounded":
|
|
line.append( "%sc_cardinality['%s'] = {\"min\":%s}" % (
|
|
INDENT, prop.pyname, pmin))
|
|
else:
|
|
line.append(
|
|
"%sc_cardinality['%s'] = {\"min\":%s, \"max\":%d}" % (
|
|
INDENT, prop.pyname, pmin, pmax))
|
|
|
|
child.append(prop.pyname)
|
|
if lista:
|
|
args.append((prop.pyname, "%s or []" % (prop.pyname,),
|
|
None))
|
|
else:
|
|
args.append((prop.pyname, prop.pyname, None))
|
|
|
|
return args, child, inh
|
|
|
|
def _superiors(self, cdict):
|
|
imps = {}
|
|
|
|
try:
|
|
superior = self.superior
|
|
sups = []
|
|
for sup in superior:
|
|
klass = self.knamn(sup, cdict)
|
|
sups.append(klass)
|
|
|
|
imps[klass] = []
|
|
for cla in cdict[sup].properties[0]:
|
|
if cla.pyname and cla.pyname not in imps[klass]:
|
|
imps[klass].append(cla.pyname)
|
|
except AttributeError:
|
|
superior = []
|
|
sups = []
|
|
|
|
return superior, sups, imps
|
|
|
|
def class_definition(self, target_namespace, cdict=None, ignore=None):
|
|
line = []
|
|
|
|
if self.root:
|
|
if self.name not in [c.name for c in self.root.elems]:
|
|
self.root.elems.append(self)
|
|
|
|
(superior, sups, imps) = self._superiors(cdict)
|
|
|
|
c_name = klass_namn(self)
|
|
|
|
if not superior:
|
|
line.append("class %s(SamlBase):" % (c_name,))
|
|
else:
|
|
line.append("class %s(%s):" % (c_name, ",".join(sups)))
|
|
|
|
if hasattr(self, 'scoped'):
|
|
pass
|
|
else:
|
|
line.append("%s\"\"\"The %s:%s element \"\"\"" % (INDENT,
|
|
target_namespace,
|
|
self.name))
|
|
line.append("")
|
|
line.append("%sc_tag = '%s'" % (INDENT, self.name))
|
|
line.append("%sc_namespace = NAMESPACE" % (INDENT,))
|
|
try:
|
|
if self.value_type:
|
|
if isinstance(self.value_type, basestring):
|
|
line.append("%sc_value_type = '%s'" % (INDENT,
|
|
self.value_type))
|
|
else:
|
|
line.append("%sc_value_type = %s" % (INDENT,
|
|
self.value_type))
|
|
except AttributeError:
|
|
pass
|
|
|
|
if not superior:
|
|
for var, cps in CLASS_PROP:
|
|
line.append("%s%s = SamlBase.%s%s" % (INDENT, var, var, cps))
|
|
else:
|
|
for sup in sups:
|
|
for var, cps in CLASS_PROP:
|
|
line.append("%s%s = %s.%s%s" % (INDENT, var, sup, var,
|
|
cps))
|
|
|
|
(args, child, inh) = self._do_properties(line, cdict, ignore,
|
|
target_namespace)
|
|
|
|
if child:
|
|
line.append("%sc_child_order.extend([%s])" % (INDENT,
|
|
"'"+"', '".join(child)+"'"))
|
|
|
|
if args:
|
|
if inh:
|
|
cname = self.knamn(self.superior[0], cdict)
|
|
imps = {cname: [c.pyname for c in inh if c.pyname]}
|
|
line.append("")
|
|
line.extend(def_init(imps, args))
|
|
line.extend(base_init(imps))
|
|
line.extend(initialize(args))
|
|
|
|
line.append("")
|
|
if not self.abstract or not self.class_name.endswith("_"):
|
|
line.append("def %s_from_string(xml_string):" % pyify(
|
|
self.class_name))
|
|
line.append(
|
|
"%sreturn saml2.create_class_from_xml_string(%s, xml_string)" % (
|
|
INDENT, self.class_name))
|
|
line.append("")
|
|
|
|
self.done = True
|
|
return "\n".join(line)
|
|
|
|
def prepend(add, orig):
|
|
# return a list which is the lists concatenated with the second list first
|
|
res = [add]
|
|
if orig:
|
|
res.extend(orig)
|
|
return res
|
|
|
|
def pyobj_factory(name, value_type, elms=None):
|
|
pyobj = PyObj(name, pyify(name))
|
|
pyobj.value_type = value_type
|
|
if elms:
|
|
if name not in [c.name for c in elms]:
|
|
elms.append(pyobj)
|
|
return pyobj
|
|
|
|
def pyelement_factory(name, value_type, elms=None):
|
|
obj = PyElement(name, pyify(name))
|
|
obj.value_type = value_type
|
|
if elms:
|
|
if name not in [c.name for c in elms]:
|
|
elms.append(obj)
|
|
return obj
|
|
|
|
def expand_groups(properties, cdict):
|
|
res = []
|
|
for prop in properties:
|
|
if isinstance(prop, PyGroup):
|
|
# only own, what about inherited ? Not on groups ?
|
|
cname = prop.ref[1]
|
|
res.extend(cdict[cname].properties[0])
|
|
else:
|
|
res.append(prop)
|
|
|
|
return res
|
|
|
|
class PyElement(PyObj):
|
|
def __init__(self, name=None, pyname=None, root=None, parent=""):
|
|
PyObj.__init__(self, name, pyname, root)
|
|
if parent:
|
|
self.class_name = "%s_%s" % (leading_uppercase(parent), self.name)
|
|
else:
|
|
self.class_name = leading_uppercase(self.name)
|
|
self.ref = None
|
|
self.min = 1
|
|
self.max = 1
|
|
self.definition = None
|
|
self.orig = None
|
|
|
|
# def prereq(self, prop):
|
|
# prtext = prop.text(target_namespace, cdict)
|
|
# if prtext == None:
|
|
# return []
|
|
# else:
|
|
# prop.done = True
|
|
# return prtext
|
|
|
|
def undefined(self, cdict):
|
|
try:
|
|
(mod, typ) = self.type
|
|
if not mod:
|
|
cname = leading_uppercase(typ)
|
|
if not cdict[cname].done:
|
|
return [cdict[cname]], []
|
|
except ValueError:
|
|
pass
|
|
except TypeError: # could be a ref then or a PyObj instance
|
|
if isinstance(self.type, PyType):
|
|
return self.type.undefined(cdict)
|
|
elif isinstance(self.ref, tuple):
|
|
pass
|
|
else:
|
|
cname = leading_uppercase(self.ref)
|
|
if not cdict[cname].done:
|
|
return [cdict[cname]], []
|
|
return [], []
|
|
|
|
def _local_class(self, typ, cdict, child, target_namespace, ignore):
|
|
if typ in cdict and not cdict[typ].done:
|
|
raise MissingPrerequisite(typ)
|
|
else:
|
|
self.orig = {"type": self.type}
|
|
try:
|
|
self.orig["superior"] = self.superior
|
|
except AttributeError:
|
|
self.orig["superior"] = []
|
|
self.superior = [typ]
|
|
req = self.class_definition(target_namespace, cdict,
|
|
ignore)
|
|
if not child:
|
|
req = [req]
|
|
|
|
if not hasattr(self, 'scoped'):
|
|
cdict[self.name] = self
|
|
cdict[self.name].done = True
|
|
if child:
|
|
cdict[self.name].local = True
|
|
self.type = (None, self.name)
|
|
|
|
return req
|
|
|
|
def _external_class(self, mod, typ, cdict, child, target_namespace,
|
|
ignore):
|
|
# Will raise exception if class can't be found
|
|
cname = self.root.modul[mod].factory(typ).__class__.__name__
|
|
imp_name = "%s.%s" % (mod, cname)
|
|
|
|
if imp_name not in cdict:
|
|
# create import object so I can get the properties from it
|
|
# later
|
|
impo = pyelement_factory(imp_name, None, None)
|
|
impo.properties = [_import_attrs(self.root.modul[mod], typ,
|
|
self.root),[]]
|
|
impo.class_name = imp_name
|
|
cdict[imp_name] = impo
|
|
impo.done = True
|
|
if child:
|
|
impo.local = True
|
|
# and now for this object
|
|
self.superior = [imp_name]
|
|
text = self.class_definition(target_namespace, cdict,
|
|
ignore=ignore)
|
|
|
|
return text
|
|
|
|
def text(self, target_namespace, cdict, child=True, ignore=None):
|
|
if ignore is None:
|
|
ignore = []
|
|
|
|
if child:
|
|
text = []
|
|
else:
|
|
text = None
|
|
req = []
|
|
try:
|
|
(mod, typ) = self.type
|
|
if not mod:
|
|
req = self._local_class(typ, cdict, child,
|
|
target_namespace, ignore)
|
|
else:
|
|
text = self._external_class(mod, typ, cdict, child,
|
|
target_namespace, ignore)
|
|
except ValueError: # Simple type element
|
|
if self.type:
|
|
text = self.class_definition(target_namespace, cdict,
|
|
ignore=ignore)
|
|
if child:
|
|
self.local = True
|
|
self.done = True
|
|
|
|
except TypeError: # could be a ref then or a PyObj instance
|
|
if isinstance(self.type, PyObj):
|
|
pyobj = self.type
|
|
pyobj.name = self.name
|
|
pyobj.pyname = self.pyname
|
|
pyobj.class_name = self.class_name
|
|
cdict[self.name] = pyobj
|
|
return pyobj.text(target_namespace, cdict, ignore=ignore)
|
|
elif isinstance(self.ref, tuple):
|
|
(mod, typ) = self.ref
|
|
if mod:
|
|
#self.superior = ["%s.%s" % (mod, typ)]
|
|
if verify_import(self.root.modul[mod], typ):
|
|
return req, text
|
|
else:
|
|
raise Exception(
|
|
"Import attempted on %s from %s module failed - wasn't there" % (
|
|
typ,mod))
|
|
elif not child:
|
|
self.superior = [typ]
|
|
text = self.class_definition(target_namespace, cdict,
|
|
ignore=ignore)
|
|
else:
|
|
if not cdict[class_pyify(self.ref)].done:
|
|
raise MissingPrerequisite(self.ref)
|
|
|
|
self.done = True
|
|
return req, text
|
|
|
|
def _do(obj, target_namespace, cdict, prep):
|
|
try:
|
|
(req, text) = obj.text(target_namespace, cdict)
|
|
except MissingPrerequisite:
|
|
return [], None
|
|
|
|
if text is None:
|
|
if req:
|
|
#prep = prepend(req, prep)
|
|
prep.append(req)
|
|
return prep, None
|
|
else:
|
|
obj.done = True
|
|
if req:
|
|
if isinstance(req, basestring):
|
|
prep.append(req)
|
|
else:
|
|
prep.extend(req)
|
|
if text:
|
|
#prep = prepend(text, prep)
|
|
prep.append(text)
|
|
return prep
|
|
|
|
def reqursive_superior(supc, cdict):
|
|
properties = supc.properties[0]
|
|
for sup in supc.superior:
|
|
rsup = cdict[sup]
|
|
if rsup.properties[1]:
|
|
properties.extend(rsup.properties[1])
|
|
else:
|
|
properties.extend(reqursive_superior(rsup, cdict))
|
|
return properties
|
|
|
|
class PyType(PyObj):
|
|
def __init__(self, name=None, pyname=None, root=None, superior=None,
|
|
internal=True, namespace=None):
|
|
PyObj.__init__(self, name, pyname, root)
|
|
self.class_name = leading_uppercase(self.name + '_')
|
|
self.properties = ([], [])
|
|
if superior:
|
|
self.superior = [superior]
|
|
else:
|
|
self.superior = []
|
|
self.value_type = None
|
|
self.internal = internal
|
|
self.namespace = namespace
|
|
|
|
def text(self, target_namespace, cdict, _child=True, ignore=None,
|
|
_session=None):
|
|
if not self.properties and not self.type \
|
|
and not self.superior:
|
|
self.done = True
|
|
return [], self.class_definition(target_namespace, cdict)
|
|
|
|
if ignore is None:
|
|
ignore = []
|
|
req = []
|
|
inherited_properties = []
|
|
for sup in self.superior:
|
|
try:
|
|
supc = cdict[sup]
|
|
except KeyError:
|
|
(mod, typ) = sup.split('.')
|
|
supc = pyobj_factory(sup, None, None)
|
|
if mod:
|
|
supc.properties = [_import_attrs(self.root.modul[mod],
|
|
typ, self.root),[]]
|
|
cdict[sup] = supc
|
|
supc.done = True
|
|
|
|
if not supc.done:
|
|
res = _do(supc, target_namespace, cdict, req)
|
|
if isinstance(res, tuple):
|
|
return res
|
|
|
|
if not self.properties[1]:
|
|
inherited_properties = reqursive_superior(supc, cdict)
|
|
|
|
if inherited_properties:
|
|
self.properties = (self.properties[0],
|
|
rm_duplicates(inherited_properties))
|
|
|
|
(own, inh) = self.properties
|
|
own = rm_duplicates(expand_groups(own, cdict))
|
|
self.properties = (own, inh)
|
|
for prop in own:
|
|
if not prop.name: # Ignore
|
|
continue
|
|
if not prop.done:
|
|
if prop.name in ignore:
|
|
continue
|
|
res = _do(prop, target_namespace, cdict, req)
|
|
if res == ([], None):
|
|
# # Cleaning up
|
|
# for prp in own:
|
|
# if prp == prop:
|
|
# break
|
|
# try:
|
|
# if cdict[prp.name].local:
|
|
# del cdict[prp.name]
|
|
# if hasattr(prp, "orig") and prp.orig:
|
|
# for key, val in prp.orig.items():
|
|
# setattr(prp, key, val)
|
|
# prp.done = False
|
|
# prp.local = False
|
|
# except KeyError:
|
|
# pass
|
|
res = (req, None)
|
|
if isinstance(res, tuple):
|
|
return res
|
|
|
|
return req, self.class_definition(target_namespace, cdict, ignore)
|
|
|
|
def undefined(self, cdict):
|
|
undef = ([], [])
|
|
|
|
for sup in self.superior:
|
|
supc = cdict[sup]
|
|
if not supc.done:
|
|
undef[0].append(supc)
|
|
|
|
(own, _) = self.properties
|
|
for prop in own:
|
|
if not prop.name: # Ignore
|
|
continue
|
|
if isinstance(prop, PyAttribute):
|
|
continue
|
|
if not prop.done:
|
|
undef[1].append(prop)
|
|
return undef
|
|
|
|
class PyAttribute(PyObj):
|
|
def __init__(self, name=None, pyname=None, root=None, external=False,
|
|
namespace="", required=False, typ=""):
|
|
PyObj.__init__(self, name, pyname, root)
|
|
|
|
self.required = required
|
|
self.external = external
|
|
self.namespace = namespace
|
|
self.base = None
|
|
self.type = typ
|
|
|
|
def text(self, _target_namespace, cdict, _child=True):
|
|
if isinstance(self.type, PyObj):
|
|
if not cdict[self.type.name].done:
|
|
raise MissingPrerequisite(self.type.name)
|
|
|
|
return [], [] # Means this elements definition is empty
|
|
|
|
def spec(self):
|
|
if isinstance(self.type, PyObj):
|
|
return "('%s', %s, %s)" % (self.pyname, self.type.class_name,
|
|
self.required)
|
|
else:
|
|
if self.type:
|
|
return "('%s', '%s', %s)" % (self.pyname, self.type,
|
|
self.required)
|
|
else:
|
|
return "('%s', '%s', %s)" % (self.pyname, self.base,
|
|
self.required)
|
|
|
|
class PyAny(PyObj):
|
|
def __init__(self, name=None, pyname=None, _external=False, _namespace=""):
|
|
PyObj.__init__(self, name, pyname)
|
|
self.done = True
|
|
|
|
class PyAttributeGroup(object):
|
|
def __init__(self, name, root):
|
|
self.name = name
|
|
self.root = root
|
|
self.properties = []
|
|
|
|
class PyGroup(object):
|
|
def __init__(self, name, root):
|
|
self.name = name
|
|
self.root = root
|
|
self.properties = []
|
|
self.done = False
|
|
|
|
def text(self, _target_namespace, _dict, _child, _ignore):
|
|
return [], []
|
|
|
|
def undefined(self, _cdict):
|
|
undef = ([], [])
|
|
|
|
(own, _) = self.properties
|
|
for prop in own:
|
|
if not prop.name: # Ignore
|
|
continue
|
|
if not prop.done:
|
|
undef[1].append(prop)
|
|
return undef
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def verify_import(modul, tag):
|
|
try:
|
|
_ = modul.factory(tag)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
def external_namespace(modul):
|
|
return modul.NAMESPACE
|
|
|
|
def _import_attrs(modul, tag, top):
|
|
obj = modul.factory(tag)
|
|
properties = [PyAttribute(key, val[0], top, True, obj.c_namespace, val[2],
|
|
val[1]) for key,val in obj.c_attributes.items()]
|
|
for child in obj.c_child_order:
|
|
for key, val in obj.c_children.items():
|
|
(pyn, mul) = val
|
|
maximum = 1
|
|
if isinstance(mul, list):
|
|
mul = mul[0]
|
|
maximum = "unbounded"
|
|
if pyn == child:
|
|
cpy = PyElement(name=mul.c_tag, pyname=pyn, root=top)
|
|
# internal=False, ns=obj.c_namespace)
|
|
cpy.max = maximum
|
|
properties.append(cpy)
|
|
|
|
return properties
|
|
|
|
# ------------------------------------------------------------------------
|
|
|
|
def _spec(elem):
|
|
try:
|
|
name = elem.name
|
|
except AttributeError:
|
|
name = "anonymous"
|
|
txt = "%s" % name
|
|
try:
|
|
txt += " ref: %s" % elem.ref
|
|
except AttributeError:
|
|
try:
|
|
txt += " type: %s" % elem.type
|
|
except AttributeError:
|
|
pass
|
|
|
|
return txt
|
|
|
|
# def _klass(elem, _namespace, sup, top):
|
|
# if elem.name in top.py_elements:
|
|
# return None
|
|
# else:
|
|
# kl = PyType(elem.name, root=top)
|
|
# top.py_elements[elem.name] = kl
|
|
# if sup != "SamlBase":
|
|
# kl.superior.append(sup)
|
|
# return kl
|
|
|
|
def _do_from_string(name):
|
|
print
|
|
print "def %s_from_string(xml_string):" % pyify(name)
|
|
print "%sreturn saml2.create_class_from_xml_string(%s, xml_string)" % (
|
|
INDENT, name)
|
|
|
|
def _namespace_and_tag(obj, param, top):
|
|
try:
|
|
(namespace, tag) = param.split(":")
|
|
except ValueError:
|
|
namespace = ""
|
|
tag = param
|
|
|
|
return namespace, tag
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
class Simple(object):
|
|
def __init__(self, elem):
|
|
self.default = None
|
|
self.fixed = None
|
|
self.xmlns_map = []
|
|
self.name = ""
|
|
self.type = None
|
|
self.use = None
|
|
self.ref = None
|
|
self.scoped = False
|
|
|
|
for attribute, value in elem.attrib.iteritems():
|
|
self.__setattr__(attribute, value)
|
|
|
|
def collect(self, top, sup, argv=None, parent=""):
|
|
argv_copy = sd_copy(argv)
|
|
rval = self.repr(top, sup, argv_copy, True, parent)
|
|
if rval:
|
|
return [rval], []
|
|
else:
|
|
return [], []
|
|
|
|
def repr(self, _top=None, _sup=None, _argv=None, _child=True, _parent=""):
|
|
return None
|
|
|
|
def elements(self, _top):
|
|
return []
|
|
|
|
|
|
class Any(Simple):
|
|
|
|
def repr(self, _top=None, _sup=None, _argv=None, _child=True, _parent=""):
|
|
return PyAny()
|
|
|
|
class AnyAttribute(Simple):
|
|
|
|
def repr(self, _top=None, _sup=None, _argv=None, _child=True, _parent=""):
|
|
return PyAny()
|
|
|
|
class Attribute(Simple):
|
|
def repr(self, top=None, sup=None, _argv=None, _child=True, _parent=""):
|
|
# default, fixed, use, type
|
|
|
|
if DEBUG:
|
|
print "#ATTR", self.__dict__
|
|
|
|
external = False
|
|
name = ""
|
|
try:
|
|
(namespace, tag) = _namespace_and_tag(self, self.ref, top)
|
|
ref = True
|
|
pyname = tag
|
|
if namespace in self.xmlns_map:
|
|
if self.xmlns_map[namespace] == top.target_namespace:
|
|
name = tag
|
|
else :
|
|
external = True
|
|
name = "{%s}%s" % (self.xmlns_map[namespace], tag)
|
|
else:
|
|
if namespace == "xml":
|
|
name = "{%s}%s" % (XML_NAMESPACE, tag)
|
|
except AttributeError:
|
|
name = self.name
|
|
pyname = pyify(name)
|
|
ref = False
|
|
except ValueError: # self.ref exists but does not split into two parts
|
|
ref = True
|
|
if "" == top.target_namespace:
|
|
name = self.ref
|
|
pyname = pyify(name)
|
|
else: # referering to what
|
|
raise Exception("Strange reference: %s" % self.ref)
|
|
|
|
objekt = PyAttribute(name, pyname, external=external, root=top)
|
|
|
|
# Initial declaration
|
|
if not ref:
|
|
try:
|
|
(namespace, klass) = _namespace_and_tag(self, self.type, top)
|
|
if self.xmlns_map[namespace] == top.target_namespace:
|
|
ctyp = get_type_def(klass, top.parts)
|
|
if not ctyp.py_class:
|
|
ctyp.repr(top, sup)
|
|
objekt.type = ctyp.py_class
|
|
elif self.xmlns_map[namespace] == XMLSCHEMA:
|
|
objekt.type = klass
|
|
else:
|
|
objekt.type = self.type
|
|
except ValueError:
|
|
if self.xmlns_map[""] == top.target_namespace:
|
|
ctyp = get_type_def(self.type.replace("-","_"), top.parts)
|
|
if not ctyp.py_class:
|
|
ctyp.repr(top, sup)
|
|
objekt.type = ctyp.py_class
|
|
else:
|
|
objekt.type = self.type
|
|
except AttributeError:
|
|
objekt.type = None
|
|
try:
|
|
if self.use == "required":
|
|
objekt.required = True
|
|
except AttributeError:
|
|
pass
|
|
|
|
# in init
|
|
try:
|
|
objekt.default = self.default
|
|
except AttributeError:
|
|
pass
|
|
|
|
# attr def
|
|
try:
|
|
objekt.fixed = self.fixed
|
|
except AttributeError:
|
|
pass
|
|
|
|
if DEBUG:
|
|
print "#--ATTR py_attr:%s" % (objekt,)
|
|
|
|
return objekt
|
|
|
|
class Enumeration(Simple):
|
|
pass
|
|
|
|
class Union(Simple):
|
|
pass
|
|
|
|
class Import(Simple):
|
|
pass
|
|
|
|
class Documentation(Simple):
|
|
pass
|
|
|
|
class MaxLength(Simple):
|
|
pass
|
|
|
|
class Length(Simple):
|
|
pass
|
|
|
|
class MinInclusive(Simple):
|
|
pass
|
|
|
|
class MaxInclusive(Simple):
|
|
pass
|
|
|
|
class MinExclusive(Simple):
|
|
pass
|
|
|
|
class MaxExclusive(Simple):
|
|
pass
|
|
|
|
class List(Simple):
|
|
pass
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
def sequence(elem):
|
|
return [evaluate(child.tag, child) for child in elem]
|
|
|
|
def name_or_ref(elem, top):
|
|
try:
|
|
(namespace, name) = _namespace_and_tag(elem, elem.ref, top)
|
|
if namespace and elem.xmlns_map[namespace] == top.target_namespace:
|
|
return name
|
|
else:
|
|
return elem.ref
|
|
except AttributeError:
|
|
return elem.name
|
|
|
|
class Complex(object):
|
|
def __init__(self, elem):
|
|
self.value_of = ""
|
|
self.parts = []
|
|
self._own = []
|
|
self._inherited = []
|
|
self._generated = False
|
|
self.py_class = None
|
|
self.properties = []
|
|
# From Elementtree
|
|
self.ref = None
|
|
self.type = None
|
|
self.xmlns_map = []
|
|
self.maxOccurs = 1
|
|
self.minOccurs = 1
|
|
self.base = None
|
|
self.scoped = False
|
|
self.abstract = False
|
|
|
|
for attribute, value in elem.attrib.iteritems():
|
|
self.__setattr__(attribute, value)
|
|
|
|
try:
|
|
if elem.text.strip():
|
|
self.value_of = elem.text.strip()
|
|
except AttributeError:
|
|
pass
|
|
|
|
self.do_child(elem)
|
|
|
|
try:
|
|
self.name = self.name.replace("-","_")
|
|
except AttributeError:
|
|
pass
|
|
|
|
def _extend(self, top, sup, argv=None, parent="", base=""):
|
|
argv_copy = sd_copy(argv)
|
|
for part in self.parts:
|
|
(own, inh) = part.collect(top, sup, argv_copy, parent)
|
|
if own and base:
|
|
if len(own) == 1 and isinstance(own[0], PyAttribute):
|
|
own[0].base = base
|
|
self._own.extend(own)
|
|
self._inherited.extend(inh)
|
|
|
|
def collect(self, top, sup, argv=None, parent=""):
|
|
if self._own or self._inherited:
|
|
return self._own, self._inherited
|
|
|
|
if DEBUG:
|
|
print self.__dict__
|
|
print "#-- %d parts" % len(self.parts)
|
|
|
|
self._extend(top, sup, argv, parent)
|
|
|
|
return self._own, self._inherited
|
|
|
|
def do_child(self, elem):
|
|
for child in elem:
|
|
self.parts.append(evaluate(child.tag, child))
|
|
|
|
def elements(self, top):
|
|
res = []
|
|
# try:
|
|
# string = "== %s (%s)" % (self.name,self.__class__)
|
|
# except AttributeError:
|
|
# string = "== (%s)" % (self.__class__,)
|
|
# print string
|
|
for part in self.parts:
|
|
if isinstance(part, Element):
|
|
res.append(name_or_ref(part, top))
|
|
else:
|
|
if isinstance(part, Extension):
|
|
res.append(part.base)
|
|
res.extend(part.elements(top))
|
|
|
|
return res
|
|
|
|
def repr(self, _top=None, _sup=None, _argv=None, _child=True, parent=""):
|
|
return None
|
|
|
|
def significant_parts(self):
|
|
res = []
|
|
for p in self.parts:
|
|
if isinstance(p, Annotation):
|
|
continue
|
|
else:
|
|
res.append(p)
|
|
|
|
return res
|
|
|
|
def min_max(cls, objekt, argv):
|
|
try:
|
|
objekt.max = argv["maxOccurs"]
|
|
if cls.maxOccurs != 1:
|
|
objekt.max = cls.maxOccurs
|
|
except (KeyError, TypeError):
|
|
objekt.max = cls.maxOccurs
|
|
|
|
try:
|
|
objekt.min = argv["minOccurs"]
|
|
if cls.minOccurs != 1:
|
|
objekt.min = cls.minOccurs
|
|
except (KeyError, TypeError):
|
|
objekt.min = cls.minOccurs
|
|
|
|
|
|
class Element(Complex):
|
|
def __str__(self):
|
|
return "%s" % (self.__dict__,)
|
|
|
|
def klass(self, top):
|
|
xns = None
|
|
ctyp = None
|
|
ref = False
|
|
try:
|
|
(namespace, name) = _namespace_and_tag(self, self.ref, top)
|
|
ref = True
|
|
except AttributeError:
|
|
try:
|
|
(namespace, name) = self.type.split(":")
|
|
except ValueError:
|
|
namespace = None
|
|
name = self.type
|
|
except AttributeError:
|
|
namespace = name = None
|
|
|
|
if self.xmlns_map[namespace] == top.target_namespace:
|
|
ctyp = get_type_def(name, top.parts)
|
|
else:
|
|
xns = namespace
|
|
|
|
return namespace, name, ctyp, xns, ref
|
|
|
|
def collect(self, top, sup, argv=None, parent=""):
|
|
""" means this element is part of a larger object, hence a property of
|
|
that object """
|
|
|
|
try:
|
|
argv_copy = sd_copy(argv)
|
|
return [self.repr(top, sup, argv_copy, parent=parent)], []
|
|
except AttributeError, exc:
|
|
print "#!!!!", exc
|
|
return [], []
|
|
|
|
def elements(self, top):
|
|
(_namespace, name, ctyp, xns, _) = self.klass(top)
|
|
if ctyp:
|
|
return ctyp.elements(top)
|
|
elif xns:
|
|
return ["%s.%s" % (xns, name)]
|
|
else:
|
|
return []
|
|
|
|
def repr(self, top=None, sup=None, argv=None, child=True, parent=""):
|
|
#<element ref='xenc:ReferenceList' ...
|
|
#<element name='Transforms' type='xenc:TransformsType' ...
|
|
#<element name='CarriedKeyName' type='string' ...
|
|
#<element name="RecipientKeyInfo" type="ds:KeyInfoType" ...
|
|
#<element name='ReferenceList'>
|
|
|
|
if self.py_class:
|
|
return self.py_class
|
|
|
|
try:
|
|
myname = self.name
|
|
except AttributeError:
|
|
myname = ""
|
|
|
|
if DEBUG:
|
|
print "#Element.repr '%s' (child=%s) [%s]" % (myname,
|
|
child,
|
|
self._generated)
|
|
|
|
self.py_class = objekt = PyElement(myname, root=top)
|
|
min_max(self, objekt, argv)
|
|
|
|
try:
|
|
(namespace, superkl) = _namespace_and_tag(self, self.ref, top)
|
|
# internal or external reference
|
|
if not myname:
|
|
objekt.name = superkl
|
|
objekt.pyname = pyify(superkl)
|
|
if self.xmlns_map[namespace] == top.target_namespace:
|
|
objekt.ref = superkl
|
|
else:
|
|
objekt.ref = (namespace, superkl)
|
|
except AttributeError, exc:
|
|
if DEBUG:
|
|
print "#===>", exc
|
|
|
|
typ = self.type
|
|
|
|
try:
|
|
(namespace, klass) = _namespace_and_tag(self, typ, top)
|
|
if self.xmlns_map[namespace] == top.target_namespace:
|
|
objekt.type = (None, klass)
|
|
elif self.xmlns_map[namespace] == XMLSCHEMA:
|
|
objekt.type = klass
|
|
objekt.value_type = {"base": klass}
|
|
else:
|
|
objekt.type = (namespace, klass)
|
|
|
|
except ValueError:
|
|
objekt.type = typ
|
|
objekt.value_type = {"base": typ}
|
|
|
|
except AttributeError, exc:
|
|
# neither type nor reference, definitely local
|
|
if hasattr(self, "parts"):
|
|
if len(self.parts) == 1:
|
|
if isinstance(self.parts[0], ComplexType) or \
|
|
isinstance(self.parts[0], SimpleType):
|
|
self.parts[0].name = self.name
|
|
objekt.type = self.parts[0].repr(top, sup,
|
|
parent=self.name)
|
|
objekt.scoped = True
|
|
elif len(self.parts) == 2:# One child might be Annotation
|
|
if isinstance(self.parts[0], Annotation):
|
|
self.parts[1].name = self.name
|
|
objekt.type = self.parts[1].repr(top, sup,
|
|
parent=self.name)
|
|
objekt.scoped = True
|
|
elif isinstance(self.parts[1], Annotation):
|
|
self.parts[0].name = self.name
|
|
objekt.type = self.parts[0].repr(top, sup,
|
|
parent=self.name)
|
|
objekt.scoped = True
|
|
else:
|
|
if DEBUG:
|
|
print "$", self
|
|
raise
|
|
|
|
if parent:
|
|
objekt.class_name = "%s_%s" % (
|
|
leading_uppercase(parent),
|
|
objekt.name)
|
|
objekt.scoped = True
|
|
|
|
return objekt
|
|
|
|
class SimpleType(Complex):
|
|
def repr(self, top=None, _sup=None, _argv=None, _child=True, parent=""):
|
|
if self.py_class:
|
|
return self.py_class
|
|
|
|
obj = PyType(self.name, root=top)
|
|
try:
|
|
if len(self.parts) == 1:
|
|
part = self.parts[0]
|
|
if isinstance(part, Restriction):
|
|
if part.parts:
|
|
if isinstance(part.parts[0], Enumeration):
|
|
lista = [p.value for p in part.parts]
|
|
obj.value_type = {"base":part.base,
|
|
"enumeration":lista}
|
|
elif isinstance(part.parts[0], MaxLength):
|
|
obj.value_type = {"base":part.base,
|
|
"maxlen":part.parts[0].value}
|
|
elif isinstance(part.parts[0], Length):
|
|
obj.value_type = {"base":part.base,
|
|
"len":part.parts[0].value}
|
|
else:
|
|
obj.value_type = {"base":part.base}
|
|
elif isinstance(part, List):
|
|
if part.itemType:
|
|
obj.value_type = {"base":"list", "member":part.itemType}
|
|
except ValueError:
|
|
pass
|
|
|
|
self.py_class = obj
|
|
return obj
|
|
|
|
class Sequence(Complex):
|
|
def collect(self, top, sup, argv=None, parent=""):
|
|
argv_copy = sd_copy(argv)
|
|
for key, val in self.__dict__.items():
|
|
if key not in ['xmlns_map'] and not key.startswith("_"):
|
|
argv_copy[key] = val
|
|
|
|
if DEBUG:
|
|
print "#Sequence: %s" % argv
|
|
return Complex.collect(self, top, sup, argv_copy, parent)
|
|
|
|
class SimpleContent(Complex):
|
|
pass
|
|
|
|
class ComplexContent(Complex):
|
|
pass
|
|
|
|
class Key(Complex):
|
|
pass
|
|
|
|
class Extension(Complex):
|
|
def collect(self, top, sup, argv=None, parent=""):
|
|
if self._own or self._inherited:
|
|
return self._own, self._inherited
|
|
|
|
if DEBUG:
|
|
print "#!!!", self.__dict__
|
|
|
|
try:
|
|
base = self.base
|
|
(namespace, tag) = _namespace_and_tag(self, base, top)
|
|
|
|
if self.xmlns_map[namespace] == top.target_namespace:
|
|
cti = get_type_def(tag, top.parts)
|
|
if not cti.py_class:
|
|
cti.repr(top, sup)
|
|
#print "#EXT..",ct._collection
|
|
self._inherited = cti.py_class.properties[0][:]
|
|
self._inherited.extend(cti.py_class.properties[1])
|
|
elif self.xmlns_map[namespace] == XMLSCHEMA:
|
|
base = tag
|
|
else:
|
|
iattr = _import_attrs(top.modul[namespace], tag, top)
|
|
#print "#EXT..-", ia
|
|
self._inherited = iattr
|
|
except (AttributeError, ValueError):
|
|
pass
|
|
|
|
self._extend(top, sup, argv, parent, base)
|
|
|
|
return self._own, self._inherited
|
|
|
|
class Choice(Complex):
|
|
def collect(self, top, sup, argv=None, parent=""):
|
|
argv_copy = sd_copy(argv)
|
|
for key, val in self.__dict__.items():
|
|
if key not in ['xmlns_map'] and not key.startswith("_"):
|
|
argv_copy[key] = val
|
|
|
|
# A choice means each element may not be part of the choice
|
|
argv_copy["minOccurs"] = 0
|
|
|
|
if DEBUG:
|
|
print "#Choice: %s" % argv
|
|
return Complex.collect(self, top, sup, argv_copy, parent=parent)
|
|
|
|
class Restriction(Complex):
|
|
pass
|
|
# if isinstance(self.parts[0], Enumeration):
|
|
# values = [enum.value for enum in self.parts]
|
|
|
|
class ComplexType(Complex):
|
|
def repr(self, top=None, sup=None, _argv=None, _child=True, parent=""):
|
|
if self.py_class:
|
|
return self.py_class
|
|
|
|
# looking for a pattern here
|
|
significant_parts = self.significant_parts()
|
|
value_type = ""
|
|
if len(significant_parts) == 1:
|
|
if isinstance(significant_parts[0], ComplexContent) or \
|
|
isinstance(significant_parts[0], SimpleContent):
|
|
cci = significant_parts[0]
|
|
if len(cci.parts) == 1:
|
|
if isinstance(cci.parts[0], Extension):
|
|
ext = cci.parts[0]
|
|
|
|
(namespace, name) = _namespace_and_tag(ext, ext.base,
|
|
top)
|
|
|
|
if ext.xmlns_map[namespace] == top.target_namespace:
|
|
new_sup = name
|
|
cti = get_type_def(new_sup, top.parts)
|
|
if cti and not cti.py_class:
|
|
cti.repr(top, sup)
|
|
elif ext.xmlns_map[namespace] == XMLSCHEMA:
|
|
new_sup = None
|
|
value_type = name
|
|
else:
|
|
new_sup = "%s.%s" % (namespace, name)
|
|
|
|
#print "#Superior: %s" % new_sup
|
|
if new_sup:
|
|
sup = new_sup
|
|
else:
|
|
#print "#>>", self.parts[0].__class__
|
|
pass
|
|
|
|
try:
|
|
self.py_class = PyType(self.name, superior=sup,
|
|
namespace=top.target_namespace, root=top)
|
|
except AttributeError: # No name
|
|
self.py_class = PyType("", superior=sup,
|
|
namespace=top.target_namespace, root=top)
|
|
|
|
try:
|
|
self.py_class.abstract = self.abstract
|
|
except AttributeError:
|
|
pass
|
|
|
|
if value_type:
|
|
self.py_class.value_type = {"base": value_type}
|
|
|
|
try:
|
|
if not parent:
|
|
try:
|
|
parent = self.name
|
|
except AttributeError:
|
|
parent = ""
|
|
|
|
self.py_class.properties = self.collect(top, sup, parent=parent)
|
|
except ValueError:
|
|
pass
|
|
|
|
return self.py_class
|
|
|
|
class Annotation(Complex):
|
|
pass
|
|
|
|
class All(Complex):
|
|
pass
|
|
|
|
class Group(Complex):
|
|
def collect(self, top, sup, argv=None, parent=""):
|
|
""" means this element is part of a larger object, hence a property of
|
|
that object """
|
|
|
|
try:
|
|
#objekt = PyGroup("", root=top)
|
|
(namespace, tag) = _namespace_and_tag(self, self.ref, top)
|
|
|
|
try:
|
|
if self.xmlns_map[namespace] == top.target_namespace:
|
|
cti = get_type_def(tag, top.parts)
|
|
try:
|
|
return cti.py_class.properties
|
|
except ValueError:
|
|
return cti.collect(top, sup)
|
|
else:
|
|
raise Exception(
|
|
"Reference to group in other XSD file, not supported")
|
|
except KeyError:
|
|
raise Exception("Missing namespace definition")
|
|
except AttributeError, exc:
|
|
print "#!!!!", exc
|
|
return [], []
|
|
|
|
def repr(self, top=None, sup=None, argv=None, _child=True, parent=""):
|
|
if self.py_class:
|
|
return self.py_class
|
|
|
|
self.py_class = objekt = PyGroup(self.name, root=top)
|
|
|
|
min_max(self, objekt, argv)
|
|
|
|
try:
|
|
self._extend(top, sup, argv)
|
|
objekt.properties = (self._own, self._inherited)
|
|
except ValueError:
|
|
pass
|
|
|
|
return objekt
|
|
|
|
class Unique(Complex):
|
|
pass
|
|
|
|
class Selector(Complex):
|
|
pass
|
|
|
|
class Field(Complex):
|
|
pass
|
|
|
|
class AttributeGroup(Complex):
|
|
def collect(self, top, sup, argv=None, parent=""):
|
|
try:
|
|
(_namespace, typ) = _namespace_and_tag(self, self.ref, top)
|
|
# TODO: use definitions in other XSD
|
|
cti = get_type_def(typ, top.parts)
|
|
return cti.collect(top, sup)
|
|
except AttributeError:
|
|
if self._own or self._inherited:
|
|
return self._own, self._inherited
|
|
|
|
argv_copy = sd_copy(argv)
|
|
|
|
for prop in self.parts:
|
|
if isinstance(prop, Attribute):
|
|
self._own.append(prop.repr(top, sup, argv_copy, parent))
|
|
|
|
return self._own, self._inherited
|
|
|
|
def repr(self, top=None, sup=None, _argv=None, _child=True, parent=""):
|
|
if self.py_class:
|
|
return self.py_class
|
|
|
|
self.py_class = PyAttributeGroup(self.name, root=top)
|
|
|
|
try:
|
|
self.py_class.properties = self.collect(top, sup)
|
|
except ValueError:
|
|
pass
|
|
|
|
return self.py_class
|
|
|
|
def pyify_0(name):
|
|
res = ""
|
|
match = re.match(
|
|
r"^(([A-Z])[a-z]+)(([A-Z])[a-z]+)?(([A-Z])[a-z]+)?(([A-Z])[a-z]+)?",
|
|
name)
|
|
res += match.group(1).lower()
|
|
for num in range(3, len(match.groups()), 2):
|
|
try:
|
|
res += "_"+match.group(num+1).lower()+match.group(num)[1:]
|
|
except AttributeError:
|
|
break
|
|
|
|
res = res.replace("-","_")
|
|
if res in ["class"]:
|
|
res += "_"
|
|
return res
|
|
|
|
def pyify(name):
|
|
# AssertionIDRef
|
|
res = []
|
|
|
|
upc = []
|
|
pre = ""
|
|
for char in name:
|
|
if char >= "A" and char <= "Z":
|
|
upc.append(char)
|
|
elif char == "-":
|
|
upc.append("_")
|
|
else:
|
|
if upc:
|
|
if len(upc) == 1:
|
|
res.append(pre+upc[0].lower())
|
|
else:
|
|
if pre:
|
|
res.append(pre)
|
|
for uch in upc[:-1]:
|
|
res.append(uch.lower())
|
|
res.append("_"+upc[-1].lower())
|
|
|
|
upc = []
|
|
res.append(char)
|
|
pre = "_"
|
|
if upc:
|
|
if len(upc) == len(name):
|
|
return name.lower()
|
|
else:
|
|
res.append("_"+("".join(upc).lower()))
|
|
|
|
return "".join(res)
|
|
|
|
def get_type_def( typ, defs):
|
|
for cdef in defs:
|
|
try:
|
|
if cdef.name == typ:
|
|
return cdef
|
|
except AttributeError:
|
|
pass
|
|
return None
|
|
|
|
|
|
def sort_elements(els):
|
|
res = []
|
|
|
|
diff = False
|
|
for key, val in els.items():
|
|
if not val:
|
|
res.append(key)
|
|
del els[key]
|
|
diff = True
|
|
|
|
res.sort()
|
|
while diff:
|
|
diff = False
|
|
for key, val in els.items():
|
|
pres = [v for v in val if v not in res and ':' not in v]
|
|
els[key] = pres
|
|
if pres != val:
|
|
diff = True
|
|
|
|
#print els
|
|
partres = []
|
|
for key, val in els.items():
|
|
if not val:
|
|
partres.append(key)
|
|
del els[key]
|
|
diff = True
|
|
partres.sort()
|
|
res.extend(partres)
|
|
|
|
return res, els
|
|
|
|
def output(elem, target_namespace, eldict, ignore=None):
|
|
done = 0
|
|
|
|
if ignore is None:
|
|
ignore = []
|
|
|
|
try:
|
|
(preps, text) = elem.text(target_namespace, eldict, False, ignore)
|
|
except TypeError:
|
|
return done
|
|
except MissingPrerequisite:
|
|
return done
|
|
|
|
for prep in preps:
|
|
if prep:
|
|
done = 1
|
|
if isinstance(prep, basestring):
|
|
print prep
|
|
else:
|
|
for item in prep:
|
|
print item
|
|
print
|
|
print
|
|
|
|
if text:
|
|
done = 1
|
|
elem.done = True
|
|
print text
|
|
print
|
|
|
|
return done
|
|
|
|
def intro():
|
|
print """#!/usr/bin/env python
|
|
|
|
#
|
|
# Generated %s by parse_xsd.py version %s.
|
|
#
|
|
|
|
import saml2
|
|
from saml2 import SamlBase
|
|
""" % (time.ctime(), __version__)
|
|
|
|
#NAMESPACE = 'http://www.w3.org/2000/09/xmldsig#'
|
|
|
|
def block_items(objekt, block, eldict):
|
|
if objekt not in block:
|
|
if isinstance(objekt.type, PyType):
|
|
if objekt.type not in block:
|
|
block.append(objekt.type)
|
|
block.append(objekt)
|
|
if isinstance(objekt, PyType):
|
|
others = [p for p in eldict.values() if isinstance(p,
|
|
PyElement) and p.type[1] == objekt.name]
|
|
for item in others:
|
|
if item not in block:
|
|
block.append(item)
|
|
return block
|
|
|
|
|
|
def find_parent(elm, eldict):
|
|
if isinstance(elm, PyElement):
|
|
if elm.type:
|
|
sup = eldict[elm.type[1]]
|
|
return find_parent(sup, eldict)
|
|
elif elm.ref:
|
|
sup = eldict[elm.ref]
|
|
return find_parent(sup, eldict)
|
|
else:
|
|
if elm.superior:
|
|
sup = eldict[elm.superior[0]]
|
|
if sup.done:
|
|
return elm
|
|
return find_parent(sup, eldict)
|
|
|
|
return elm
|
|
|
|
class Schema(Complex):
|
|
|
|
def __init__(self, elem, impo, add, modul, defs):
|
|
Complex.__init__(self, elem)
|
|
self.impo = impo
|
|
self.add = add
|
|
self.modul = modul
|
|
self.py_elements = {}
|
|
self.py_attributes = {}
|
|
self.elems = []
|
|
self.attrgrp = []
|
|
self.defs = []
|
|
try:
|
|
self.target_namespace = self.targetNamespace
|
|
except AttributeError:
|
|
self.target_namespace = ""
|
|
for def_file in defs:
|
|
self.defs.append(open(def_file).read())
|
|
|
|
def _mk_list(self, objekt, alla, eldict):
|
|
tup = []
|
|
for prop in alla:
|
|
(mod, cname) = _mod_cname(prop, eldict)
|
|
|
|
if prop.max == "unbounded":
|
|
lista = True
|
|
else:
|
|
lista = False
|
|
|
|
spec = objekt.child_spec(self.target_namespace,
|
|
prop, mod, cname,
|
|
lista)
|
|
lines = ["%s.%s" % (objekt.class_name, spec)]
|
|
tup.append((prop, lines, spec))
|
|
|
|
return tup
|
|
|
|
def adjust(self, eldict, block):
|
|
udict = {}
|
|
for elem in self.elems:
|
|
if isinstance(elem, PyAttribute) or isinstance(elem, PyGroup):
|
|
elem.done = True
|
|
continue
|
|
if elem in block:
|
|
continue
|
|
if not elem.done:
|
|
udict[elem] = elem.undefined(eldict)
|
|
|
|
keys = [k.name for k in udict.keys()]
|
|
print "#", keys
|
|
res = (None, [])
|
|
if not udict:
|
|
return res
|
|
level = 1
|
|
rblocked = [p.name for p in block]
|
|
while True:
|
|
non_child = 0
|
|
for objekt, (sup, elems) in udict.items():
|
|
if sup:
|
|
continue
|
|
else:
|
|
non_child += 1
|
|
signif = []
|
|
other = []
|
|
for elem in elems:
|
|
if elem.name in keys:
|
|
signif.append(elem)
|
|
elif elem.ref in rblocked:
|
|
other.append(elem)
|
|
if len(signif) <= level:
|
|
alla = signif
|
|
alla.extend(other)
|
|
tup = self._mk_list(objekt, alla, eldict)
|
|
res = (objekt, tup)
|
|
break
|
|
if res[0]:
|
|
ref = res[0].name
|
|
tups = res[1]
|
|
for objekt, (sups, elems) in udict.items():
|
|
if sups:
|
|
for sup in sups:
|
|
if sup.name == ref:
|
|
for tup in tups:
|
|
tup[1].append("%s.%s" % (objekt.class_name,
|
|
tup[2]))
|
|
break
|
|
else:
|
|
pass
|
|
elif not non_child or level > 10:
|
|
elm = udict.keys()[0]
|
|
parent = find_parent(elm, eldict)
|
|
signif = []
|
|
other = []
|
|
tot = parent.properties[0]
|
|
tot.extend(parent.properties[1])
|
|
alla = []
|
|
for elem in tot:
|
|
if isinstance(elem, PyAttribute):
|
|
continue
|
|
else:
|
|
alla.append(elem)
|
|
tup = self._mk_list(parent, alla, eldict)
|
|
res = (parent, tup)
|
|
|
|
if res[0]:
|
|
break
|
|
else:
|
|
level += 1
|
|
return res
|
|
|
|
def _do(self, eldict):
|
|
not_done = 1
|
|
undone = 0
|
|
while not_done:
|
|
not_done = 0
|
|
undone = 0
|
|
for elem in self.elems:
|
|
if isinstance(elem, PyGroup) or elem.done:
|
|
continue
|
|
undone += 1
|
|
not_done += output(elem, self.target_namespace, eldict)
|
|
return undone
|
|
|
|
def _element_from_string(self):
|
|
print "ELEMENT_FROM_STRING = {"
|
|
for elem in self.elems:
|
|
if isinstance(elem, PyAttribute) or isinstance(elem, PyGroup):
|
|
continue
|
|
if elem.abstract:
|
|
continue
|
|
print "%s%s.c_tag: %s_from_string," % (INDENT, elem.class_name,
|
|
pyify(elem.class_name))
|
|
print "}"
|
|
print
|
|
|
|
def _element_by_tag(self):
|
|
print "ELEMENT_BY_TAG = {"
|
|
listed = []
|
|
for elem in self.elems:
|
|
if isinstance(elem, PyAttribute) or isinstance(elem, PyGroup):
|
|
continue
|
|
if elem.abstract:
|
|
continue
|
|
lcen = elem.name
|
|
print "%s'%s': %s," % (INDENT, lcen, elem.class_name)
|
|
listed.append(lcen)
|
|
for elem in self.elems:
|
|
if isinstance(elem, PyAttribute) or isinstance(elem, PyGroup):
|
|
continue
|
|
lcen = elem.name
|
|
if elem.abstract and lcen not in listed:
|
|
print "%s'%s': %s," % (INDENT, lcen, elem.class_name)
|
|
listed.append(lcen)
|
|
print "}"
|
|
print
|
|
|
|
def out(self):
|
|
for part in self.parts:
|
|
if isinstance(part, Import):
|
|
continue
|
|
if part is None:
|
|
continue
|
|
|
|
elem = part.repr(self, "", {}, False)
|
|
if elem:
|
|
if isinstance(elem, PyAttributeGroup):
|
|
self.attrgrp.append(elem)
|
|
else:
|
|
self.elems.append(elem)
|
|
|
|
eldict = {}
|
|
for elem in self.elems:
|
|
eldict[elem.name] = elem
|
|
|
|
#print eldict.keys()
|
|
|
|
intro()
|
|
for modul in self.add:
|
|
print "from %s import *" % modul
|
|
for _namespace, (mod, namn) in self.impo.items():
|
|
if namn:
|
|
print "import %s as %s" % (mod, namn)
|
|
print
|
|
print "NAMESPACE = '%s'" % self.target_namespace
|
|
print
|
|
|
|
for defs in self.defs:
|
|
print defs
|
|
print
|
|
|
|
exceptions = []
|
|
block = []
|
|
while self._do(eldict):
|
|
print "#.................."
|
|
(objekt, tups) = self.adjust(eldict, block)
|
|
if not objekt:
|
|
break
|
|
ignore = [p.name for (p, _l, _s) in tups]
|
|
done = output(objekt, self.target_namespace, eldict, ignore)
|
|
if done:
|
|
for (prop, lines, _) in tups:
|
|
exceptions.extend(lines)
|
|
block = []
|
|
else:
|
|
block = block_items(objekt, block, eldict)
|
|
|
|
if exceptions:
|
|
print "#", 70*'+'
|
|
for line in exceptions:
|
|
print line
|
|
print "#", 70*'+'
|
|
print
|
|
|
|
for attrgrp in self.attrgrp:
|
|
print "AG_%s = [" % attrgrp.name
|
|
for prop in attrgrp.properties[0]:
|
|
if isinstance(prop.type, PyObj):
|
|
print "%s('%s', %s_, %s)," % (INDENT, prop.name,
|
|
prop.type.name, prop.required)
|
|
else:
|
|
print "%s('%s', '%s', %s)," % (INDENT, prop.name,
|
|
prop.type, prop.required)
|
|
print "]"
|
|
print
|
|
|
|
self._element_from_string()
|
|
self._element_by_tag()
|
|
print
|
|
print "def factory(tag, **kwargs):"
|
|
print " return ELEMENT_BY_TAG[tag](**kwargs)"
|
|
print
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
NAMESPACE_BASE = ["http://www.w3.org/2001/XMLSchema",
|
|
"http://www.w3.org/2000/10/XMLSchema"]
|
|
|
|
_MAP = {
|
|
"element": Element,
|
|
"complexType": ComplexType,
|
|
"sequence": Sequence,
|
|
"any": Any,
|
|
"all": All,
|
|
"anyAttribute": AnyAttribute,
|
|
"simpleContent": SimpleContent,
|
|
"extension": Extension,
|
|
"union": Union,
|
|
"restriction": Restriction,
|
|
"enumeration": Enumeration,
|
|
"import": Import,
|
|
"annotation": Annotation,
|
|
"attributeGroup":AttributeGroup,
|
|
"attribute":Attribute,
|
|
"choice": Choice,
|
|
"complexContent": ComplexContent,
|
|
"documentation": Documentation,
|
|
"simpleType": SimpleType,
|
|
"maxLength": MaxLength,
|
|
"list": List,
|
|
"unique": Unique,
|
|
"group": Group,
|
|
"selector": Selector,
|
|
"field": Field,
|
|
"key": Key,
|
|
}
|
|
|
|
ELEMENTFUNCTION = {}
|
|
|
|
for nsp in NAMESPACE_BASE:
|
|
for nskey, func in _MAP.items():
|
|
ELEMENTFUNCTION["{%s}%s" % (nsp, nskey)] = func
|
|
|
|
|
|
def evaluate(typ, elem):
|
|
try:
|
|
return ELEMENTFUNCTION[typ](elem)
|
|
except KeyError:
|
|
print "Unknown type", typ
|
|
|
|
|
|
NS_MAP = "xmlns_map"
|
|
|
|
def parse_nsmap(fil):
|
|
|
|
events = "start", "start-ns", "end-ns"
|
|
|
|
root = None
|
|
ns_map = []
|
|
|
|
for event, elem in ElementTree.iterparse(fil, events):
|
|
if event == "start-ns":
|
|
ns_map.append(elem)
|
|
elif event == "end-ns":
|
|
ns_map.pop()
|
|
elif event == "start":
|
|
if root is None:
|
|
root = elem
|
|
elem.set(NS_MAP, dict(ns_map))
|
|
|
|
return ElementTree.ElementTree(root)
|
|
|
|
def usage():
|
|
print "Usage: parse_xsd [-i <module:as>] xsd.file > module.py"
|
|
|
|
def recursive_find_module(name, path=None):
|
|
parts = name.split(".")
|
|
|
|
mod_a = None
|
|
for part in parts:
|
|
#print "$$", part, path
|
|
try:
|
|
(fil, pathname, desc) = imp.find_module(part, path)
|
|
except ImportError:
|
|
raise
|
|
|
|
mod_a = imp.load_module(name, fil, pathname, desc)
|
|
sys.modules[name] = mod_a
|
|
path = mod_a.__path__
|
|
|
|
return mod_a
|
|
|
|
def get_mod(name, path=None):
|
|
try:
|
|
mod_a = sys.modules[name]
|
|
if not isinstance(mod_a, types.ModuleType):
|
|
raise KeyError
|
|
except KeyError:
|
|
try:
|
|
(fil, pathname, desc) = imp.find_module(name, path)
|
|
mod_a = imp.load_module(name, fil, pathname, desc)
|
|
except ImportError:
|
|
if "." in name:
|
|
mod_a = recursive_find_module(name, path)
|
|
else:
|
|
raise
|
|
sys.modules[name] = mod_a
|
|
return mod_a
|
|
|
|
def main(argv):
|
|
try:
|
|
opts, args = getopt.getopt(argv, "a:d:hi:I:",
|
|
["add=", "help", "import=", "defs="])
|
|
except getopt.GetoptError, err:
|
|
# print help information and exit:
|
|
print str(err) # will print something like "option -a not recognized"
|
|
usage()
|
|
sys.exit(2)
|
|
|
|
add = []
|
|
defs = []
|
|
impo = {}
|
|
modul = {}
|
|
ignore = []
|
|
|
|
for opt, arg in opts:
|
|
if opt in ("-a", "--add"):
|
|
add.append(arg)
|
|
elif opt in ("-d", "--defs"):
|
|
defs.append(arg)
|
|
elif opt in ("-h", "--help"):
|
|
usage()
|
|
sys.exit()
|
|
elif opt in ("-i", "--import"):
|
|
mod = get_mod(arg, ['.'])
|
|
modul[mod.NAMESPACE] = mod
|
|
impo[mod.NAMESPACE] = [arg, None]
|
|
elif opt in ("-I", "--ignore"):
|
|
ignore.append(arg)
|
|
else:
|
|
assert False, "unhandled option"
|
|
|
|
if not args:
|
|
print "No XSD-file specified"
|
|
usage()
|
|
sys.exit(2)
|
|
|
|
tree = parse_nsmap(args[0])
|
|
|
|
known = NAMESPACE_BASE[:]
|
|
known.append(XML_NAMESPACE)
|
|
for key, namespace in tree._root.attrib["xmlns_map"].items():
|
|
if namespace in known:
|
|
continue
|
|
else:
|
|
try:
|
|
modul[key] = modul[namespace]
|
|
impo[namespace][1] = key
|
|
except KeyError:
|
|
if namespace == tree._root.attrib["targetNamespace"]:
|
|
continue
|
|
elif namespace in ignore:
|
|
continue
|
|
else:
|
|
raise Exception("Undefined namespace: %s" % namespace)
|
|
|
|
schema = Schema(tree._root, impo, add, modul, defs)
|
|
|
|
#print schema.__dict__
|
|
schema.out()
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv[1:])
|
|
|
|
|