Skip to content

Commit

Permalink
Merge pull request paulhoule#1 from paulhoule/sphinx_rdf
Browse files Browse the repository at this point in the history
Sphinx rdf
  • Loading branch information
paulhoule authored Jan 2, 2018
2 parents c1da4c6 + d268e81 commit 02abe83
Show file tree
Hide file tree
Showing 6 changed files with 430 additions and 1 deletion.
7 changes: 6 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#

from pkg_resources import get_distribution
from rdflib import Graph,URIRef

extensions = ['sphinx.ext.autodoc',
'sphinx.ext.doctest',
Expand All @@ -15,7 +16,11 @@
'sphinx.ext.mathjax',
'sphinx.ext.ifconfig',
'sphinx.ext.viewcode',
'sphinx.ext.githubpages']
'sphinx.ext.githubpages',
'gastrodon.domain']

rdf_tbox=Graph()
rdf_tbox.parse(open("tbox.ttl"),format="ttl")

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
Expand Down
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Table of Contents
:caption: Table of Contents:

api
sphinx_metaobjects
uri_resolution_examples


Indices and tables
Expand Down
94 changes: 94 additions & 0 deletions docs/sphinx_metaobjects.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
Sphinx Metaobjects
==================

How do we do it?

:rdf:uri:`http://www.w3.org/2000/01/rdf-schema#Class`

In the abstract, Sphinx has an appealing capability of modelling multiple "domains"
simultaneously. This way we could write about a set of Python definitions (of modules,
methods, etc.) and also C definitions for associated code in the same project.

What would really be cool is to apply the same facilities to both code and model objects:
thus the REST API definition written in a language like RAML, with the Python code object.
Similarly I want to visualize OWL and RDF ontologies.

Some Use Cases
--------------

* Document an RDFS Ontology
* Document an OWL Ontology
* Document some domain described by an RDFS or OWL ontology (instances of the classes defined in the model)
* Document an set of EMOF definitions

Thoughts on Architecture
------------------------

There is not a lot of shared code between Sphinx domains; each domain has its own unique way of doing
things (can be customized), but the implementation of a new domain is tedious. If you want automatic
extraction of markup (eg. the :module:`sphinx.ext.autodoc`) extension that has to be done fresh for
each language you support. If you want to render summaries (eg. the :module:`sphinx.ext.autosummary`)
that is also domain dependent.

The following package

https://bitbucket.org/klorenz/sphinxcontrib-domaintools

helps you create domains with model-based definitions, but it doesn't do anything in the extraction
or summary departments. Rather than building on that, I decided to build a minimalist framework
for RDF and layer features on top of it. `sphinxcontrib-domaintools` does point out how dynamic we
can get. Based on data we have at initialization time (all or part of the RDF graph), we can
create new roles and directives. We could also create multiple instances of the RDF domain with
different graphs and configurations.

The Core vocabulary
-------------------

.. rst:role:: rdf:uri
The uri role. Use this role to refer to a named URI resource. The following formats are supported::

:rdf:uri:`rdfs:Class`
:rdf:uri:`http://www.w3.org/2000/01/rdf-schema#Class`
:rdf:uri:`<http://www.w3.org/2000/01/rdf-schema#Class>`

see :doc:`uri_resolution_examples` for details. If a namespace is declared, uris in that namespace
will be displayed in shortened form (eg. :rdf:uri:`rdfs:Class`) but will otherwise be displayed in long form
(eg. :rdf:uri:`http://example.com/`)

.. rst:directive:: rdf:subject
The `rdf:subject` directive contains a description of a named URI resource. You can think of this
as a description of the URI, or as a description of the thing the URI refers to. Here is an example::

.. rdf:subject:: rdfs:Class



:rdfs isDefinedBy: :rdf:uri:`http://www.w3.org/2000/01/rdf-schema#`
:rdfs label: Class
:rdfs comment: The class of classes.
:rdfs subClassOf: :rdf:uri:`rdfs:Resource`

which renders like

.. rdf:subject:: rdfs:Class
This is what I have to say about this wooly and wonderful subject!

:rdfs isDefinedBy: :rdf:uri:`http://www.w3.org/2000/01/rdf-schema#`
:rdfs label: - Class
:rdfs comment: The class of classes.
:rdfs subClassOf: :rdf:uri:`rdfs:Resource`

Triples are represented using the `Field List`_ mechanism in reStructuredText. Because colons are not
allowed in field names without escaping, the prefix is separated from the localname with a space.

I see two different ways to move forward with this. The existing :class:`sphinx.directives.ObjectDescription`
contains a fairly complex implementation which could possibly be used if we load a list of all predicates
into the TBox, so that a set of field definitions can be generated. The other possibility is to develop
something which is RDF Native. I'll need to look over the code and sleep on it. This may also be a good
time to bring the branch back to master, because I think the current implementation is a keeper, but
I might want to do development of property displays on a new branch to manage risk.

.. _Field List: http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#field-lists
109 changes: 109 additions & 0 deletions docs/tbox.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix dc: <http://purl.org/dc/elements/1.1/> .

<http://www.w3.org/2000/01/rdf-schema#> a owl:Ontology ;
dc:title "The RDF Schema vocabulary (RDFS)" .

rdfs:Resource a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "Resource" ;
rdfs:comment "The class resource, everything." .

rdfs:Class a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "Class" ;
rdfs:comment "The class of classes." ;
rdfs:subClassOf rdfs:Resource .

rdfs:subClassOf a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "subClassOf" ;
rdfs:comment "The subject is a subclass of a class." ;
rdfs:range rdfs:Class ;
rdfs:domain rdfs:Class .

rdfs:subPropertyOf a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "subPropertyOf" ;
rdfs:comment "The subject is a subproperty of a property." ;
rdfs:range rdf:Property ;
rdfs:domain rdf:Property .

rdfs:comment a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "comment" ;
rdfs:comment "A description of the subject resource." ;
rdfs:domain rdfs:Resource ;
rdfs:range rdfs:Literal .

rdfs:label a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "label" ;
rdfs:comment "A human-readable name for the subject." ;
rdfs:domain rdfs:Resource ;
rdfs:range rdfs:Literal .

rdfs:domain a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "domain" ;
rdfs:comment "A domain of the subject property." ;
rdfs:range rdfs:Class ;
rdfs:domain rdf:Property .

rdfs:range a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "range" ;
rdfs:comment "A range of the subject property." ;
rdfs:range rdfs:Class ;
rdfs:domain rdf:Property .

rdfs:seeAlso a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "seeAlso" ;
rdfs:comment "Further information about the subject resource." ;
rdfs:range rdfs:Resource ;
rdfs:domain rdfs:Resource .

rdfs:isDefinedBy a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:subPropertyOf rdfs:seeAlso ;
rdfs:label "isDefinedBy" ;
rdfs:comment "The defininition of the subject resource." ;
rdfs:range rdfs:Resource ;
rdfs:domain rdfs:Resource .

rdfs:Literal a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "Literal" ;
rdfs:comment "The class of literal values, eg. textual strings and integers." ;
rdfs:subClassOf rdfs:Resource .

rdfs:Container a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "Container" ;
rdfs:subClassOf rdfs:Resource ;
rdfs:comment "The class of RDF containers." .

rdfs:ContainerMembershipProperty a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "ContainerMembershipProperty" ;
rdfs:comment """The class of container membership properties, rdf:_1, rdf:_2, ...,
all of which are sub-properties of 'member'.""" ;
rdfs:subClassOf rdf:Property .

rdfs:member a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "member" ;
rdfs:comment "A member of the subject resource." ;
rdfs:domain rdfs:Resource ;
rdfs:range rdfs:Resource .

rdfs:Datatype a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
rdfs:label "Datatype" ;
rdfs:comment "The class of RDF datatypes." ;
rdfs:subClassOf rdfs:Class .

<http://www.w3.org/2000/01/rdf-schema#> rdfs:seeAlso <http://www.w3.org/2000/01/rdf-schema-more> .
85 changes: 85 additions & 0 deletions docs/uri_resolution_examples.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
URI Resolution Examples
=======================

Probably the best way to explain URI resolution in roles and directives is to work through some examples. The
work is done internally with a :class:`gastrodon.domain.UriResolver`, which takes both a set of namespace
declarations and a base URI.

.. testsetup:: *

from gastrodon.domain import UriResolver

.. doctest::

>>> x=UriResolver(
... {
... "rdfs":"http://www.w3.org/2000/01/rdf-schema#",
... "dc":"http://purl.org/dc/elements/1.1/"
... },
... "http://dbpedia.org/resource/"
... )

Writing URIs in Sphinx Markup
-----------------------------

In either a directive or a role, the target or signature is processed to a URI string for indexing,
so that no matter how a URI is written, the URI will be matched inside Sphinx. Plain URIs can be written
with or without the angle brackets that one would use to write them in Turtle. URIs are resolved relative
to the base URI in case a URI is not complete.

.. doctest::

>>> x.any_to_uri("Curry")
'http://dbpedia.org/resource/Curry'
>>> x.any_to_uri("<Proton>")
'http://dbpedia.org/resource/Proton'
>>> x.any_to_uri("<..>")
'http://dbpedia.org/'
>>> x.any_to_uri("/ontology/Person")
'http://dbpedia.org/ontology/Person'
>>> x.any_to_uri("http://slashdot.org/")
'http://slashdot.org/'
>>> x.any_to_uri("<http://reddit.com/>")
'http://reddit.com/'

It is also possible to write URIs as *Qualified Names* (QNames) or at least an close approximation. If a
URI does not contain angle brackets, and begins with a declared prefix, it is resolved relative to that
namespace.

.. doctest::

>>> x.any_to_uri("rdfs:Class")
'http://www.w3.org/2000/01/rdf-schema#Class'
>>> x.any_to_uri("dc:title")
'http://purl.org/dc/elements/1.1/title'

The difference between this convention and QNames is that, in Sphinx markup, you can use an unlimited range
of characters after the prefix, whereas the legacy of XML means that QNames don't allow constructions like

.. doctest::

>>> x.any_to_uri("dc:Work/title")
'http://purl.org/dc/elements/1.1/Work/title'

(This is not a real predicate from the Dublin Core vocabulary, but this is useful while documenting
certain vocabularies, such as DBpedia, where you will find slashes, parenthesis and other unusual
characters in the URIs.)

How URIs are displayed in Sphinx Markup
---------------------------------------

When displaying titles for roles and directives, Sphinx markup is processed through the
:method:gastrodon.domain.UriResolver.humanize_uri` method, which displays URIs in a human-friendly
manner. Note that short URIs might contain characters that are not valid in QNames.


.. doctest::

>>> x.humanize_uri("http://www.w3.org/2000/01/rdf-schema#Class")
'rdfs:Class'
>>> x.humanize_uri("http://purl.org/dc/elements/1.1/title")
'dc:title'
>>> x.humanize_uri("https://tonyortega.org/")
'<https://tonyortega.org/>'
>>> x.humanize_uri("http://dbpedia.org/resource/Fishbone")
'<Fishbone>'
Loading

0 comments on commit 02abe83

Please sign in to comment.