
    3j                       d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ  ej&                  d      Z G d dej*                        Z G d d      Z G d de      Z G d de      Z G d de	j4                        Zedk(  rddlZ ej:                  e       yy)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                      e Zd Zy)FreezeThawExceptionN)__name__
__module____qualname__     ?/DATA/.local/lib/python3.12/site-packages/music21/freezeThaw.pyr   r   [   s    r   r   c                  &    e Zd ZdZd ZddZddZy)StreamFreezeThawBasezZ
    Contains a few methods that are used for both
    StreamFreezer and StreamThawer
    c                    d | _         y Nstream)selfs    r   __init__zStreamFreezeThawBase.__init__g   s	    r   c                    t        |t        j                        st        j                  |      }t        t	        j                               }|dt        j                  |      z   dz   z  S )Nzm21-z.p)
isinstancepathlibPathstrtimer   getMd5)r   	directory	streamStrs      r   getPickleFpz StreamFreezeThawBase.getPickleFpj   sN    )W\\2Y/I 		$	FV]]9%==DEEr   c                B    | j                  |      j                  d      S )Nz.p.json)r%   with_suffix)r   r#   s     r   	getJsonFpzStreamFreezeThawBase.getJsonFpr   s    	*66yAAr   N)r#   zstr | pathlib.Pathreturnzpathlib.Path)r   r   r   __doc__r   r%   r(   r   r   r   r   r   a   s    
FBr   r   c                  n     e Zd ZdZd fd	ZddZddZd Zd Zd Z		 	 	 d	 ddZ
d	 Zdd
ZddZ xZS )StreamFreezera}	  
    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         |           d | _        || _        || _        i | _        ||du rt        j                  |      | _        y ||| _        y y )NF)superr   r   topLevel	streamIdssubStreamFreezerscopydeepcopy)r   	streamObjfastButUnsafer/   r0   	__class__s        r   r   zStreamFreezer.__init__   s\     "!# ]e%; --	2DK"#DK #r   c                j    || j                   }| j                  |       |t        j                  d}|S )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   r4   storages      r   
packStreamzStreamFreezer.packStream   s4     I''	2&dllCr   c                   || j                   }|t        d      t        |j                  d            }| j                  du r| j                  |       |D ]  }t        |t        j                        r4t        |j                  d| j                  d      }|j                          Qt        |t        j                        r4t        |j                  d| j                  d      }|j                          |j                   s| j#                  |        | j#                  |       | j%                  |       | j                  du r| j'                  |       yy)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)r5   r0   r/   )r   r   listrecurser/   findActiveStreamIdsInHierarchyr   r
   Variantr,   _streamr0   r9   r	   SpannerspannerStorageisStreamremoveStreamStatusClientsetupStoredElementOffsetTuplesrecursiveClearSites)r   r4   allElselsubSFs        r   r9   z(StreamFreezer.setupSerializationScaffold   s)   * I )*^__i''5'AB==D //	:B"goo.%JJ"&"nn"	 002B0%%%"&"nn"	 002--b1) . 	%%i0++I6==D $$Y/ !r   c                @    t        |d      rd|j                  _        yy)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hasattrrO   clientr   r4   s     r   rH   z&StreamFreezer.removeStreamStatusClient/  s!     9n-,0I"") .r   c                   t        |d      r1|j                  }|D ]  \  }}|j                  r| j                  |       t	        |t
        j                        r| j                  |j                         t	        |t        j                        r| j                  |j                         t        |d      rt        j                         |_        t        |d      ri |_        |j                  j!                          d|_         t        j                         |_        |j                  j!                          d|_        yy)ak  
        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)rQ   rU   rG   rJ   r   r	   rE   rF   r
   rC   rD   r   
DerivationrV   rW   sitesclear
activeSite)r   startObjstoredElementOffsetTuplesrL   unused_offsets        r   rJ   z!StreamFreezer.recursiveClearSites;  s    p 89:(0(K(K%%>!M;;,,R0b'//2,,R->->?b'//2,,RZZ82}-%/%:%:%<BN2}-%'BN  $ &? $.#8#8#:H NN  ""&H% ;r   c                ,   t        |d      ryg }|j                  D ]e  }||j                  |      f}|j                  |       |j                  r| j                  |       |j                  j                  |       d|_        g |j                  D ]V  }|df}|j                  |       |j                  r| j                  |       |j                  j                  |       d|_        X ||_
        i |_        g |_        g |_	        |j                          y)aX  
        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

        rU   Nend)rQ   	_elementselementOffsetappendrG   rI   rY   remover[   _endElementsrU   rW   coreElementsChanged)r   r4   r]   eelementTuples        r   rI   z,StreamFreezer.setupStoredElementOffsetTuples  s    p 9:; $&!$$Ay66q9:L%,,\:zz33A6GGNN9%AL % ''Au:L%,,\:zz 33A6GGNN9%AL ( 0I	, "	 	!#	%%'r   c                   || j                   }n|}|j                  ddd      }|D cg c]  }t        |       }}|du r|j                  }||j	                         z  }|du rR|j                  d      j                  t        j                        D ]   }	|| j                  |	j                        z  }" t        t        |            }|| _        |S c c}w )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)streamsOnlyr?   includeSelf)rk   )r   rA   idspannerBundlegetSpannerStorageIdsgetElementsByClassr
   rC   rB   rD   r@   setr0   )
r   hierarchyObjectgetSpannersgetVariantsr4   streamsFoundGeneratorsr0   rm   rL   s
             r   rB   z,StreamFreezer.findActiveStreamIdsInHierarchy  s    x "I'I ) 1 1dEJ>B !2 !D %::$9qRU$9	:$%33M;;==I$''D'9LLW__]T@@LL	 ^
 Y(	" ;s   Cc                Z    |y|j                         j                         }|dv ry|dv ryy)aA  
        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)prw   )
jsonpicklejsonry   )striplower)r   fmts     r   parseWriteFmtzStreamFreezer.parseWriteFmtV  s:     ;iik!/!**r   c                   |dvrt        d      | j                  |      }|It        j                         }|j	                  d      r| j                  |      }nx| j                  |      }nft        |t              rt        j                  |      }t        |t        j                        r'|j                         st        j                         |z  }| j                  | j                        }t        |t        j                        r t        j                  dt        |      g       |dk(  rt        j                   |      }|dk(  rt#        j$                  |      }t        |t&        j(                        s(t+        |d      5 }|j-                  |       ddd       |S |j-                  |       |S |d	k(  rid
dl}	 |	j0                  |fi |}
|dk(  r#t#        j$                  |
j1                               }
t+        |dd      5 }|j-                  |
       ddd       |S t        d|       # 1 sw Y   |S xY w# 1 sw Y   |S xY w)a  
        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 typeNrz   z
writing fprw   r   wbry   r   wutf-8encodingbad StreamFreezer format: )r   r~   environLocalgetRootTempDir
startswithr(   r%   r   r    r   r   is_absoluter<   r   
printDebugrw   dumpsr   compressioBytesIOopenwritery   encode)r   r}   fpzipTypekeywordsr#   r;   pickleStringfry   datas              r   r   zStreamFreezer.writeo  s    .(%&IJJ  %:$335I~~f%^^I.%%i0"c"\\"%"gll+BNN4D!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 	s   H,H9,H69Ic                    | j                  |      }| j                  | j                        }|dk(  rt        j                  |d      }|S |dk(  rddl} |j                  |fi |}|S t        d|       )zb
        Convert the object to a pickled/jsonpickled string
        and return the string
        rw   protocolry   r   Nr   )r~   r<   r   rw   r   ry   r   r   )r   r}   r   r;   outry   s         r   writeStrzStreamFreezer.writeStr  s    
   %//$++.(?,,w4C 
 L #*##G8x8C
 
 &(B3%&HIIr   )NFTNr   )NTT)r)   z	list[int])rw   NN)r   r   r   r*   r   r<   r9   rH   rJ   rI   rB   r~   r   r   __classcell__r6   s   @r   r,   r,   w   sZ    Qf$$*;0z
1J'XW(v 	r
 
rl2>@r   r,   c                  T     e Zd ZdZ fdZd
dZd Zd Zd Zd Z	d
dZ
d
dd	Z xZS )StreamThawera,  
    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         |           d | _        y r   )r.   r   r   )r   r6   s    r   r   zStreamThawer.__init__  s    r   c                
   || j                   }|t        d      |j                  }d|_        | j                  |       | j	                  |       t        |j                               }|D ]  }|j                  }d|v r-t               }|j                  |j                         i |_        @d|v r-t               }|j                  |j                         i |_        q|j                  s~| j	                  |        ||_        y)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)
        Nr>   FrC   rE   )r   r   autoSortrestoreElementsFromTuplesrestoreStreamStatusClientr@   rA   classesr   teardownSerializationScaffoldrD   _cacherF   rG   )r   r4   storedAutoSortrK   rg   eClassesrM   s          r   r   z*StreamThawer.teardownSerializationScaffold  s      I )*^__"++"	&&y1&&y1i'')*AyyHH$$33AII> h&$33A4D4DE..q1 ( ,	r   c                   t        |d      rP|j                  D ]/  \  }}|dk7  r	 |j                  ||       |j                  |       1 |`|j                          |D ]"  }|j                  du s| j                  |       $ y# t        $ r9 t	        d       t	        ||       t	        |       t	        |j
                          w xY w)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>
        rU   r`   z-Problem in decoding. Here is some debug info:TN)
rQ   rU   
coreInsertAttributeErrorprintr[   coreStoreAtEndrf   rG   r   )r   r4   rg   offset
subElements        r   r   z&StreamThawer.restoreElementsFromTuples  s    Z 9:;&AA	6U?!,,VQ7 ,,Q/ B 4))+#J""d* ..z: $ * MNfa(i(i223s   BACc                @    t        |d      r||j                  _        yy)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.)
        rO   NrP   rS   s     r   r   z&StreamThawer.restoreStreamStatusClient^  s!     9n-,5I"") .r   c                    |d   }|t         j                  k7  rt        j                  d       |d   }| j	                  |       |S )z<
        Convert from storage dictionary to Stream.
        r8   z?this pickled file is out of date and may not function properly.r   )r   r:   r   warnr   )r   r;   versionr4   s       r   unpackStreamzStreamThawer.unpackStreamg  sG     ,'dll"_`H%	**95r   c                n    t        |t              r|j                  d      ryy|j                  d      ryy)z;
        Look at the file and determine the format
        s   {"ry   rw   z{")r   bytesr   )r   r;   s     r   parseOpenFmtzStreamThawer.parseOpenFmts  s6     gu%!!%(#!!$'#r   c                   t         j                  j                  |      st        j	                         }||z  }t        |d      5 }|j                         }ddd       | j                        }|dk(  rt        j                          t        |d      5 }|t        j                  |      }nc|dk(  r<|j                         }t        j                  |      }		 t        j                  |	      }n"t        j                          t!        d|       | j#                  |      | _        ddd       t        j                          y|dk(  rSddl}t        |d	d
      5 }|j                         }ddd       |j)                        }| j#                  |      | _        yt!        d|      # 1 sw Y   UxY w# t        $ r(}
t        j                          t!        d|
       |
d}
~
ww xY w# 1 sw Y   xY w# 1 sw Y   xY w)zH
        For a supplied file path to a pickled stream, unpickle
        rbNrw   r   zProblem in decoding: zUnknown zipType ry   r   rr   r   r   )ospathexistsr   r   r   readr   r   !restorePathClassesAfterUnpicklingrw   loadr   
decompressloadsr   r   r   r   ry   decode)r   r   r   r#   r   fileDatar}   r;   compressedStringuncompressedrg   ry   r   s                r   r   zStreamThawer.open  s    ww~~b!$335IR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   F)AG*F6-9G*G6)F36	G'?#G""G''G**G36G?c                   ||}n| j                  |      }|dk(  rt        j                  |      }n)|dk(  rddl}|j	                  |      }nt        d|       t        j                  d|        | j                  |      | _	        y)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.
        Nrw   ry   r   r   z"StreamThawer:openStr: storage is: )
r   rw   r   ry   r   r   r   r   r   r   )r   r   pickleFormatr}   r;   ry   s         r   openStrz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   r   r   r   r   r   r   r   r   r   s   @r   r   r     s:    62,hC;J6
 "(LV1 1r   r   c                  N    e Zd Zd Zd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zy)Testc                   ddl m} ddl m} |j                         }|j                         }|j	                         }|j                  d|       |j                  d|       t        |      }|j                         }~~~t               }|j                  |       |j                  }	| j                  t        |	      d       | j                  |	d   j                  d       y )Nr   r   note       @      @   )music21r   r   StreamNoteinsertr,   r   r   r   assertEquallenr   )
r   r   r   ru   sDummynsfr   st	outStreams
             r   testSimpleFreezeThawzTest.testSimpleFreezeThaw  s    " MMOIIK	ac11kkm^


3II	Y+1,,c2r   c                   ddl m} ddl m} |j                         }|j                         }|j	                         }t        j                  |g      }|j                  d|       |j                  d|       |j                  d|       | j                  |j                  d   j                         |j                  d          t        |      }|j                  d      }~~~t               }	|	j                  |       |	j                  }
| j!                  t#        |
      d	       | j!                  |
j                  d   j$                  d       | j                  |
j                  d   j                         |
j                  d          y )
Nr   r   r   g        r   r   ry   r}      )r   r   r   r   r   r	   Slurr   assertIsspannersgetFirstnotesr,   r   r   r   r   r   r   )r   r   r   ru   r   r   sl1r   r   r   r   s              r   testFreezeThawWithSpannerz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                    ddl m} |j                  d      j                  d   j	                  dd      }t        |      }|j                  d      }t               }|j                  |       y)	zs
        Versions of jsonpickle prior to  0.9.3 were having problems serializing Enums.

        Works now
        r   corpusluca/gloriar   r   ry   r   N)	r   r   parsepartsmeasuresr,   r   r   r   )r   r   csf2data2st2s         r   testFreezeThawJsonPickleEnumz!Test.testFreezeThawJsonPickleEnum  sZ     	#LL'--a099!Q?A.nEr   c                <   ddl m} |j                  d      }t        |      }|j	                  d      }t               }|j                  |       |j                  }| j                  t        |j                  d   j                  d      j                        d       y )Nr   r   r   rw   r         )r   r   r   r,   r   r   r   r   r   r   r   measurer   )r   r   r   r   r   r   ru   s          r   $testFreezeThawCorpusFileWithSpannersz)Test.testFreezeThawCorpusFileWithSpanners  sw    "LL'1{{x{(^


4IIQWWQZ//28891=r   c                ~   ddl m} ddl m} |j                  d      j                  d   j                  d      j                  }|d   }|d   }|j                  |d      }|j                          |j                  j                  D ]  } |j                  j                  D ]  } t        j                  |d	      }y )
Nr   r   r   zbwv66.6r   Tr5   r   r   )r   r   r   r   r   r   r   r,   r9   rY   siteDictrw   r   )r   r   r   r   n1n2r   dummys           r   x_testSimplePicklezTest.x_testSimplePickle  s    &"LL#))!,44Q7== qTqT%%at%<
%%'XX&&E ' XX&&E '
 Q,r   c                   ddl m} ddl m} |j                  d      }|j	                  |d      }|j                         }|j                         }|j                  |       |j                  }|j                         D ]  } y )Nr   r   r   r   Tr   )
r   r   r   r   r,   r   r   r   r   rA   )	r   r   r   r   r   dr   ru   r  s	            r   x_testFreezeThawPicklezTest.x_testFreezeThawPickleB  sq    &"LL' %%at%<KKM $$&


1II YY[E !r   c                ~   ddl m} ddl m} ddl m} |j	                         }|j                         }|j                  |j                  d             |j                  |       |j	                         }|j                         }|j                  d      }d|j                  _	        |j                  |       |j                  |       t        j                  |      }	|j                  d|	       |j                  |      }
|
j                         }|j                         }|j!                  |       |j                  }y )Nr   r   r   r   whole)typezD#4)r   r   r   r   r   Measurerc   r   durationr
  r
   rC   r   r,   r   r   r   )r   r   r   r   ru   ms2m2r  vr   r  r   s                r   testFreezeThawSimpleVariantz Test.testFreezeThawSimpleVariantU  s    &" MMONN	()	]]_^^YYu"
		"
		"OOB	A%%a(KKM$$&


1IIr   c                p   ddl m} ddl m} ddl m} ddl m} |j                  d      }g d}|j                         }|j                         }|D ]8  \  }	}
|j                  |	      }|
|j                  _
        |j                  |       : |j                  |       t        j                  |j                  d   d|d	d
       |j                  j                         j!                  t        j"                        j                         }|j%                  |d      }|j'                         }|j)                         }|j+                  |       |j                  }|j                  d   }|j!                  t        j"                        }|d   }| j-                  |j.                  d   d   j0                  d       y )Nr   r   r   r   r   r   ))r   eighth)r   quarter)ar  )r  r  g      @rhythmic_switchr   )variantNamereplacementQuarterLengthTr   r   g      ?)r   r   r   r   r   r   r   r  r   r  r
  rc   r
   
addVariantr   firstro   rC   r,   r   r   r   r   rD   r   )r   r   r   r   r   r   data2M2stream2r  	pitchNamedurTyper   	unused_v1r   r  r   ru   p0variantsv2s                       r   testFreezeThawVariantzTest.testFreezeThawVariantp  sV   &"" LL'X--/NN")Iw		)$A%AJJOHHQK #* 	q1771:sG'8SV	X GGMMO66wGMMO	%%at%<KKM
 $$&


1IIWWQZ((9a[Aq)00#6r   c                ^   ddl m} ddl m} ddl m} |j	                         }|j                         }|j                         }|j                  |       |j                  |       |j                  |d      }|j                          | j                  ||v        | j                  ||v        y )Nr   r   r   r   Fr   )
r   r   r   r   r   r   rc   r,   r9   
assertTrue)r   r   r   r   r  s1r  r   s           r   testSerializationScaffoldAzTest.testSerializationScaffoldA  s     "&YY[]]_]]_
		"
		"%%b%>
%%' 	b!b!r   c                h   ddl m} ddl m} ddl m} |j	                  d      }|j	                  d      }|j                         }t        j                  ||g      }|j                  d|       |j                  |       |j                  |       |j                  |d      }|j                  |      }	y )Nr   	converterr   r   CD
jsonPickle)r   r*  r   r   r   r   r	   Liner   rc   	freezeStrthawStr)
r   r*  r   r   r  r  r&  spfrozenunused_thaweds
             r   testJSONPickleSpannerzTest.testJSONPickleSpanner  s    % "YYs^YYs^]]_\\2r(#
		!R
		"
		"$$R6!))&1r   c                ~   ddl m} ddl m} t        t	        j
                         dz  dz  dz        }|j                  |      }|j                  |      }|j                  |      }| j                  |j                  d   j                         j                  d   j                  j                  |j                         y )	Nr   r)  r   miditestPrimitivez
test03.midr      )r   r*  r   r    r   getSourceFilePathr   r/  r0  assertIsInstancer   flattenr   volumerR   NotRest)r   r*  r   r  r   r   r  s          r   testPickleMidizTest.testPickleMidi  s    % ((*!"*+ (( ) OOA"a GGAJ &&r*1188LL	r   N)r   r   r   r   r   r   r   r  r  r  r#  r'  r4  r>  r   r   r   r   r     s<    3,L6	>-R&6%7P"(2r   r   __main__)r*   
__future__r   r2   r   r   r   rw   r!   unittestr   r   r   r   r   r   r   r	   r
   Environmentr   Music21Exceptionr   r   r,   r   TestCaser   r   mainTestr   r   r   <module>rF     s   6n #  	 	             &{&&|4
	,77 	B B,I	( I	XD1' D1V|8 |@ zGT r   