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.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...>

If you turn sites.WEAKREF_ACTIVE to False then .siteWeakref just stores another reference to the site. Bad for memory. Good for debugging pickling.

SiteRef bases

SiteRef read/write properties

SiteRef.site

Sites

class music21.sites.Sites

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

Most of these objects are locations (also called sites), or Streams that contain this object.

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. Automatically called on stream.insert(n), etc.

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(sortByCreationTime=False, priorityTarget=None, excludeNone=False)

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

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.

>>> class Mock(base.Music21Object):
...     pass
...
>>> aObj = Mock()
>>> bObj = Mock()
>>> cObj = Mock()
>>> aSites = sites.Sites()
>>> aSites.add(cObj) 
>>> aSites.add(aObj)
>>> aSites.add(bObj)

Arbitrary order...

>>> set(aSites.get()) == set([None, cObj, aObj, bObj])
True

Particular order, with None at the end.

>>> aSites.get(sortByCreationTime=True) == [bObj, aObj, cObj, None]
True

Priority target

>>> begotten = aSites.get(sortByCreationTime=True, priorityTarget=cObj, excludeNone=True) 
>>> begotten == [cObj, bObj, aObj]
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.

Works in reverse order, so most recent site is returned first.

>>> class Mock(base.Music21Object):
...     attr1 = 234
...
>>> aObj = Mock()
>>> aObj.attr1 = 234
>>> bObj = Mock()
>>> bObj.attr1 = 98
>>> aSites = sites.Sites()
>>> len(aSites)
1
>>> aSites.add(aObj)
>>> len(aSites)
2
>>> aSites.getAttrByName('attr1') == 234
True
>>> aSites.remove(aObj)
>>> aSites.add(bObj)
>>> aSites.getAttrByName('attr1') == 98
True

An incorrect attribute name will just give none:

>>> aSites.getAttrByName('blah') is None
True
Sites.getById(siteId)

Return the object specified by an id. Used for testing and debugging. Should NOT be used in production code.

>>> a = note.Note()
>>> s = stream.Stream()
>>> s.append(a)
>>> a.sites.getById(id(s)) is s
True
Sites.getObjByClass(className, callerFirst=None, sortByCreationTime=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. use priorityTarget=activeSite to prioritize that.

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

>>> class Mock(base.Music21Object):
...     pass
...
>>> import time
>>> aObj = Mock()
>>> bObj = Mock()
>>> aSites = sites.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.getSiteCount()

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

>>> a = note.Note()
>>> a.sites.getSiteCount()
0
>>> s = stream.Stream()
>>> s.append(a)
>>> a.sites.getSiteCount()
1
>>> sf = s.flat
>>> a.sites.getSiteCount()
2
Sites.getSiteIds()

Return a set of all site Ids.

>>> class Mock(base.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> dc = sites.Sites()
>>> dc.add(aSite)
>>> dc.getSiteIds() == set([None, id(aSite)])
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

>>> class Mock(base.Music21Object):
...     pass
...
>>> aObj = Mock()
>>> bObj = Mock()
>>> cObj = stream.Stream()
>>> aSites = sites.Sites()
>>> aSites.add(aObj)
>>> aSites.add(bObj)
>>> aSites.add(cObj)
>>> 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.

>>> class Mock(base.Music21Object):
...     pass
...
>>> aSite = Mock()
>>> bSite = Mock()
>>> dc = sites.Sites()
>>> dc.add(aSite)
>>> dc.hasSiteId(id(aSite))
True
>>> dc.hasSiteId(id(bSite))
False

Note that we use “None” not id(None) as a key:

>>> dc.hasSiteId(id(None))
False        
>>> dc.hasSiteId(None)
True
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.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.

>>> class Mock(base.Music21Object):
...     pass
...
>>> aStream = stream.Stream()
>>> bStream = stream.Stream()
>>> mySites = sites.Sites()
>>> len(mySites)
1
>>> mySites.add(aStream)
>>> mySites.add(bStream)
>>> del aStream

We still have 3 locations – just because aStream is gone, doesn’t make it disappear from sites

>>> len(mySites)
3
>>> mySites.purgeLocations(rescanIsDead=True)
>>> len(mySites)
2
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()
>>> len(aSites)
1
>>> aSites.add(aSite)
>>> len(aSites)
2
>>> aSites.add(bSite)
>>> len(aSites)
3
>>> aSites.add(cSite)
>>> len(aSites)
4
>>> aSites.remove(aSite)
>>> len(aSites)
3
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.

>>> class Mock(base.Music21Object):
...     attr1 = 234
...
>>> aObj = Mock()
>>> bObj = Mock()
>>> bObj.attr1 = 98
>>> aSites = sites.Sites()
>>> aSites.add(aObj)
>>> aSites.add(bObj)
>>> aSites.setAttrByName('attr1', 'test')
>>> aSites.getAttrByName('attr1') == 'test'
True
Sites.yieldSites(sortByCreationTime=False, priorityTarget=None, excludeNone=False)

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

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]

Note that priorityTarget is searched only on id – this could be dangerous if the target has been garbage collected and the id is reused. Unlikely since you gotta pass in the priorityTarget itself so therefore it still exists...

This can be much faster than .get in the case where the sought-for site is earlier in the list.

>>> class Mock(base.Music21Object):
...     def __init__(self, idEl):
...         self.id = idEl
...
>>> aObj = Mock('a')
>>> bObj = Mock('b')
>>> cObj = Mock('c')
>>> aSites = sites.Sites()
>>> aSites.add(cObj) 
>>> aSites.add(aObj)
>>> aSites.add(bObj)

Returns a generator (The ellipsis in the repr here is because Python 3.5 gives a fully qualified name to a generator object):

>>> ys = aSites.yieldSites()
>>> ys
<generator object ...yieldSites at 0x1058085e8>        

That’s no help, so iterate over it instead...

>>> for s in aSites.yieldSites(sortByCreationTime=True, excludeNone=True):
...     print(s.id)
b
a
c

With priorityTarget

>>> for s in aSites.yieldSites(sortByCreationTime=True, priorityTarget=cObj, 
...                            excludeNone=True):
...     print(s.id)
c
b
a

changed drammatically from the unused version in v.3