
    rh8                    R   % S SK Jr  / SQ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
  \
R                  " S5      r   SS jr " S S	5      r " S
 S5      r " S S\R                   5      r " S S\R$                  5      r/ rS\S'   \S:X  a  S SKr\R.                  " \5        gg)    )annotations)JobProcessorMetadataCachingJobcacheMetadataWorkerProcessN)common)environmentzmetadata.cachingc                   SSK Jn  UR                  SS9nU c
  USS SS/-   n [        R                  " U 5      (       d  U 4n [        R
                  " 5       nUR                  5         / nU  H(  nUR                  U5      nXhR                  XU5      -  nM*     SU S	3n	USL a  [        R                  U	5        O[        R                  U	5        U H9  n
S
U
 3n	USL a  [        R                  U	5        M$  [        R                  U	5        M;     g)zj
Cache metadata from corpora in `corpusNames` as local cache files.

Call as ``metadata.cacheMetadata()``
r   )managerT)skipNoneNlocalcorezcache: final writing time: z secondszpath failed to parse: )music21.corpusr   listLocalCorporaNamesr   
isIterableTimerstartfromNamer   environLocalwarn
printDebug)corpusNamesuseMultiprocessingverboser   localCorporaNamestimerfailingFilePaths
corpusNamecorpusObjectmessagefailingFilePaths              R/home/james-whalen/.local/lib/python3.13/site-packages/music21/metadata/caching.pyr   r   !   s
    '55t5D'*gv-@@[))"nLLNE	KKM  "
''
3667ITYZZ " ,E7(;G$'"(+*?*;<d?g&##G, ,    c                  \    \ rS rSrSrSS jrS rS rS rS r	S	 r
S
 rS r\S 5       rSrg)r   P   a  
Parses one corpus path, and attempts to extract metadata from it:

>>> job = metadata.caching.MetadataCachingJob(
...     'bach/bwv66.6',
...     parseUsingCorpus=True,
...     corpusName='core',
...     )
>>> job.jobNumber
0
>>> job.corpusName
'core'
>>> job.run()
((<music21.metadata.bundles.MetadataEntry 'bach_bwv66_6'>,), ())
>>> results = job.getResults()
>>> errors = job.getErrors()

TODO: error list, not just numbers needs to be reported back up.
Nc                    [         R                  " U5      U l        / U l        [	        U5      U l        / U l        [        U5      U l        X@l	        g N)
pathlibPathfilePathfilePathErrorsint	jobNumberresultsboolparseUsingCorpusr   )selfr*   r-   r0   r   s        r"   __init__MetadataCachingJob.__init__f   s?    X. Y $%5 6$r#   c                P   SS K n/ U l        U R                  5       n[        R	                  SU R
                   SU 35        Ub3  SUR                  ;   a  U R                  U5        OU R                  U5        AUR                  5         U R                  5       U R                  5       4$ )Nr   zGot ParsedObject from z: Opus)gcr.   parseFilePathr   r   r*   classes	parseOpusparseNonOpuscollect
getResults	getErrors)r1   r6   parsedObjects      r"   runMetadataCachingJob.runn   s    ))+$T]]O2l^D	F#---|,!!,/


 $.."222r#   c                   SSK Jn  SSK Jn  S n U R                  SL a  UR	                  U R
                  SS9nU$ UR	                  [        U R
                  5      SS9n U$ ! [         a}  n[        R                  SU R
                   SU 35        [        R                  [        R                  " 5       5        U R                  R                  U R
                  5         S nAU$ S nAff = f)	Nr   )	converter)corpusFT)forceSourcezparse failed: z, )music21rB   rC   r0   parser*   str	Exceptionr   r   	traceback
format_excr+   append)r1   rB   rC   r>   es        r"   r7    MetadataCachingJob.parseFilePath}   s    %"	6$$-(t}}$O   &||C,>D|Q
 	  	6##nT]]O2aS$IJ##I$8$8$:;&&t}}55		6s   )A! #A! !
C(+A2C##C(c           	        SSK Jn   UR                  R                  R	                  U R
                  5      nUR                  b  UR                  5       nUR                  UR                  5        UR                  U5        [        R                  SU 35        UR                  R                  U R
                  UU R                  S9nU R                  R                  U5        g [        R                  SR                  [         R"                  " [%        U R
                  5      5      5      5        UR                  R                  U R
                  S U R                  S9nU R                  R                  U5        g ! [&         aY    [        R)                  SR                  U R*                  5      5        [        R)                  [,        R.                  " 5       5         g f = f)Nr   metadatazupdateMetadataCache: storing: )
sourcePathmetadataPayloadr   z=addFromPaths: got stream without metadata, creating stub: {0}z=Had a problem with extracting metadata for {0}, piece ignored)rE   rP   bundlesMetadataBundlecorpusPathToKeycleanFilePathRichMetadatamergeupdater   r   MetadataEntryr   r.   rK   formatr   relativepathrG   rH   r   r*   rI   rJ   )r1   r>   rP   
corpusPathrichMetadatametadataEntrys         r"   r:   MetadataCachingJob.parseNonOpus   s   $	6!))88HH""$J$$0'446""<#8#89##L1''4ZLAC ( 0 0 > >#11$0# !? !
 ##M2''))/++C0B0B,CD*FG !) 0 0 > >#11$(# !? !
 ##M2 	6 77=vdmm7LNi2245	6s   CE9 #BE9 9A GGc           
        SSK Jn  Sn [        UR                  5       H  u  p4U R	                  XC5        AM     UR                  R                  U R                   S S9nU R"                  R%                  U5        g ! [
         aj  n[        R                  SR                  X0R                  [        U5      5      5        [        R                  [        R                  " 5       5         S nANS nAff = f)Nr   rO   THad a problem with extracting metadata for score {0} in {1}, whole opus ignored: {2})rQ   rR   )rE   rP   	enumeratescoresparseScoreInsideOpusrH   r   r   r[   r*   rG   r   rI   rJ   rS   rZ   rV   r.   rK   )r1   r>   rP   scoreNumberscore	exceptionr_   s          r"   r9   MetadataCachingJob.parseOpus   s    $ 		<&/0C0C&D"))%= 'E !((66))  7 
 	M*  	<228&I3@A ##I$8$8$:;;	<s   0A: :
C.A C))C.c           
     z   SSK Jn   UR                  5       nUR                  UR                  5        UR	                  U5        UR                  b  UR                  R
                  c/  [        R                  SR                  U R                  5      5        g UR                  R                  R                  U R                  UR                  R
                  S9n[        R                  SU 35        UR                  R                  U R                  UR                  R
                  US9nU R                  R!                  U5        g ! ["         aj  n[        R%                  SR                  X R                  ['        U5      5      5        [        R                  [(        R*                  " 5       5         S nAg S nAff = f)Nr   rO   zOaddFromPaths: got Opus that contains Streams that do not have work numbers: {0})numberzaddFromPaths: storing: )rQ   rk   rR   rb   )rE   rP   rW   rX   rY   rk   r   r   r[   r*   rS   rT   rU   rV   rZ   r.   rK   rH   r   rG   rI   rJ   )r1   rg   rf   rP   r^   r]   r_   rh   s           r"   re   'MetadataCachingJob.parseScoreInsideOpus   sh    	%	<#002Lu~~.&~~%)>)>)F'' &/1 &--<<LL&& >>00 M 
 ''-j\:< ( 0 0 > >#11 >>00$0 !? !
 ##M2 	<fI@A
 ##I$8$8$:;;	<s    BE B.E 
F:A F55F:c                ,    [        U R                  5      $ r'   )tupler+   r1   s    r"   r=   MetadataCachingJob.getErrors   s    T(())r#   c                ,    [        U R                  5      $ r'   )rn   r.   ro   s    r"   r<   MetadataCachingJob.getResults   s    T\\""r#   c                    [         R                  " 5       n U R                  R                  U5      nU$ ! [         a    U R                  n U$ f = fr'   )r   getCorpusFilePathr*   relative_to
ValueError)r1   r]   rV   s      r"   rV    MetadataCachingJob.cleanFilePath   sQ    --/
	* MM55jAM   	* MMM	*s   4 AA)r   r*   r+   r-   r0   r.   )r   TN)__name__
__module____qualname____firstlineno____doc__r2   r?   r7   r:   r9   re   r=   r<   propertyrV   __static_attributes__ r#   r"   r   r   P   sE    *%3 6B+2#<N*#
  r#   r   c                  P    \ rS rSrSr\S 5       r\SS j5       r\S	S j5       rSr	g)
r   i  a  
Processes metadata-caching jobs, either serially (e.g. single-threaded) or
in parallel, as a generator.

Yields a dictionary of:

* MetadataEntry instances
* failed file paths
* the last processed file path
* the number of remaining jobs

>>> jobs = []
>>> mdb = corpus.corpora.CoreCorpus().search('monteverdi')[:3]
>>> paths = [x.sourcePath for x in mdb]

>>> for corpusPath in paths:
...     job = metadata.caching.MetadataCachingJob(
...         corpusPath,
...         parseUsingCorpus=True,
...         corpusName='core',
...         )
...     jobs.append(job)
>>> jobGenerator = metadata.caching.JobProcessor.process_serial(jobs)
>>> for result in jobGenerator:
...     print(result['remainingJobs'])
...
2
1
0
c                2    SR                  X-
  U UU5      nU$ )z#
Report on the current job status.
z>updated {0} of {1} files; total errors: {2} ... last file: {3})r[   )	totalJobsremainingJobsr*   filePathErrorCountr    s        r"   _reportJobProcessor._report%  s+    
 SYY%	
 r#   Nc              #    #    U=(       d    [         R                  " 5       n[        US5      n[        U 5      n[	        X5      n[
        R                  SU SU S35        / n[        R                  " 5       n[        R                  " 5       n[        U5       Vs/ s H  n[        XE5      PM     nnU H  nUR                  5         M     U (       a  U  H(  n	UR                  [        R                  " U	5      5        M*     [        [        U 5      5       H_  n
[        R                   " UR#                  5       5      n	U	R%                  5       nU	R'                  5       nUS-  nUUU	R(                  US.v   Ma     U H  nUR                  S5        M     UR+                  5         UR-                  5         UR-                  5         U H  nUR+                  5         M     gs  snf 7f)z
Process jobs in parallel, with `processCount` processes.

If `processCount` is none, use 1 fewer process than the number of
available cores.

jobs is a list of :class:`~music21.metadata.MetadataCachingJob` objects.

   zProcessing z jobs in parallel, with z processes.metadataEntrieserrorsr*   r   N)r   cpusmaxlenminr   r   multiprocessingJoinableQueueQueueranger   r   putpickledumpsloadsgetr<   r=   r*   joinclose)jobsprocessCountr   r.   	job_queueresult_queue_workersworkerjobunused_jobCounterr   s               r"   process_parallelJobProcessor.process_parallel4  s     $4v{{}<+D	<7-(@kZ	\#113	&,,.!,/1/q !9/ 	 1FLLN fll3/0 %*3t9%5!ll<#3#3#56..*"'.$ #%2	  &6 FMM$ FKKM /1s   BGG-D0Gc              #     #    [        U 5      nU  H-  nUR                  5       u  p4US-  nUUUR                  US.v   M/     g7f)z
Process jobs serially.
r   r   N)r   r?   r*   )r   r   r   r.   r   s        r"   process_serialJobProcessor.process_seriale  sK     
 D	C!ggiOGQM#* LL!.	  s   A Ar   r'   )r   zlist[MetadataCachingJob])
rx   ry   rz   r{   r|   staticmethodr   r   r   r~   r   r#   r"   r   r     sE    B 
 
 - -`  r#   r   c                  2   ^  \ rS rSrSrU 4S jrS rSrU =r$ )r   iz  zO
A worker process for use by the multithreaded metadata-caching job
processor.
c                :   > [         TU ]  5         Xl        X l        g r'   )superr2   r   r   )r1   r   r   	__class__s      r"   r2   WorkerProcess.__init__  s    "(r#   c                T    U R                   R                  5       nUc  U R                   R                  5         g [        R                  " U5      nUR                  5         U R                   R                  5         U R                  R                  [        R                  " USS95        M  )Nr   )protocol)	r   r   	task_doner   r   r?   r   r   r   )r1   r   s     r"   r?   WorkerProcess.run  sz    ..$$&C{((*,,s#CGGINN$$&!!&,,sQ"?@ r#   )r   r   )	rx   ry   rz   r{   r|   r2   r?   r~   __classcell__)r   s   @r"   r   r   z  s    )
A 
Ar#   r   c                      \ rS rSrSrg)Testi  r   N)rx   ry   rz   r{   r~   r   r#   r"   r   r     s    r#   r   z
list[type]
_DOC_ORDER__main__)NTF)
__future__r   __all__r   r(   r   rI   unittestrE   r   r	   Environmentr   r   r   r   Processr   TestCaser   r   __annotations__rx   mainTestr   r#   r"   <module>r      s    #       &&'9: #%))-^m mfp pnAO++ A>	8 	
 
J zT r#   