
    rh                       % 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
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r\R(                  R*                  r\R,                  " S	\5      r\R,                  " S
\5      r\R,                  " S\5      r\R,                  " S\5      r\R,                  " S\5      r\R,                  " S\5      r\R,                  " S\5      r\R,                  " S\5      r\R,                  " S\5      rSr Sr!Sr"Sr#Sr$SFS jr%S r&S r'S r( " S S5      r) " S S\)5      r* " S S\)5      r+ " S  S!\)5      r, " S" S#\)5      r- " S$ S%\R\                  \)5      r/ " S& S'5      r0 " S( S)\05      r1 " S* S+\05      r2 " S, S-\25      r3 " S. S/\25      r4 " S0 S1\05      r5 " S2 S3\55      r6 " S4 S5\55      r7 " S6 S7\75      r8 " S8 S95      r9/ r:S:\;S;'    " S< S=\	Rx                  5      r= " S> S?\	Rx                  5      r>S@ r?\@SA:X  a  \A" \R                  5      SB:X  a  \?" 5         g\>" 5       rC\=" 5       rD\A" \R                  5      SC:  d  \R                  SB   SD;   a  S SKr\R                  " \>5        g\R                  SB   SE:X  a  \F" \D\R                  SC   5      " 5         g\G" \C\R                  SB   5      (       a  \F" \C\R                  SB   5      " 5         ggg)G    )annotationsN)reload)common)environment)exceptions21	configureTzFinale.*\.appzSibelius\.appzFinale.*\.exezSibelius\.exezFinale Reader\.appzMuseScore.*\.appz"Musescore.*\\bin\\MuseScore.*\.exezDorico.*\.appzDorico.*\.exez$https://www.music21.org/music21docs/zhttps://www.musescore.orgz'https://groups.google.com/g/music21listN   c                   [         R                  " U 5      (       a  U nOU R                  S5      n/ nU(       aZ  U HS  nUS:X  a  UR                  S5        M  US:X  a  UR                  S5        M5  U[        R
                  " U[        5      -  nMU     OUnSn[        U5       H  u  pxUS:X  a  SnOUUS:X  a  [        U5      S:  a  SU S3nO9US:X  a  [        U5      S:X  a  SU S3nOU[        U5      S-
  :  a  U S3nOU S3nUS:  a!  Xb-  S:X  a  [        SS9n	U	R                  5         [        R                  R                  U5        [        R                  R                  5         US-  nM     g	)
zW
Display a message to the user, handling multiple lines as necessary and wrapping text

  r      z 
zPausing for page.promptHeaderN)r   
isListLikesplitappendtextwrapwrap
LINE_WIDTH	enumeratelenAnyKeyaskUsersysstdoutwriteflush)
msg	wrapLineslinesPerPagelinespostsub	lineCountilineds
             K/home/james-whalen/.local/lib/python3.13/site-packages/music21/configure.pywriteToUserr*   H   sQ   
  		$DCbyBC  c:66  IT?2:D!VD	AvS>D!VD	QvQ<DTQV3<DV1:Dq=Y5:$78AIIK



Q	) #    c                 .    [         R                  " S5      $ )Npurelib)	sysconfigget_path r+   r)   getSitePackagesr1   w   s    i((r+   c                    0 n  SSK nUR                  U S'   [        [        S5      (       a,  [        R
                  " 5       nUS    SUS    SUS    3U S	'   OSU S	'   [        R                  " S
[        R                  " 5       5      U S'   [        R                  U S'   [        R                  n[        U5      S:X  a  US   S;  a
  US   U S'   U $ US   U S'   U $ ! [         a    SU S'    Nf = f)z$
Return a dictionary with user data
r   Nzmusic21.versionNoneunamez,       zos.unamez%a, %d %b %Y %H:%M:%Sztime.gmtimeztime.timezoner   )Nr3   r   ztime.tzname)music21VERSION_STRImportErrorhasattrosr4   timestrftimegmtimetimezonetznamer   )r#   r7   r4   r@   s       r)   getUserDatarA   |   s     D)")"5"5 r7
#AhZr%(2eAhZ@Z!Z--(?OD MMD[[F
6{aF1I-??$Qi] K %Qi]K#  )"()s   C C+*C+c                >   U nUnSn [         R                  SU 35        Ub   U$ [        [        R                  " U5      5       H)  nXQ:X  d  M
  [        R
                  R                  X55      n  O   Un[        R
                  R                  U5      u  p6X2:X  a   U$ M  )zH
Ascend up paths given a start; return when target file has been found.
Nzat dir: )environLocal
printDebugsortedr;   listdirpathjoinr   )starttargetlastDirthisDirmatchfnjunks          r)   _crawlPathUpwardrP      s     GGE
(7) 45 L G,-B|W1 . g.L r+   c                  (    \ rS rSrSrSS jrS rSrg)DialogError   z3
DialogError is a normal object, not an Exception.
Nc                    Xl         g Nsrc)selfrW   s     r)   __init__DialogError.__init__   s    r+   c                P    SU R                   R                   SU R                   S3$ )Nz<music21.configure.z: >)	__class____name__rW   rX   s    r)   __repr__DialogError.__repr__   s&    $T^^%<%<$=RzKKr+   rV   rU   )r^   
__module____qualname____firstlineno____doc__rY   r`   __static_attributes__r0   r+   r)   rR   rR      s    Lr+   rR   c                  0   ^  \ rS rSrSrSU 4S jjrSrU =r$ )KeyInterruptError   zA
Subclass of DialogError that deals with Keyboard Interruptions.
c                    > [         TU ]  US9  g NrV   superrY   rX   rW   r]   s     r)   rY   KeyInterruptError.__init__       S!r+   r0   rU   r^   rb   rc   rd   re   rY   rf   __classcell__r]   s   @r)   rh   rh          " "r+   rh   c                  0   ^  \ rS rSrSrSU 4S jjrSrU =r$ )IncompleteInput   zj
Subclass of DialogError that runs when the user has provided
incomplete input that cannot be understood.
c                    > [         TU ]  US9  g rk   rl   rn   s     r)   rY   IncompleteInput.__init__   rp   r+   r0   rU   rq   rs   s   @r)   rv   rv      s    
" "r+   rv   c                  0   ^  \ rS rSrSrSU 4S jjrSrU =r$ )NoInput   z^
Subclass of DialogError for when the user has provided no input, and there is not a default.
c                    > [         TU ]  US9  g rk   rl   rn   s     r)   rY   NoInput.__init__   rp   r+   r0   rU   rq   rs   s   @r)   r{   r{      rt   r+   r{   c                  0   ^  \ rS rSrSrSU 4S jjrSrU =r$ )BadConditions   z
Subclass of DialogError for when the user's system does support the
action of the dialog: something is missing or
otherwise prohibits operation.
c                    > [         TU ]  US9  g rk   rl   rn   s     r)   rY   BadConditions.__init__   rp   r+   r0   rU   rq   rs   s   @r)   r   r      s    " "r+   r   c                      \ rS rSrSrg)DialogException   r0   N)r^   rb   rc   rd   rf   r0   r+   r)   r   r      s    r+   r   c                      \ rS rSrSrSS jrS rS rS rS r	SS	 jr
SS
 jrSS jrS rS rS rS rS rSS jrSSS.S jjrSS jrSS jrSS jrSrg)Dialog   a#  
Model a dialog as a question and response. Have different subclasses for
different types of questions. Store all in a Conversation, or multiple dialog passes.

A `default`, if provided, is returned if the users provides no input and just enters return.

The `tryAgain` option determines if, if a user provides incomplete or no response,
and there is no default (for no response), whether the user is given another chance
to provide valid input.

The `promptHeader` is a string header that is placed in front of any common header
for this dialog.
Nc                    S U l         S U l        U R                  U5      n[        U[        5      (       d  X@l        OS U l        X l        X0l        SU l        / SQU l	        g )N   )windarwinnix)
_result_resultPrior_parseUserInput
isinstancerR   _default	_tryAgain_promptHeader_maxAttempts
_platforms)rX   defaulttryAgainr   defaultCookeds        r)   rY   Dialog.__init__   s^      ,,W5 -55)M !DM!)  3r+   c                    [        U5        g)z2
Write output to user. Call module-level function
N)r*   rX   r   s     r)   _writeToUserDialog._writeToUser  s     	Cr+   c                z     [        5       nU$ ! [         a    [        5       s $ [         a    [	        5       s $ f = f)z6
Collect from user; return None if an empty response.
)inputKeyboardInterruptrh   	ExceptionrR   )rX   r#   s     r)   _readFromUserDialog._readFromUser  s;    
	!7DK  	'$&& 	!= 	!s    :::c                x    UR                  5       nU R                  b  U SU R                   3U l        gXl        g)a  
Add a message to the front of the stored prompt header.

>>> d = configure.Dialog()
>>> d.prependPromptHeader('test')
>>> d._promptHeader
'test'

>>> d = configure.Dialog(promptHeader='this is it')
>>> d.prependPromptHeader('test')
>>> d._promptHeader
'test this is it'
Nr   stripr   r   s     r)   prependPromptHeaderDialog.prependPromptHeader(  s;     iik)$'5$*<*<)=!>D!$r+   c                x    UR                  5       nU R                  b  U R                   SU 3U l        gXl        g)z

>>> d = configure.Dialog()
>>> d.appendPromptHeader('test')
>>> d._promptHeader
'test'

>>> d = configure.Dialog(promptHeader='this is it')
>>> d.appendPromptHeader('test')
>>> d._promptHeader
'this is it test'
Nr   r   r   s     r)   appendPromptHeaderDialog.appendPromptHeader<  s;     iik)$($6$6#7q!>D!$r+   c                    [        USSS9nUR                  US9  UR                  5       n[        U[        5      (       a  gU$ )a  
What to do if input is incomplete

>>> prompt = configure.YesOrNo(default=True)
>>> prompt._askTryAgain(force='yes')
True
>>> prompt._askTryAgain(force='n')
False
>>> prompt._askTryAgain(force='')  # gets default
True
>>> prompt._askTryAgain(force='blah')  # error gets False
False
Fz)Your input was not understood. Try Again?r   r   r   force)YesOrNor   	getResultr   rR   rX   r   r   r(   r#   s        r)   _askTryAgainDialog._askTryAgainO  sD     Ge!LN				{{}dK((Kr+   c                h   U R                   b  U R                   R                  5       nUR                  S5      (       d  UR                  S5      (       a  SnOSnU R                   R                  S5      (       a  US-  nO+U R                   R                  S5      (       a  US-  nOUS-  nU U U 3nU$ )a  
Prepare the header, given a string.

>>> from music21 import configure
>>> d = configure.Dialog()
>>> d._rawQueryPrepareHeader('test')
'test'
>>> d = configure.Dialog(promptHeader='what are you doing?')
>>> d._rawQueryPrepareHeader('test')
'what are you doing? test'
?.r   :z

r   r   )r   r   endswith)rX   r   headerdivs       r)   _rawQueryPrepareHeaderDialog._rawQueryPrepareHeaderh  s     )''--/Fs##vs';';!!**622v##,,T22ts
HSE#'C
r+   c                    U R                   bf  UR                  5       nUR                  S5      (       a  SnUSS nUR                  5         OSnU R                  U R                   5      nU SU SU S3nU$ )z&
Prepare the end of the query message
Nr   r   z (default is )r   )r   r   r   _formatResultForUser)rX   r   r   r   s       r)   _rawQueryPrepareFooterDialog._rawQueryPrepareFooter  sw     ==$))+C||C  #2h		//>GEwiqQ7C
r+   c                    g)5
Return a multiline presentation of an introduction.
Nr0   r_   s    r)   _rawIntroductionDialog._rawIntroduction       r+   c                    g)2
Return a multiline presentation of the question.
Nr0   r_   s    r)   	_rawQueryDialog._rawQuery       	r+   c                    U$ )z
For various result options, we may need to at times convert the internal
representation of the result into something else. For example, we might present
the user with 'Yes' or 'No' but store the result as True or False.
r0   rX   results     r)   r   Dialog._formatResultForUser  s	     r+   c                    U$ )z
Translate string to desired output. Pass None through
(as no input), convert '' to None, and pass all other
outputs as IncompleteInput objects.
r0   rX   raws     r)   r   Dialog._parseUserInput  s	     
r+   c                    g)z
Evaluate the user's string entry after parsing; do not return None:
either return a valid response, default if available, or IncompleteInput object.
Nr0   r   s     r)   _evaluateUserInputDialog._evaluateUserInput      
 	r+   c                    g)zt
Call this method immediately before calling askUser.
Can be used for configuration getting additional information.
Nr0   rX   r   s     r)   _preAskUserDialog._preAskUser  r   r+   F)	skipIntroc                  U R                  5       nUb  U(       d  U R                  U5        U R                  US9nUSL a  [        5       U l        g[        U R                  5       H  nU R                  5       n[        U[        5      (       a  X`l          gUc"  U R                  U5        U R                  5       nO[        R                  SU/5        Un[        U[        5      (       a  Xpl          gU R                  U5      n[        U[        [         45      (       a2  Xl        U R"                  (       a  U R%                  5       (       a  M    g  gXl          g   g)zz
Ask the user, display the query. The force argument can
be provided to test. Sets self._result; does not return a value.
Nr   FzwriteToUser:)r   r   r   r   r   ranger   r   r   rR   r   rC   rD   rh   r   r{   rv   r   r   )	rX   r   r   intror#   r&   queryrawInputcookedInputs	            r)   r   Dialog.askUser  s0    %%'Ye$ e,5=(?DL t(()A NN$E%--$}!!%(--/''(?@ 
 ($566'
 11(;K
 +'ABB*>>((**  +_ *r+   c                    U R                   $ )zr
Return the result, or None if not set. This may also do a
processing routine that is part of the desired result.
)r   rX   simulates     r)   r   Dialog.getResult  s    
 ||r+   c                    g)z$
does nothing; redefine in subclass
Nr0   r   s     r)   _performActionDialog._performAction  r   r+   c                t   U R                  5       n[        U R                  [        5      (       a6  [        R                  SU R                   35        U R                  SS/5        gU(       a#  [        R                  SU R                   35        g U R                  US9  g! [         a    [        S5      ef = f)z
After getting a result, the query might require an action
to be performed. If result is None, this will use whatever
value is found in _result.

If simulate is True, no action will be taken.
z0performAction() called, but result is an error: zNo action taken.r   z0performAction() called, but in simulation mode: r   z(perform action raised a dialog exceptionN)	r   r   r   rR   rC   rD   r   r   r   )rX   r   dummys      r)   performActionDialog.performAction  s      dllK00##B4<<.QS1378##B4<<.QSR##X#6" R &&PQQ	Rs   B! !B7)r   r   r   r   r   r   r   NTN)TN)r   rU   )TF)r^   rb   rc   rd   re   rY   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rf   r0   r+   r)   r   r      sl    34!%(%&28"Au AHRr+   r   c                  <   ^  \ rS rSrSrSU 4S jjrS rS rSrU =r	$ )r   i/  z
Press any key to continue
c                "   > [         TU ]  XUS9  g Nr   rl   rX   r   r   r   r]   s       r)   rY   AnyKey.__init__4      ,Wr+   c                ,    SnU R                  U5      nU$ )r   zPress return to continue.)r   r   s     r)   r   AnyKey._rawQuery7  s      *))#. 
r+   c                    g)z
Always returns True
Tr0   r   s     r)   r   AnyKey._parseUserInputA  r   r+   r0   )NFN)
r^   rb   rc   rd   re   rY   r   r   rf   rr   rs   s   @r)   r   r   /  s    X r+   r   c                  H   ^  \ rS rSrSrS	U 4S jjrS rS rS rS r	Sr
U =r$ )
r   iI  a   
Ask a yes or no question.

>>> d = configure.YesOrNo(default=True)
>>> d.askUser('yes')  # force arg for testing
>>> d.getResult()
True

>>> d = configure.YesOrNo(tryAgain=False)
>>> d.askUser('junk')  # force arg for testing
>>> d.getResult()
 <music21.configure.IncompleteInput: junk>
c                "   > [         TU ]  XUS9  g r   rl   r   s       r)   rY   YesOrNo.__init__X  r   r+   c                6    USL a  gUSL a  g[        SU 35      e)z
For various result options, we may need to at times convert
the internal representation of the result into something else.
For example, we might present the user with 'Yes' or 'No' but
store the result as True or False.
TYesFNoz&attempting to format result for user: )r   r   s     r)   r   YesOrNo._formatResultForUser[  s-     T>u_ "$J6("STTr+   c                N    SnU R                  U5      nU R                  U5      nU$ )a  
Return a multiline presentation of the question.

>>> d = configure.YesOrNo(default=True)
>>> d._rawQuery()
'Enter Yes or No (default is Yes): '
>>> d = configure.YesOrNo(default=False)
>>> d._rawQuery()
'Enter Yes or No (default is No): '

>>> d = configure.YesOrNo(default=True, promptHeader='Would you like more time?')
>>> d._rawQuery()
'Would you like more time? Enter Yes or No (default is Yes): '
zEnter Yes or No: )r   r   r   s     r)   r   YesOrNo._rawQueryk  s.     "))#.))#.
r+   c                    Uc
  [        5       $ [        U5      nUR                  5       nUR                  5       nUS:X  a
  [        5       $ US;   a  gUS;   a  g[	        U5      $ )aQ  
Translate string to desired output. Pass None and '' (as no input), as
NoInput objects, and pass all other outputs as IncompleteInput objects.

>>> d = configure.YesOrNo()
>>> d._parseUserInput('y')
True
>>> d._parseUserInput('')
<music21.configure.NoInput: None>
>>> d._parseUserInput('asdf')
<music21.configure.IncompleteInput: asdf>
r   yesy1trueT)non0falseF)r{   strr   lowerrv   r   s     r)   r   YesOrNo._parseUserInput  sb     ;9#hiikiik"99++--s##r+   c                    U R                  U5      n[        U[        5      (       a  U R                  b  U R                  $ U$ )a  
Evaluate the user's string entry after parsing;
do not return None: either return a valid response,
default if available, IncompleteInput, NoInput objects.

>>> d = configure.YesOrNo()
>>> d._evaluateUserInput('y')
True
>>> d._evaluateUserInput('False')
False
>>> d._evaluateUserInput('')  # there is no default,
<music21.configure.NoInput: None>
>>> d._evaluateUserInput('wer')  # there is no default,
<music21.configure.IncompleteInput: wer>

>>> d = configure.YesOrNo('yes')
>>> d._evaluateUserInput('')  # there is a default
True
>>> d._evaluateUserInput('wbf')  # there is a default
<music21.configure.IncompleteInput: wbf>

>>> d = configure.YesOrNo('n')
>>> d._evaluateUserInput('')  # there is a default
False
>>> d._evaluateUserInput(None)  # None is processed as NoInput
False
>>> d._evaluateUserInput('blah')  # None is processed as NoInput
<music21.configure.IncompleteInput: blah>
r   r   r{   r   rX   r   	rawParseds      r)   r   YesOrNo._evaluateUserInput  s;    < ((-	i))}}(}}$r+   r0   r   )r^   rb   rc   rd   re   rY   r   r   r   r   rf   rr   rs   s   @r)   r   r   I  s(    XU ($:$ $r+   r   c                  >   ^  \ rS rSrSr  SU 4S jjrSS jrSrU =r$ )AskOpenInBrowseri  zv
Ask the user if the want to open a URL in a browser.

>>> d = configure.AskOpenInBrowser('https://www.music21.org/')
c                   > [         TU ]  X#US9  Xl        Ub  XPl        g SU R                   S3nU R	                  U5        g )Nr   zOpen the following URL (z) in a web browser?
)rm   rY   
_urlTargetr   r   )rX   	urlTargetr   r   r   promptr   r]   s          r)   rY   AskOpenInBrowser.__init__  sH    ,W#!',T__,==RSC##C(r+   c                |    U R                  5       nUSL a!  [        R                  " U R                  5        gUSL a  gg)M
The action here is to open the stored URL in a browser, if the user agrees.
TFN)r   
webbrowseropen_newr  rX   r   r   s      r)   r   AskOpenInBrowser._performAction  s8     !T>0u_ r+   )r   r  TTNNr   )	r^   rb   rc   rd   re   rY   r   rf   rr   rs   s   @r)   r  r    s     :>+/) r+   r  c                  D   ^  \ rS rSrSr  SU 4S jjrS rSS jrSrU =r	$ )	AskSendInstallationReporti  zN
Ask the user if they want to send a report
regarding their system and usage.
c                ^   > [         TU ]  XUS9  Uc  0 nX@l        SnU R                  U5        g )Nr   zWould you like to send a pre-formatted email to music21 regarding your installation? Installation reports help us make music21 work better for you)rm   rY   _additionalEntriesr   )rX   r   r   r   additionalEntriesr   r]   s         r)   rY   "AskSendInstallationReport.__init__  s=    ,W$ ""3]$r+   c                |   / nUR                  S5        UR                  S5        UR                  S5        UR                  S5        [        5       nUR                  U R                  5        [	        U5       H  nUR                  U SX#    35        M     UR                  S5        UR                  [
        R                  5        UR                  S5        UR                  S5        UR                  S5        [        R                  " 5       nUS:X  a  SR                  U5      nOS	R                  U5      nS
U 3nU$ )NzYPlease send the following email; your return email address will never be used in any way.r   zNThe following information on your installation will be used only for research.z // zpython version:zBelow, please provide a few words about what sorts of tasks or problems you plan to explore with music21. Any information on your background is also appreciated (e.g., amateur musician, computer programmer, professional music researcher). Thanks!r   z%0D%0Ar   zGmailto:music21stats@gmail.com?subject=music21 Installation Report&body=)
r   rA   updater,  rE   r   versionr   getPlatformrH   )rX   bodyuserDatakeyplatformr   s         r)   _getMailToStr'AskSendInstallationReport._getMailToStr  s    5 	6B 6 	7B=//0(#CKK3%tHM?34 $%&CKK B S 	T 	B%%'u==&D99T?DYZ^Y_b
r+   c                x    U R                  5       nUSL a%  [        R                  " U R                  5       5        gg)r#  TN)r   r$  openr7  r&  s      r)   r   (AskSendInstallationReport._performAction  s1     !T>OOD..01 r+   )r,  r(  r   )
r^   rb   rc   rd   re   rY   r7  r   rf   rr   rs   s   @r)   r*  r*    s%     /36:
% D2 2r+   r*  c                  j   ^  \ rS rSrSrSU 4S jjrSS jrS rSS jrSS jr	SS jr
S	 rS
 rSrU =r$ )SelectFromListi#  a  
General class to select values from a list.

>>> d = configure.SelectFromList()  # empty selection list
>>> d.askUser('no')  # results in bad condition
>>> d.getResult()
<music21.configure.BadConditions: None>

>>> d = configure.SelectFromList()  # empty selection list
>>> def validResults(force=None):
...     return range(5)
>>> d._getValidResults = validResults  # provide alt function for testing
>>> d.askUser(2)  # results in bad condition
>>> d.getResult()
2
c                "   > [         TU ]  XUS9  g r   rl   r   s       r)   rY   SelectFromList.__init__5  r   r+   c                    Ub  U$ / $ )zW
Return a list of valid results that are possible and should be displayed to the user.
r0   r   s     r)   _getValidResultsSelectFromList._getValidResults8  s     LIr+   c                    U$ )zF
Reduce each complete file path to stub, or otherwise compact display
r0   r   s     r)   r   #SelectFromList._formatResultForUserC  s	     r+   c                    [        USSS9nUR                  US9  UR                  5       n[        U[        5      (       a  gUS;  a  [        S5      eU$ )a  
What to do if the selection list is empty. Only return True or False:
if we should continue or not.

>>> prompt = configure.SelectFromList(default=True)
>>> prompt._askFillEmptyList(force='yes')
True
>>> prompt._askFillEmptyList(force='n')
False
>>> prompt._askFillEmptyList(force='')  # no default, returns False
False
>>> prompt._askFillEmptyList(force='blah')  # error gets false
False
Fz'The selection list is empty. Try Again?r   r   )TFz>_askFillEmptyList(): sub-command returned non True/False value)r   r   r   r   rR   r   r   s        r)   _askFillEmptyList SelectFromList._askFillEmptyListI  sa      G"!JL 	
			{{}dK((=(%TV VKr+   c                T    U R                  5       nU(       d  U R                  US9nU$ g)aL  
Before we ask user, we need to run _askFillEmptyList list if the list is empty.

>>> d = configure.SelectFromList()
>>> d._preAskUser('no')  # force for testing
False
>>> d._preAskUser('yes')  # force for testing
True
>>> d._preAskUser('')  # no default, returns False
False
>>> d._preAskUser('x')  # bad input returns False
False
r   T)rA  rF  )rX   r   optionsr#   s       r)   r   SelectFromList._preAskUserh  s0     ''))))6DKr+   c                   / nSnU R                  US9nU(       d  [        S5      $ U H0  nU R                  U5      nUR                  SU SU 35        US-  nM2     SnU R	                  U5      nU R                  U5      nUSU/-   $ )a  
Return a multiline presentation of the question.

>>> d = configure.SelectFromList()
>>> d._rawQuery(['a', 'b', 'c'])
['[1] a', '[2] b', '[3] c', ' ', 'Choose a number from the preceding options: ']

>>> d = configure.SelectFromList(default=1)
>>> d._default
1
>>> d._rawQuery(['a', 'b', 'c'])
['[1] a', '[2] b', '[3] c', ' ',
 'Choose a number from the preceding options (default is 1): ']
r   r   zno options available[z] z,Choose a number from the preceding options: r   )rA  r   r   r   r   r   )rX   r   headr&   rI  entryr$   tails           r)   r   SelectFromList._rawQuery~  s     ''e'4 !788E++E2CKK!A3b'FA 
 >**40**40sDk!!r+   c                    Uc
  [        5       $ US:X  a
  [        5       $ US;   a  SnU$  [        U5      nU$ ! [        [        [        4 a    [        U5      s $ f = f)z
Convert all values to an integer, or return NoInput or IncompleteInput.
Do not yet evaluate whether the number is valid in the context of the selection choices.

>>> d = configure.SelectFromList()
r   r
  r   )r{   int
ValueError	TypeErrorZeroDivisionErrorrv   )rX   r   r#   s      r)   r   SelectFromList._parseUserInput  sp     ;9"99 ++D ,3x  	+<= ,&s++,s   6  AAc                    U R                  U5      n[        U[        5      (       a  U R                  b  U R                  $ U$ rU   r  r  s      r)   r   !SelectFromList._evaluateUserInput  s=    ((-	 i))}}(}}$ r+   r0   r   rU   NN)r^   rb   rc   rd   re   rY   rA  r   rF  r   r   r   r   rf   rr   rs   s   @r)   r=  r=  #  s7    "X	>,"@0	 	r+   r=  c                  P   ^  \ rS rSrSrS	U 4S jjrS rS
S jrS rSS jr	Sr
U =r$ )AskAutoDownloadi  -
General class to select values from a list.
c                "   > [         TU ]  XUS9  g r   rl   r   s       r)   rY   AskAutoDownload.__init__  r   r+   c                
    / SQ$ )r   )a  The BSD-licensed music21 software is distributed with a corpus of encoded compositions which are distributed with the permission of the encoders (and, where needed, the composers or arrangers) and where permitted under United States copyright law. Some encodings included in the corpus may not be used for commercial uses or have other restrictions: please see the licenses embedded in individual compositions or directories for more details.r   aN  In addition to the corpus distributed with music21, other pieces are not included in this distribution, but are indexed as links to other web sites where they can be downloaded (the "virtual corpus"). If you would like, music21 can help your computer automatically resolve these links and bring them to your hard drive for analysis.  a  To the best of our knowledge, the music (if not the encodings) in the corpus are either out of copyright in the United States and/or are licensed for non-commercial use. These works, along with any works linked to in the virtual corpus, may or may not be free in your jurisdiction. If you believe this message to be in error regarding one or more works please contact Michael Cuthbert at michael.asato.cuthbert@gmail.comr   zWould you like to:r0   r_   s    r)   r    AskAutoDownload._rawIntroduction  s     	r+   c                    Ub  U$ / SQ$ )z
Just return number options
)zPAcknowledge these terms and allow music21 to aid in finding pieces in the corpusz4Acknowledge these terms and block the virtual corpusz}Do not agree to these terms and will not use music21 (agreeing to the terms of the corpus is mandatory for using the system).r0   r   s     r)   rA   AskAutoDownload._getValidResults  s     L r+   c                    U R                  U5      n[        U[        5      (       a  U R                  b  U R                  n[        U[        5      (       a  U$ SUs=::  a  S::  a   U$   [        U5      $ )z
Evaluate the user's string entry after parsing; do not return None:
either return a valid response, default if available, IncompleteInput, NoInput objects.
r      )r   r   r{   r   rR   rv   r  s      r)   r   "AskAutoDownload._evaluateUserInput  sp    
 ((-	i))}}( !MM	 i--	Q  #9--r+   c                J   U R                  5       nUS;   a\  [        [        5        US:X  a  [        R                  " SS5        O/US:X  a  [        R                  " SS5        OUS:X  a  [	        S5      eUS	;   a+  U R                  S
[        R                  " S5       3S/5        gg)z
override base.
)r   r5   rd  r   autoDownloadallowr5   denyrd  z6user selected an option that terminates configuration.)r   r5   zAuto Download set to: r   N)r   r   r   setr   r   getr&  s      r)   r   AskAutoDownload._performAction  s     !Y;{8171%&^__V!78W7XY[^_` r+   r0   )r   TNrU   r   )r^   rb   rc   rd   re   rY   r   rA  r   r   rf   rr   rs   s   @r)   r[  r[    s)    X:..a ar+   r[  c                  ^   ^  \ rS rSrSrSU 4S jjrSS. SS jjrSS jrSS jrS	 r	S
r
U =r$ )SelectFilePathi#  r\  c                "   > [         TU ]  XUS9  g r   rl   r   s       r)   rY   SelectFilePath.__init__(  r   r+   z**/*globc                   [         R                  " U5      nUR                  U5       H5  nU" [        U5      5      (       d  M  UR	                  [        U5      5        M7     g)ah  
Uses comparisonFunction to see if a file in path0 matches
the RE embedded in comparisonFunction and if so manipulate the list
in post

comparisonFunction = function (lambda function on path returning True/False)
path0 = os-specific string
post = list of matching results to fill
glob = string to glob for (default **/*, but **/*.exe on Windows and * on Mac).
N)pathlibPathrr  r  r   )rX   comparisonFunctionpath0r#   rr  path0_as_pathpath1s          r)   _getAppOSIndependent#SelectFilePath._getAppOSIndependent+  sF      U+"''-E!#e*--CJ' .r+   c                    / nS[         R                  " SSS94 H+  n[        U[        5      (       d   eU R	                  XUSS9  M-     U$ )z
Provide a comparison function that returns True or False based on the file name.
This looks at everything in Applications, as well as every directory in Applications
z/Applicationsz~/ApplicationsF)returnPathlib*rq  )r   	cleanpathr   r  rz  )rX   rv  r#   rw  s       r)   _getDarwinAppSelectFilePath._getDarwinApp=  sX    
 %v'7'78HX]'^_EeS))))%%&8C%P ` r+   c                    / nSnU HE  nU[         R                  ;  a  M  [         R                  U   nUS:X  a  M4  U R                  XUSS9  MG     U$ )zR
Provide a comparison function that returns True or False based on the file name.
)ProgramFileszProgramFiles(x86)ProgramW6432r   z**/*.exerq  )r;   environrz  )rX   rv  r#   environKeyspossibleEnvironKeyenvironPaths         r)   
_getWinAppSelectFilePath._getWinAppH  s`    
 K"-!3**%78Kb %%&8tR\%] #. r+   c                $   U R                  U5      n[        U[        5      (       a  U R                  b  U R                  n[        U[        5      (       a  U$ U R                  5       nSUs=::  a  [        U5      ::  a
  O  OX2S-
     $ [        U5      $ )z
Evaluate the user's string entry after parsing;
do not return None: either return a valid response, default if available,
IncompleteInput, NoInput objects.

Here, we convert the user-selected number into a file path
r   )r   r   r{   r   rR   rA  r   rv   )rX   r   r  rI  s       r)   r   !SelectFilePath._evaluateUserInputY  s     ((-	i))}}( !MM	 i-- '')	)S\)q=))"9--r+   r0   r   )rw  r  r#   	list[str]rr  r  )returnr  )r^   rb   rc   rd   re   rY   rz  r  r  r   rf   rr   rs   s   @r)   rn  rn  #  s4    X
 *0(#&($	". .r+   rn  c                  X    \ rS rSrSrSS jrS rS rS rS r	SS	 jr
SS
 jrSS jrSrg)SelectMusicXMLReaderiv  zB
Select a MusicXML Reader by presenting a user a list of options.
Nc                B    [         R                  U UUUS9  SS/U l        g )Nr   r   r   )rn  rY   r   )rX   r   r   r   s       r)   rY   SelectMusicXMLReader.__init__{  s.    (/)1-9 	  	; $U+r+   c                
    SS/$ )r   zDefining an XML Reader permits automatically opening music21-generated MusicXML in an editor for display and manipulation when calling the show() method. Setting this option is highly recommended. r   r0   r_   s    r)   r   %SelectMusicXMLReader._rawIntroduction  s    
O 	
 	
r+   c                $   S nS nS nS nS nU R                  U5      nX`R                  U5      -  nX`R                  U5      -  nX`R                  U5      -  nX`R                  U5      -  n/ nU H  nX;  d  M
  UR                  U5        M     U$ )z@
Get all possible MusicXML Reader paths on Darwin (i.e., macOS)
c                0    [         R                  U 5      S L$ rU   )reFinaleAppsearchxs    r)   comparisonFinaleGSelectMusicXMLReader._getMusicXMLReaderDarwin.<locals>.comparisonFinale      %%a(44r+   c                0    [         R                  U 5      S L$ rU   )reMuseScoreAppr  r  s    r)   comparisonMuseScoreJSelectMusicXMLReader._getMusicXMLReaderDarwin.<locals>.comparisonMuseScore  s    !((+477r+   c                0    [         R                  U 5      S L$ rU   )reFinaleReaderAppr  r  s    r)   comparisonFinaleReaderMSelectMusicXMLReader._getMusicXMLReaderDarwin.<locals>.comparisonFinaleReader  s    $++A.d::r+   c                0    [         R                  U 5      S L$ rU   )reSibeliusAppr  r  s    r)   comparisonSibeliusISelectMusicXMLReader._getMusicXMLReaderDarwin.<locals>.comparisonSibelius       ''*$66r+   c                0    [         R                  U 5      S L$ rU   )reDoricoAppr  r  s    r)   comparisonDoricoGSelectMusicXMLReader._getMusicXMLReaderDarwin.<locals>.comparisonDorico  r  r+   )r  r   )	rX   r  r  r  r  r  resultsresone_paths	            r)   _getMusicXMLReaderDarwin-SelectMusicXMLReader._getMusicXMLReaderDarwin  s    	5	8	;	7	5 $$%89%%&677%%&<==%%&899%%&677 H"

8$   
r+   c                    S nS nS nS nU R                  U5      nXPR                  U5      -  nXPR                  U5      -  nXPR                  U5      -  n/ nU H  nXv;  d  M
  UR                  U5        M     U$ )z3
Get all possible MusicXML Reader paths on Windows
c                0    [         R                  U 5      S L$ rU   )reFinaleExer  r  s    r)   r  DSelectMusicXMLReader._getMusicXMLReaderWin.<locals>.comparisonFinale  r  r+   c                H    [         R                  U 5      S L=(       a    SU ;  $ )Nzcrash-reporter)reMuseScoreExer  r  s    r)   r  GSelectMusicXMLReader._getMusicXMLReaderWin.<locals>.comparisonMuseScore  s$    !((+47U<LTU<UUr+   c                0    [         R                  U 5      S L$ rU   )reSibeliusExer  r  s    r)   r  FSelectMusicXMLReader._getMusicXMLReaderWin.<locals>.comparisonSibelius  r  r+   c                0    [         R                  U 5      S L$ rU   )reDoricoExer  r  s    r)   r  DSelectMusicXMLReader._getMusicXMLReaderWin.<locals>.comparisonDorico  r  r+   )r  r   )rX   r  r  r  r  r  r  r  s           r)   _getMusicXMLReaderWin*SelectMusicXMLReader._getMusicXMLReaderWin  s    	5	V	7	5 //"56??#344??#566??#344 H"

8$   
r+   c                    / $ )z0
Get all possible MusicXML Reader paths on Unix
r0   r_   s    r)   _getMusicXMLReaderNix*SelectMusicXMLReader._getMusicXMLReaderNix  s	     	r+   c                    Ub  U$ [         R                  " 5       nUS:X  a  U R                  5       nU$ US:X  a  U R                  5       nU$ US:X  a  U R	                  5       nU$ U R	                  5       nU$ )z
Return a list of valid results that are possible and
should be displayed to the user.
These will be processed by _formatResultForUser before usage.
r   r   r   )r   r2  r  r  r  )rX   r   r6  r#   s       r)   rA  %SelectMusicXMLReader._getValidResults  s     L%%'u--/D  !002D
 	 --/D  --/Dr+   c                   [         n[        USSSS9nUR                  US9  UR                  5       nUR	                  5         [        U[        5      (       a  gUSL af  [        U R                  5       HM  n[        SSSS9nUR                  US9  UR                  5       nUSL a    U$ [        U[        5      (       d  ML    U$    U$ )zL
If we do not have any musicxml readers, ask user if they want to download.
TFz}No available MusicXML readers are found on your system. We recommend downloading and installing a reader before continuing.

)r  r   r   r   r   Are you ready to continue?r   )
urlMuseScorer  r   r   r   r   rR   r   r   r   )rX   r   r   r  r(   r#   r   s          r)   rF  &SelectMusicXMLReader._askFillEmptyList  s     !	 X	Y 	
			{{}	 dK(( t|"4#4#45Eu-IKAIIEI*;;=Dt| K $D+66K 6 Kr+   c                    U R                  5       nUbi  [        U[        5      (       dS  [        [        5        [        R
                  " SU5        [        R                  " S5      nU R                  SU 3S/5        ggg)r#  NmusicxmlPathzMusicXML Reader set to: r   )r   r   rR   r   r   rj  rk  r   )rX   r   r   musicXmlNews       r)   r   #SelectMusicXMLReader._performAction  sl     !j&E&E; OONF3%//.9K!9+GMN 'Fr+   )r   r   rU   rY  r   )r^   rb   rc   rd   re   rY   r   r  r  r  rA  rF  r   rf   r0   r+   r)   r  r  v  s2    ,	
 D<*#JOr+   r  c                  D    \ rS rSrSrSS jrS rS rS rS r	SS	 jr
S
rg)ConfigurationAssistanti  z2
Class for managing numerous configuration tasks.
c                r    Xl         [        R                  " 5       U l        / U l        U R                  5         g rU   )	_simulater   r2  	_platform_dialogs
getDialogsr   s     r)   rY   ConfigurationAssistant.__init__#  s*    !++- r+   c                   [        SS9nU R                  R                  U5        [        SS9nU R                  R                  U5        [	        SS9nU R                  R                  U5        [        [        SS9nU R                  R                  U5        [        [        SS9nU R                  R                  U5        [        SS9nU R                  R                  U5        g )	Nr   r   TzThe music21 discussion group provides a forum for asking questions and getting help. Would you like to see the music21 discussion list or sign up for updates?)r  r   zBWould you like to view the music21 documentation in a web browser?z0The music21 Configuration Assistant is complete.r   )	r  r  r   r[  r*  r  urlMusic21ListurlGettingStartedr   rX   r(   s     r)   r  !ConfigurationAssistant.getDialogs+  s     +QD)Q%d3Q$@A
 	Q 'WY 	Q RSQr+   c                    / nUR                  S5        UR                  S5        UR                  S5        UR                  S5        [        U5        g )NzWelcome to the music21 Configuration Assistant. You will be guided through a number of questions to install and set up music21. Simply pressing return at a prompt will select a default, if available.r   zUYou may run this configuration again at a later time by running music21/configure.py.r   )r   r*   r   s     r)   _introduction$ConfigurationAssistant._introductionF  sK    

 _ 	` 	

2

 8 	9

3Cr+   c                    g rU   r0   r_   s    r)   _conclusion"ConfigurationAssistant._conclusionR  s    r+   c                p    / nUR                  S[        -  5        UR                  S5        [        U5        g)z
Draw a line
_r   N)r   r   r*   r   s     r)   _hrConfigurationAssistant._hrU  s-     

3#$

3Cr+   Nc                   Uc  / nU R                  5         U R                  5         [        U R                  5       H  u  p#U R                  UR
                  ;  a  M!  U R                  5         [        U5      U:  a  X   nOSnUR                  US9  UR                  5       n UR                  U R                  S9  M     U R                  5         g! [         a       M  f = f)zv
The forceList, if provided, is a list of string arguments
passed in order to the included dialogs. Used for testing.
Nr   r   )r  r  r   r  r  r   r   r   r   r   r  r   r  )rX   	forceListr&   r(   r   unused_posts         r)   runConfigurationAssistant.run^  s    
 I
dmm,DA~~Q\\1HHJ9~!!IIEI"++-K8 -* 	 # s   "C
CC)r  r  r  r   rU   )r^   rb   rc   rd   re   rY   r  r  r  r  r  rf   r0   r+   r)   r  r    s%     6
r+   r  z
list[type]
_DOC_ORDERc                  8    \ rS rSrS rS rS rS rS rS r	Sr
g	)
TestUserInputi  c                   [        5         [        S5        [        5       nUR                  5         [        SUR                  5       5        [        5         [        S5        [        SS9nUR                  5         [        SUR                  5       5        [        5         [        S5        [        SS9nUR                  5         [        SUR                  5       5        g )Nzstarting: YesOrNo()getResult():zstarting: YesOrNo(default=True)Tr  z starting: YesOrNo(default=False)F)printr   r   r   r  s     r)   testYesOrNoTestUserInput.testYesOrNo  s    #$I			nakkm,/0D!			nakkm,01E"			nakkm,r+   c                    [        5         [        S5        [        5       nUR                  5         [        SUR                  5       5        g )N starting: SelectMusicXMLReader()r  r  r  r   r   r  s     r)   testSelectMusicXMLReader&TestUserInput.testSelectMusicXMLReader  s1    01 "			nakkm,r+   c                    [        5         [        S5        [        SS9nUR                  5         [        SUR                  5       5        g )Nz)starting: SelectMusicXMLReader(default=1)r   r  r  r  r  s     r)   testSelectMusicXMLReaderDefault-TestUserInput.testSelectMusicXMLReaderDefault  s3    9: +			nakkm,r+   c                    [        5         [        S5      nUR                  5         [        SUR                  5       5        UR	                  5         g )Nzhttps://www.music21.org/r  )r  r  r   r   r   r  s     r)   testOpenInBrowserTestUserInput.testOpenInBrowser  s5    78			nakkm,	r+   c                ~   [        5         [        S5        [        5       nUR                  5         [        SUR                  5       5        UR	                  5         [        5         [        S5        [        5       nSS jnX!l        UR                  5         [        SUR                  5       5        UR	                  5         g )Nr  r  z,starting: SelectMusicXMLReader() w/o resultsc                    / $ rU   r0   r   s    r)   getValidResults@TestUserInput.testSelectMusicXMLReader2.<locals>.getValidResults      Ir+   rU   )r  r  r   r   r   rA  )rX   r(   r  s      r)   testSelectMusicXMLReader2'TestUserInput.testSelectMusicXMLReader2  s    01 "			nakkm,	<= "	 -			nakkm,	r+   c                L    [        S5        [        SS9nUR                  5         g )Nz"Running ConfigurationAssistant allTr   )r  r  r  )rX   
configAssts     r)   testConfigurationAssistant(TestUserInput.testConfigurationAssistant  s    23+T:
r+   r0   N)r^   rb   rc   rd   r  r  r  r  r  r	  rf   r0   r+   r)   r  r    s     -&--*r+   r  c                  D    \ 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  UR                  SSSS9nUR                  S5        U R	                  [        UR                  5       5      S5        UR                  S	5        U R	                  [        UR                  5       5      S
5        UR                  S5        U R	                  [        UR                  5       5      S
5        UR                  S5        U R	                  [        UR                  5       5      S5        UR                  S SSS9nUR                  S5        U R	                  [        UR                  5       5      S5        UR                  S	5        U R	                  [        UR                  5       5      S
5        UR                  S5        U R	                  [        UR                  5       5      S5        UR                  S5        U R	                  [        UR                  5       5      S5        g )Nr   r   TFr  r   r  Falser  Truer   blahz)<music21.configure.IncompleteInput: blah>z!<music21.configure.NoInput: None>)r7   r   r   r   assertEqualr  r   rX   r   r(   s      r)   r  Test.testYesOrNo  st   %dU+G  I			#Q[[]+W5			#Q[[]+V4			"Q[[]+V4			&Q[[]+-XYdU+G  I			#Q[[]+W5			#Q[[]+V4			"Q[[]+-PQ			&Q[[]+-XYr+   c                f    SSK Jn  UR                  SS9nU R                  UR                  S5        g )Nr   r  r   r  )r7   r   r=  r  r   r  s      r)   testSelectFromListTest.testSelectFromList  s,    %$$Q$/Q'r+   c                    SSK Jn  UR                  5       nSS jnX2l        UR	                  SSS9  UR                  5       nU R                  XAR                  5        g )Nr   r  c                    / $ rU   r0   r   s    r)   r  7Test.testSelectMusicXMLReaders.<locals>.getValidResults&  r  r+   r  T)r   r   rU   )r7   r   r  rA  r   r   assertIsInstancer   )rX   r   r(   r  r#   s        r)   testSelectMusicXMLReadersTest.testSelectMusicXMLReaders!  sO    %**,	 -			t	,{{}d$;$;<r+   c                p   [         R                  S5      nU R                  UR                  S5      S5        U R                  [         R                  S5      S 5        [         R                  S5      nU R                  UR                  S5      S5        U R                  [         R                  S5      S 5        g )NzFinale 2011.appr   zfinal blah 2011z
Finale.appzFinal Cut 2017.app)r  r  r  group)rX   gs     r)   testReTest.testRe/  s    01%67++,=>E|,\2++,@A4Hr+   c                    [        SS9ng )NTr   )r  )rX   	unused_cas     r)   r	  Test.testConfigurationAssistant:  s    *D9	r+   c                    [        5       ng rU   )r*  rX   unused_ds     r)   testGetUserDataTest.testGetUserData=  s
    ,.r+   c                    [        5       ng rU   )r[  r'  s     r)   testGetUserData2Test.testGetUserData2C  s
    "$r+   c                    [        5       ng rU   )r   r'  s     r)   
testAnyKeyTest.testAnyKeyI  s	    8r+   r0   N)r^   rb   rc   rd   r  r  r  r!  r	  r)  r,  r/  rf   r0   r+   r)   r  r    s,    Z0(
=	I:/%r+   r  c                 8    [        5       n U R                  5         g rU   )r  r  )cas    r)   r  r  P  s    		!BFFHr+   __main__r   r5   )alltestte)T   )H
__future__r   r;   rt  rer<   r   r.   r   unittestr$  	importlibr   r7   r   r   r   EnvironmentrC   _DOC_IGNORE_MODULE_OR_PACKAGE	RegexFlag
IGNORECASEcompiler  r  r  r  r  r  r  r  r  
urlMusic21r  r  r  r   r*   r1   rA   rP   rR   rh   rv   r{   r   Music21Exceptionr   r   r   r   r  r*  r=  r[  rn  r  r  r  __annotations__TestCaser  r  r  r^   r   argvtestInstancer6  mainTestgetattrr:   r0   r+   r)   <module>rI     s   # 	  	  
         &&{3 $ \\$$
 jj):6

+Z8jj):6

+Z8JJ4jA /<A:Njj):6jj):63
*: :
&,^)
84L L" ""k ""k ""K "	l33[ 	@R @RH
V 4wf wvw D92 92z\V \~^an ^aBP.^ P.feO> eOR^ ^v 
J BH%% BJH8 H\
 z
388} v_sxx=1 >T" XXa[D B$&\388A;//L#((1+.0 0' r+   