diff options
Diffstat (limited to 'lv2specgen')
| -rw-r--r-- | lv2specgen/DTD/xhtml-rdfa-1.dtd | 1 | ||||
| -rwxr-xr-x | lv2specgen/lv2docgen.py | 34 | ||||
| -rwxr-xr-x | lv2specgen/lv2specgen.py | 262 | ||||
| -rw-r--r-- | lv2specgen/meson.build | 48 | ||||
| -rw-r--r-- | lv2specgen/template.html | 9 |
5 files changed, 128 insertions, 226 deletions
diff --git a/lv2specgen/DTD/xhtml-rdfa-1.dtd b/lv2specgen/DTD/xhtml-rdfa-1.dtd index bd3479e..53890bb 100644 --- a/lv2specgen/DTD/xhtml-rdfa-1.dtd +++ b/lv2specgen/DTD/xhtml-rdfa-1.dtd @@ -444,7 +444,6 @@ validation, so here we are. --> -<!ATTLIST html xmlns:dcs CDATA #IMPLIED> <!ATTLIST html xmlns:dcterms CDATA #IMPLIED> <!ATTLIST html xmlns:doap CDATA #IMPLIED> <!ATTLIST html xmlns:foaf CDATA #IMPLIED> diff --git a/lv2specgen/lv2docgen.py b/lv2specgen/lv2docgen.py index c5e13a7..271e434 100755 --- a/lv2specgen/lv2docgen.py +++ b/lv2specgen/lv2docgen.py @@ -1,20 +1,14 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# lv2docgen, a documentation generator for LV2 plugins + # Copyright 2012 David Robillard <d@drobilla.net> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# SPDX-License-Identifier: ISC + +"""LV2 plugin documentation generator.""" + +# pylint: disable=consider-using-f-string +# pylint: disable=missing-function-docstring +# pylint: disable=redefined-outer-name +# pylint: disable=invalid-name import errno import os @@ -106,10 +100,10 @@ def plugin_doc(model, plugin, style_uri): html += get_doc(model, plugin) ports_html = "" - for p in model.triples([plugin, lv2.port, None]): - ports_html += port_doc(model, p[2]) + for link in model.triples([plugin, lv2.port, None]): + ports_html += port_doc(model, link[2]) - if len(ports_html): + if ports_html: html += ( """ <h2 class="sec">Ports</h2> @@ -124,8 +118,6 @@ def plugin_doc(model, plugin, style_uri): if __name__ == "__main__": - "LV2 plugin documentation generator" - if len(sys.argv) < 2: print("Usage: %s OUTDIR FILE..." % sys.argv[0]) sys.exit(1) @@ -153,5 +145,5 @@ if __name__ == "__main__": raise print("Writing <%s> documentation to %s" % (plugin, outpath)) - with open(outpath, "w") as out: + with open(outpath, "w", encoding="utf-8") as out: out.write(html) diff --git a/lv2specgen/lv2specgen.py b/lv2specgen/lv2specgen.py index 3345b5a..7927261 100755 --- a/lv2specgen/lv2specgen.py +++ b/lv2specgen/lv2specgen.py @@ -1,46 +1,47 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # -# lv2specgen, a documentation generator for LV2 specifications. -# Copyright (c) 2009-2014 David Robillard <d@drobilla.net> +# Copyright 2009-2022 David Robillard <d@drobilla.net> +# Copyright 2003-2008 Christopher Schmidt <crschmidt@crschmidt.net> +# Copyright 2005-2008 Uldis Bojars <uldis.bojars@deri.org> +# Copyright 2007-2008 Sergio Fernández <sergio.fernandez@fundacionctic.org> +# SPDX-License-Identifier: MIT # # Based on SpecGen: # <http://forge.morfeo-project.org/wiki_en/index.php/SpecGen> -# Copyright (c) 2003-2008 Christopher Schmidt <crschmidt@crschmidt.net> -# Copyright (c) 2005-2008 Uldis Bojars <uldis.bojars@deri.org> -# Copyright (c) 2007-2008 Sergio Fernández <sergio.fernandez@fundacionctic.org> -# -# This software is licensed under the terms of the MIT License. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. + +"""Ontology specification generator tool.""" + +# pylint: disable=broad-exception-caught +# pylint: disable=cell-var-from-loop +# pylint: disable=consider-using-f-string +# pylint: disable=deprecated-module +# pylint: disable=global-statement +# pylint: disable=global-variable-not-assigned +# pylint: disable=invalid-name +# pylint: disable=missing-function-docstring +# pylint: disable=no-member +# pylint: disable=redefined-outer-name +# pylint: disable=too-many-arguments +# pylint: disable=too-many-boolean-expressions +# pylint: disable=too-many-branches +# pylint: disable=too-many-lines +# pylint: disable=too-many-locals +# pylint: disable=too-many-positional-arguments +# pylint: disable=too-many-statements +# pylint: disable=unused-argument import datetime -import markdown -import markdown.extensions import optparse import os import re import sys import time -import xml.sax.saxutils import xml.dom import xml.dom.minidom +import xml.sax.saxutils + +import markdown +import markdown.extensions __date__ = "2011-10-26" __version__ = __date__.replace("-", ".") @@ -86,7 +87,6 @@ spec_pre = None spec_bundle = None specgendir = None ns_list = { - "http://ontologi.es/doap-changeset#": "dcs", "http://purl.org/dc/terms/": "dcterms", "http://usefulinc.com/ns/doap#": "doap", "http://xmlns.com/foaf/0.1/": "foaf", @@ -101,7 +101,6 @@ rdfs = rdflib.Namespace("http://www.w3.org/2000/01/rdf-schema#") owl = rdflib.Namespace("http://www.w3.org/2002/07/owl#") lv2 = rdflib.Namespace("http://lv2plug.in/ns/lv2core#") doap = rdflib.Namespace("http://usefulinc.com/ns/doap#") -dcs = rdflib.Namespace("http://ontologi.es/doap-changeset#") foaf = rdflib.Namespace("http://xmlns.com/foaf/0.1/") @@ -134,33 +133,34 @@ def getLiteralString(s): def isResource(n): - return type(n) == rdflib.URIRef + return isinstance(n, rdflib.URIRef) def isBlank(n): - return type(n) == rdflib.BNode + return isinstance(n, rdflib.BNode) def isLiteral(n): - return type(n) == rdflib.Literal + return isinstance(n, rdflib.Literal) def niceName(uri): - global spec_bundle if uri.startswith(spec_ns_str): return uri.replace(spec_ns_str, "") - elif uri == str(rdfs.seeAlso): + + if uri == str(rdfs.seeAlso): return "See also" regexp = re.compile("^(.*[/#])([^/#]+)$") rez = regexp.search(uri) if not rez: return uri + pref = rez.group(1) if pref in ns_list: return ns_list.get(pref, pref) + ":" + rez.group(2) - else: - return uri + + return uri def termName(m, urinode): @@ -172,17 +172,17 @@ def getLabel(m, urinode): statement = findOne(m, urinode, rdfs.label, None) if statement: return getLiteralString(getObject(statement)) - else: - return "" + + return "" def linkifyCodeIdentifiers(string): "Add links to code documentation for identifiers like LV2_Type" - if linkmap == {}: + if not linkmap: return string - if string in linkmap.keys(): + if string in linkmap: # Exact match for complete string return linkmap[string] @@ -217,13 +217,14 @@ def linkifyVocabIdentifiers(m, string, classlist, proplist, instalist): ): print("warning: Link to undefined resource <%s>\n" % text) return '<a href="#%s">%s</a>' % (name, name) - elif prefix in namespaces: + + if prefix in namespaces: return '<a href="%s">%s</a>' % ( namespaces[match.group(1)] + match.group(2), match.group(0), ) - else: - return text + + return text return rgx.sub(translateLink, string) @@ -279,15 +280,17 @@ def prettifyHtml(m, markup, subject, classlist, proplist, instalist): or (proplist and uri in proplist) ): return '%s<a href="#%s">%s</a>' % (space, name, name) - else: - print("warning: Link to undefined resource <%s>\n" % name) - return text + + print("warning: Link to undefined resource <%s>\n" % name) + return text markup = rgx.sub(translateLocalLink, markup) if not have_lxml: print("warning: No Python lxml module found, output may be invalid") else: + oldcwd = os.getcwd() + try: # Parse and validate documentation as XHTML Basic 1.1 doc = ( @@ -306,7 +309,6 @@ def prettifyHtml(m, markup, subject, classlist, proplist, instalist): </html>""" ) - oldcwd = os.getcwd() os.chdir(specgendir) parser = etree.XMLParser(dtd_validation=True, no_network=True) etree.fromstring(doc.encode("utf-8"), parser) @@ -340,11 +342,11 @@ def formatDoc(m, urinode, literal, classlist, proplist, instalist): doc = doc.replace("</%s>\n" % tag, "") return prettifyHtml(m, doc, urinode, classlist, proplist, instalist) - else: - doc = xml.sax.saxutils.escape(string) - doc = linkifyCodeIdentifiers(doc) - doc = linkifyVocabIdentifiers(m, doc, classlist, proplist, instalist) - return "<p>%s</p>" % doc + + doc = xml.sax.saxutils.escape(string) + doc = linkifyCodeIdentifiers(doc) + doc = linkifyVocabIdentifiers(m, doc, classlist, proplist, instalist) + return "<p>%s</p>" % doc def getComment(m, subject, classlist, proplist, instalist): @@ -397,16 +399,11 @@ def getProperty(val, first=True): def endProperties(first): - if first: - return "</tr>" - else: - return "" + return "</tr>" if first else "" def rdfsPropertyInfo(term, m): """Generate HTML for properties: Domain, range""" - global classranges - global classdomains doc = "" label = getLabel(m, term) @@ -501,8 +498,8 @@ def getTermLink(uri, subject=None, predicate=None): extra, niceName(uri), ) - else: - return '<a href="%s" %s>%s</a>' % (uri, extra, niceName(uri)) + + return '<a href="%s" %s>%s</a>' % (uri, extra, niceName(uri)) def owlRestrictionInfo(term, m): @@ -560,8 +557,6 @@ def owlRestrictionInfo(term, m): def rdfsClassInfo(term, m): """Generate rdfs-type information for Classes: ranges, and domains.""" - global classranges - global classdomains doc = "" label = getLabel(m, term) @@ -730,8 +725,8 @@ def owlInfo(term, m): def owlTypeInfo(term, propertyType, name): if findOne(m, term, rdf.type, propertyType): return "<tr><th>Type</th><td>%s</td></tr>\n" % name - else: - return "" + + return "" res += owlTypeInfo(term, owl.DatatypeProperty, "Datatype Property") res += owlTypeInfo(term, owl.ObjectProperty, "Object Property") @@ -749,14 +744,14 @@ def isDeprecated(m, subject): return deprecated and (str(deprecated[2]).find("true") >= 0) -def docTerms(category, list, m, classlist, proplist, instalist): +def docTerms(category, termlist, m, classlist, proplist, instalist): """ A wrapper class for listing all the terms in a specific class (either Properties, or Classes. Category is 'Property' or 'Class', list is a list of term URI strings, return value is a chunk of HTML. """ doc = "" - for term in list: + for term in termlist: if not term.startswith(spec_ns_str): continue @@ -815,17 +810,17 @@ def docTerms(category, list, m, classlist, proplist, instalist): def getShortName(uri): uri = str(uri) if "#" in uri: - return uri.split("#")[-1] - else: - return uri.split("/")[-1] + return uri.split("#", maxsplit=1)[-1] + + return uri.split("/", maxsplit=1)[-1] def getAnchor(uri): uri = str(uri) if uri.startswith(spec_ns_str): return uri.replace(spec_ns_str, "").replace("/", "_") - else: - return getShortName(uri) + + return getShortName(uri) def buildIndex(m, classlist, proplist, instalist=None): @@ -839,8 +834,8 @@ def buildIndex(m, classlist, proplist, instalist=None): if str(t).startswith(spec_ns_str): name = termName(m, t) return '<a href="#%s">%s</a>' % (name, name) - else: - return '<a href="%s">%s</a>' % (str(t), str(t)) + + return '<a href="%s">%s</a>' % (str(t), str(t)) if len(classlist) > 0: head += '<th><a href="#ref-classes" />Classes</th>' @@ -923,22 +918,20 @@ def specInformation(m, ns): and proplist. Global variables classranges and classdomains are also filled as appropriate. """ - global classranges - global classdomains # Find the class information: Ranges, domains, and list of all names. classtypes = [rdfs.Class, owl.Class, rdfs.Datatype] classlist = [] for onetype in classtypes: for classStatement in findStatements(m, None, rdf.type, onetype): - for range in findStatements( + for rangelink in findStatements( m, None, rdfs.range, getSubject(classStatement) ): if not isBlank(getSubject(classStatement)): add( classranges, str(getSubject(classStatement)), - str(getSubject(range)), + str(getSubject(rangelink)), ) for domain in findStatements( m, None, rdfs.domain, getSubject(classStatement) @@ -1025,94 +1018,22 @@ def specAuthors(m, subject): maintdoc = "" first = True - for m in sorted(maint): + for name in sorted(maint): if not first: maintdoc += ", " - maintdoc += ( - f'<span class="author" property="doap:maintainer">{m}</span>' - ) + maintdoc += '<span class="author" property="doap:maintainer">' + maintdoc += name + maintdoc += "</span>" first = False - if len(maint): + + if maint: label = "Maintainer" if len(maint) == 1 else "Maintainers" doc += f'<tr><th class="metahead">{label}</th><td>{maintdoc}</td></tr>' return doc -def releaseChangeset(m, release, prefix=""): - changeset = findOne(m, release, dcs.changeset, None) - if changeset is None: - return "" - - entry = "" - # entry = '<dd><ul>\n' - for i in sorted(findStatements(m, getObject(changeset), dcs.item, None)): - item = getObject(i) - label = findOne(m, item, rdfs.label, None) - if not label: - print("error: dcs:item has no rdfs:label") - continue - - text = getLiteralString(getObject(label)) - if prefix: - text = prefix + ": " + text - - entry += "<li>%s</li>\n" % text - - # entry += '</ul></dd>\n' - return entry - - -def specHistoryEntries(m, subject, entries): - for r in findStatements(m, subject, doap.release, None): - release = getObject(r) - revNode = findOne(m, release, doap.revision, None) - if not revNode: - print("error: doap:release has no doap:revision") - continue - - rev = getLiteralString(getObject(revNode)) - - created = findOne(m, release, doap.created, None) - - dist = findOne(m, release, doap["file-release"], None) - if dist: - entry = '<dt><a href="%s">Version %s</a>' % (getObject(dist), rev) - else: - entry = "<dt>Version %s" % rev - # print("warning: doap:release has no doap:file-release") - - if created: - entry += " (%s)</dt>\n" % getLiteralString(getObject(created)) - else: - entry += ' (<span class="warning">EXPERIMENTAL</span>)</dt>' - - entry += "<dd><ul>\n%s" % releaseChangeset(m, release) - - if dist is not None: - entries[(getObject(created), getObject(dist))] = entry - elif int(rev.split(".")[-1]) % 2 == 0: - print("warning: %s %s has no file-release" % (subject, rev)) - - return entries - - -def specHistoryMarkup(entries): - if len(entries) > 0: - history = "<dl>\n" - for e in sorted(entries.keys(), reverse=True): - history += entries[e] + "</ul></dd>" - history += "</dl>\n" - return history - else: - return "" - - -def specHistory(m, subject): - return specHistoryMarkup(specHistoryEntries(m, subject, {})) - - def specVersion(m, subject): """ Return a (minorVersion, microVersion, date) tuple @@ -1192,12 +1113,12 @@ def load_tags(path, docdir): anchor, sym, ) - else: - return '<span><a href="%s/%s">%s</a></span>' % ( - docdir, - filename, - sym, - ) + + return '<span><a href="%s/%s">%s</a></span>' % ( + docdir, + filename, + sym, + ) tagdoc = xml.dom.minidom.parse(path) root = tagdoc.documentElement @@ -1208,7 +1129,6 @@ def load_tags(path, docdir): and cn.tagName == "compound" and cn.getAttribute("kind") != "page" ): - name = getChildText(cn, "name") filename = getChildText(cn, "filename") anchor = getChildText(cn, "anchor") @@ -1248,14 +1168,12 @@ def specgen( global spec_ns_str global spec_ns global spec_pre - global ns_list - global specgendir global linkmap spec_bundle = "file://%s/" % os.path.abspath(os.path.dirname(specloc)) # Template - with open(template_path, "r") as f: + with open(template_path, "r", encoding="utf-8") as f: template = f.read() # Load code documentation link map from tags file @@ -1340,6 +1258,7 @@ def specgen( proplist = docTerms( "Property", proplist, m, classlist, proplist, instalist ) + instlist = "" if instances: instlist = docTerms( "Instance", instalist, m, classlist, proplist, instalist @@ -1383,7 +1302,6 @@ def specgen( template = template.replace("@REFERENCE@", termlist) template = template.replace("@FILENAME@", filename) template = template.replace("@HEADER@", basename + ".h") - template = template.replace("@HISTORY@", specHistory(m, spec)) mail_row = "" if "list_email" in opts: @@ -1425,13 +1343,13 @@ def specgen( template = template.replace("@DESCRIPTION@", docs) now = int(os.environ.get("SOURCE_DATE_EPOCH", time.time())) - build_date = datetime.datetime.utcfromtimestamp(now) + build_date = datetime.datetime.fromtimestamp(now, datetime.timezone.utc) template = template.replace("@DATE@", build_date.strftime("%F")) template = template.replace("@TIME@", build_date.strftime("%F %H:%M UTC")) # Validate complete output page + oldcwd = os.getcwd() try: - oldcwd = os.getcwd() os.chdir(specgendir) etree.fromstring( template.replace( @@ -1452,7 +1370,7 @@ def specgen( def save(path, text): try: - with open(path, "w") as f: + with open(path, "w", encoding="utf-8") as f: f.write(text) f.flush() except Exception: @@ -1511,8 +1429,6 @@ def _data_dirs(): if __name__ == "__main__": - """Ontology specification generator tool""" - data_dir = None for d in _data_dirs(): path = os.path.join(d, "lv2specgen") diff --git a/lv2specgen/meson.build b/lv2specgen/meson.build index 9ecb8d4..bc3a616 100644 --- a/lv2specgen/meson.build +++ b/lv2specgen/meson.build @@ -1,5 +1,5 @@ -# Copyright 2022 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: CC0-1.0 OR ISC +# Copyright 2022-2025 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC lv2specgen_py = files('lv2specgen.py') @@ -10,32 +10,36 @@ lv2specgen_command_prefix = [ lv2specgen_py, '--list-email=' + lv2_list_email, '--list-page=' + lv2_list_page, + '--style-dir=' + lv2_source_root / 'doc' / 'style', + '--template', files('template.html'), ] if is_variable('lv2_tags') lv2specgen_command_prefix += [ - '--tags', lv2_tags.full_path(), # TODO: Remove full_path() in meson 0.60.0 + ['--tags', lv2_tags.full_path()], # TODO: Remove full_path() in meson 0.60.0 ] endif -install_data( - files('lv2specgen.py'), - install_dir: get_option('bindir'), - install_mode: 'rwxr-xr-x', -) - meson.override_find_program('lv2specgen.py', lv2specgen_py) -install_data( - files( - '../doc/style/pygments.css', - '../doc/style/style.css', - 'template.html', - ), - install_dir: get_option('datadir') / 'lv2specgen', -) - -install_subdir( - 'DTD', - install_dir: get_option('datadir') / 'lv2specgen', -) +if not get_option('tools').disabled() + install_data( + files('lv2specgen.py'), + install_dir: get_option('bindir'), + install_mode: 'rwxr-xr-x', + ) + + install_data( + files( + '../doc/style/pygments.css', + '../doc/style/style.css', + 'template.html', + ), + install_dir: get_option('datadir') / 'lv2specgen', + ) + + install_subdir( + 'DTD', + install_dir: get_option('datadir') / 'lv2specgen', + ) +endif diff --git a/lv2specgen/template.html b/lv2specgen/template.html index 4eb3d96..dcc2759 100644 --- a/lv2specgen/template.html +++ b/lv2specgen/template.html @@ -2,7 +2,6 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"> <html about="@URI@" xmlns="http://www.w3.org/1999/xhtml" - xmlns:dcs="http://ontologi.es/doap-changeset#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:doap="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" @@ -30,7 +29,6 @@ <table id="meta"> <tr><th>ID</th><td><a href="@URI@">@URI@</a></td></tr> <tr><th>Version</th><td>@VERSION@</td></tr> - <tr><th>Date</th><td>@DATE@</td></tr> @MAIL@ @AUTHORS@ </table> @@ -44,7 +42,6 @@ <ul id="contents"> <!-- <li><a href="#sec-description">Description</a></li> --> <li><a href="#sec-index">Index</a></li> - <li><a href="#sec-history">History</a></li> @CONTENT_LINKS@ </ul> </div> @@ -63,12 +60,6 @@ @REFERENCE@ </div> - <!-- HISTORY --> - <h2 id="sec-history">History</h2> - <div class="section"> - @HISTORY@ - </div> - <!-- FOOTER --> <div id="footer"> <div> |