diff options
Diffstat (limited to 'bin/SConsDoc.py')
| -rw-r--r-- | bin/SConsDoc.py | 220 | 
1 files changed, 119 insertions, 101 deletions
diff --git a/bin/SConsDoc.py b/bin/SConsDoc.py index cfb4e54..e44a8db 100644 --- a/bin/SConsDoc.py +++ b/bin/SConsDoc.py @@ -112,7 +112,6 @@ Tool example:      </tool>  """ -import imp  import os.path  import re  import sys @@ -156,8 +155,8 @@ if not has_etree:                  except ImportError:                      raise ImportError("Failed to import ElementTree from any known place") -re_entity = re.compile("\&([^;]+);") -re_entity_header = re.compile("<!DOCTYPE\s+sconsdoc\s+[^\]]+\]>") +re_entity = re.compile(r"\&([^;]+);") +re_entity_header = re.compile(r"<!DOCTYPE\s+sconsdoc\s+[^\]]+\]>")  # Namespace for the SCons Docbook XSD  dbxsd="http://www.scons.org/dbxsd/v1.0" @@ -168,7 +167,7 @@ xsi = "http://www.w3.org/2001/XMLSchema-instance"  # Header comment with copyright  copyright_comment = """ -Copyright (c) 2001 - 2017 The SCons Foundation +Copyright (c) 2001 - 2019 The SCons Foundation  This file is processed by the bin/SConsDoc.py module.  See its __doc__ string for a discussion of the format. @@ -179,22 +178,21 @@ def isSConsXml(fpath):          contains the default target namespace definition.      """      try: -        f = open(fpath,'r') -        content = f.read() -        f.close() +        with open(fpath,'r') as f: +            content = f.read()          if content.find('xmlns="%s"' % dbxsd) >= 0:              return True      except:          pass -     -    return False  + +    return False  def remove_entities(content):      # Cut out entity inclusions      content = re_entity_header.sub("", content, re.M)      # Cut out entities themselves      content = re_entity.sub(lambda match: match.group(1), content) -     +      return content  default_xsd = os.path.join('doc','xsd','scons.xsd') @@ -202,11 +200,11 @@ default_xsd = os.path.join('doc','xsd','scons.xsd')  ARG = "dbscons"  class Libxml2ValidityHandler: -     +      def __init__(self):          self.errors = []          self.warnings = [] -         +      def error(self, msg, data):          if data != ARG:              raise Exception("Error handler did not receive correct argument") @@ -222,14 +220,14 @@ class DoctypeEntity:      def __init__(self, name_, uri_):          self.name = name_          self.uri = uri_ -         +      def getEntityString(self):          txt = """    <!ENTITY %(perc)s %(name)s SYSTEM "%(uri)s">      %(perc)s%(name)s;  """ % {'perc' : perc, 'name' : self.name, 'uri' : self.uri}          return txt -         +  class DoctypeDeclaration:      def __init__(self, name_=None):          self.name = name_ @@ -242,23 +240,23 @@ class DoctypeDeclaration:              self.addEntity("functions-mod", "functions.mod")              self.addEntity("tools-mod", "tools.mod")              self.addEntity("variables-mod", "variables.mod") -         +      def addEntity(self, name, uri):          self.entries.append(DoctypeEntity(name, uri)) -         +      def createDoctype(self):          content = '<!DOCTYPE %s [\n' % self.name          for e in self.entries:              content += e.getEntityString()          content += ']>\n' -         +          return content  if not has_libxml2:      class TreeFactory:          def __init__(self):              pass -         +          def newNode(self, tag):              return etree.Element(tag) @@ -269,22 +267,22 @@ if not has_libxml2:                  return etree.Element(tag, nsmap=NSMAP)              return etree.Element(tag) -         +          def copyNode(self, node):              return copy.deepcopy(node) -         +          def appendNode(self, parent, child):              parent.append(child)          def hasAttribute(self, node, att):              return att in node.attrib -         +          def getAttribute(self, node, att):              return node.attrib[att] -         +          def setAttribute(self, node, att, value):              node.attrib[att] = value -             +          def getText(self, root):              return root.text @@ -293,30 +291,27 @@ if not has_libxml2:          def writeGenTree(self, root, fp):              dt = DoctypeDeclaration() -            fp.write(etree.tostring(root, xml_declaration=True,  -                                    encoding="UTF-8", pretty_print=True,  +            fp.write(etree.tostring(root, xml_declaration=True, +                                    encoding="UTF-8", pretty_print=True,                                      doctype=dt.createDoctype()))          def writeTree(self, root, fpath): -            fp = open(fpath, 'w') -            fp.write(etree.tostring(root, xml_declaration=True,  -                                    encoding="UTF-8", pretty_print=True))             -            fp.close() +            with open(fpath, 'w') as fp: +                fp.write(etree.tostring(root, xml_declaration=True, +                                        encoding="UTF-8", pretty_print=True))          def prettyPrintFile(self, fpath): -            fin = open(fpath,'r') -            tree = etree.parse(fin) -            pretty_content = etree.tostring(tree, pretty_print=True) -            fin.close() -     -            fout = open(fpath,'w') -            fout.write(pretty_content) -            fout.close() +            with open(fpath,'r') as fin: +                tree = etree.parse(fin) +                pretty_content = etree.tostring(tree, pretty_print=True) + +            with open(fpath,'w') as fout: +                fout.write(pretty_content)          def decorateWithHeader(self, root):              root.attrib["{"+xsi+"}schemaLocation"] = "%s %s/scons.xsd" % (dbxsd, dbxsd)              return root -             +          def newXmlTree(self, root):              """ Return a XML file tree with the correct namespaces set,                  the element root as top entry and the given header comment. @@ -325,7 +320,7 @@ if not has_libxml2:                       'xsi' : xsi}              t = etree.Element(root, nsmap=NSMAP)              return self.decorateWithHeader(t) -         +          def validateXml(self, fpath, xmlschema_context):              # Use lxml              xmlschema = etree.XMLSchema(xmlschema_context) @@ -362,21 +357,21 @@ if not has_libxml2:                  current XML toolkit.              """              return [root] -         -else:         + +else:      class TreeFactory:          def __init__(self):              pass -         +          def newNode(self, tag):              return libxml2.newNode(tag)          def newEtreeNode(self, tag, init_ns=False):              return etree.Element(tag) -         +          def copyNode(self, node):              return node.copyNode(1) -         +          def appendNode(self, parent, child):              if hasattr(parent, 'addChild'):                  parent.addChild(child) @@ -387,7 +382,7 @@ else:              if hasattr(node, 'hasProp'):                  return node.hasProp(att)              return att in node.attrib -         +          def getAttribute(self, node, att):              if hasattr(node, 'prop'):                  return node.prop(att) @@ -398,7 +393,7 @@ else:                  node.setProp(att, value)              else:                  node.attrib[att] = value -                 +          def getText(self, root):              if hasattr(root, 'getContent'):                  return root.getContent() @@ -426,20 +421,18 @@ else:              doc.freeDoc()          def writeTree(self, root, fpath): -            fp = open(fpath, 'w') -            doc = libxml2.newDoc('1.0') -            doc.setRootElement(root) -            fp.write(doc.serialize("UTF-8", 1)) -            doc.freeDoc() -            fp.close() +            with open(fpath, 'w') as fp: +                doc = libxml2.newDoc('1.0') +                doc.setRootElement(root) +                fp.write(doc.serialize("UTF-8", 1)) +                doc.freeDoc()          def prettyPrintFile(self, fpath):              # Read file and resolve entities              doc = libxml2.readFile(fpath, None, libxml2d.XML_PARSE_NOENT) -            fp = open(fpath, 'w') -            # Prettyprint -            fp.write(doc.serialize("UTF-8", 1)) -            fp.close() +            with open(fpath, 'w') as fp: +                # Prettyprint +                fp.write(doc.serialize("UTF-8", 1))              # Cleanup              doc.freeDoc() @@ -448,9 +441,9 @@ else:              ns = root.newNs(dbxsd, None)              xi = root.newNs(xsi, 'xsi')              root.setNs(ns)  #put this node in the target namespace -     +              root.setNsProp(xi, 'schemaLocation', "%s %s/scons.xsd" % (dbxsd, dbxsd)) -         +              return root          def newXmlTree(self, root): @@ -461,6 +454,8 @@ else:              return self.decorateWithHeader(t)          def validateXml(self, fpath, xmlschema_context): +            retval = True +              # Create validation context              validation_context = xmlschema_context.schemaNewValidCtxt()              # Set error/warning handlers @@ -470,17 +465,19 @@ else:              doc = libxml2.readFile(fpath, None, libxml2.XML_PARSE_NOENT)              doc.xincludeProcessFlags(libxml2.XML_PARSE_NOENT)              err = validation_context.schemaValidateDoc(doc) -            # Cleanup -            doc.freeDoc() -            del validation_context -         +              if err or eh.errors:                  for e in eh.errors:                      print(e.rstrip("\n")) +                # import pdb; pdb.set_trace()                  print("%s fails to validate" % fpath) -                return False -                 -            return True +                retval = False + +            # Cleanup +            doc.freeDoc() +            del validation_context + +            return retval          def findAll(self, root, tag, ns=None, xpath_context=None, nsmap=None):              if hasattr(root, 'xpathEval') and xpath_context: @@ -503,7 +500,7 @@ else:                  expression = "./%s/node()" % tag                  if ns:                      expression = "./%s:%s/node()" % (ns, tag) -                 +                  return xpath_context.xpathEval(expression)              else:                  expression = "./{%s}%s/node()" % (nsmap[ns], tag) @@ -533,7 +530,7 @@ else:              if child.tail:                  tail = libxml2.newText(child.tail)                  elements.append(tail) -                 +              return elements          def convertElementTree(self, root): @@ -559,7 +556,7 @@ else:              if root.tail:                  tail = libxml2.newText(root.tail)                  elements.append(tail) -                 +              return elements  tf = TreeFactory() @@ -600,7 +597,7 @@ class SConsDocTree:              # Register namespaces              for key, val in self.nsmap.items():                  self.xpath_context.xpathRegisterNs(key, val) -             +      def __del__(self):          if self.doc is not None:              self.doc.freeDoc() @@ -619,7 +616,7 @@ def validate_all_xml(dpaths, xsdfile=default_xsd):          ctxt = libxml2.schemaNewParserCtxt(xsdfile)          xmlschema_context = ctxt.schemaParse()          del ctxt -     +      fpaths = []      for dp in dpaths:          if dp.endswith('.xml') and isSConsXml(dp): @@ -632,13 +629,13 @@ def validate_all_xml(dpaths, xsdfile=default_xsd):                          fp = os.path.join(path, f)                          if isSConsXml(fp):                              fpaths.append(fp) -                 +      fails = []      for idx, fp in enumerate(fpaths):          fpath = os.path.join(path, fp)          print("%.2f%s (%d/%d) %s" % (float(idx+1)*100.0/float(len(fpaths)),                                       perc, idx+1, len(fpaths),fp)) -                                               +          if not tf.validateXml(fp, xmlschema_context):              fails.append(fp)              continue @@ -649,7 +646,7 @@ def validate_all_xml(dpaths, xsdfile=default_xsd):      if fails:          return False -     +      return True  class Item(object): @@ -730,10 +727,10 @@ class SConsDocHandler(object):              uses.extend(self.parseItems(u, xpath_context, nsmap))          for s in tf.findAll(domelem, "sets", dbxid, xpath_context, nsmap):              sets.extend(self.parseItems(s, xpath_context, nsmap)) -         +          return sorted(uses), sorted(sets) -    def parseInstance(self, domelem, map, Class,  +    def parseInstance(self, domelem, map, Class,                          xpath_context, nsmap, include_entities=True):          name = 'unknown'          if tf.hasAttribute(domelem, 'name'): @@ -757,24 +754,24 @@ class SConsDocHandler(object):                      instance.arguments = []                  instance.arguments.append(tf.copyNode(a)) -    def parseDomtree(self, root, xpath_context=None, nsmap=None, include_entities=True):     +    def parseDomtree(self, root, xpath_context=None, nsmap=None, include_entities=True):          # Process Builders          for b in tf.findAll(root, "builder", dbxid, xpath_context, nsmap): -            self.parseInstance(b, self.builders, Builder,  +            self.parseInstance(b, self.builders, Builder,                                 xpath_context, nsmap, include_entities)          # Process Functions          for f in tf.findAll(root, "scons_function", dbxid, xpath_context, nsmap): -            self.parseInstance(f, self.functions, Function,  +            self.parseInstance(f, self.functions, Function,                                 xpath_context, nsmap, include_entities)          # Process Tools          for t in tf.findAll(root, "tool", dbxid, xpath_context, nsmap): -            self.parseInstance(t, self.tools, Tool,  +            self.parseInstance(t, self.tools, Tool,                                 xpath_context, nsmap, include_entities)          # Process CVars          for c in tf.findAll(root, "cvar", dbxid, xpath_context, nsmap): -            self.parseInstance(c, self.cvars, ConstructionVariable,  +            self.parseInstance(c, self.cvars, ConstructionVariable,                                 xpath_context, nsmap, include_entities) -         +      def parseContent(self, content, include_entities=True):          """ Parses the given content as XML file. This method              is used when we generate the basic lists of entities @@ -795,27 +792,48 @@ class SConsDocHandler(object):          t.parseXmlFile(fpath)          # Parse it          self.parseDomtree(t.root, t.xpath_context, t.nsmap) -         +  # lifted from Ka-Ping Yee's way cool pydoc module. -def importfile(path): -    """Import a Python source file or compiled file given its path.""" -    magic = imp.get_magic() -    file = open(path, 'r') -    if file.read(len(magic)) == magic: -        kind = imp.PY_COMPILED -    else: -        kind = imp.PY_SOURCE -    file.close() -    filename = os.path.basename(path) -    name, ext = os.path.splitext(filename) -    file = open(path, 'r') -    try: -        module = imp.load_module(name, file, path, (ext, 'r', kind)) -    except ImportError as e: -        sys.stderr.write("Could not import %s: %s\n" % (path, e)) -        return None -    file.close() -    return module +if sys.version_info[0] == 2: +    def importfile(path): +        """Import a Python source file or compiled file given its path.""" +        import imp +        magic = imp.get_magic() +        with open(path, 'r') as ifp: +            if ifp.read(len(magic)) == magic: +                kind = imp.PY_COMPILED +            else: +                kind = imp.PY_SOURCE +        filename = os.path.basename(path) +        name, ext = os.path.splitext(filename) +        with open(path, 'r') as ifp: +            try: +                module = imp.load_module(name, ifp, path, (ext, 'r', kind)) +            except ImportError as e: +                sys.stderr.write("Could not import %s: %s\n" % (path, e)) +                return None +        return module + +else:  # PY3 version, from newer pydoc +    def importfile(path): +        """Import a Python source file or compiled file given its path.""" +        import importlib +        from pydoc import ErrorDuringImport +        magic = importlib.util.MAGIC_NUMBER +        with open(path, 'rb') as ifp: +            is_bytecode = magic == ifp.read(len(magic)) +        filename = os.path.basename(path) +        name, ext = os.path.splitext(filename) +        if is_bytecode: +            loader = importlib._bootstrap_external.SourcelessFileLoader(name, path) +        else: +            loader = importlib._bootstrap_external.SourceFileLoader(name, path) +        # XXX We probably don't need to pass in the loader here. +        spec = importlib.util.spec_from_file_location(name, path, loader=loader) +        try: +            return importlib._bootstrap._load(spec) +        except: +            raise ErrorDuringImport(path, sys.exc_info())  # Local Variables:  # tab-width:4  | 
