diff options
| author | Rafael H. Schloming <rhs@apache.org> | 2007-07-23 14:36:05 +0000 |
|---|---|---|
| committer | Rafael H. Schloming <rhs@apache.org> | 2007-07-23 14:36:05 +0000 |
| commit | c2c6f451dfecb3671082be6d5c37e8bb03468427 (patch) | |
| tree | b780601566a2ee8df19c6e991f98cd2abe81813d | |
| parent | 09e0292f2be2c7bf4efe69df7254ba17d342eb32 (diff) | |
| download | qpid-python-c2c6f451dfecb3671082be6d5c37e8bb03468427.tar.gz | |
Added a better XML library.
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@558742 13f79535-47bb-0310-9956-ffa450edef68
| -rw-r--r-- | python/mllib/__init__.py | 60 | ||||
| -rw-r--r-- | python/mllib/dom.py | 250 | ||||
| -rw-r--r-- | python/mllib/parsers.py | 139 | ||||
| -rw-r--r-- | python/mllib/transforms.py | 164 | ||||
| -rw-r--r-- | python/qpid/spec.py | 39 | ||||
| -rw-r--r-- | python/qpid/xmlutil.py | 119 |
6 files changed, 632 insertions, 139 deletions
diff --git a/python/mllib/__init__.py b/python/mllib/__init__.py new file mode 100644 index 0000000000..44b78126fb --- /dev/null +++ b/python/mllib/__init__.py @@ -0,0 +1,60 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +""" +This module provides document parsing and transformation utilities for +both SGML and XML. +""" + +import dom, transforms, parsers +import xml.sax, types +from cStringIO import StringIO + +def transform(node, *args): + result = node + for t in args: + if isinstance(t, types.ClassType): + t = t() + result = result.dispatch(t) + return result + +def sgml_parse(source): + if isinstance(source, basestring): + source = StringIO(source) + fname = "<string>" + elif hasattr(source, "name"): + fname = source.name + p = parsers.SGMLParser() + num = 1 + for line in source: + p.feed(line) + p.parser.line(fname, num, None) + num += 1 + p.close() + return p.parser.tree + +def xml_parse(source): + p = parsers.XMLParser() + xml.sax.parse(source, p) + return p.parser.tree + +def sexp(node): + s = transforms.Sexp() + node.dispatch(s) + return s.out diff --git a/python/mllib/dom.py b/python/mllib/dom.py new file mode 100644 index 0000000000..9b1740055b --- /dev/null +++ b/python/mllib/dom.py @@ -0,0 +1,250 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +""" +Simple DOM for both SGML and XML documents. +""" + +from transforms import Text + +class Container: + + def __init__(self): + self.children = [] + + def add(self, child): + child.parent = self + self.children.append(child) + + def extend(self, children): + for child in children: + child.parent = self + self.children.append(child) + +class Component: + + def __init__(self): + self.parent = None + + def index(self): + if self.parent: + return self.parent.children.index(self) + else: + return 0 + + def _line(self, file, line, column): + self.file = file + self.line = line + self.column = column + +class DispatchError(Exception): + + def __init__(self, scope, f): + msg = "no such attribtue" + +class Dispatcher: + + def is_type(self, type): + cls = self + while cls != None: + if cls.type == type: + return True + cls = cls.base + return False + + def dispatch(self, f): + cls = self + while cls != None: + if hasattr(f, cls.type): + return getattr(f, cls.type)(self) + else: + cls = cls.base + + cls = self + attrs = "" + while cls != None: + if attrs: + sep = ", " + if cls.base == None: + sep += "or " + else: + sep = "" + attrs += "%s'%s'" % (sep, cls.type) + cls = cls.base + + raise AttributeError("'%s' object has no attribute %s" % + (f.__class__.__name__, attrs)) + +class Node(Container, Component, Dispatcher): + + type = "node" + base = None + + def __init__(self): + Container.__init__(self) + Component.__init__(self) + self.query = Query([self]) + + def __getitem__(self, name): + for nd in self.query[name]: + return nd + + def text(self): + return self.dispatch(Text()) + + def tag(self, name, *attrs, **kwargs): + t = Tag(name, *attrs, **kwargs) + self.add(t) + return t + + def data(self, s): + d = Data(s) + self.add(d) + return d + + def entity(self, s): + e = Entity(s) + self.add(e) + return e + +class Tree(Node): + + type = "tree" + base = Node + +class Tag(Node): + + type = "tag" + base = Node + + def __init__(self, _name, *attrs, **kwargs): + Node.__init__(self) + self.name = _name + self.attrs = list(attrs) + self.attrs.extend(kwargs.items()) + self.singleton = False + + def get_attr(self, name): + for k, v in self.attrs: + if name == k: + return v + + def __getitem__(self, name): + if name and name[0] == "@": + return self.get_attr(name[1:]) + else: + for nd in self.query[name]: + return nd + return self.get_attr(name) + + def dispatch(self, f): + try: + method = getattr(f, "do_" + self.name) + except AttributeError: + return Dispatcher.dispatch(self, f) + return method(self) + +class Leaf(Component, Dispatcher): + + type = "leaf" + base = None + + def __init__(self, data): + assert isinstance(data, basestring) + self.data = data + +class Data(Leaf): + type = "data" + base = Leaf + +class Entity(Leaf): + type = "entity" + base = Leaf + +class Character(Leaf): + type = "character" + base = Leaf + +class Comment(Leaf): + type = "comment" + base = Leaf + +################### +## Query Classes ## +########################################################################### + +class View: + + def __init__(self, source): + self.source = source + +class Filter(View): + + def __init__(self, predicate, source): + View.__init__(self, source) + if callable(predicate): + self.predicate = predicate + elif predicate[0] == "#": + type = predicate[1:] + self.predicate = lambda x: x.is_type(type) + else: + self.predicate = lambda x: isinstance(x, Tag) and x.name == predicate + + def __iter__(self): + for nd in self.source: + if self.predicate(nd): yield nd + +class Flatten(View): + + def __iter__(self): + sources = [iter(self.source)] + while sources: + try: + nd = sources[-1].next() + if isinstance(nd, Tree): + sources.append(iter(nd.children)) + else: + yield nd + except StopIteration: + sources.pop() + +class Children(View): + + def __iter__(self): + for nd in self.source: + for child in nd.children: + yield child + +class Query(View): + + def __iter__(self): + for nd in self.source: + yield nd + + def __getitem__(self, predicate): + if isinstance(predicate, basestring): + path = predicate.split("/") + else: + path = [predicate] + + query = self.source + for p in path: + query = Query(Filter(p, Flatten(Children(query)))) + + return query diff --git a/python/mllib/parsers.py b/python/mllib/parsers.py new file mode 100644 index 0000000000..3e7cc10dc2 --- /dev/null +++ b/python/mllib/parsers.py @@ -0,0 +1,139 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +""" +Parsers for SGML and XML to dom. +""" + +import sgmllib, xml.sax.handler +from dom import * + +class Parser: + + def __init__(self): + self.tree = Tree() + self.node = self.tree + self.nodes = [] + + def line(self, id, lineno, colno): + while self.nodes: + n = self.nodes.pop() + n._line(id, lineno, colno) + + def add(self, node): + self.node.add(node) + self.nodes.append(node) + + def start(self, name, attrs): + tag = Tag(name, *attrs) + self.add(tag) + self.node = tag + + def end(self, name): + self.balance(name) + self.node = self.node.parent + + def data(self, data): + children = self.node.children + if children and isinstance(children[-1], Data): + children[-1].data += data + else: + self.add(Data(data)) + + def comment(self, comment): + self.add(Comment(comment)) + + def entity(self, ref): + self.add(Entity(ref)) + + def character(self, ref): + self.add(Character(ref)) + + def balance(self, name = None): + while self.node != self.tree and name != self.node.name: + self.node.parent.extend(self.node.children) + del self.node.children[:] + self.node.singleton = True + self.node = self.node.parent + + +class SGMLParser(sgmllib.SGMLParser): + + def __init__(self, entitydefs = None): + sgmllib.SGMLParser.__init__(self) + if entitydefs == None: + self.entitydefs = {} + else: + self.entitydefs = entitydefs + self.parser = Parser() + + def unknown_starttag(self, name, attrs): + self.parser.start(name, attrs) + + def handle_data(self, data): + self.parser.data(data) + + def handle_comment(self, comment): + self.parser.comment(comment) + + def unknown_entityref(self, ref): + self.parser.entity(ref) + + def unknown_charref(self, ref): + self.parser.character(ref) + + def unknown_endtag(self, name): + self.parser.end(name) + + def close(self): + sgmllib.SGMLParser.close(self) + self.parser.balance() + assert self.parser.node == self.parser.tree + +class XMLParser(xml.sax.handler.ContentHandler): + + def __init__(self): + self.parser = Parser() + self.locator = None + + def line(self): + if self.locator != None: + self.parser.line(self.locator.getSystemId(), + self.locator.getLineNumber(), + self.locator.getColumnNumber()) + + def setDocumentLocator(self, locator): + self.locator = locator + + def startElement(self, name, attrs): + self.parser.start(name, attrs.items()) + self.line() + + def endElement(self, name): + self.parser.end(name) + self.line() + + def characters(self, content): + self.parser.data(content) + self.line() + + def skippedEntity(self, name): + self.parser.entity(name) + self.line() + diff --git a/python/mllib/transforms.py b/python/mllib/transforms.py new file mode 100644 index 0000000000..bb79dcf192 --- /dev/null +++ b/python/mllib/transforms.py @@ -0,0 +1,164 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +""" +Useful transforms for dom objects. +""" + +from cStringIO import StringIO +from dom import * + +class Visitor: + + def descend(self, node): + for child in node.children: + child.dispatch(self) + + def node(self, node): + self.descend(node) + + def leaf(self, leaf): + pass + +class Identity: + + def descend(self, node): + result = [] + for child in node.children: + result.append(child.dispatch(self)) + return result + + def default(self, tag): + result = Tag(tag.name, *tag.attrs) + result.extend(self.descend(tag)) + return result + + def tree(self, tree): + result = Tree() + result.extend(self.descend(tree)) + return result + + def tag(self, tag): + return self.default(tag) + + def leaf(self, leaf): + return leaf.__class__(leaf.data) + +class Sexp(Identity): + + def __init__(self): + self.stack = [] + self.level = 0 + self.out = "" + + def open(self, s): + self.out += "(%s" % s + self.level += len(s) + 1 + self.stack.append(s) + + def line(self, s = ""): + self.out = self.out.rstrip() + self.out += "\n" + " "*self.level + s + + def close(self): + s = self.stack.pop() + self.level -= len(s) + 1 + self.out = self.out.rstrip() + self.out += ")" + + def tree(self, tree): + self.open("+ ") + for child in tree.children: + self.line(); child.dispatch(self) + self.close() + + def tag(self, tag): + self.open("Node(%s) " % tag.name) + for child in tag.children: + self.line(); child.dispatch(self) + self.close() + + def leaf(self, leaf): + self.line("%s(%s)" % (leaf.__class__.__name__, leaf.data)) + +class Output: + + def descend(self, node): + out = StringIO() + for child in node.children: + out.write(child.dispatch(self)) + return out.getvalue() + + def default(self, tag): + out = StringIO() + out.write("<%s" % tag.name) + for k, v in tag.attrs: + out.write(' %s="%s"' % (k, v)) + out.write(">") + out.write(self.descend(tag)) + if not tag.singleton: + out.write("</%s>" % tag.name) + return out.getvalue() + + def tree(self, tree): + return self.descend(tree) + + def tag(self, tag): + return self.default(tag) + + def data(self, leaf): + return leaf.data + + def entity(self, leaf): + return "&%s;" % leaf.data + + def character(self, leaf): + raise Exception("TODO") + + def comment(self, leaf): + return "<!-- %s -->" % leaf.data + +class Empty(Output): + + def tag(self, tag): + return self.descend(tag) + + def data(self, leaf): + return "" + + def entity(self, leaf): + return "" + + def character(self, leaf): + return "" + + def comment(self, leaf): + return "" + +class Text(Empty): + + def data(self, leaf): + return leaf.data + + def entity(self, leaf): + return "&%s;" % leaf.data + + def character(self, leaf): + # XXX: is this right? + return "&#%s;" % leaf.data diff --git a/python/qpid/spec.py b/python/qpid/spec.py index f8e37737e2..88faedd825 100644 --- a/python/qpid/spec.py +++ b/python/qpid/spec.py @@ -29,7 +29,7 @@ class so that the generated code can be reused in a variety of situations. """ -import re, textwrap, new, xmlutil +import re, textwrap, new, mllib class SpecContainer: @@ -275,21 +275,20 @@ class Field(Metadata): self.docs = docs def get_desc(nd): - label = nd.get("@label") + label = nd["@label"] if not label: - label = nd.text + label = nd.text() if label: label = label.strip() return label def get_docs(nd): - return [n.text for n in nd["doc"]] + return [n.text() for n in nd.query["doc"]] def load_fields(nd, l, domains): - for f_nd in nd["field"]: - try: - type = f_nd["@domain"] - except KeyError: + for f_nd in nd.query["field"]: + type = f_nd["@domain"] + if type == None: type = f_nd["@type"] type = pythonize(type) domain = None @@ -300,27 +299,27 @@ def load_fields(nd, l, domains): get_desc(f_nd), get_docs(f_nd))) def load(specfile, *errata): - doc = xmlutil.parse(specfile) - spec_root = doc["amqp"][0] + doc = mllib.xml_parse(specfile) + spec_root = doc["amqp"] spec = Spec(int(spec_root["@major"]), int(spec_root["@minor"]), specfile) - for root in [spec_root] + map(lambda x: xmlutil.parse(x)["amqp"][0], errata): + for root in [spec_root] + map(lambda x: mllib.xml_parse(x)["amqp"], errata): # constants - for nd in root["constant"]: + for nd in root.query["constant"]: const = Constant(spec, pythonize(nd["@name"]), int(nd["@value"]), - nd.get("@class"), get_docs(nd)) + nd["@class"], get_docs(nd)) try: spec.constants.add(const) - except ValueError, e: + except ValueError, e: print "Warning:", e # domains are typedefs - for nd in root["domain"]: + for nd in root.query["domain"]: spec.domains.add(Domain(spec, nd.index(), pythonize(nd["@name"]), pythonize(nd["@type"]), get_desc(nd), get_docs(nd))) # classes - for c_nd in root["class"]: + for c_nd in root.query["class"]: cname = pythonize(c_nd["@name"]) if spec.classes.byname.has_key(cname): klass = spec.classes.byname[cname] @@ -331,16 +330,16 @@ def load(specfile, *errata): added_methods = [] load_fields(c_nd, klass.fields, spec.domains.byname) - for m_nd in c_nd["method"]: + for m_nd in c_nd.query["method"]: mname = pythonize(m_nd["@name"]) if klass.methods.byname.has_key(mname): meth = klass.methods.byname[mname] else: meth = Method(klass, mname, int(m_nd["@index"]), - m_nd.get_bool("@content", False), - [pythonize(nd["@name"]) for nd in m_nd["response"]], - m_nd.get_bool("@synchronous", False), + m_nd["@content"] == "1", + [pythonize(nd["@name"]) for nd in m_nd.query["response"]], + m_nd["@synchronous"] == "1", get_desc(m_nd), get_docs(m_nd)) klass.methods.add(meth) diff --git a/python/qpid/xmlutil.py b/python/qpid/xmlutil.py deleted file mode 100644 index 585516b44f..0000000000 --- a/python/qpid/xmlutil.py +++ /dev/null @@ -1,119 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -""" -XML utilities used by spec.py -""" - -import xml.sax -from xml.sax.handler import ContentHandler - -def parse(file): - doc = Node("root") - xml.sax.parse(file, Builder(doc)) - return doc - -class Node: - - def __init__(self, name, attrs = None, text = None, parent = None): - self.name = name - self.attrs = attrs - self.text = text - self.parent = parent - self.children = [] - if parent != None: - parent.children.append(self) - - def get_bool(self, key, default = False): - v = self.get(key) - if v == None: - return default - else: - return bool(int(v)) - - def index(self): - if self.parent: - return self.parent.children.index(self) - else: - return 0 - - def has(self, key): - try: - result = self[key] - return True - except KeyError: - return False - except IndexError: - return False - - def get(self, key, default = None): - if self.has(key): - return self[key] - else: - return default - - def __getitem__(self, key): - if callable(key): - return filter(key, self.children) - else: - t = key.__class__ - meth = "__get%s__" % t.__name__ - if hasattr(self, meth): - return getattr(self, meth)(key) - else: - raise KeyError(key) - - def __getstr__(self, name): - if name[:1] == "@": - return self.attrs[name[1:]] - else: - return self[lambda nd: nd.name == name] - - def __getint__(self, index): - return self.children[index] - - def __iter__(self): - return iter(self.children) - - def path(self): - if self.parent == None: - return "/%s" % self.name - else: - return "%s/%s" % (self.parent.path(), self.name) - -class Builder(ContentHandler): - - def __init__(self, start = None): - self.node = start - - def __setitem__(self, element, type): - self.types[element] = type - - def startElement(self, name, attrs): - self.node = Node(name, attrs, None, self.node) - - def endElement(self, name): - self.node = self.node.parent - - def characters(self, content): - if self.node.text == None: - self.node.text = content - else: - self.node.text += content - |
