This is an automated email from the ASF dual-hosted git repository.
astitcher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-proton.git
The following commit(s) were added to refs/heads/main by this push:
new 7c08f80ed PROTON-2844: [Python tooling] Eliminate use of mllib
7c08f80ed is described below
commit 7c08f80ed69745a4504b0c3e6325a4ae6baf2b3c
Author: Andrew Stitcher <[email protected]>
AuthorDate: Fri Aug 2 11:52:07 2024 -0400
PROTON-2844: [Python tooling] Eliminate use of mllib
Python has good enough suppport for xml in the standard library
so there is no longer any need to use a home built solution.
---
c/src/encodings.h.py | 21 +--
c/src/protocol.h.py | 14 +-
c/src/protocol.py | 48 +++---
tools/python/mllib/__init__.py | 76 ---------
tools/python/mllib/dom.py | 331 ---------------------------------------
tools/python/mllib/parsers.py | 105 -------------
tools/python/mllib/transforms.py | 171 --------------------
7 files changed, 40 insertions(+), 726 deletions(-)
diff --git a/c/src/encodings.h.py b/c/src/encodings.h.py
index f03f3a288..33e9e04b3 100644
--- a/c/src/encodings.h.py
+++ b/c/src/encodings.h.py
@@ -18,13 +18,13 @@
# under the License.
#
-import mllib
-import optparse
import os
-import sys
+import xml.etree.ElementTree as ET
+ns = {'amqp': 'http://www.amqp.org/schema/amqp.xsd'}
xml = os.path.join(os.path.dirname(__file__), "types.xml")
-doc = mllib.xml_parse(xml)
+doc = ET.parse(xml).getroot()
+
print("/* generated from %s */" % xml)
print("#ifndef _PROTON_ENCODINGS_H")
@@ -32,13 +32,14 @@ print("#define _PROTON_ENCODINGS_H 1")
print()
print("#define PNE_DESCRIPTOR (0x00)")
-for enc in doc.query["amqp/section/type/encoding"]:
- name = enc["@name"] or enc.parent["@name"]
- # XXX: a bit hacky
- if name == "ieee-754":
- name = enc.parent["@name"]
+types = doc.findall('./amqp:section/amqp:type', ns)
+encodings = [(t.attrib['name'], e) for t in types for e in
t.findall('./amqp:encoding', ns)]
+for parentname, enc in encodings:
+ name = enc.attrib.get("name")
+ if not name or name == "ieee-754":
+ name = parentname
cname = "PNE_" + name.replace("-", "_").upper()
- print("#define %s%s(%s)" % (cname, " " * (20 - len(cname)), enc["@code"]))
+ print("#define %s%s(%s)" % (cname, " " * (20 - len(cname)),
enc.attrib["code"]))
print()
print("#endif /* encodings.h */")
diff --git a/c/src/protocol.h.py b/c/src/protocol.h.py
index a6a789e70..620d72917 100644
--- a/c/src/protocol.h.py
+++ b/c/src/protocol.h.py
@@ -30,10 +30,10 @@ fields = {}
for type in TYPES:
fidx = 0
- for f in type.query["field"]:
+ for f in type.findall("amqp:field", ns):
print("#define %s_%s (%s)" % (field_kw(type), field_kw(f), fidx))
fidx += 1
- d = f["@default"]
+ d = f.attrib.get("default")
if d:
ft = ftype(f)
# Don't bother to emit a boolean default that is False
@@ -51,13 +51,13 @@ for type in TYPES:
idx = 0
for type in TYPES:
- desc = type["descriptor"]
- name = type["@name"].upper().replace("-", "_")
- print("#define %s_SYM (\"%s\")" % (name, desc["@name"]))
- hi, lo = [int(x, 0) for x in desc["@code"].split(":")]
+ desc = type.find("./amqp:descriptor", ns)
+ name = type.attrib["name"].upper().replace("-", "_")
+ print("#define %s_SYM (\"%s\")" % (name, desc.attrib["name"]))
+ hi, lo = [int(x, 0) for x in desc.attrib["code"].split(":")]
code = (hi << 32) + lo
print("#define %s ((uint64_t) %s)" % (name, code))
- fields[code] = (type["@name"], [f["@name"] for f in type.query["field"]])
+ fields[code] = (type.attrib["name"], [f.attrib["name"] for f in
type.findall("amqp:field", ns)])
idx += 1
print("""
diff --git a/c/src/protocol.py b/c/src/protocol.py
index b112b0060..da3773150 100644
--- a/c/src/protocol.py
+++ b/c/src/protocol.py
@@ -16,25 +16,21 @@
# specific language governing permissions and limitations
# under the License.
#
-import mllib
import os
-import sys
+import xml.etree.ElementTree as ET
-doc = mllib.xml_parse(os.path.join(os.path.dirname(__file__), "transport.xml"))
-mdoc = mllib.xml_parse(os.path.join(os.path.dirname(__file__),
"messaging.xml"))
-tdoc = mllib.xml_parse(os.path.join(os.path.dirname(__file__),
"transactions.xml"))
-sdoc = mllib.xml_parse(os.path.join(os.path.dirname(__file__), "security.xml"))
+ns = {'amqp': 'http://www.amqp.org/schema/amqp.xsd'}
+doc = ET.parse(os.path.join(os.path.dirname(__file__),
"transport.xml")).getroot()
+mdoc = ET.parse(os.path.join(os.path.dirname(__file__),
"messaging.xml")).getroot()
+tdoc = ET.parse(os.path.join(os.path.dirname(__file__),
"transactions.xml")).getroot()
+sdoc = ET.parse(os.path.join(os.path.dirname(__file__),
"security.xml")).getroot()
-def eq(attr, value):
- return lambda nd: nd[attr] == value
-
-
-TYPEStmp = doc.query["amqp/section/type", eq("@class", "composite")] + \
- mdoc.query["amqp/section/type", eq("@class", "composite")] + \
- tdoc.query["amqp/section/type", eq("@class", "composite")] + \
- sdoc.query["amqp/section/type", eq("@class", "composite")] + \
- mdoc.query["amqp/section/type", eq("@provides", "section")]
+TYPEStmp = doc.findall("./amqp:section/amqp:type/[@class='composite']", ns) + \
+ mdoc.findall("./amqp:section/amqp:type/[@class='composite']", ns) + \
+ tdoc.findall("./amqp:section/amqp:type/[@class='composite']", ns) + \
+ sdoc.findall("./amqp:section/amqp:type/[@class='composite']", ns) + \
+ mdoc.findall("./amqp:section/amqp:type/[@provides='section']", ns)
TYPES = []
for ty in TYPEStmp:
if ty not in TYPES:
@@ -42,14 +38,14 @@ for ty in TYPEStmp:
RESTRICTIONS = {}
COMPOSITES = {}
-for type in doc.query["amqp/section/type"] + mdoc.query["amqp/section/type"] +
\
- sdoc.query["amqp/section/type"] + tdoc.query["amqp/section/type"]:
+for type in doc.findall("./amqp:section/amqp:type", ns) +
mdoc.findall("./amqp:section/amqp:type", ns) + \
+ sdoc.findall("./amqp:section/amqp:type", ns) +
tdoc.findall("./amqp:section/amqp:type", ns):
- source = type["@source"]
+ source = type.attrib["source"]
if source:
- RESTRICTIONS[type["@name"]] = source
- if type["@class"] == "composite":
- COMPOSITES[type["@name"]] = type
+ RESTRICTIONS[type.attrib["name"]] = source
+ if type.attrib["class"] == "composite":
+ COMPOSITES[type.attrib["name"]] = type
def resolve(name):
@@ -90,24 +86,24 @@ NULLABLE = set(["string", "symbol"])
def fname(field):
- return field["@name"].replace("-", "_")
+ return field.attrib["name"].replace("-", "_")
def tname(t):
- return t["@name"].replace("-", "_")
+ return t.attrib["name"].replace("-", "_")
def multi(f):
- return f["@multiple"] == "true"
+ return f.attrib.get("multiple") == "true"
def ftype(field):
if multi(field):
return "list"
- elif field["@type"] in COMPOSITES:
+ elif field.attrib["type"] in COMPOSITES:
return "box"
else:
- return resolve(field["@type"]).replace("-", "_")
+ return resolve(field.attrib["type"]).replace("-", "_")
def fconstruct(field, expr):
diff --git a/tools/python/mllib/__init__.py b/tools/python/mllib/__init__.py
deleted file mode 100644
index 20daa3f9f..000000000
--- a/tools/python/mllib/__init__.py
+++ /dev/null
@@ -1,76 +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.
-#
-
-"""
-This module provides document parsing and transformation utilities for XML.
-"""
-
-import os
-import sys
-import xml.sax
-import types
-from xml.sax.handler import ErrorHandler
-from xml.sax.xmlreader import InputSource
-
-from io import StringIO
-
-from . import dom
-from . import transforms
-from . import parsers
-
-CLASS_TYPES = (type,)
-
-def transform(node, *args):
- result = node
- for t in args:
- if isinstance(t, CLASS_TYPES):
- t = t()
- result = result.dispatch(t)
- return result
-
-
-class Resolver:
-
- def __init__(self, path):
- self.path = path
-
- def resolveEntity(self, publicId, systemId):
- for p in self.path:
- fname = os.path.join(p, systemId)
- if os.path.exists(fname):
- source = InputSource(systemId)
- source.setByteStream(open(fname))
- return source
- return InputSource(systemId)
-
-
-def xml_parse(filename, path=()):
- h = parsers.XMLParser()
- p = xml.sax.make_parser()
- p.setContentHandler(h)
- p.setErrorHandler(ErrorHandler())
- p.setEntityResolver(Resolver(path))
- p.parse(filename)
- return h.parser.tree
-
-
-def sexp(node):
- s = transforms.Sexp()
- node.dispatch(s)
- return s.out
diff --git a/tools/python/mllib/dom.py b/tools/python/mllib/dom.py
deleted file mode 100644
index b99174057..000000000
--- a/tools/python/mllib/dom.py
+++ /dev/null
@@ -1,331 +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.
-#
-
-"""
-Simple DOM for both SGML and XML documents.
-"""
-
-import sys
-STRING_TYPES = (str,)
-
-
-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 attribute"
-
-
-class Dispatcher:
-
- def is_type(self, type):
- cls = self
- while cls is not None:
- if cls.type == type:
- return True
- cls = cls.base
- return False
-
- def dispatch(self, f, attrs=""):
- cls = self
- while cls is not None:
- if hasattr(f, cls.type):
- return getattr(f, cls.type)(self)
- else:
- cls = cls.base
-
- cls = self
- while cls is not None:
- if attrs:
- sep = ", "
- if cls.base is 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):
- from . import transforms
- return self.dispatch(transforms.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 _idx(self, attr):
- idx = 0
- for k, v in self.attrs:
- if k == attr:
- return idx
- idx += 1
- return None
-
- def set_attr(self, name, value):
- idx = self._idx(name)
- if idx is None:
- self.attrs.append((name, value))
- else:
- self.attrs[idx] = (name, value)
-
- def dispatch(self, f):
- try:
- attr = "do_" + self.name
- method = getattr(f, attr)
- except AttributeError:
- return Dispatcher.dispatch(self, f, "'%s'" % attr)
- return method(self)
-
-
-class Leaf(Component, Dispatcher):
-
- type = "leaf"
- base = None
-
- def __init__(self, data):
- assert isinstance(data, STRING_TYPES)
- 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 Adder:
-
- def __add__(self, other):
- return Sum(self, other)
-
-
-class Sum(Adder):
-
- def __init__(self, left, right):
- self.left = left
- self.right = right
-
- def __iter__(self):
- for x in self.left:
- yield x
- for x in self.right:
- yield x
-
-
-class View(Adder):
-
- def __init__(self, source):
- self.source = source
-
-
-class Filter(View):
-
- def __init__(self, predicate, source):
- View.__init__(self, source)
- self.predicate = 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 = next(sources[-1])
- 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 Attributes(View):
-
- def __iter__(self):
- for nd in self.source:
- for a in nd.attrs:
- yield a
-
-
-class Values(View):
-
- def __iter__(self):
- for name, value in self.source:
- yield value
-
-
-def flatten_path(path):
- if isinstance(path, STRING_TYPES):
- for part in path.split("/"):
- yield part
- elif callable(path):
- yield path
- else:
- for p in path:
- for fp in flatten_path(p):
- yield fp
-
-
-class Query(View):
-
- def __iter__(self):
- for nd in self.source:
- yield nd
-
- def __getitem__(self, path):
- query = self.source
- for p in flatten_path(path):
- if callable(p):
- select = Query
- pred = p
- source = query
- elif isinstance(p, STRING_TYPES):
- if p[0] == "@":
- select = Values
- pred = lambda x, n=p[1:]: x[0] == n
- source = Attributes(query)
- elif p[0] == "#":
- select = Query
- pred = lambda x, t=p[1:]: x.is_type(t)
- source = Children(query)
- else:
- select = Query
- def pred(x, n=p): return isinstance(x, Tag) and x.name == n
- source = Flatten(Children(query))
- else:
- raise ValueError(p)
- query = select(Filter(pred, source))
-
- return query
diff --git a/tools/python/mllib/parsers.py b/tools/python/mllib/parsers.py
deleted file mode 100644
index 9904b8831..000000000
--- a/tools/python/mllib/parsers.py
+++ /dev/null
@@ -1,105 +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.
-#
-
-"""
-Parsers for XML to dom.
-"""
-import 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 XMLParser(xml.sax.handler.ContentHandler):
-
- def __init__(self):
- self.parser = Parser()
- self.locator = None
-
- def line(self):
- if self.locator is not 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/tools/python/mllib/transforms.py b/tools/python/mllib/transforms.py
deleted file mode 100644
index b84c7f65d..000000000
--- a/tools/python/mllib/transforms.py
+++ /dev/null
@@ -1,171 +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.
-#
-
-"""
-Useful transforms for dom objects.
-"""
-from . import dom
-from io import StringIO
-
-
-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 = dom.Tag(tag.name, *tag.attrs)
- result.extend(self.descend(tag))
- return result
-
- def tree(self, tree):
- result = dom.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
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]