
    rh                       S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
r
SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SS	KJr  SS
KJr  \R&                  " S5      r " S S\R*                  5      r " S S5      r " S S\5      r " S S\5      r " S S\	R4                  5      r\S:X  a  SSKr\R:                  " \5        gg)a/  
This module contains objects for storing complete `Music21Objects`, especially
`Stream` and `Score` objects on disk.  Freezing (or "pickling") refers to
writing the object to a file on disk (or to a string).  Thawing (or
"unpickling") refers to reading in a string or file and returning a Music21
object.

This module offers alternatives to writing a `Score` to `MusicXML` with
`s.write('musicxml')`.  `FreezeThaw` has some advantages over using `.write()`:
virtually every aspect of a music21 object is retained when Freezing.  So
objects like `roman.RomanNumeral`, which aren't supported by most formats, can be
stored with `FreezeThaw` and then read back again.  Freezing is also much
faster than most conversion methods.  But there's a big downside: only
`music21` and `Python` can use the `Thaw` side to get back `Music21Objects`
(though more information can be brought out of the JSONFreeze format through
any .json reader).  In fact, there's not even a guarantee that future versions
of music21 will be able to read a frozen version of a `Stream`.  So the
advantages and disadvantages of this model definitely need to be kept in mind.

There are two formats that `freezeThaw` can produce: "Pickle" or JSON (for
JavaScript Object Notation -- essentially a string representation of the
JavaScript equivalent of a Python dictionary).

Pickle is a Python-specific
idea for storing objects.  The `pickle` module stores objects as a text file
that can't be easily read by non-Python applications; it also isn't guaranteed
to work across Python versions or even computers.  However, it works well, is
fast, and is a standard part of python.

JSON was originally created to pass
JavaScript objects from a web server to a web browser, but its utility
(combining the power of XML with the ease of use of objects) has made it a
growing standard for other languages.  (see
https://docs.python.org/3/library/json.html).
Music21 has two implementations of JSON (confusing, no?)
because we weren't sure and are still not sure which will be best in the long-run:
the first approach
uses explicit lists of attributes that need to be stored and just saves those. This uses a
homemade set of methods that are specifically tailored for music21; but it misses some things that
may be important to you.  The second approach uses the freely distributable
`jsonpickle` module. This approach probably stores more data than
a person not using music21 or python is likely to want, but can be used to get back an entire
music21 Stream with no conversion.

Both JSON and Pickle files can be huge, but `freezeThaw` can compress them with
`gzip` or `ZipFile` and thus they're not that large at all.

Streams need to be run through .setupSerializationScaffold and .teardownSerializationScaffold
before and after either Pickle or jsonpickle in order to restore all the weakrefs that we use.

The name freezeThaw comes from Perl's implementation of similar methods -- I
like the idea of thawing something that's frozen; "unpickling" just doesn't
seem possible.  In any event, I needed a name that wouldn't already
exist in the Python namespace.
    )annotationsN)base)common)
derivation)environment)exceptions21)spanner)variant
freezeThawc                      \ rS rSrSrg)FreezeThawException\    N)__name__
__module____qualname____firstlineno____static_attributes__r       L/home/james-whalen/.local/lib/python3.13/site-packages/music21/freezeThaw.pyr   r   \   s    r   r   c                  2    \ rS rSrSrS rSS jrSS jrSrg)	StreamFreezeThawBaseb   zN
Contains a few methods that are used for both
StreamFreezer and StreamThawer
c                    S U l         g Nstream)selfs    r   __init__StreamFreezeThawBase.__init__h   s	    r   c                    [        U[        R                  5      (       d  [        R                  " U5      n[        [        R                  " 5       5      nUS[
        R                  " U5      -   S-   -  $ )Nzm21-z.p)
isinstancepathlibPathstrtimer   getMd5)r   	directory	streamStrs      r   getPickleFp StreamFreezeThawBase.getPickleFpk   sQ    )W\\22Y/I 		$	FV]]9%==DEEr   c                B    U R                  U5      R                  S5      $ )Nz.p.json)r*   with_suffix)r   r(   s     r   	getJsonFpStreamFreezeThawBase.getJsonFps   s    	*66yAAr   r   N)r(   zstr | pathlib.Pathreturnzpathlib.Path)	r   r   r   r   __doc__r   r*   r.   r   r   r   r   r   r   b   s    
FBr   r   c                     ^  \ rS rSrSrSU 4S jjrSS jrSS jrS rS r	S r
   S SS	 jjrS
 rSS jrSS jrSrU =r$ )StreamFreezerx   av  
This class is used to freeze a Stream, preparing it for serialization
and providing conversion routines.

In general, use :func:`~music21.converter.freeze`
for serializing to a file.

Use the :func:`~music21.converter.unfreeze` to read from a
serialized file

>>> s = stream.Stream()
>>> s.repeatAppend(note.Note('C4'), 8)
>>> temp = [s[n].transpose(n, inPlace=True) for n in range(len(s))]

>>> sf = freezeThaw.StreamFreezer(s)  # provide a Stream at init
>>> data = sf.writeStr(fmt='pickle')  # pickle is default format

>>> st = freezeThaw.StreamThawer()
>>> st.openStr(data)
>>> s = st.stream
>>> s.show('t')
{0.0} <music21.note.Note C>
{1.0} <music21.note.Note C#>
{2.0} <music21.note.Note D>
{3.0} <music21.note.Note E->
{4.0} <music21.note.Note E>
{5.0} <music21.note.Note F>
{6.0} <music21.note.Note F#>
{7.0} <music21.note.Note G>

>>> sf2 = freezeThaw.StreamFreezer(s)  # do not reuse StreamFreezers
>>> data2 = sf2.writeStr(fmt='jsonpickle')

>>> st2 = freezeThaw.StreamThawer()
>>> st2.openStr(data2)
>>> s2 = st2.stream
>>> s2.show('t')
{0.0} <music21.note.Note C>
{1.0} <music21.note.Note C#>
{2.0} <music21.note.Note D>
{3.0} <music21.note.Note E->
{4.0} <music21.note.Note E>
{5.0} <music21.note.Note F>
{6.0} <music21.note.Note F#>
{7.0} <music21.note.Note G>


>>> c = corpus.parse('luca/gloria')
>>> sf = freezeThaw.StreamFreezer(c)
>>> data = sf.writeStr(fmt='pickle')

>>> st = freezeThaw.StreamThawer()
>>> st.openStr(data)
>>> s2 = st.stream
>>> len(s2.parts[0].measure(7).notes) == 6
True

JSONPickle is also an acceptable way of Freezing streams.  Especially
for going to music21j in Javascript:

>>> sf2 = freezeThaw.StreamFreezer(c)  # do not reuse StreamFreezers
>>> data2 = sf2.writeStr(fmt='jsonpickle')
>>> st2 = freezeThaw.StreamThawer()
>>> st2.openStr(data2)
>>> s3 = st2.stream
>>> len(s3.parts[0].measure(7).notes) == 6
True

Or by writing to disk:

>>> sf2 = freezeThaw.StreamFreezer(c)  # do not reuse StreamFreezers
>>> fp2 = sf2.write(fmt='jsonpickle')
>>> st2 = freezeThaw.StreamThawer()
>>> st2.open(fp2)
>>> s3 = st2.stream
>>> len(s3.parts[0].measure(7).notes) == 6
True

OMIT_FROM_DOCS

>>> import os
>>> os.remove(fp2)
c                   > [         TU ]  5         S U l        X0l        X@l        0 U l        Ub!  USL a  [        R                  " U5      U l        g Ub  Xl        g g )NF)superr   r   topLevel	streamIdssubStreamFreezerscopydeepcopy)r   	streamObjfastButUnsafer7   r8   	__class__s        r   r   StreamFreezer.__init__   sV     "!# ]e%; --	2DK"#K #r   c                l    Uc  U R                   nU R                  U5        U[        R                  S.nU$ )a/  
Prepare the passed in Stream in place, return storage
dictionary format.

>>> from pprint import pprint
>>> s = stream.Stream()
>>> n = note.Note('D#4')
>>> s.append(n)
>>> sf = freezeThaw.StreamFreezer(s)
>>> pprint(sf.packStream())
{'m21Version': (...), 'stream': <music21.stream.Stream 0x1289212>}

)r   
m21Version)r   setupSerializationScaffoldr   VERSION)r   r<   storages      r   
packStreamStreamFreezer.packStream   s4     I''	2&dllCr   c                   Uc  U R                   nUc  [        S5      e[        UR                  SS95      nU R                  SL a  U R                  U5        U H  n[        U[        R                  5      (       a2  [        UR                  SU R                  SS9nUR                  5         MT  [        U[        R                  5      (       a2  [        UR                  SU R                  SS9nUR                  5         M  UR                   (       d  M  U R#                  U5        M     U R#                  U5        U R%                  U5        U R                  SL a  U R'                  U5        gg)a*  
Prepare this stream and all of its contents for pickle/pickling, that
is, serializing and storing an object representation on file or as a string.

The `topLevel` and `streamIdsFound` arguments are used to keep track of recursive calls.

Note that this is a destructive process: elements contained within this Stream
will have their sites cleared of all contents not in the hierarchy
of the Streams. Thus, when doing a normal .write('pickle')
the Stream is deepcopied before this method is called. The
attribute `fastButUnsafe = True` setting of StreamFreezer ignores the destructive
effects of these processes and skips the deepcopy.

>>> a = stream.Stream()
>>> n = note.Note()
>>> n.duration.type = 'whole'
>>> a.repeatAppend(n, 10)
>>> sf = freezeThaw.StreamFreezer(a)
>>> sf.setupSerializationScaffold()
N2You need to pass in a stream when creating to workF)restoreActiveSitesT)r=   r8   r7   )r   r   listrecurser7   findActiveStreamIdsInHierarchyr"   r
   Variantr3   _streamr8   rB   r	   SpannerspannerStorageisStreamremoveStreamStatusClientsetupStoredElementOffsetTuplesrecursiveClearSites)r   r<   allElselsubSFs        r   rB   (StreamFreezer.setupSerializationScaffold   s1   * I )*^__i''5'AB==D //	:B"goo..%JJ"&"nn"	 002B00%%%"&"nn"	 002--b1) . 	%%i0++I6==D $$Y/ !r   c                J    [        US5      (       a  SUR                  l        gg)z
if s is a stream then

s.streamStatus._client is s

this can be hard to pickle, so this method removes the streamStatus._client from the
streamObj (not recursive).  Called by setupSerializationScaffold.
streamStatusNhasattrrZ   clientr   r<   s     r   rR   &StreamFreezer.removeStreamStatusClient1  s$     9n--,0I"") .r   c                   [        US5      (       GaO  UR                  nU GH   u  p4UR                  (       a  U R                  U5        [	        U[
        R                  5      (       a  U R                  UR                  5        [	        U[        R                  5      (       a  U R                  UR                  5        [        US5      (       a  [        R                  " 5       Ul        [        US5      (       a  0 Ul        UR                  R!                  5         SUl        GM     [        R                  " 5       Ul        UR                  R!                  5         SUl        gg)a  
recursively clear all sites, including activeSites, taking into account
that spanners and variants behave differently.

Called by setupSerializationScaffold.

To be run after setupStoredElementOffsetTuples() has been run

>>> n = note.Note('D#4')
>>> len(n.sites)
1
>>> s = stream.Stream()
>>> s.id = 'stream s'
>>> s.insert(10, n)
>>> len(n.sites)
2
>>> s2 = stream.Stream()
>>> s2.insert(20, n)
>>> s2.id = 'stream s2'
>>> len(n.sites)
3

>>> n.getOffsetBySite(s)
10.0
>>> n.getOffsetBySite(s2)
20.0

>>> sf = freezeThaw.StreamFreezer()

This will remove n from s but leave the rest of the sites intact:

>>> sf.setupStoredElementOffsetTuples(s)
>>> len(n.sites)
2
>>> n.getOffsetBySite(s)
Traceback (most recent call last):
music21.sites.SitesException: an entry for this object <music21.note.Note D#>
       is not stored in stream <music21.stream.Stream stream s>
>>> n.getOffsetBySite(s2)
20.0

After recursiveClearSites n will be not know its location anywhere:

>>> sf.recursiveClearSites(s)
>>> len(n.sites)  # just the None site
1

This leaves n and s2 in strange positions, because n is in s2.elements still:

>>> n in s2.elements
True

This predicament is why when the standard freezeThaw call is made, what is frozen is a
deepcopy of the Stream so that nothing is left in an unusable position
_storedElementOffsetTuples_derivation_offsetDictN)r\   ra   rQ   rT   r"   r	   rO   rP   r
   rM   rN   r   
Derivationrb   rc   sitesclear
activeSite)r   startObjstoredElementOffsetTuplesrV   unused_offsets        r   rT   !StreamFreezer.recursiveClearSites=  s    p 89::(0(K(K%%>!;;,,R0b'//22,,R->->?b'//22,,RZZ82}--%/%:%:%<BN2}--%'BN  $ &? $.#8#8#:H NN  ""&H% ;r   c                N   [        US5      (       a  g/ nUR                   Hj  nX1R                  U5      4nUR                  U5        UR                  (       a  U R                  U5        UR                  R                  U5        SUl        Ml     UR                   H\  nUS4nUR                  U5        UR                  (       a  U R                  U5        UR                  R                  U5        SUl        M^     X!l
        0 Ul        / Ul        / Ul	        UR                  5         g)a  
move all elements from ._elements and ._endElements
to a new attribute ._storedElementOffsetTuples
which contains a list of tuples of the form
(el, offset or 'end').

Called by setupSerializationScaffold.

>>> s = stream.Measure()
>>> n1 = note.Note('C#')
>>> n2 = note.Note('E-')
>>> bl1 = bar.Barline()
>>> s.insert(0.0, n1)
>>> s.insert(1.0, n2)
>>> s.storeAtEnd(bl1)

>>> sFreeze = freezeThaw.StreamFreezer()
>>> sFreeze.setupStoredElementOffsetTuples(s)
>>> s._elements, s._endElements
([], [])
>>> s._storedElementOffsetTuples
[(<music21.note.Note C#>, 0.0),
 (<music21.note.Note E->, 1.0),
 (<music21.bar.Barline type=regular>, 'end')]
>>> n1.getOffsetBySite(s)
Traceback (most recent call last):
music21.sites.SitesException: an entry for this object <music21.note.Note C#> is
     not stored in stream <music21.stream.Measure 0 offset=0.0>

Trying it again, but now with substreams:

>>> s2 = stream.Measure()
>>> n1 = note.Note('C#')
>>> n2 = note.Note('E-')
>>> bl1 = bar.Barline()
>>> v1 = stream.Voice()
>>> n3 = note.Note('F#')
>>> v1.insert(2.0, n3)
>>> s2.insert(0.0, n1)
>>> s2.insert(1.0, n2)
>>> s2.storeAtEnd(bl1)
>>> s2.insert(2.0, v1)
>>> sFreeze.setupStoredElementOffsetTuples(s2)

>>> v1._storedElementOffsetTuples
[(<music21.note.Note F#>, 2.0)]
>>> s2._storedElementOffsetTuples
[(<music21.note.Note C#>, 0.0),
 (<music21.note.Note E->, 1.0),
 (<music21.stream.Voice ...>, 2.0),
 (<music21.bar.Barline type=regular>, 'end')]
>>> s2._storedElementOffsetTuples[2][0] is v1
True

ra   Nend)r\   	_elementselementOffsetappendrQ   rS   re   removerg   _endElementsra   rc   coreElementsChanged)r   r<   ri   eelementTuples        r   rS   ,StreamFreezer.setupStoredElementOffsetTuples  s    p 9:;; $&!$$A66q9:L%,,\:zz33A6GGNN9%AL % ''Au:L%,,\:zz 33A6GGNN9%AL ( 0I, "	 	!#	%%'r   c                   Uc  U R                   nOUnUR                  SSSS9nU Vs/ s H  n[        U5      PM     nnUSL a  UR                  nXxR	                  5       -  nUSL aP  UR                  SS9R                  [        R                  5       H   n	XpR                  U	R                  5      -  nM"     [        [        U5      5      nXpl        U$ s  snf )a  
Return a list of all Stream ids anywhere in the hierarchy.  By id,
we mean `id(s)` not `s.id` -- so they are memory locations and unique.

Stores them in .streamIds.

if hierarchyObject is None, uses self.stream.


>>> sc = stream.Score()
>>> p1 = stream.Part()
>>> p2 = stream.Part()
>>> m1 = stream.Measure()
>>> v1 = stream.Voice()
>>> m1.insert(0, v1)
>>> p2.insert(0, m1)
>>> sc.insert(0, p1)
>>> sc.insert(0, p2)
>>> shouldFindIds = [id(sc), id(p1), id(p2), id(m1), id(v1)]

fastButUnsafe is needed because it does not make a deepcopy
and thus lets you compare ids before and after.

>>> sf = freezeThaw.StreamFreezer(sc, fastButUnsafe=True)
>>> foundIds = sf.findActiveStreamIdsInHierarchy()
>>> for thisId in shouldFindIds:
...     if thisId not in foundIds:
...         raise ValueError('Missing Id')
>>> for thisId in foundIds:
...     if thisId not in shouldFindIds:
...         raise ValueError('Additional Id Found')

Spanners are included unless getSpanners is False

>>> staffGroup = layout.StaffGroup([p1, p2])
>>> sc.insert(0, staffGroup)

:class:`~music21.layout.StaffGroup` is a spanner, so
it should be found

>>> sf2 = freezeThaw.StreamFreezer(sc, fastButUnsafe=True)
>>> foundIds = sf2.findActiveStreamIdsInHierarchy()

But you won't find the id of the spanner itself in
the foundIds:

>>> id(staffGroup) in foundIds
False

instead it's the id of the storage object:

>>> id(staffGroup.spannerStorage) in foundIds
True

Variants are treated similarly:

>>> s = stream.Stream()
>>> m = stream.Measure()
>>> m.append(note.Note(type='whole'))
>>> s.append(m)

>>> s2 = stream.Stream()
>>> m2 = stream.Measure()
>>> n2 = note.Note('D#4')
>>> n2.duration.type = 'whole'
>>> m2.append(n2)
>>> s2.append(m2)
>>> v = variant.Variant(s2.elements)
>>> s.insert(0, v)
>>> sf = freezeThaw.StreamFreezer(s, fastButUnsafe=True)
>>> allIds = sf.findActiveStreamIdsInHierarchy()
>>> len(allIds)
4
>>> for streamElement in [s, m, m2, v._stream]:
...    if id(streamElement) not in allIds:
...        print('this should not happen!', allIds, id(streamElement))

N.B. with variants:

>>> id(s2) == id(v._stream)
False

The method also sets self.streamIds to the returned list:

>>> sf.streamIds is allIds
True
TF)streamsOnlyrI   includeSelf)ry   )r   rK   idspannerBundlegetSpannerStorageIdsgetElementsByClassr
   rM   rL   rN   rJ   setr8   )
r   hierarchyObjectgetSpannersgetVariantsr<   streamsFoundGeneratorsr8   r{   rV   s
             r   rL   ,StreamFreezer.findActiveStreamIdsInHierarchy  s    z "I'I ) 1 1dEJ>B !2 !D %::$9qRU$9	:$%33M;;==I$''D'9LLW__]@@LL	 ^
 Y(	" ;s   Cc                d    Uc  gUR                  5       R                  5       nUS;   a  gUS;   a  gg)z
Parse a passed-in write format

>>> sf = freezeThaw.StreamFreezer()
>>> sf.parseWriteFmt(None)
'pickle'
>>> sf.parseWriteFmt('JSON')
'jsonpickle'

Anything else returns 'pickle' as a default:

>>> sf.parseWriteFmt('inconceivable')
'pickle'
pickle)pr   )
jsonpicklejsonr   )striplower)r   fmts     r   parseWriteFmtStreamFreezer.parseWriteFmtY  s:     ;iik!/!**r   c                   US;  a  [        S5      eU R                  U5      nUcN  [        R                  5       nUR	                  S5      (       a  U R                  U5      nOU R                  U5      nOv[        U[        5      (       a  [        R                  " U5      n[        U[        R                  5      (       a,  UR                  5       (       d  [        R                  5       U-  nU R                  U R                  5      n[        U[        R                  5      (       a   [        R                  S[        U5      /5        US:X  a  [        R                   " U5      nUS:X  a  ["        R$                  " U5      n[        U[&        R(                  5      (       d(  [+        US5       nUR-                  U5        SSS5        U$ UR-                  U5         U$ US	:X  ah  S
SKn	U	R0                  " U40 UD6n
US:X  a$  ["        R$                  " U
R1                  5       5      n
[+        USSS9 nUR-                  U
5        SSS5        U$ [        SU 35      e! , (       d  f       U$ = f! , (       d  f       U$ = f)ax  
For a supplied Stream, write a serialized version to
disk in either 'pickle' or 'jsonpickle' format and
return the filepath to the file.

jsonpickle is the better format for transporting from
one computer to another, but slower and may have some bugs.

If zipType == 'zlib' then zlib compression is done after serializing.
No other compression types are currently supported.
)Nzlibz!Cannot zip files except zlib typeNr   z
writing fpr   r   wbr   r   wutf-8encodingbad StreamFreezer format: )r   r   environLocalgetRootTempDir
startswithr.   r*   r"   r%   r#   r$   is_absoluterE   r   
printDebugr   dumpsr   compressioBytesIOopenwriter   encode)r   r   fpzipTypekeywordsr(   rD   pickleStringfr   datas              r   r   StreamFreezer.writer  s    .(%&IJJ  %:$335I~~f%%^^I.%%i0"c""\\"%"gll++BNN4D4D!002R7//$++.b',,''##\3r7$;<(? "<<0L& #}}\:b"**--"d^qGGL) $$ 	 & 	 L $$W99D& }}T[[]3b#0A 1 		 &(B3%&HII $^$ 	 10 	s   'I)I%
I"%
I4c                    U R                  U5      nU R                  U R                  5      nUS:X  a  [        R                  " USS9nU$ US:X  a  SSKnUR                  " U40 UD6nU$ [        SU 35      e)zJ
Convert the object to a pickled/jsonpickled string
and return the string
r   protocolr   r   Nr   )r   rE   r   r   r   r   r   r   )r   r   r   rD   outr   s         r   writeStrStreamFreezer.writeStr  s    
   %//$++.(?,,w4C 
 L ##G8x8C
 
 &(B3%&HIIr   )r   r8   r9   r7   )NFTNr   )NTT)r0   z	list[int])r   NN)r   r   r   r   r1   r   rE   rB   rR   rT   rS   rL   r   r   r   r   __classcell__r>   s   @r   r3   r3   x   s_    Rh$$*;0z
1J'XW(v 	s
 
sn2>@ r   r3   c                  f   ^  \ rS rSrSrU 4S jrSS jrS rS rS r	S r
SS	 jrSSS
 jjrSrU =r$ )StreamThaweri  a  
This class is used to thaw a data string into a Stream

In general user :func:`~music21.converter.parse` to read from a
serialized file.

>>> s = stream.Stream()
>>> s.repeatAppend(note.Note('C4'), 8)
>>> temp = [s[n].transpose(n, inPlace=True) for n in range(len(s))]

>>> sf = freezeThaw.StreamFreezer(s)  # provide a Stream at init
>>> data = sf.writeStr(fmt='pickle')  # pickle is default format

>>> sfOut = freezeThaw.StreamThawer()
>>> sfOut.openStr(data)
>>> s = sfOut.stream
>>> s.show('t')
{0.0} <music21.note.Note C>
{1.0} <music21.note.Note C#>
{2.0} <music21.note.Note D>
{3.0} <music21.note.Note E->
{4.0} <music21.note.Note E>
{5.0} <music21.note.Note F>
{6.0} <music21.note.Note F#>
{7.0} <music21.note.Note G>
c                0   > [         TU ]  5         S U l        g r   )r6   r   r   )r   r>   s    r   r   StreamThawer.__init__  s    r   c                (   Uc  U R                   nUc  [        S5      eUR                  nSUl        U R                  U5        U R	                  U5        [        UR                  5       5      nU H  nUR                  nSU;   a.  [        5       nUR                  UR                  5        0 Ul        MC  SU;   a.  [        5       nUR                  UR                  5        0 Ul        Mw  UR                  (       d  M  U R	                  U5        M     X!l        g)a  
After rebuilding this Stream from pickled storage, prepare this as a normal `Stream`.

If streamObj is None, runs it on the embedded stream

>>> a = stream.Stream()
>>> n = note.Note()
>>> n.duration.type = 'whole'
>>> a.repeatAppend(n, 10)
>>> sf = freezeThaw.StreamFreezer(a)
>>> sf.setupSerializationScaffold()

>>> st = freezeThaw.StreamThawer()
>>> st.teardownSerializationScaffold(a)
NrH   FrM   rO   )r   r   autoSortrestoreElementsFromTuplesrestoreStreamStatusClientrJ   rK   classesr   teardownSerializationScaffoldrN   _cacherP   rQ   )r   r<   storedAutoSortrU   rt   eClassesrW   s          r   r   *StreamThawer.teardownSerializationScaffold  s      I )*^__"++"	&&y1&&y1i'')*AyyHH$$33AII> h&$33A4D4DE..q1 ( ,r   c                   [        US5      (       aR  UR                   H0  u  p#US:w  a   UR                  X25        M  UR                  U5        M2     U?UR                  5         U H%  nUR                  SL d  M  U R                  U5        M'     g! [         a8    [	        S5        [	        X25        [	        U5        [	        UR
                  5        e f = f)a  
Take a Stream with elements and offsets stored in
a list of tuples (element, offset or 'end') at
_storedElementOffsetTuples
and restore it to the ._elements and ._endElements lists
in the proper locations:


>>> s = stream.Measure()
>>> s._elements, s._endElements
([], [])

>>> n1 = note.Note('C#')
>>> n2 = note.Note('E-')
>>> bl1 = bar.Barline()
>>> tupleList = [(n1, 0.0), (n2, 1.0), (bl1, 'end')]
>>> s._storedElementOffsetTuples = tupleList

>>> sThaw = freezeThaw.StreamThawer()
>>> sThaw.restoreElementsFromTuples(s)
>>> s.show('text')
{0.0} <music21.note.Note C#>
{1.0} <music21.note.Note E->
{2.0} <music21.bar.Barline type=regular>
>>> s._endElements
[<music21.bar.Barline type=regular>]
>>> s[1].getOffsetBySite(s)
1.0

Trying it again, but now with substreams:

>>> s2 = stream.Measure()
>>> v1 = stream.Voice()
>>> n3 = note.Note('F#')
>>> v1._storedElementOffsetTuples = [(n3, 2.0)]
>>> tupleList = [(n1, 0.0), (n2, 1.0), (bl1, 'end'), (v1, 2.0)]
>>> s2._storedElementOffsetTuples = tupleList
>>> sThaw.restoreElementsFromTuples(s2)
>>> s2.show('text')
{0.0} <music21.note.Note C#>
{1.0} <music21.note.Note E->
{2.0} <music21.stream.Voice ...>
    {2.0} <music21.note.Note F#>
{5.0} <music21.bar.Barline type=regular>
ra   rm   z-Problem in decoding. Here is some debug info:TN)
r\   ra   
coreInsertAttributeErrorprintrg   coreStoreAtEndrs   rQ   r   )r   r<   rt   offset
subElements        r   r   &StreamThawer.restoreElementsFromTuples  s    \ 9:;;&AA	U?!,,V7 ,,Q/ B 4))+#J""d* ..z: $ * MNf(i(i223s   BACc                H    [        US5      (       a  XR                  l        gg)z
Restore the streamStatus client to point to the Stream
(do we do this for derivations?  No: there should not be derivations stored.
Other objects?  Unclear at present.)
rZ   Nr[   r^   s     r   r   &StreamThawer.restoreStreamStatusClientb  s"     9n--,5"") .r   c                    US   nU[         R                  :w  a  [        R                  S5        US   nU R	                  U5        U$ )z,
Convert from storage dictionary to Stream.
rA   z?this pickled file is out of date and may not function properly.r   )r   rC   r   warnr   )r   rD   versionr<   s       r   unpackStreamStreamThawer.unpackStreamk  sG     ,'dll"_`H%	**95r   c                    [        U[        5      (       a  UR                  S5      (       a  ggUR                  S5      (       a  gg)z+
Look at the file and determine the format
s   {"r   r   z{")r"   bytesr   )r   rD   s     r   parseOpenFmtStreamThawer.parseOpenFmtw  s?     gu%%!!%((#!!$''#r   c                >   [         R                  R                  U5      (       d  [        R	                  5       nX1-  n[        US5       nUR                  5       nSSS5        U R                  W5      nUS:X  a  [        R                  " 5         [        US5       nUc  [        R                  " U5      nOgUS:X  a>  UR                  5       n[        R                  " U5      n	 [        R                  " U	5      nO#[        R                  " 5         [!        SU 35      eU R#                  U5      U l        SSS5        [        R                  " 5         gUS:X  aP  SSKn[        US	S
S9 nUR                  5       nSSS5        UR)                  W5      nU R#                  U5      U l        g[!        SU< 35      e! , (       d  f       GNb= f! [         a)  n
[        R                  " 5         [!        SU
 35      U
eSn
A
ff = f! , (       d  f       N= f! , (       d  f       N= f)z8
For a supplied file path to a pickled stream, unpickle
rbNr   r   zProblem in decoding: zUnknown zipType r   r   rr   r   r   )ospathexistsr   r   r   readr   r   !restorePathClassesAfterUnpicklingr   loadr   
decompressloadsr   r   r   r   r   decode)r   r   r   r(   r   fileDatar   rD   compressedStringuncompressedrt   r   r   s                r   r   StreamThawer.open  s    ww~~b!!$335IB"d^qvvxH  )(?446b$1?$kk!nG&'(vvx$#'??3C#DL!"(,,|"< <<>-0@	.JKK"//8!  " 446L b#0Avvx 1 ''-G++G4DK%(B3'&JKKA ^ * !@@B13A37 !!  ( 10sI   	F5AG="G8:G=%H5
G
G:$G55G::G==
H
Hc                   Ub  UnOU R                  U5      nUS:X  a  [        R                  " U5      nO*US:X  a  SSKnUR	                  U5      nO[        SU 35      e[        R                  SU 35        U R                  U5      U l	        g)a&  
Take bytes representing a Frozen(pickled/jsonpickled)
Stream and convert it to a normal Stream.

if format is None then the format is automatically
determined from the bytes contents.

The name of the function is a legacy of Py2.  With
pickle (not jsonpickle), it works on bytes, not strings.
Nr   r   r   r   z"StreamThawer:openStr: storage is: )
r   r   r   r   r   r   r   r   r   r   )r   r   pickleFormatr   rD   r   s         r   openStrStreamThawer.openStr  s     #C##H-C(?ll8,GL  ''1G%(B3%&HII"DWI NO''0r   r   r   )r   r   )r   r   r   r   r1   r   r   r   r   r   r   r   r   r   r   r   s   @r   r   r     s:    62,hD;L6
 "(LV1 1r   r   c                  V    \ rS rSrS rS rS rS rS rS r	S r
S	 rS
 rS rS rSrg)Testi  c                   SSK Jn  SSK Jn  UR                  5       nUR                  5       nUR	                  5       nUR                  SU5        UR                  SU5        [        U5      nUR                  5       nAAA[        5       nUR                  U5        UR                  n	U R                  [        U	5      S5        U R                  U	S   R                  S5        g )Nr   r   note       @      @   )music21r   r   StreamNoteinsertr3   r   r   r   assertEquallenr   )
r   r   r   r   sDummynsfr   st	outStreams
             r   testSimpleFreezeThawTest.testSimpleFreezeThaw  s    " MMOIIK	ac11kkm^


3II	Y+1,,c2r   c                   SSK Jn  SSK Jn  UR                  5       nUR                  5       nUR	                  5       n[
        R                  " U/5      nUR                  SU5        UR                  SU5        UR                  SU5        U R                  UR                  S   R                  5       UR                  S   5        [        U5      nUR                  SS9nAAA[        5       n	U	R                  U5        U	R                  n
U R!                  [#        U
5      S	5        U R!                  U
R                  S   R$                  S5        U R                  U
R                  S   R                  5       U
R                  S   5        g )
Nr   r   r   g        r   r   r   r      )r   r   r   r   r   r	   Slurr   assertIsspannersgetFirstnotesr3   r   r   r   r   r   r   )r   r   r   r   r   r   sl1r   r   r   r   s              r   testFreezeThawWithSpannerTest.testFreezeThawWithSpanner  s&   " MMOIIKllA3	c	ac1ajjm,,.
;1kklk+^


3II	Y++22C8i((+446	8JKr   c                    SSK Jn  UR                  S5      R                  S   R	                  SS5      n[        U5      nUR                  SS9n[        5       nUR                  U5        g)	z[
Versions of jsonpickle prior to  0.9.3 were having problems serializing Enums.

Works now
r   corpusluca/gloriar   r   r   r   N)	r   r
  parsepartsmeasuresr3   r   r   r   )r   r
  csf2data2st2s         r   testFreezeThawJsonPickleEnum!Test.testFreezeThawJsonPickleEnum  sZ     	#LL'--a099!Q?A.nEr   c                6   SSK Jn  UR                  S5      n[        U5      nUR	                  SS9n[        5       nUR                  U5        UR                  nU R                  [        UR                  S   R                  S5      R                  5      S5        g )Nr   r	  r  r   r         )r   r
  r  r3   r   r   r   r   r   r   r  measurer  )r   r
  r  r   r   r   r   s          r   $testFreezeThawCorpusFileWithSpanners)Test.testFreezeThawCorpusFileWithSpanners  sw    "LL'1{{x{(^


4IIQWWQZ//28891=r   c                |   SSK Jn  SSK Jn  UR                  S5      R                  S   R                  S5      R                  nUS   nUS   nUR                  USS9nUR                  5         UR                  R                   H  nM     UR                  R                   H  nM     [        R                  " USS	9ng )
Nr   r   r	  zbwv66.6r   Tr=   r   r   )r   r   r
  r  r  r  r  r3   rB   re   siteDictr   r   )r   r   r
  r  n1n2r   dummys           r   x_testSimplePickleTest.x_testSimplePickle  s    &"LL#))!,44Q7== qTqT%%at%<
%%'XX&&E ' XX&&E '
 Q,r   c                   SSK Jn  SSK Jn  UR                  S5      nUR	                  USS9nUR                  5       nUR                  5       nUR                  U5        UR                  nUR                  5        H  nM     g )Nr   r  r	  r  Tr  )
r   r   r
  r  r3   r   r   r   r   rK   )	r   r   r
  r  r   dr   r   r!  s	            r   x_testFreezeThawPickleTest.x_testFreezeThawPickleD  sq    &"LL' %%at%<KKM $$&


1II YY[E !r   c                z   SSK Jn  SSK Jn  SSK Jn  UR	                  5       nUR                  5       nUR                  UR                  SS95        UR                  U5        UR	                  5       nUR                  5       nUR                  S5      nSUR                  l	        UR                  U5        UR                  U5        [        R                  " U5      n	UR                  SU	5        UR                  U5      n
U
R                  5       nUR                  5       nUR!                  U5        UR                  ng )Nr   r  r   r   whole)typezD#4)r   r   r   r   r   Measurerp   r   durationr*  r
   rM   r   r3   r   r   r   )r   r   r   r   r   ms2m2r   vr   r%  r   s                r   testFreezeThawSimpleVariant Test.testFreezeThawSimpleVariantW  s    &" MMONN	()	]]_^^YYu"
		"
		"OOB	A%%a(KKM$$&


1IIr   c                f   SSK Jn  SSK Jn  SSK Jn  SSK Jn  UR                  S5      n/ SQnUR                  5       nUR                  5       nU H7  u  pUR                  U	5      nXR                  l
        UR                  U5        M9     UR                  U5        [        R                  " UR                  S   SUS	S
S9  UR                  R                  5       R!                  [        R"                  5      R                  5       nUR%                  USS9nUR'                  5       nUR)                  5       nUR+                  U5        UR                  nUR                  S   nUR!                  [        R"                  5      nUS   nU R-                  UR.                  S   S   R0                  S5        g )Nr   r  r	  r   r   r  ))r   eighth)r  quarter)ar4  )r6  r5  g      @rhythmic_switchr   )variantNamereplacementDurationTr  r   g      ?)r   r   r
  r   r   r  r   r+  r   r,  r*  rp   r
   
addVariantr  firstr}   rM   r3   r   r   r   r   rN   r   )r   r   r
  r   r   r  data2M2stream2r-  	pitchNamedurTyper   	unused_v1r   r%  r   r   p0variantsv2s                       r   testFreezeThawVariantTest.testFreezeThawVariantr  sQ   &"" LL'X--/NN")I		)$A%JJOHHQK #* 	q1771:sG'8c	S GGMMO66wGMMO	%%at%<KKM
 $$&


1IIWWQZ((9a[Aq)00#6r   c                X   SSK Jn  SSK Jn  SSK Jn  UR	                  5       nUR                  5       nUR                  5       nUR                  U5        UR                  U5        UR                  USS9nUR                  5         U R                  XF;   5        U R                  XE;   5        g )Nr   r   r   r  Fr  )
r   r   r   r   r   r   rp   r3   rB   
assertTrue)r   r   r   r   r  s1r.  r   s           r   testSerializationScaffoldATest.testSerializationScaffoldA  s     "&YY[]]_]]_
		"
		"%%b%>
%%' 	!!r   c                h   SSK Jn  SSK Jn  SSK Jn  UR	                  S5      nUR	                  S5      nUR                  5       n[        R                  " XE/5      nUR                  SU5        UR                  U5        UR                  U5        UR                  US5      nUR                  U5      n	g )Nr   	converterr   r   CD
jsonPickle)r   rM  r   r   r   r   r	   Liner   rp   	freezeStrthawStr)
r   rM  r   r   r  r   rH  spfrozenunused_thaweds
             r   testJSONPickleSpannerTest.testJSONPickleSpanner  s    % "YYs^YYs^]]_\\2(#
		!R
		"
		"$$R6!))&1r   c                   SSK Jn  SSK Jn  [        [        R
                  " 5       S-  S-  S-  5      nUR                  U5      nUR                  U5      nUR                  U5      nU R                  UR                  S   R                  5       R                  S   R                  R                  UR                  5        g )	Nr   rL  r   miditestPrimitivez
test03.midr      )r   rM  r   r%   r   getSourceFilePathr  rR  rS  assertIsInstancer  flattenr  volumer]   NotRest)r   rM  r   r6  r  r   r%  s          r   testPickleMidiTest.testPickleMidi  s    % ((*!"*+ (( ) OOA"a GGAJ &&r*1188LL	r   r   N)r   r   r   r   r   r  r  r  r"  r&  r1  rD  rI  rW  rb  r   r   r   r   r   r     s<    3,L6	>-N&6%7P"(2r   r   __main__)r1   
__future__r   r:   r   r   r#   r   r&   unittestr   r   r   r   r   r   r   r	   r
   Environmentr   Music21Exceptionr   r   r3   r   TestCaser   r   mainTestr   r   r   <module>rk     s   6n #  	 	             &&|4
	,77 	B B,K	( K	\E1' E1Xz8 z| zT r   