
    3j`'                        d Z ddlmZ ddlZddlZ G d d      Z[ G d dej                        Ze	dk(  rddl
Z
 e
j                  e       yy)	z
Classes for pseudo-m21 objects to inherit from.  The most import attributes that nearly
everything in music21 -- not just things that live in streams --
should inherit from are given below.

Concept borrowed from m21j.
    )annotationsNc                      e Zd ZU dZddgZi Zded<   i Zded<   i Zded	<   d
Z	ded<   e
dd       Ze
dd       ZddZddZy)ProtoM21Objectam  
    A class for pseudo-m21 objects to inherit from.  Any object can inherit from
    ProtoM21Object, and it makes sense for anything a user is likely to encounter
    to inherit from it.  Certain translators, etc. can choose to skip it.

    >>> class PitchCounter(prebase.ProtoM21Object):
    ...     def _reprInternal(self):
    ...         return 'no pitches'

    >>> pc = PitchCounter()
    >>> pc.classes
    ('PitchCounter', 'ProtoM21Object', 'object')
    >>> PitchCounter in pc.classSet
    True
    >>> 'Note' in pc.classSet
    False

    For a True/False intersection check against an iterable, use `classSet.isdisjoint`:

    >>> classList = ('music21.note.Note', 'music21.note.Rest')
    >>> pc.classSet.isdisjoint(classList)
    True
    >>> repr(pc)
    '<...PitchCounter no pitches>'

    ProtoM21Objects, like other Python primitives, cannot be put into streams --
    this is what base.Music21Object does.

    A ProtoM21Object defines several methods relating to unified representation
    and keeping track of the classes of the object.  It has no instance attributes
    or properties, and thus adds a very small creation time impact: recent
    tests show that an empty object with an empty `__init__()` method can
    be created in about 175ns while an empty object that subclasses ProtoM21Object
    with the same empty `__init__()` takes only 180ns, or a 5ns impact.  On
    real objects, the creation time percentage hit is usually much smaller.

    ProtoM21Objects have no __init__() defined, so do not call super().__init__() on
    objects that only inherit from ProtoM21Object unless you like wasting 200ns.
    classesclassSetzdict[str, str]	_DOC_ATTRzdict[type, tuple[str, ...]]_classTupleCacheDictz!dict[type, frozenset[str | type]]_classSetCacheDict tuple[str, ...]	__slots__c                    	 | j                   | j                     S # t        $ rH t        d | j                  j	                         D              }|| j                   | j                  <   |cY S w xY w)a[  
        Returns a tuple containing the names (strings, not objects) of classes that this
        object belongs to -- starting with the object's class name and going up the mro()
        for the object.

        Notes are Music21Objects:

        >>> n = note.Note('C#')
        >>> n.classes
        ('Note', 'NotRest', 'GeneralNote', 'Music21Object', 'ProtoM21Object', 'object')

        Durations are not, but they inherit from ProtoM21Object

        >>> d = duration.Duration('half')
        >>> d.classes
        ('Duration', 'ProtoM21Object', 'SlottedObjectMixin', 'object')

        Having quick access to these things as strings makes it easier to do comparisons:

        Example: find GClefs that are not Treble clefs (or treble 8vb, etc.):

        >>> s = stream.Stream()
        >>> s.insert(10, clef.GClef())
        >>> s.insert(20, clef.TrebleClef())
        >>> s.insert(30, clef.FrenchViolinClef())
        >>> s.insert(40, clef.Treble8vbClef())
        >>> s.insert(50, clef.BassClef())
        >>> s2 = stream.Stream()
        >>> for thing in s:
        ...    if isinstance(thing, clef.GClef) and not isinstance(thing, clef.TrebleClef):
        ...        s2.insert(thing)
        >>> s2.show('text')
        {10.0} <music21.clef.GClef>
        {30.0} <music21.clef.FrenchViolinClef>

        * Changed in v2: returns a tuple, not a list.
        c              3  4   K   | ]  }|j                     y w)N)__name__).0xs     </DATA/.local/lib/python3.12/site-packages/music21/prebase.py	<genexpr>z)ProtoM21Object.classes.<locals>.<genexpr>z   s     H3Gaqzz3Gs   )r	   	__class__KeyErrortuplemro)self
classTuples     r   r   zProtoM21Object.classesP   sc    N	,,T^^<< 	H4>>3E3E3GHHJ8BD%%dnn5	s    AA,+A,c                8   	 | j                   | j                     S # t        $ r t        | j                        }|j                  | j                  j                                | j                  j                         D cg c]  }|j                  dz   |j                  z     nc c}w }}|j                  |       |D cg c]  }|j                  dd       nc c}w }}|j                  |       t        |      }|| j                   | j                  <   |cY S w xY w)a  
        Returns a set (that is, unordered, but indexed) of all classes that
        this class belongs to, including
        string names, fullyQualified string names, and objects themselves.

        It's cached on a per-class basis, so makes for a really fast way of checking to
        see if something belongs
        to a particular class when you don't know if the user has given a string,
        a fully qualified string name, or an object.

        Did I mention it's fast?  It's not as fast as x in n.classes or isinstance(n, x)
        if you know whether it's a string or class, but this is good and safe to take
        in either a string or a class.

        >>> n = note.Note()
        >>> 'Note' in n.classSet
        True
        >>> 'music21.note.Note' in n.classSet
        True
        >>> note.Note in n.classSet
        True

        >>> 'Rest' in n.classSet
        False
        >>> note.Rest in n.classSet
        False

        For checking if an object is part of any number of objects use `not`
        with `isdisjoint`.  A little unwiedly but works super fast:

        >>> checkClasses = (spanner.Slur, note.NotRest)
        >>> not n.classSet.isdisjoint(checkClasses)
        True

        >>> object in n.classSet
        True

        >>> sorted([s for s in n.classSet if isinstance(s, str)])
        ['GeneralNote',
         'Music21Object',
         'NotRest',
         'Note',
         'ProtoM21Object',
         'base.Music21Object',
         'builtins.object',
         'music21.base.Music21Object',
         'music21.note.GeneralNote',
         'music21.note.NotRest',
         'music21.note.Note',
         'music21.prebase.ProtoM21Object',
         'note.GeneralNote',
         'note.NotRest',
         'note.Note',
         'object',
         'prebase.ProtoM21Object']

        >>> sorted([s for s in n.classSet if not isinstance(s, str)], key=lambda x: x.__name__)
        [<class 'music21.note.GeneralNote'>,
         <class 'music21.base.Music21Object'>,
         <class 'music21.note.NotRest'>,
         <class 'music21.note.Note'>,
         <class 'music21.prebase.ProtoM21Object'>,
         <class 'object'>]

        * Changed in v8: partially qualified objects such as 'note.Note' have been added.
        .zmusic21. )r
   r   r   listr   extendr   
__module__r   replace	frozenset)r   	classListr   fullyQualifiedStringspartiallyQualifiedStringsr   s         r   r   zProtoM21Object.classSet~   s    H	**4>>:: 		(,T\\(:IT^^//12NRnnN`N`Nb$cNbQ\\C%7!**%DNb$c!$c23La(bLaq:r)BLa(b%(b67 +H6>D##DNN3O		s(    A#D>#B"!D>C?DDc                2   d}| j                   dk7  r|| j                   dz   z  }|| j                  j                  z  }d|v rd|vr|j                  dd      }| j	                         }|r|j                  d      s|d	z  }|r||j                         z  }|d
z   S )a^  
        Defines the default representation for a ProtoM21Object
        which includes the module name, the class name, and additional
        information, such as the memory location:

        >>> p = prebase.ProtoM21Object()
        >>> repr(p)
        '<music21.prebase.ProtoM21Object object at 0x112590380>'

        The additional information is defined in the `_reprInternal` method,
        so objects inheriting from ProtoM21Object (such as Music21Object)
        should change `_reprInternal` and not `__repr__`.

        Except for music21.base itself, any object in a file called base.py
        has the base part removed.

        >>> from music21.midi.base import MidiEvent
        >>> me = MidiEvent()
        >>> repr(me)
        '<music21.midi.MidiEvent UNKNOWN, track=None>'
        >>> me._reprInternal()
        'UNKNOWN, track=None'
        <__main__r   z.base.zmusic21.basez.baser   : >)r    r   __qualname__r!   _reprInternal
startswithstrip)r   reprHeadstrReprs      r   __repr__zProtoM21Object.__repr__   s    0 ??j(#--HDNN///xN($B''4H$$&7--c2OH'H#~    c                0    dt        t        |              S )av  
        Defines the insides of the representation.

        Overload this method for most objects.

        A default representation:

        >>> p = prebase.ProtoM21Object()
        >>> p._reprInternal()
        'object at 0x112590380'

        A more complex `_reprInternal` that handles the case of objects
        with `.id` defined is found in Music21Object.
        z
object at )hexid)r   s    r   r-   zProtoM21Object._reprInternal   s     C4M?++r3   N)returnr   )r7   zfrozenset[str | type])r7   str)r   r    r,   __doc__
_DOC_ORDERr   __annotations__r	   r
   r   propertyr   r   r2   r-   r   r3   r   r   r      s|    &T 	J !#I~"
 9;5::<7<!#I#+ +Z N N`$L,r3   r   c                      e Zd Zd Zd Zy)Testc                2    ddl m}  || t                      y )Nr   )testCopyAll)music21.test.commonTestr@   globals)r   r@   s     r   testCopyAndDeepcopyzTest.testCopyAndDeepcopy  s    7D')$r3   c                f    ddl m}  |       }d|_        t        |      }| j	                  |d       y )Nr   )Music21Objecthelloz%<music21.base.Music21Object id=hello>)music21.baserE   r6   reprassertEqual)r   rE   brs       r   test_reprInternalzTest.test_reprInternal  s-    .OGCDr3   N)r   r    r,   rC   rL   r   r3   r   r>   r>   
  s    %Er3   r>   r(   )r9   
__future__r   typingtunittestr   TestCaser>   r   music21mainTestr   r3   r   <module>rT      s]    #  n, n,b 
E8 
E zGT r3   