
    wil/                         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mZmZmZmZmZmZmZ 	 d dlZdZn# e$ r dZdZY nw xY wd d	lmZmZmZ er8	 d dlZd dlZd dl Z dZ!dZ"n&# e$ rZ#dZ!e#Z"e"xj$        d
z  c_$        Y dZ#[#ndZ#[#ww xY wde%dee%         ddfdZ&	 	 d"dedee%         deee%e'f                  dee%         deeddf         f
dZ(	 	 	 d#dedee%         dee%         dee%         dee%         deee%e%df         fdZ)	 	 d#dedee%         dee%         dee%         dee%         de%fdZ*d$dede+deeef         fdZ,deee%e'f                  de+de-fdZ. G d d          Z/de+de
fdZ0 G d  d!          Z1dS )%    N)	timedelta)	cpu_count)SyncManager)SynchronizedArray)Callable
Collection	GeneratorIterableListOptionalTupleUnionTF)RUNNING_MACOSRUNNING_WINDOWSmp_dillzZ If you're using Conda, you can run `conda install pywin32` to install the missing module.pidmaskreturnc                     t           rTt          st          d}|D ]
}|d|z  z  }t          j        t
          j        d|           }t          j        ||           dS t          rdS t          j        | |           dS )a>  
    Sets the CPU affinity for a given process.

    On Windows-based systems with more than 64 processors, I'm not sure if this will work. See
    https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setprocessaffinitymask#parameters.

    :param pid: Process ID
    :param mask: List of CPU IDs
    r      TN)r   WIN32API_AVAILABLEWIN32API_ERRORwin32apiOpenProcesswin32conPROCESS_ALL_ACCESSwin32processSetProcessAffinityMaskr   ossched_setaffinity)r   r   windows_maskcpu_idhandles        c/var/www/development/aibuddy-work/election-extract/venv/lib/python3.11/site-packages/mpire/utils.pyset_cpu_affinityr%   $   s      ( " 	!    	( 	(FAK'LL %h&A4MM+FLAAAAA	 (
S$'''''    iterable_of_argsiterable_len
chunk_sizen_splitsc              #     K   ||t          d          |9||}n/t          | d          rt          |           }nt          d          ||z  }t          |           }|}d}	 t          rHt          | t          j                  r.| ||t          dt          j
        |                    z            }nBt          t          j        |t          dt          j
        |                                        }t          |          dk    rdS |+|t          |          z   |k    r|d||z
           }|r|V  dS |V  ||z   t          j
        |          z
  }|t          |          z  })a  
    Chunks tasks such that individual workers will receive chunks of tasks rather than individual ones, which can
    speed up processing drastically.

    :param iterable_of_args: A numpy array or an iterable containing tuples of arguments to pass to a worker, which
        passes it to the function
    :param iterable_len: Number of tasks available in ``iterable_of_args``. Only needed when ``iterable_of_args`` is a
        generator
    :param chunk_size: Number of simultaneous tasks to give to a worker. If ``None``, will use ``n_splits`` to determine
        the chunk size
    :param n_splits: Number of splits to use when ``chunk_size`` is ``None``
    :return: Generator of chunked task arguments
    Nz+chunk_size and n_splits cannot both be None__len__zqEither iterable_len or an iterable with a len() function should be provided when chunk_size and n_splits are Noner   T   )
ValueErrorhasattrleniterNUMPY_INSTALLED
isinstancenpndarraymaxmathceiltuple	itertoolsislice)	r'   r(   r)   r*   n_tasks	args_itercurrent_chunk_sizen_elements_returnedchunks	            r$   chunk_tasksrA   C   s       h.FGGG #"GG%y11 	A*++GG @ A A A x'
 %&&I#* 	^z*:BJGG 	^$%89LsSTVZV_`rVsVsOtOt9t%tuEE)*9c!TYGY=Z=Z6[6[\\]]E u::??F #(;c%jj(H<(W(W=<*===>E F0:=K]A^A^^s5zz))*r&   n_jobsc           	          |
| d|         } t          | ||||          }t          t          | t          |           ||p||dz  nd                    } d}d}| |||fS )a  
    If we're dealing with numpy arrays, chunk them using numpy slicing and return changed map parameters

    :param iterable_of_args: A numpy array or an iterable containing tuples of arguments to pass to a worker, which
        passes it to the function
    :param iterable_len: When chunk_size is set to ``None`` it needs to know the number of tasks. This can either be
        provided by implementing the ``__len__`` function on the iterable object, or by specifying the number of tasks
    :param chunk_size: Number of simultaneous tasks to give to a worker. If ``None``, will generate ``n_jobs * 4``
        number of chunks
    :param n_splits: Number of splits to use when ``chunk_size`` is ``None``
    :param n_jobs: Number of workers to spawn. If ``None``, will use ``cpu_count()``.
    :return: Chunked ``iterable_of_args`` with updated ``iterable_len``, ``chunk_size`` and ``n_splits``
    N   r-   )get_n_chunksmake_single_argumentsrA   r0   )r'   r(   r)   r*   rB   s        r$   apply_numpy_chunkingrG      s      +M\M: 0,
HV\]]L,[9I3O_K`K`bl9A9qTZTffqjjlp.s .s t tJH\:x??r&   c                 B   |0t          | d          rt          |t          |                     n|}n/t          | d          rt          |           }nt          d          |||p|pt	                      dz  z  }t          |t          j        ||z                      S )a  
    Get number of chunks

    :param iterable_of_args: A numpy array or an iterable containing tuples of arguments to pass to a worker, which
        passes it to the function
    :param iterable_len: Number of tasks available in ``iterable_of_args``. Only needed when ``iterable_of_args`` is a
        generator
    :param chunk_size: Number of simultaneous tasks to give to a worker. If ``None``, will use ``n_splits`` to determine
        the chunk size
    :param n_splits: Number of splits to use when ``chunk_size`` is ``None``
    :param n_jobs: Number of workers to spawn. If ``None``, will use ``cpu_count()``
    :return: Number of chunks that will be created by the chunker
    Nr,   zFailed to obtain length of iterable. Remedy: either provide an iterable with a len() function or specify iterable_len in the function callrD   )r/   minr0   r.   r   r7   r8   )r'   r(   r)   r*   rB   r<   s         r$   rE   rE      s      >EFVXa>b>bt#lC(8$9$9:::ht	!9	-	- R&'' Q R R 	R  GV-By{{a,GH
w	'J"677888r&   	generatorc                 @    d | D             }|r|nt          |          S )a  
    Converts an iterable of single arguments to an iterable of single argument tuples

    :param iterable_of_args: A numpy array or an iterable containing tuples of arguments to pass to a worker, which
        passes it to the function
    :param generator: Whether or not to return a generator, otherwise a materialized list will be returned
    :return: Iterable of single argument tuples
    c              3      K   | ]}|fV  d S N ).0args     r$   	<genexpr>z(make_single_arguments.<locals>.<genexpr>   s$      
.
.cC6
.
.
.
.
.
.r&   )list)r'   rJ   gens      r$   rF   rF      s-     /
.-
.
.
.C*33c*r&   secondswith_millisecondsc                     | dS t          t          |                                         dd          }|r/t          |          dk    r|d          d|d         dd          }n|d         }|S )z
    Format seconds to a string, optionally with or without milliseconds

    :param seconds: Number of seconds
    :param with_milliseconds: Whether to display milliseconds as well
    :return: String formatted time
    N )rT   .r-   r      )strr   rsplitr0   )rT   rU   durations      r$   format_secondsr]      s     r 9W---..55c1==H S]]Q..qk55HQKO55A;Or&   c                   h    e Zd ZdZ	 	 ddee         dedee         dee         ddf
dZdd	Z	dd
Z
dS )TimeItz Simple class that provides a context manager for keeping track of task duration and adds the total number
     of seconds in a designated output array Ncum_time_array	array_idxmax_time_arrayformat_args_funcr   c                 L    || _         || _        || _        || _        d| _        dS )a  
        :param cum_time_array: Optional array to store cumulative time in
        :param array_idx: Index of cum_time_array to store the time value to
        :param max_time_array: Optional array to store maximum time duration in. Note that the array_idx doesn't apply
            to this array. The entire array is used for heapq
        :param format_args_func: Optional function which should return the formatted args corresponding to the function
            called within this context manager
        N)r`   ra   rb   rc   
start_time)selfr`   ra   rb   rc   s        r$   __init__zTimeIt.__init__   s.     -", 0r&   c                 6    t          j                     | _        d S rM   )timere   )rf   s    r$   	__enter__zTimeIt.__enter__   s    )++r&   c                 0   t          j                     | j        z
  }| j        | j        | j        xx         |z  cc<   | j        P|| j        d         d         k    r;t          j        | j        || j        |                                 nd f           d S d S d S )Nr   )ri   re   r`   ra   rb   heapqheappushpoprc   )rf   exc_typeexc_valexc_tbr\   s        r$   __exit__zTimeIt.__exit__   s    9;;0*///8;///*x$:Ma:PQR:S/S/Sd1'DDYDe)>)>)@)@)@kopr r r r r +*/S/Sr&   )NN)r   N)__name__
__module____qualname____doc__r   r   intr   rg   rj   rq   rN   r&   r$   r_   r_      s        1 1 @D8< x0A'B s !)*;!<#+H#5AE   "& & & &r r r r r rr&   r_   use_dillc                     t          j        d          }| r t          j                            |          nt	          |          S )z
    Create a SyncManager instance

    :param use_dill: Whether dill is used as serialization library
    :return: SyncManager instance
       )authkey)r   urandomr   managersr   )rw   rz   s     r$   create_sync_managerr}      sA     jnnG<Df7'''888+^eJfJfJffr&   c                   J    e Zd ZdZdeddfdZdefdZdefdZ	d	eddfd
Z
dS )NonPickledSyncManagerz+ SyncManager wrapper that won't be pickled rw   r   Nc                 .    t          |          | _        dS )zP
        :param use_dill: Whether dill is used as serialization library
        N)r}   manager)rf   rw   s     r$   rg   zNonPickledSyncManager.__init__  s     +844r&   itemc                 ,    t          | j        |          S rM   )getattrr   )rf   r   s     r$   __getattr__z!NonPickledSyncManager.__getattr__  s    t|T***r&   c                 B    | j                                         }d|d<   |S )z
        Returns the state excluding the manager object, as this is not picklable and not needed.
        
        :return: State dict
        Nr   )__dict__copyrf   states     r$   __getstate__z"NonPickledSyncManager.__getstate__  s&     ""$$ir&   r   c                     || _         dS )zJ
        Set the state.
        
        :param state: State dict
        N)r   r   s     r$   __setstate__z"NonPickledSyncManager.__setstate__  s     r&   )rr   rs   rt   ru   boolrg   rZ   r   dictr   r   rN   r&   r$   r   r     s        555 5$ 5 5 5 5+ + + + +d    $ 4      r&   r   )NNN)NNNN)T)2rl   r:   r7   r   ri   datetimer   multiprocessingr   multiprocessing.managersr   multiprocessing.sharedctypesr   typingr   r   r	   r
   r   r   r   r   numpyr4   r2   ImportErrormpire.contextr   r   r   r   r   r   r   r   emsgrv   r%   floatrA   rG   rE   r   rF   rZ   r]   r_   r}   r   rN   r&   r$   <module>r      s          				        % % % % % % 0 0 0 0 0 0 : : : : : : Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z ZOO   	BOOO B A A A A A A A A A  (
(! ( ( (" ( 	(((# (T#Y (4 ( ( ( (> KOZ^9* 9*( 9*(3- 9*$U3:%679*JRSV-9*Zt+,9* 9* 9* 9*x TXUY15@ @8 @8C= @%-c]@EMc]@!)#@:?#sTX@X:Y@ @ @ @6 nrIM9 98 98C= 9]efi]j 9#C=99A#9RU9 9 9 9>
+ 
+H 
+ 
+QVW[]fWfQg 
+ 
+ 
+ 
+HU3:%67 D UX    *r r r r r r r rDg$ g; g g g g         s*   A	 		AA%A6 6B;BB