Skip to content

Commit

Permalink
clean up testStream
Browse files Browse the repository at this point in the history
Got to check in _elements and _endElements even if we are storing in one location or another.

This worked before but it was slow.  Then it broke but was fast.  Now it works and is fast.  :-)
  • Loading branch information
Myke Cuthbert committed Sep 22, 2020
1 parent 68420dd commit f941223
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 208 deletions.
59 changes: 39 additions & 20 deletions music21/musicxml/m21ToXml.py
Original file line number Diff line number Diff line change
Expand Up @@ -675,12 +675,46 @@ def fromPitch(self, p):
return self.fromMeasure(out)


def dumpString(obj, *, noCopy=False) -> str:
r'''
wrapper around xml.etree.ElementTree as ET that returns a string
in every case and indents tags and sorts attributes. (Prints, does not return)
>>> from music21.musicxml.m21ToXml import Element
>>> e = Element('accidental')
>>> musicxml.m21ToXml.dumpString(e)
'<accidental />'
>>> e.text = '∆'
>>> e.text == '∆'
True
>>> musicxml.m21ToXml.dumpString(e)
'<accidental>∆</accidental>'
'''
if noCopy is False:
xmlEl = copy.deepcopy(obj) # adds 5% overhead
else:
xmlEl = obj
XMLExporterBase.indent(xmlEl) # adds 5% overhead

for el in xmlEl.iter():
attrib = el.attrib
if len(attrib) > 1:
# adjust attribute order, e.g. by sorting
attribs = sorted(attrib.items())
attrib.clear()
attrib.update(attribs)
xStr = ET.tostring(xmlEl, encoding='unicode')
xStr = xStr.rstrip()
return xStr


class XMLExporterBase:
'''
contains functions that could be called
at multiple levels of exporting (Score, Part, Measure).
'''

def __init__(self):
self.xmlRoot = None

Expand All @@ -693,11 +727,8 @@ def asBytes(self, noCopy=True) -> bytes:
sio = io.BytesIO()
sio.write(self.xmlHeader())
rootObj = self.xmlRoot
if noCopy is False:
rootObj = copy.deepcopy(rootObj)
self.indent(rootObj)
et = ElementTree(rootObj)
et.write(sio, encoding='utf-8', xml_declaration=False)
rootObj_string = dumpString(rootObj, noCopy=noCopy)
sio.write(rootObj_string.encode('utf-8'))
v = sio.getvalue()
sio.close()
return v
Expand Down Expand Up @@ -737,7 +768,7 @@ def addDividerComment(self, comment: str = '') -> None:
def dump(obj):
r'''
wrapper around xml.etree.ElementTree as ET that prints a string
in every case. (Prints, does not return)
in every case and indents tags and sorts attributes. (Prints, does not return)
>>> from music21.musicxml.m21ToXml import Element
>>> e = Element('accidental')
Expand All @@ -752,19 +783,7 @@ def dump(obj):
>>> XB.dump(e)
<accidental>∆</accidental>
'''
xmlEl = copy.deepcopy(obj) # adds 5% overhead
XMLExporterBase.indent(xmlEl) # adds 5% overhead

for el in xmlEl.iter():
attrib = el.attrib
if len(attrib) > 1:
# adjust attribute order, e.g. by sorting
attribs = sorted(attrib.items())
attrib.clear()
attrib.update(attribs)
xStr = ET.tostring(xmlEl, encoding='unicode')
xStr = xStr.rstrip()
print(xStr)
print(dumpString(obj))

@staticmethod
def indent(elem, level=0):
Expand Down
8 changes: 1 addition & 7 deletions music21/stream/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1590,7 +1590,6 @@ def elementOffset(self, element, stringReturns=False):
Return the offset as an opFrac (float or Fraction) from the offsetMap.
highly optimized for speed.


>>> m = stream.Measure(number=1)
>>> m.append(note.Note('C'))
>>> d = note.Note('D')
Expand Down Expand Up @@ -1630,7 +1629,6 @@ def elementOffset(self, element, stringReturns=False):
music21.sites.SitesException: an entry for this object 0x... is not stored in
stream <music21.stream.Part sPart>


Performance note: because it will not follow derivation chains, and does
not need to unwrap a weakref, this method
should usually be about 3x faster than element.getOffsetBySite(self) --
Expand Down Expand Up @@ -2077,7 +2075,7 @@ def storeAtEnd(self, itemOrList, ignoreSort=False):
+ 'Duration into the highest time elements list')

# checks of element is self; possibly performs additional checks
self.coreGuardBeforeAddElement(element, searchEndElements=True)
self.coreGuardBeforeAddElement(element)

# element.sites.add(self, 'highestTime')
# # need to explicitly set the activeSite of the element
Expand Down Expand Up @@ -7134,17 +7132,13 @@ def highestTime(self):
in quarter lengths. This value usually represents the last
"release" in the Stream.


Stream.duration is usually equal to the highestTime
expressed as a Duration object, but it can be set separately
for advanced operations.


Example: Insert a dotted half note at position 0 and see where
it cuts off:



>>> p1 = stream.Stream()
>>> p1.highestTime
0.0
Expand Down
20 changes: 10 additions & 10 deletions music21/stream/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def coreGetElementByMemoryLocation(self, objId):
return None

# --------------------------------------------------------------------------
def coreGuardBeforeAddElement(self, element, *, checkRedundancy=True, searchEndElements=False):
def coreGuardBeforeAddElement(self, element, *, checkRedundancy=True):
'''
Before adding an element, this method provides
important checks to that element.
Expand All @@ -305,23 +305,23 @@ def coreGuardBeforeAddElement(self, element, *, checkRedundancy=True, searchEndE
raise StreamException('this Stream cannot be contained within itself')
if checkRedundancy:
idElement = id(element)
search_place = self._elements if not searchEndElements else self._endElements
if idElement in self._offsetDict:
# now go slow for safety -- maybe something is amiss in the index.
# this should not happen, but we have slipped many times in not clearing out
# old _offsetDict entries.
for eInStream in search_place:
if eInStream is element:
raise StreamException(
'the object '
+ '(%s, id()=%s) is already found in this Stream (%s, id()=%s)' %
(element, id(element), self, id(self)))
for search_place in (self._elements, self._endElements):
for eInStream in search_place:
if eInStream is element:
raise StreamException(
f'the object ({element!r}, id()={id(element)} '
+ f'is already found in this Stream ({self!r}, id()={id(self)})'
)
# something was old... delete from _offsetDict
# environLocal.warn('stale object')
del self._offsetDict[idElement] # pragma: no cover
# if we do not purge locations here, we may have ids() for
# Stream that no longer exist stored in the locations entry
# note that dead locations are also purged from .sites during
# Streams that no longer exist stored in the locations entry for element.
# Note that dead locations are also purged from .sites during
# all get() calls.
element.purgeLocations()

Expand Down
Loading

0 comments on commit f941223

Please sign in to comment.