Getting started with MoPy: our Python interface to the Music Ontology

OMRAS2 offers a head start to using the web of data if you plan to develop in Python: the MoPy library helps you to read and write RDF even if you have no knowledge of RDF whatsoever! MoPy converts RDF automatically to Python objects. It provides a Python interface to the Music Ontology, allowing for easy import/export of MO-based documents and manipulation of MO data.

Mopy depends on:

  1. rdflib ( v2.4.0 (easy_install)

Mopy can be checked out from the sourceforge motools project using svn
$ svn co motools

then build Mopy using
$ python ./motools/mopy/genpy

You will see Mopy collecting information about various ontologies. Since Mopy depends on ontologies that are still changing, it is not available as a static python egg. Instead, you will want to create a symbolic link to the Mopy directory in your working directory so you can import it.

$ cd workingdir
$ ln -s ~/motools/mopy/mopy mopy

Now let's open the python interpreter and give it a go...

[GCC 4.0.1 (Apple Computer, Inc. build 5341)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import mopy
>>> me = mopy.foaf.Person("")
>>> = "Joe Bloggs"
>>> artist ="")
AttributeError: 'module' object has no attribute 'music_artist'
>>> dir(
['AnalogSignal', 'Arrangement', 'Arranger', 'AudioFile', 'CD', 'Composer', 'Composition', 'Conductor',
'CorporateBody', 'DAT', 'DCC', 'DVDA', 'DigitalSignal', 'ED2K', 'Festival', 'Genre', 'Instrument',
'Instrumentation', 'Label', 'Libretto', 'Listener', 'Lyrics', 'MD', 'MagneticTape', 'Medium', 'Movement',
'MusicArtist', 'MusicGroup', 'MusicalExpression', 'MusicalItem', 'MusicalManifestation', 'MusicalWork',
'Orchestration', 'Performance', 'Performer', 'PublishedLibretto', 'PublishedLyrics', 'PublishedScore', 'Record',
'Recording', 'ReleaseStatus', 'ReleaseType', 'SACD', 'Score', 'Show', 'Signal', 'SoloMusicArtist', 'Sound',
'SoundEngineer', 'Stream', 'Torrent', 'Track', 'Vinyl', '__builtins__', '__doc__', '__file__', '__name__',
'__path__', 'album', 'audiobook', 'bootleg', 'compilation', 'ep', 'interview', 'live', 'mopy', 'official',
'promotion', 'remix', 'soundtrack', 'spokenword']
>>> artist ="")
>>> artist.nam = "Superstar Artist"
AttributeError: Not allowed add new attributes to classes ! Typo ?
>>> = "Superstar Artist"
>>> me.interest = artist
TypeError: Invalid type ! Got but expected one of :
>>> help (mopy.foaf.Person.interest)
Help on property:
A page about a topic of interest to this person.
>>> me.knows = artist

`mopy` Classes

Start by importing the mopy package :

>>> import mopy

Terms and instances are organised by namespace :

>>> dir(mopy)
['MusicInfo', 'PropertySet', 'RDFInterface', '__builtins__', '__doc__', '__file__', '__name__',
'__path__', 'chord', 'event', 'exportRDFFile', 'exportRDFGraph', 'foaf', 'frbr', 'geo', 'importRDFFile',
'importRDFGraph', 'key', 'mo', 'model', 'owl', 'rdfs', 'time', 'timeline', 'tzont']
>>> dir(mopy.key)
['Key', 'Note', '__builtins__', '__doc__', '__file__', '__name__', '__path__', 'mopy']

And you can pull terms from a particular ontology as you'd expect :

>>> from import MusicArtist, Record, Performance

To check what properties an object can take, use dir :

>>> dir(

Constructors take the URI of the resource, or if omitted, act as blank nodes :

>>> band1 =
>>> print band1
-- MusicGroup --
>>> band2 ="")
>>> print band2
-- MusicGroup @ --

`mopy` Properties

Properties are all treated as sets (so have no ordering), but we provide the shortcut of the '=' operator to assign a one element set :

>>> = "awesome band"
>>> print band1
-- MusicGroup --
name : awesome band
>>> member_names = ["Baz", "Bill", "Bob", "Clifton"]
>>> for m in member_names:
... member =
... = m
... band1.member.add(member)
>>> print band1
-- MusicGroup --
member :
member :
member :
member :
name : awesome band
>>> print "\n".join(str(x) for x in band1.member)
-- SoloMusicArtist --
name : Baz
-- SoloMusicArtist --
name : Bill
-- SoloMusicArtist --
name : Bob
-- SoloMusicArtist --
name : Clifton

`MusicInfo` objects

To bundle up multiple mopy objects for serialisation, you can use the `MusicInfo` class :

>>> mi = mopy.MusicInfo([band1] + list(band1.member))
>>> for o in mi.MusicGroupIdx.values():
... print o
-- MusicGroup @ --
member : @ blind:5429B6CE9A4F5096
member : @ blind:7EA44980E55B36F6
member : @ blind:367299F35DFDD780
member : @ blind:47FF277BE93C35CD
name : awesome band
>>> for o in mi.SoloMusicArtistIdx.values():
... print o
-- SoloMusicArtist @ blind:7EA44980E55B36F6 --
name : Bill
-- SoloMusicArtist @ blind:367299F35DFDD780 --
name : Bob
-- SoloMusicArtist @ blind:5429B6CE9A4F5096 --
name : Baz
-- SoloMusicArtist @ blind:47FF277BE93C35CD --
name : Clifton

Two python objects with the same URI will become a single python object within the MusicInfo object :

>>> mi2 = mopy.MusicInfo()
>>> a1 ="")
>>> = "Martin"
>>> a2 ="")
>>> = "Martin Jones"
>>> mi2.add(a1)
>>> mi2.add(a2)
>>> for o in mi2.MainIdx.values():
... print o
-- MusicArtist @ --
name : Martin Jones
name : Martin

The functions in mopy.RDFInterface allow for creation of MusicInfo objects by reading in RDF, and the serialisation of MusicInfo objects as RDF (in XML or N3 format).

>>> mopy.RDFInterface.exportRDFFile(mi, "greatband.rdf", "n3")


@prefix _25: .
@prefix foaf: .
@prefix mo: .
@prefix rdf: .
_25:us a mo:MusicGroup;
foaf:member [ a mo:SoloMusicArtist;
foaf:name "Bob"],
[ a mo:SoloMusicArtist;
foaf:name "Clifton"],
[ a mo:SoloMusicArtist;
foaf:name "Bill"],
[ a mo:SoloMusicArtist;
foaf:name "Baz"];
foaf:name "DEATH MONKEYS",
"awesome band".

Examples of `mopy` in use

* The [`gnat` project]( is using `mopy` to store information about a user's music collection.

eg. for metadata lookup : (from ``)

lookup = MbzTrackLookup(filename)
mbzuri = lookup.getMbzTrackURI()
mbzconvert = MbzURIConverter(mbzuri)
af = AudioFile(urlencode(os.path.basename(filename)))
mbz = Track(mbzconvert.getURI())
mbz.available_as = af
mi.add(af); mi.add(mbz)

* A more detailed use can be found in the fingerprinting file.

* Some Chord Ontology [convertor tools]( are using mopy's timeline and chord ontology support to convert existing transcription formats to RDF.

eg. from `` :

tl = RelativeTimeLine("#tl")
tl.label = "Timeline derived from "+infilename
tl.maker = program
intervalNum = 0
for line in lines:
i = Interval("#i"+str(intervalNum))
[start_s, end_s, label] = parseLabLine(line)
i.beginsAtDuration = secondsToXSDDuration(start_s)
i.endsAtDuration = secondsToXSDDuration(end_s)
i.label = "Interval containing "+label+" chord."
i.onTimeLine = tl

# Produce chord object for the label :
chordURI = ""+label.replace("#","s")
c = mopy.chord.Chord(chordURI)
c_event = mopy.chord.ChordEvent("#ce"+str(intervalNum))
c_event.chord = c
c_event.time = i
except Exception, e:
raise Exception("Problem parsing input file at line "+str(intervalNum+1)+" !\n"+str(e))

* The [MyPySpace]( project is using `mopy` to model FOAF relationships between MySpace users.


  1. Christopher Sutton : `chris (at) chrissutton (dot) org`
  2. Yves Raimond : `yves (at) dbtune (dot) org`