From da0b43d98c505dbe1a73bfe88fa42294e535288c Mon Sep 17 00:00:00 2001 From: Alex Merry Date: Mon, 13 Dec 2010 16:30:16 +0000 Subject: Merge in upstream changes to the tooling. Also, ditch a couple of files we don't use. --- tools/README | 24 --- tools/doc-generator.py | 8 +- tools/extract-nodename.py | 11 -- tools/git-which-branch.sh | 25 ---- tools/spec-to-introspect.xsl | 51 ------- tools/specparser.py | 348 +++++++++++++++++++++++++++++++++++++------ 6 files changed, 309 insertions(+), 158 deletions(-) delete mode 100644 tools/README delete mode 100644 tools/extract-nodename.py delete mode 100644 tools/git-which-branch.sh delete mode 100644 tools/spec-to-introspect.xsl (limited to 'tools') diff --git a/tools/README b/tools/README deleted file mode 100644 index 5ffc9fd..0000000 --- a/tools/README +++ /dev/null @@ -1,24 +0,0 @@ -Some tools that used to be here are now maintained elsewhere. - -c-constants-generator.xsl - Now in telepathy-glib (with a modified version in libtelepathy, and a - copy in Gabble for its spec extensions) - -c-interfaces-generator.xsl - Now in telepathy-glib (with a modified version in libtelepathy, and a - copy in Gabble for its spec extensions) - -genginterface.py - Now in telepathy-glib (with a copy in Gabble for its spec extensions) - -python-constants-generator.xsl - Now in telepathy-python - -python-errors-generator.xsl - Now in telepathy-python - -python-interfaces-generator.xsl - Now in telepathy-python - -spec-to-python.xsl - Now in telepathy-python diff --git a/tools/doc-generator.py b/tools/doc-generator.py index 3623470..c40f224 100755 --- a/tools/doc-generator.py +++ b/tools/doc-generator.py @@ -37,6 +37,12 @@ except ImportError, e: import specparser +# one day, OptionParser +allow_externals = False +if '--allow-externals' in sys.argv: + allow_externals = True + sys.argv.remove('--allow-externals') + program, spec_file, output_path, project, namespace = sys.argv template_path = os.path.join(os.path.dirname(program), '../doc/templates') @@ -68,7 +74,7 @@ def load_template(filename): return template_def -spec = specparser.parse(spec_file, namespace) +spec = specparser.parse(spec_file, namespace, allow_externals=allow_externals) # write out HTML files for each of the interfaces diff --git a/tools/extract-nodename.py b/tools/extract-nodename.py deleted file mode 100644 index d253ff8..0000000 --- a/tools/extract-nodename.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/python - -import sys -import xml.dom.minidom - -if __name__ == '__main__': - dom = xml.dom.minidom.parse(sys.argv[1]) - - nodes = dom.getElementsByTagName("node") - assert len(nodes) == 1 - print nodes[0].getAttributeNode("name").nodeValue.replace('/', '') diff --git a/tools/git-which-branch.sh b/tools/git-which-branch.sh deleted file mode 100644 index b96b5d5..0000000 --- a/tools/git-which-branch.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -# git-which-branch.sh - output the name of the current git branch -# -# The canonical location of this program is the telepathy-spec tools/ -# directory, please synchronize any changes with that copy. -# -# Copyright (C) 2008 Collabora Ltd. -# -# Copying and distribution of this file, with or without modification, -# are permitted in any medium without royalty provided the copyright -# notice and this notice are preserved. - -default="$1" -if { ref="`git symbolic-ref HEAD 2>/dev/null`"; }; then - echo ${ref#refs/heads/} - exit 0 -fi - -if test -n "$default"; then - echo "$default" >/dev/null - exit 0 -fi - -echo "no git branch found" >&2 -exit 1 diff --git a/tools/spec-to-introspect.xsl b/tools/spec-to-introspect.xsl deleted file mode 100644 index 980604a..0000000 --- a/tools/spec-to-introspect.xsl +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/specparser.py b/tools/specparser.py index 5e8c059..d4eab19 100644 --- a/tools/specparser.py +++ b/tools/specparser.py @@ -41,6 +41,9 @@ class WrongNumberOfChildren(Exception): pass class MismatchedFlagsAndEnum(Exception): pass class TypeMismatch(Exception): pass class MissingVersion(Exception): pass +class DuplicateEnumValueValue(Exception): pass +class BadFlagValue(Exception): pass +class BadFlagsType(Exception): pass class Xzibit(Exception): def __init__(self, parent, child): @@ -79,8 +82,9 @@ def getOnlyChildByName(dom, namespace, name): return None if len(kids) > 1: - raise WrongNumberOfChildren('%s node should have at most one child of type ' - '{%s}%s' % (dom.tagName, namespace, name)) + raise WrongNumberOfChildren( + '<%s> node should have at most one <%s xmlns="%s"/> child' % + (dom.tagName, name, namespace)) return kids[0] @@ -163,8 +167,13 @@ class Base(object): def get_interface(self): return self.parent.get_interface() + def get_anchor(self): + return "%s:%s" % ( + self.get_type_name().replace(' ', '-'), + self.short_name) + def get_url(self): - return "%s#%s" % (self.get_interface().get_url(), self.name) + return "%s#%s" % (self.get_interface().get_url(), self.get_anchor()) def _get_generic_with_ver(self, nnode, htmlclass, txt): if nnode is None: @@ -174,14 +183,20 @@ class Base(object): node = nnode.cloneNode(True) node.tagName = 'div' node.baseURI = None - node.setAttribute('class', htmlclass) + node.setAttribute('class', 'annotation %s' % htmlclass) try: node.removeAttribute('version') - span = xml.dom.minidom.parseString( - ('%s\n' % txt) % - nnode.getAttribute('version')).firstChild + doc = self.get_spec().document + + span = doc.createElement('span') + span.setAttribute('class', 'version') + + text = doc.createTextNode( + txt % nnode.getAttribute('version') + ' ') + span.appendChild(text) + node.insertBefore(span, node.firstChild) except xml.dom.NotFoundErr: raise MissingVersion( @@ -223,7 +238,8 @@ class Base(object): def _convert_to_html(self, node): spec = self.get_spec() - namespace = self.get_root_namespace() + doc = spec.document + root_namespace = self.get_root_namespace() # rewrite for n in node.getElementsByTagNameNS(XMLNS_TP, 'rationale'): @@ -231,22 +247,27 @@ class Base(object): if nested: raise Xzibit(n, nested[0]) - rationale_div = xml.dom.minidom.parseString( - """ + """
Rationale:
-
+
<- inner_div
- """).documentElement - n.parentNode.replaceChild(rationale_div, n) + """ + outer_div = doc.createElement('div') + outer_div.setAttribute('class', 'rationale') + + h5 = doc.createElement('h5') + h5.appendChild(doc.createTextNode('Rationale:')) + outer_div.appendChild(h5) - # It's the third child: space, h5, space, div - inner_div = rationale_div.childNodes[3] - assert inner_div.nodeName == 'div', inner_div + inner_div = doc.createElement('div') + outer_div.appendChild(inner_div) for rationale_body in n.childNodes: inner_div.appendChild(rationale_body.cloneNode(True)) + n.parentNode.replaceChild(outer_div, n) + # rewrite for n in node.getElementsByTagNameNS(XMLNS_TP, 'type'): t = spec.lookup_type(getText(n)) @@ -254,16 +275,33 @@ class Base(object): n.namespaceURI = None n.setAttribute('href', t.get_url()) + # rewrite + error_ns = spec.spec_namespace + '.Error.' + for n in node.getElementsByTagNameNS(XMLNS_TP, 'error-ref'): + try: + e = spec.errors[error_ns + getText(n)] + except KeyError: + print >> sys.stderr, """ +WARNING: Error '%s' not known in error namespace '%s' + ( in %s) + """.strip() % (getText(n), error_ns[:-1], self) + continue + + n.tagName = 'a' + n.namespaceURI = None + n.setAttribute('href', e.get_url()) + n.setAttribute('title', error_ns + getText(n)) + # rewrite for n in node.getElementsByTagNameNS(XMLNS_TP, 'member-ref'): key = getText(n) try: - o = spec.lookup(key, namespace=namespace) + o = spec.lookup(key, namespace=root_namespace) except KeyError: print >> sys.stderr, """ WARNING: Key '%s' not known in namespace '%s' ( in %s) - """.strip() % (key, namespace, self) + """.strip() % (key, root_namespace, self) continue n.tagName = 'a' @@ -276,8 +314,9 @@ WARNING: Key '%s' not known in namespace '%s' namespace = n.getAttribute('namespace') key = getText(n) - if namespace.startswith('ofdT.'): - namespace = 'org.freedesktop.Telepathy.' + namespace[5:] + if namespace.startswith('ofdT.') or namespace == 'ofdT': + namespace = namespace.replace('ofdT', + 'org.freedesktop.Telepathy') try: o = spec.lookup(key, namespace=namespace) @@ -293,12 +332,83 @@ WARNING: Key '%s' not known in namespace '%s' n.setAttribute('href', o.get_url()) n.setAttribute('title', o.get_title()) + # rewrite + for n in node.getElementsByTagNameNS(XMLNS_TP, 'token-ref'): + key = getText(n) + namespace = n.getAttribute('namespace') + + if namespace: + if namespace.startswith('ofdT.'): + namespace = 'org.freedesktop.Telepathy.' + namespace[5:] + else: + namespace = root_namespace + + try: + try: + if '/' in key: + sep = '.' + else: + sep = '/' + + o = spec.lookup(namespace + sep + key, None) + except KeyError: + o = spec.lookup(key, None) + except KeyError: + print >> sys.stderr, """ +WARNING: Key '%s' not known in namespace '%s' + ( in %s) + """.strip() % (key, namespace, self) + continue + + n.tagName = 'a' + n.namespaceURI = None + n.setAttribute('href', o.get_url()) + n.setAttribute('title', o.get_title()) + + # Fill in with a linkified list of + # properties which are also connection parameters + for n in node.getElementsByTagNameNS(XMLNS_TP, + 'list-dbus-property-parameters'): + n.tagName = 'ul' + n.namespaceURI = None + + props = (p for interface in spec.interfaces + for p in interface.properties + if p.is_connection_parameter + ) + + for p in props: + link_text = doc.createTextNode(p.name) + + a = doc.createElement('a') + a.setAttribute('href', p.get_url()) + a.appendChild(link_text) + + # FIXME: it'd be nice to include the rich type of the property + # here too. + type_text = doc.createTextNode(' (%s)' % p.dbus_type) + + li = doc.createElement('li') + li.appendChild(a) + li.appendChild(type_text) + + n.appendChild(li) + def get_title(self): return '%s %s' % (self.get_type_name(), self.name) def __repr__(self): return '%s(%s)' % (self.__class__.__name__, self.name) + def get_index_entries(self): + context = self.parent.get_index_context() + return set([ + '%s (%s in %s)' % (self.short_name, self.get_type_name(), context), + '%s %s' % (self.get_type_name(), self.name)]) + + def get_index_context(self): + return self.short_name + class DBusConstruct(Base): """Base class for signals, methods and properties.""" @@ -326,6 +436,12 @@ class PossibleError(Base): try: return spec.errors[self.name] except KeyError: + if not spec.allow_externals: + print >> sys.stderr, """ +WARNING: Error not known: '%s' + ( in %s) + """.strip() % (self.name, self.parent) + return External(self.name) def get_url(self): @@ -454,6 +570,17 @@ class Property(DBusConstruct, Typed): else: raise UnknownAccess("Unknown access '%s' on %s" % (access, self)) + is_cp = dom.getAttributeNS(XMLNS_TP, 'is-connection-parameter') + self.is_connection_parameter = is_cp != '' + + immutable = dom.getAttributeNS(XMLNS_TP, 'immutable') + self.immutable = immutable != '' + self.sometimes_immutable = immutable == 'sometimes' + + requestable = dom.getAttributeNS(XMLNS_TP, 'requestable') + self.requestable = requestable != '' + self.sometimes_requestable = requestable == 'sometimes' + def get_access(self): if self.access & self.ACCESS_READ and self.access & self.ACCESS_WRITE: return 'Read/Write' @@ -462,6 +589,21 @@ class Property(DBusConstruct, Typed): elif self.access & self.ACCESS_WRITE: return 'Write only' + def get_flag_summary(self): + descriptions = [] + + if self.sometimes_immutable: + descriptions.append("Sometimes immutable") + elif self.immutable: + descriptions.append("Immutable") + + if self.sometimes_requestable: + descriptions.append("Sometimes requestable") + elif self.requestable: + descriptions.append("Requestable") + + return ', '.join(descriptions) + class AwkwardTelepathyProperty(Typed): def get_type_name(self): return 'Telepathy Property' @@ -574,6 +716,9 @@ class Interface(Base): self.contact_attributes = build_list(self, ContactAttribute, self.name, dom.getElementsByTagNameNS(XMLNS_TP, 'contact-attribute')) + self.client_interests = build_list(self, ClientInterest, self.name, + dom.getElementsByTagNameNS(XMLNS_TP, 'client-interest')) + # build a list of types in this interface self.types = parse_types(self, dom, self.name) @@ -585,26 +730,87 @@ class Interface(Base): self.requires = map(lambda n: n.getAttribute('interface'), getChildrenByName(dom, XMLNS_TP, 'requires')) + def map_xor(element): + return map(lambda n: n.getAttribute('interface'), + getChildrenByName(element, XMLNS_TP, 'requires')) + + self.xor_requires = map(map_xor, + getChildrenByName(dom, XMLNS_TP, 'xor-requires')) + + # let's make sure there's nothing we don't know about here + self.check_for_odd_children(dom) + + self.is_channel_related = self.name.startswith(spec_namespace + '.Channel') + def get_interface(self): return self - def get_requires(self): + def lookup_requires(self, r): spec = self.get_spec() - def lookup(r): - try: - return spec.lookup(r) - except KeyError: - return External(r) + try: + return spec.lookup(r) + except KeyError: + if not spec.allow_externals: + print >> sys.stderr, """ +WARNING: Interface not known: '%s' + ( in %s) + """.strip() % (r, self) - return map(lookup, self.requires) + return External(r) + + def get_requires(self): + return map(self.lookup_requires, self.requires) + + def get_xor_requires(self): + def xor_lookup(r): + return map(self.lookup_requires, r) + + return map(xor_lookup, self.xor_requires) def get_url(self): return '%s.html' % self.name_for_bindings + def check_for_odd_children(self, dom): + expected = [ + (None, 'method'), + (None, 'property'), + (None, 'signal'), + (None, 'annotation'), + (XMLNS_TP, 'property'), + (XMLNS_TP, 'handler-capability-token'), + (XMLNS_TP, 'hct'), + (XMLNS_TP, 'contact-attribute'), + (XMLNS_TP, 'client-interest'), + (XMLNS_TP, 'simple-type'), + (XMLNS_TP, 'enum'), + (XMLNS_TP, 'flags'), + (XMLNS_TP, 'mapping'), + (XMLNS_TP, 'struct'), + (XMLNS_TP, 'external-type'), + (XMLNS_TP, 'requires'), + (XMLNS_TP, 'xor-requires'), + (XMLNS_TP, 'added'), + (XMLNS_TP, 'changed'), + (XMLNS_TP, 'deprecated'), + (XMLNS_TP, 'docstring') + ] + + unexpected = [ + x for x in dom.childNodes + if isinstance(x, xml.dom.minidom.Element) and + (x.namespaceURI, x.localName) not in expected + ] + + if unexpected: + print >> sys.stderr, """ +WARNING: Unknown element(s): %s + (in interface '%s') + """.strip() % (', '.join([x.tagName for x in unexpected]), self.name) + class Error(Base): def get_url(self): - return 'errors.html#%s' % self.name + return 'errors.html#%s' % self.get_anchor() def get_root_namespace(self): return self.namespace @@ -681,7 +887,7 @@ class DBusType(Base): else: html = 'generic-types.html' - return '%s#%s' % (html, self.name) + return '%s#%s' % (html, self.get_anchor()) class SimpleType(DBusType): def get_type_name(self): @@ -793,6 +999,17 @@ class EnumLike(DBusType): return str + def check_for_duplicates(self): + # make sure no two values have the same value + for u in self.values: + for v in [x for x in self.values if x is not u]: + if u.value == v.value: + raise DuplicateEnumValueValue('%s %s has two values ' + 'with the same value: %s=%s and %s=%s' % \ + (self.__class__.__name__, self.name, \ + u.short_name, u.value, v.short_name, v.value)) + + class Enum(EnumLike): devhelp_name = "enum" @@ -811,10 +1028,17 @@ class Enum(EnumLike): self.values = build_list(self, EnumLike.EnumValue, self.name, dom.getElementsByTagNameNS(XMLNS_TP, 'enumvalue')) + self.check_for_duplicates() + class Flags(EnumLike): def __init__(self, parent, namespace, dom): super(Flags, self).__init__(parent, namespace, dom) + if dom.getAttribute('type') != 'u': + raise BadFlagsType('Flags %s doesn\'t make sense to be of ' + 'type "%s" (only type "u" makes sense")' % ( + self.name, dom.getAttribute('type'))) + if dom.getElementsByTagNameNS(XMLNS_TP, 'enumvalue'): raise MismatchedFlagsAndEnum('%s is a tp:flags, so it should not ' 'contain tp:enumvalue' % self.name) @@ -823,6 +1047,17 @@ class Flags(EnumLike): dom.getElementsByTagNameNS(XMLNS_TP, 'flag')) self.flags = self.values # in case you're looking for it + self.check_for_duplicates() + + # make sure all these values are sane + for flag in self.values: + v = int(flag.value) + + # positive x is a power of two if (x & (x - 1)) = 0. + if v == 0 or (v & (v - 1)) != 0: + raise BadFlagValue('Flags %s has bad value (not a power of ' + 'two): %s=%s' % (self.name, flag.short_name, v)) + class TokenBase(Base): devhelp_name = "macro" # it's a constant, which is near enough... @@ -830,7 +1065,13 @@ class TokenBase(Base): def __init__(self, parent, namespace, dom): super(TokenBase, self).__init__(parent, namespace, dom) - self.name = namespace + '/' + self.short_name + + items = [ namespace ] + + if self.short_name != '': + items.append (self.short_name) + + self.name = self.separator.join (items) class ContactAttribute(TokenBase, Typed): @@ -849,6 +1090,19 @@ class HandlerCapabilityToken(TokenBase): assert is_family in ('yes', 'no', '') self.is_family = (is_family == 'yes') +class ClientInterest(TokenBase): + + def __init__(self, parent, namespace, dom): + super(ClientInterest, self).__init__(parent, namespace, dom) + + self.short_name = self.name + + def get_type_name(self): + return 'Client Interest' + + def validate(self): + pass + class SectionBase(object): """A SectionBase is an abstract base class for any type of node that can contain a , which means the top-level Spec object, or any @@ -858,7 +1112,7 @@ class SectionBase(object): """ def __init__(self, dom, spec_namespace): - + self.spec_namespace = spec_namespace self.items = [] def recurse(nodes): @@ -879,6 +1133,9 @@ class SectionBase(object): recurse(dom.childNodes) + def get_index_context(self): + return self.spec_namespace + class Section(Base, SectionBase): def __init__(self, parent, namespace, dom, spec_namespace): Base.__init__(self, parent, namespace, dom) @@ -892,7 +1149,12 @@ class ErrorsSection(Section): pass class Spec(SectionBase): - def __init__(self, dom, spec_namespace): + def __init__(self, dom, spec_namespace, allow_externals=False): + self.document = dom + self.spec_namespace = spec_namespace + self.short_name = spec_namespace + self.allow_externals = allow_externals + # build a dictionary of errors in this spec try: errorsnode = dom.getElementsByTagNameNS(XMLNS_TP, 'errors')[0] @@ -936,18 +1198,12 @@ class Spec(SectionBase): for interface in self.interfaces: self.everything[interface.name] = interface - for method in interface.methods: - self.everything[method.name] = method - for signal in interface.signals: - self.everything[signal.name] = signal - for property in interface.properties: - self.everything[property.name] = property - for property in interface.tpproperties: - self.everything[property.name] = property - for token in interface.contact_attributes: - self.everything[token.name] = token - for token in interface.handler_capability_tokens: - self.everything[token.name] = token + for things in [ 'methods', 'signals', 'properties', + 'tpproperties', 'contact_attributes', + 'handler_capability_tokens', + 'client_interests' ]: + for thing in getattr(interface, things): + self.everything[thing.name] = thing for type in interface.types: self.types[type.name] = type @@ -1036,11 +1292,11 @@ def parse_types(parent, dom, namespace = None): return types -def parse(filename, spec_namespace): +def parse(filename, spec_namespace, allow_externals=False): dom = xml.dom.minidom.parse(filename) xincludator.xincludate(dom, filename) - spec = Spec(dom, spec_namespace) + spec = Spec(dom, spec_namespace, allow_externals=allow_externals) return spec -- cgit v1.2.3