Previous topic

music21.sieve

Next topic

music21.spanner

Table Of Contents

Table Of Contents

This Page

music21.sites

sites.py – Objects for keeping track of relationships among Music21Objects

SiteRef

class music21.sites.SiteRef

a single Site (stream, container, parent, reference, etc.) stored inside the Sites object.

A very simple object.

This site would be stored in the .sites.siteDict for, say a note.Note, if it were in st at offset 20.0

>>> st = stream.Stream()
>>> st.id = 'hi'
>>> s = sites.SiteRef()
>>> s.classString = st.classes[0]
>>> s.site = st
>>> s.offset = 20.0
>>> s.isDead
False

If you call s.site, you always get an object out, but internally, there’s a .siteWeakref that stores a weakref to the site.

>>> s.site
<music21.stream.Stream hi>
>>> s.siteWeakref
<weakref at 0x...; to 'Stream' at 0x...>

Ways of working with tuplets:

>>> s.offset = 1/3.0
>>> s.offset
Fraction(1, 3)
>>> s.offsetRational # same
Fraction(1, 3)
>>> s.offsetFloat
0.333...

SiteRef bases

SiteRef read/write properties

SiteRef.offset

returns the offset without conversion to float...

SiteRef.offsetFloat

synonym for offset

SiteRef.offsetRational

returns the offset without conversion to float...

SiteRef.site

Sites

class music21.sites.Sites(containedById=None)

An object, stored within a Music21Object, that stores (weak) references to a collection of objects that may be contextually relevant to this object.

Some of these objects are locations (also called sites), or Streams that contain this object. In this case the Sites object stores an offset value, used for determining position within a Stream.

All defined contexts are stored as dictionaries in a dictionary. The outermost dictionary stores objects.

Sites bases

Sites methods

Sites.add(obj, offset=None, timeValue=None, idKey=None, classString=None)

Add a reference to the Sites collection for this object.

N.B. – like all .sites operations, this is an advanced tool not for standard music21 usage. Instead of:

elObj.add(streamObj, 20.0)

use this command, which will take care of .sites.add as well as putting elObj in streamObj.elements:

streamObj.insert(20.0, elObj)

If offset is None, then obj is interpreted as a Context (such as a temperament, a time period, etc.)

If offset is not None, then obj is interpreted as location, i.e., a Stream.

offset can also be the term highestTime which is the highest available time in the obj (used for streamObj.append(el))

The timeValue argument is used to store the time as an int (in milliseconds after Jan 1, 1970) when this object was added to locations. If set to None, then the current time is used.

idKey stores the id() of the obj. If None, then id(obj) is used.

classString stores the class of obj. If None then obj.classes[0] is used.

TODO: Tests. Including updates.

Sites.clear()

Clear all stored data.

Sites.get(locationsTrail=False, sortByCreationTime=False, priorityTarget=None, excludeNone=False)

Get references; order, based on dictionary keys, is from most recently added to least recently added.

The locationsTrail option forces locations to come after all other defined contexts.

The sortByCreationTime option will sort objects by creation time, where most-recently assigned objects are returned first. Can be [False, other], [True, 1] or [‘reverse’, -1]

If priorityTarget is defined, this object will be placed first in the list of objects.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aObj = Mock()
>>> bObj = Mock()
>>> cObj = Mock()
>>> aSites = music21.Sites()
>>> aSites.add(cObj, 345) # a locations
>>> aSites.add(aObj)
>>> aSites.add(bObj)
>>> aSites.get() == [cObj, aObj, bObj]
True
>>> aSites.get(locationsTrail=True) == [aObj, bObj, cObj]
True
>>> aSites.get(sortByCreationTime=True) == [bObj, aObj, cObj]
True
Sites.getAllByClass(className, found=None, idFound=None, memo=None)

Return all known references of a given class found in any association with this Sites object.

This will recursively search the defined contexts of existing defined contexts, and return a list of all objects that match the given class.

>>> import music21
>>> class Mock(music21.Music21Object):
...    pass
...
>>> class Mocker(music21.Music21Object):
...    pass
...
>>> aObj = Mock()
>>> bObj = Mock()
>>> cObj = Mocker()
>>> dc = music21.Sites()
>>> dc.add(aObj)
>>> dc.add(bObj)
>>> dc.add(cObj)
>>> dc.getAllByClass(Mock) == [aObj, bObj]
True
Sites.getAttrByName(attrName)

Given an attribute name, search all objects and find the first that matches this attribute name; then return a reference to this attribute.

>>> import music21
>>> class Mock(music21.Music21Object):
...     attr1 = 234
...
>>> aObj = Mock()
>>> aObj.attr1 = 234
>>> bObj = Mock()
>>> bObj.attr1 = 98
>>> aSites = music21.Sites()
>>> aSites.add(aObj)
>>> len(aSites)
1
>>> aSites.getAttrByName('attr1') == 234
True
>>> aSites.remove(aObj)
>>> aSites.add(bObj)
>>> aSites.getAttrByName('attr1') == 98
True
Sites.getById(siteId)

Return the object specified by an id. Used for testing and debugging.

Sites.getObjByClass(className, serialReverseSearch=True, callerFirst=None, sortByCreationTime=False, prioritizeActiveSite=False, priorityTarget=None, getElementMethod='getElementAtOrBefore', memo=None)

Return the most recently added reference based on className. Class name can be a string or the class name.

This will recursively search the sitesDicts of objects in Site objects in the siteDict.

The callerFirst parameters is simply used to pass a reference of the first caller; this is necessary if we are looking within a Stream for a flat offset position.

If priorityTarget is specified, this location will be searched first. The prioritizeActiveSite is pased to to any recursively called getContextByClass() calls.

The getElementMethod is a string that selects which Stream method is used to get elements for searching with getElementsByClass() calls.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> import time
>>> aObj = Mock()
>>> bObj = Mock()
>>> aSites = music21.Sites()
>>> aSites.add(aObj)
>>> aSites.add(bObj)
>>> # we get the most recently added object first
>>> aSites.getObjByClass('Mock', sortByCreationTime=True) == bObj
True
>>> aSites.getObjByClass(Mock, sortByCreationTime=True) == bObj
True
Sites.getOffsetByObjectMatch(obj, returnType='rational')

For a given object, return the offset using a direct object match. The stored id value is not used; instead, the id() of both the stored object reference and the supplied object is used.

This should be replaced by getOffsetBySite(strictDeadCheck = True)...

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> aLocations = music21.Sites()
>>> aLocations.add(aSite, 5)
>>> aLocations.add(bSite, 3.2)
>>> aLocations.getOffsetByObjectMatch(aSite)
5.0
>>> aLocations.getOffsetByObjectMatch(bSite)
Fraction(16, 5)
>>> aLocations.getOffsetByObjectMatch(bSite, returnType='float')
3.2...
Sites.getOffsetBySite(siteObj, returnType='rational')

For a given site return this Sites’s offset in it. The None site is permitted. The id() of the site is used to find the offset.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> aLocations = music21.Sites()
>>> aLocations.add(aSite, 23)
>>> aLocations.add(bSite, 121.5)
>>> aLocations.getOffsetBySite(aSite)
23.0
>>> aLocations.getOffsetBySite(bSite)
121.5

The object might not actually be in the _elements for the site object, because it may be a deep copy, etc. but the number is still returned.

Sites.getOffsetBySiteId(idKey, strictDeadCheck=False, returnType='rational')

Main method for getting an offset from a location key.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> cSite = Mock()
>>> dSite = Mock()
>>> eSite = Mock()
>>> sitesObj = music21.Sites()
>>> sitesObj.add(aSite, 0)
>>> sitesObj.add(cSite) # a context
>>> sitesObj.add(bSite, 234) # can add at same offset or a different one
>>> sitesObj.add(dSite) # a context
>>> sitesObj.getOffsetBySiteId(id(bSite))
234.0

If strictDeadCheck is False (default) we can still retrieve the context from a dead weakref. This is necessary to get the offset from an iterated Stream often. Eventually, this should become True – but too many errors for now.

>>> idBSite = id(bSite)
>>> del(bSite)
>>> sitesObj.siteDict[idBSite].siteWeakref
<weakref at 0x...; dead>
>>> sitesObj.siteDict[idBSite].siteWeakref is None
False
>>> sitesObj.siteDict[idBSite].site is None
True
>>> sitesObj.getOffsetBySiteId(idBSite, strictDeadCheck = False) # default
234.0

With this, you’ll get an exception:

>>> sitesObj.getOffsetBySiteId(idBSite, strictDeadCheck = True)
Traceback (most recent call last):
SitesException: Could not find the object with id ... in the Site marked with idKey ... (was there, now site is dead).
object <music21.sites.Sites object at 0x...>, sitesDict: {...}
containedById = ...
Sites.getOffsets(returnType='rational')

Return a list of all offsets.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> cSite = Mock()
>>> dSite = Mock()
>>> sitesObj = music21.Sites()
>>> sitesObj.add(aSite, 0)
>>> sitesObj.add(cSite) # a context -- no offset
>>> sitesObj.add(bSite, 2.33333333333) # can add at same offset or another
>>> sitesObj.add(dSite) # a context -- no offset
>>> sitesObj.getOffsets()
[0.0, Fraction(7, 3)]

Can call returnType = 'float' instead:

>>> sitesObj.getOffsets(returnType='float')
[0.0, 2.3333...]
Sites.getSiteByOffset(offset)

For a given offset return the site that fits it

More than one Site may have the same offset; this at one point returned the last site added by sorting time, but now we use a dict, so there’s no guarantee that the one you want will be there – need orderedDicts!

>>> import fractions
>>> class Mock(base.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> cSite = Mock()
>>> sitesObj = sites.Sites()
>>> sitesObj.add(aSite, 2)
>>> sitesObj.add(bSite, 10.0/3)
>>> aSite is sitesObj.getSiteByOffset(2)
True
>>> bSite is sitesObj.getSiteByOffset(fractions.Fraction(10, 3))
True
>>> bSite is sitesObj.getSiteByOffset(3.33333333333)
True
Sites.getSiteCount()

Return the number of non-dead sites, excluding the None site. This does not unwrap weakrefs for performance.

Sites.getSiteIds()

Return a list of all site Ids.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> dc = music21.Sites()
>>> dc.add(aSite, 0)
>>> dc.add(bSite) # a context
>>> dc.getSiteIds() == [id(aSite)]
True
Sites.getSites(idExclude=None, excludeNone=False)

Get all Site objects in .siteDict that are locations. Note that this unwraps all sites from weakrefs and is thus an expensive operation.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aObj = Mock()
>>> bObj = Mock()
>>> aSites = music21.Sites()
>>> aSites.add(aObj, 234)
>>> aSites.add(bObj, 3000)
>>> len(aSites._locationKeys) == 2
True
>>> len(aSites.getSites()) == 2
True
Sites.getSitesByClass(className)

Return a list of unwrapped site from siteDict.site [SiteRef.site] (generally a Stream) that matches the provided class.

Input can be either a Class object or a string

>>> import music21
>>> from music21 import stream
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aObj = Mock()
>>> bObj = Mock()
>>> cObj = stream.Stream()
>>> aSites = music21.Sites()
>>> aSites.add(aObj, 234)
>>> aSites.add(bObj, 3000)
>>> aSites.add(cObj, 200)
>>> aSites.getSitesByClass(Mock) == [aObj, bObj]
True
>>> aSites.getSitesByClass('Stream') == [cObj]
True
Sites.hasSiteId(siteId)

Return True or False if this Sites object already has this site id defined as a location

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> dc = music21.Sites()
>>> dc.add(aSite, 0)
>>> dc.add(bSite) # a context
>>> dc.hasSiteId(id(aSite))
True
>>> dc.hasSiteId(id(bSite))
False
Sites.hasSpannerSite()

Return True if this object is found in any Spanner. This is determined by looking for a SpannerStorage Stream class as a Site.

Sites.hasVariantSite()

Return True if this object is found in any Variant. This is determined by looking for a VariantStorage Stream class as a Site.

Sites.isSite(obj)

Given an object, determine if it is an object in a Site stored in this Sites’s siteDict. This will return False if the object is simply a context and not a location.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> aLocations = music21.Sites()
>>> aLocations.add(aSite, 0)
>>> aLocations.add(bSite) # a context
>>> aLocations.isSite(aSite)
True
>>> aLocations.isSite(bSite)
False
Sites.purgeLocations(rescanIsDead=False)

Clean all locations that refer to objects that no longer exist.

The removeOrphanedSites option removes sites that may have been the result of deepcopy: the element has the site, but the site does not have the element. This results b/c Sites are shallow-copied, and then elements are re-added.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> cSite = Mock()
>>> dSite = Mock()
>>> aLocations = music21.Sites()
>>> aLocations.add(aSite, 0)
>>> aLocations.add(cSite) # a context
>>> del aSite
>>> len(aLocations)
2
>>> aLocations.purgeLocations(rescanIsDead=True)
>>> len(aLocations)
1
Sites.remove(site)

Remove the object (a context or location site) specified from Sites. Object provided can be a location site (i.e., a Stream) or a pure context (like a Temperament).

N.B. – like all .sites operations, this is an advanced tool not for standard music21 usage. Instead of:

elObj.remove(streamObj)

use this command, which will take care of .sites.remove as well as removing elObj from streamObj.elements:

streamObj.remove(elObj)
>>> class Mock(base.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> cSite = Mock()
>>> aSites = sites.Sites()
>>> aSites.add(aSite, 23)
>>> len(aSites)
1
>>> aSites.add(bSite, 233)
>>> len(aSites)
2
>>> aSites.add(cSite, 232223)
>>> len(aSites)
3
>>> aSites.remove(aSite)
>>> len(aSites)
2
Sites.removeById(idKey)

Remove a site entry by id key, which is id() of the object.

Sites.setAttrByName(attrName, value)

Given an attribute name, search all objects and find the first that matches this attribute name; then return a reference to this attribute.

>>> import music21
>>> class Mock(music21.Music21Object):
...     attr1 = 234
...
>>> aObj = Mock()
>>> bObj = Mock()
>>> bObj.attr1 = 98
>>> aSites = music21.Sites()
>>> aSites.add(aObj)
>>> aSites.add(bObj)
>>> aSites.setAttrByName('attr1', 'test')
>>> aSites.getAttrByName('attr1') == 'test'
True
Sites.setOffsetBySite(site, value)

Changes the offset of the site specified. Note that this can also be done with add, but the difference is that if the site is not in Sites, it will raise an exception.

>>> import music21
>>> class Mock(music21.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> cSite = Mock()
>>> aLocations = music21.Sites()
>>> aLocations.add(aSite, 23)
>>> aLocations.add(bSite, 121.5)
>>> aLocations.setOffsetBySite(aSite, 20)
>>> aLocations.getOffsetBySite(aSite)
20.0
>>> aLocations.setOffsetBySite(cSite, 30)
Traceback (most recent call last):
SitesException: an entry for this object (<...Mock object at 0x...>) is not stored in Sites
Sites.setOffsetBySiteId(siteId, value)

Set an offset by siteId. This assumes that the site is valid, is best used for advanced, performance critical usage only.

The siteId parameter can be None.