subclassing, attribute problem

13 views
Skip to first unread message

Bruce D'Arcus

unread,
May 14, 2008, 10:28:01 AM5/14/08
to rdfalchemy-dev
I managed to answer my own question from yesterday, but am stuck on
two issues. I have a model file that looks like:

#!/usr/bin/env python
# encoding: utf-8

from darcusb.config.namespaces import BIBO, DC, FOAF
from rdfalchemy import rdfSubject, rdfSingle, rdfMultiple

class Document(rdfSubject):
rdf_type = BIBO.Document
title = rdfSingle(DC.title)
alt_titles = rdfMultiple(DC.alt)
date = rdfSingle(DC.date)
issued = rdfSingle(DC.issued)
creators = rdfMultiple(DC.creator)
authorList = rdfMultiple(BIBO.authorList)
subjects = rdfMultiple(DC.subject)

class Collection(rdfSubject):
rdf_type = BIBO.Collection
publisher = rdfSingle(DC.publisher,range_type=FOAF.Organization)

class Series(Collection):
rdf_type = BIBO.Series

class Article(Document):
rdf_type = BIBO.Article
volume = rdfSingle(BIBO.volume)
issue = rdfSingle(BIBO.issue)
pages = rdfSingle(BIBO.pages)
page_start = rdfSingle(BIBO.pageStart)
page_end = rdfSingle(BIBO.pageEnd)
periodical = rdfSingle(DC.isPartOf)

class AcademicArticle(Article):
rdf_type = BIBO.AcademicArticle
peer_reviewed = rdfSingle(BIBO.peerReviewed)
doi = rdfSingle(BIBO.doi)

class Periodical(rdfSubject):
rdf_type = BIBO.Periodical
title = rdfSingle(DC.title)
issn = BIBO.issn
publisher = rdfSingle(DC.publisher,range_type=FOAF.Organization)

class Journal(Periodical):
rdf_type = BIBO.Journal

class Chapter(Document):
# reference to a Book
book = rdfSingle(DC.isPartOf, range_type=BIBO.Book)

class Review(Article):
# reviewed resource
rdf_type = BIBO.Review
review_of = rdfSingle(BIBO.reviewOf)

class Book(Document):
rdf_type = BIBO.Book
publisher = rdfSingle(DC.publisher,range_type=FOAF.Organization)
series = rdfSingle(DC.isPartOf, range_type=BIBO.Series)

... but subclassing isn't working quite like I expect, and there's
some problem with accessing title properties. From the interactive
session:

>>> from darcusb.models.bibo import *
>>> from rdfalchemy.orm import mapper
>>> from rdfalchemy.engine import *
>>> rdfSubject.db = create_engine('sqlite:///tmp/rdf_sql')
>>> len(rdfSubject.db)
55
>>> print(rdfSubject.db.serialize(format="n3"))

@prefix _10: <info:doi/10.1068/>.
@prefix _11: <http://purl.org/net/darcusb/topics#>.
@prefix _12: <http://purl.org/net/darcusb/organizations#>.
@prefix _7: <http://purl.org/net/darcusb/publications#>.
@prefix _8: <http://purl.org/net/darcusb/info#>.
@prefix bibo: <http://purl.org/ontology/bibo/>.
@prefix dcterms: <http://purl.org/dc/terms/>.
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.

_7:DArcus2004b a bibo:BookSection;
dcterms:creator _8:me;
dcterms:isPartOf <urn:isbn:1402016131>;
dcterms:issued "2004"^^<NULL>;
dcterms:title "Globalization and Protest: Seattle and
Beyond"^^<NULL>;
bibo:pageEnd "24"^^<NULL>;
bibo:pageStart "22"^^<NULL>.

<info:doi/10.1016/S0962-6298(02)00107-5> a bibo:AcademicArticle;
dcterms:creator _8:me;
dcterms:isPartOf <urn:isbn:0962-6298>;
dcterms:issued "2003"^^<NULL>;
dcterms:title "Contested Boundaries: Native Sovereignty and State
Power at Wounded Knee, 1973"^^<NULL>;
bibo:issue "4"^^<NULL>;
bibo:pageEnd "437"^^<NULL>;
bibo:pageStart "415"^^<NULL>;
bibo:volume "22"^^<NULL>.

<info:doi/10.1046/j.1467-8330.2003.00347.x> a bibo:AcademicArticle;
dcterms:creator _8:me;
dcterms:isPartOf <urn:issn:0066-4812>;
dcterms:issued "2003"^^<NULL>;
dcterms:title "Protest, Scale and Publicity: The FBI and the H.
Rap Brown Act"^^<NULL>;
bibo:issue "4"^^<NULL>;
bibo:pageEnd "741"^^<NULL>;
bibo:pageStart "718"^^<NULL>;
bibo:volume "35"^^<NULL>.

_10:d11s a bibo:AcademicArticle;
dcterms:creator _8:me;
dcterms:isPartOf <urn:issn:0263-7758>;
dcterms:issued "2000"^^<NULL>;
dcterms:title "The “Eager Gaze of the Tourist” Meets “Our
Grandfathers’ Guns”: Producing and Contesting the Land of Enchantment
in Gallup, New Mexico"^^<NULL>;
bibo:issue "6"^^<NULL>;
bibo:pageEnd "714"^^<NULL>;
bibo:pageStart "693"^^<NULL>;
bibo:volume "18"^^<NULL>.

<info:doi/10.1080/1356257042000309652> a bibo:AcademicArticle;
dcterms:creator _8:me;
dcterms:isPartOf <urn:issn:1356-2576>;
dcterms:issued "2004"^^<NULL>;
dcterms:title "Dissent, Public Space and the Politics of
Citizenship: Riots and the “Outside Agitator”"^^<NULL>;
bibo:issue "3"^^<NULL>;
bibo:pageEnd "370"^^<NULL>;
bibo:pageStart "355"^^<NULL>;
bibo:volume "8"^^<NULL>.

<info:oclcnum/60500684> a bibo:Book;
dcterms:creator _8:me;
dcterms:date "2006"^^<NULL>;
dcterms:publisher _12:routledge;
dcterms:subject _11:protest,
_11:public_space,
_11:state;
dcterms:title "Boundaries of Dissent: Protest and State Power in
the Media Age"^^<NULL>;
bibo:isbn "041594872X"^^<NULL>,
"0415948738"^^<NULL>;
bibo:lccn "2005013429"^^<NULL>;
bibo:oclcnum "60500684"^^<NULL>.
>>> for a in AcademicArticle.ClassInstances():
... print(a.issued)
...
2003
2004
2003
2000

OK, let's pause, and note that everything is working fine so far.

Now, problem #1:

>>> for a in AcademicArticle.ClassInstances():
... print(a.title)
...
None
None
None
None

I can't figure out why the titles aren't getting returned. Am I doing
something wrong here? The namespace can't be wrong, because the
"issued" attribute is using the same namespace.

Problem #2:

>>> for a in Article.ClassInstances():
... print(a.title)
...
>>>

Shouldn't that return all instances of AcademicArticle too?

Bruce

Philip Cooper

unread,
May 14, 2008, 11:25:45 AM5/14/08
to rdfalch...@googlegroups.com, rdfli...@googlegroups.com
Bruce D'Arcus at about 5/14/08 8:28 AM said:
> I managed to answer my own question from yesterday, but am stuck on
> two issues. I have a model file that looks like:
>
> #!/usr/bin/env python
> # encoding: utf-8
>
> from darcusb.config.namespaces import BIBO, DC, FOAF
> from rdfalchemy import rdfSubject, rdfSingle, rdfMultiple
>
> class Document(rdfSubject):
> rdf_type = BIBO.Document
> title = rdfSingle(DC.title)

OK let me stop you there.

This is an issuse with Namespace

rdflib.Namespace decends from unicode which defines a func for title.
Things should work much better if you go with

DC['title'] rather than DC.title

I've asked the question of rdflib'ers here:
http://groups.google.com/group/rdflib-dev/browse_thread/thread/3b2279e691ec4d4d


This is a common problem in biblio land. Since I already modify the
rdflib Literal, maybe I should stick in my own Namespace def. I was
thinking that the rdf-python ecosystem would be better off if rdflib got
the fix. That codebase is moving forward again.


--
Phil


philip_cooper.vcf

Bruce D'Arcus

unread,
May 14, 2008, 1:33:43 PM5/14/08
to rdfalchemy-dev


On May 14, 11:25 am, Philip Cooper <philip.coo...@openvest.com> wrote:

> This is an issuse with Namespace
>
> rdflib.Namespace decends from unicode which defines a func for title.
> Things should work much better if you go with
>
>  DC['title'] rather than DC.title

OK, that solved it. Thanks.

And what about the other component to my question:

>>> for a in Article.ClassInstances():
... print(a.title)
...

Shouldn't that return all instances of AcademicArticle (which is a
subclass of Article) too?

Bruce

Philip Cooper

unread,
May 14, 2008, 4:25:50 PM5/14/08
to rdfalch...@googlegroups.com
Bruce D'Arcus at about 5/14/08 11:33 AM said:
>>>> for a in Article.ClassInstances():
> ... print(a.title)
> ...
>
> Shouldn't that return all instances of AcademicArticle (which is a
> subclass of Article) too?
>

Well that's a mighty big question. The short answer is that I've
started work on rdfsSubject

(rdfsSubject as a subclass of rdfSubject) sorry about how visually
subtle the difference is but it should be intuitive that way.

As a fix to a previous ticket{23} I just posted a upgrade to
ClassInstances(). I have also just posted the first cut at
rdfsSubject. the "s" in rdfs refers to more schema info being handled.

rdfSubject will do as it does now...ignoring subclasses. rdfsSubject
will do some inferencing and it has a version of ClassInstances() that
should perform as you expected. (maybe this should be an optional arg
like ClassInstances(infer=True)

Some triplestores have an option of turning inferencing on (sesame)
and others provide no support. There is also the question as to how
much of the inferencing logic can be ported from or even better yet
plugged in from projects like pychinko[1].

I don't have the time to head down the long and complicated road of
doing all owl inferencing (should subProperties to show up now?).

Anyway, rdfalchemy is based on a practical philosophy. Academics have
experimented with this stuff long enough and your request seems pretty
reasonable so I've put it in.

Kind of....I haven't written the test cases but if you shoot me a
sample of your rdf, I'll work it into the unit tests.

--
Phil

[1] http://www.mindswap.org/~katz/pychinko/

philip_cooper.vcf

Bruce D'Arcus

unread,
May 14, 2008, 4:57:19 PM5/14/08
to rdfalchemy-dev


On May 14, 4:25 pm, Philip Cooper <philip.coo...@openvest.com> wrote:

...

> rdfSubject will do as it does now...ignoring subclasses. rdfsSubject
> will do some inferencing and it has a version of ClassInstances() that
> should perform as you expected. (maybe this should be an optional arg
> like ClassInstances(infer=True)
>
> Some triplestores have an option of turning inferencing on (sesame)
> and others provide no support. There is also the question as to how
> much of the inferencing logic can be ported from or even better yet
> plugged in from projects like pychinko[1].
>
> I don't have the time to head down the long and complicated road of
> doing all owl inferencing (should subProperties to show up now?).
>
> Anyway, rdfalchemy is based on a practical philosophy. Academics have
> experimented with this stuff long enough and your request seems pretty
> reasonable so I've put it in.

Yeah, I know there are some challenges to all this, but I was coming
at this from the experience not so much with RDF reasoners, but rather
standard ORMs (ActiveRecord, etc.). I was just assuming that if I
define a mapping class as a subclass, the querying would take that
into account.

But I guess RDF inheritance is not exactly Python inheritance.

I wonder how this is handled in similar projects like ActiveRDF or
Oort?

> Kind of....I haven't written the test cases but if you shoot me a
> sample of your rdf, I'll work it into the unit tests.

OK, will send it off-list when I get a chance.

Bruce

Bruce

unread,
Jun 4, 2008, 2:21:34 PM6/4/08
to rdfalchemy-dev
I'd like to come back to this thread ...

On May 14, 4:25 pm, Philip Cooper <philip.coo...@openvest.com> wrote:
> Bruce D'Arcus at about 5/14/08 11:33 AM said:
>
> >>>> for a in Article.ClassInstances():
> > ...         print(a.title)
> > ...
>
> > Shouldn't that return all instances of AcademicArticle (which is a
> > subclass of Article) too?
>
> Well that's a mighty big question.  The short answer is that I've
> started work on rdfsSubject

...

So I have this little web app using RDFAlchemy and web.py*. I have a
router that looks like this:

class DocItem:
def GET(self, basedir, slug):
uri = config['rdfalchemy.identifier'] + basedir + slug
doc = Document(URIRef(uri))
content_uri = doc.formats[0] if doc.formats else None
content = get_content(basedir, content_uri.resUri) if
content_uri else None
return(render.item(doc, content))

Note the "doc" bit, which is where I grab the Document instance based
on the URI. This works nicely, except for one little problem:

If that Document is in fact an Article subclass, that gets lost, and
the attributes of that subclass don't obviously carry.

So my question is: would rdfsSubject be appropriate to solve this
issue? Does this approach even make sense? Any suggestions?

Bruce

* <http://github.com/bdarcus/mysite/tree/master>

Bruce

unread,
Jun 6, 2008, 4:17:31 PM6/6/08
to rdfalchemy-dev
Let me show what's confusing to me on this (as I was just
experimenting more):

>>> x = AcademicArticle('<http://ex.net/1>')
>>> x.rdf_type
rdflib.URIRef('http://purl.org/ontology/bibo/AcademicArticle')
>>> y = Document('<http://ex.net/1>')
>>> y == x
True
>>> y.rdf_type
rdflib.URIRef('http://purl.org/ontology/bibo/Document')
>>> x.rdf_type
rdflib.URIRef('http://purl.org/ontology/bibo/AcademicArticle')

I get the same results when using the rdfsSubject.

If x == y, then shouldn't they have the same (probably two) rdf_type
attributes? IN the case, of rdfsSubject, it seems it should definitely
privilege the subclass.

Bruce

Philip Cooper

unread,
Jun 7, 2008, 6:10:51 PM6/7/08
to rdfalch...@googlegroups.com
Bruce at about 6/6/08 2:17 PM said:
> Let me show what's confusing to me on this (as I was just
> experimenting more):
>
> >>> x = AcademicArticle('<http://ex.net/1>')
> >>> x.rdf_type
> rdflib.URIRef('http://purl.org/ontology/bibo/AcademicArticle')
> >>> y = Document('<http://ex.net/1>')
> >>> y == x
> True
> >>> y.rdf_type
> rdflib.URIRef('http://purl.org/ontology/bibo/Document')
> >>> x.rdf_type
> rdflib.URIRef('http://purl.org/ontology/bibo/AcademicArticle')
>
> I get the same results when using the rdfsSubject.
>
> If x == y, then shouldn't they have the same (probably two)
> rdf_type attributes?

I could have x == y return False but that would be a bad idea.
Equivalence does not necessarily require class equivelance. Compare
your example to:

>>> a = int('3')
>>> b = float('3')
>>> a == b
True
>>> 2/a
0
>>> 2/b
0.66666666666666663

In your example, the AcedemicArticle x and the Document y ARE the same
article .... just with different interfaces. And the interfaces
assigned them were done by the developer so he(or she) should know
what to expect. Developers will probably want to use this as a
feature rather than fear it as a bug.
(time and googlegroups will tell)

Re: what does x.rdf_type return, you could if you needed to, declare
a types descriptor:

class C(rdfSubject):
...
rdf_types = rdfMultiple(RDF.type)

Then x.rdf_types would return whatever (multiple) class type is in the
db.

The general reason for rdfsSubject will be for instances that will
match an rdf:type in the db to an appropriate python class. Something
like Zope interfaces but it might be home grown...stay tuned.

> IN the case, of rdfsSubject, it seems it should definitely
> privilege the subclass.

Not sure I am on point with you here but: any instance (x) of
rdfSubject or any decendent, when asked for x.rdf_type will return
rdf_type by looking in:

self.rdf_type (empty by default but you can set it e.g. in __init__)
self.__class__.rdf_type (where it usually is found)
the mro search for the rdf_type of a superclass

Standard (albeit not often used) python stuff there.

--
Phil

Reply all
Reply to author
Forward
0 new messages