
    5i%.                        d Z ddlmZmZmZmZ ddlZddlZddlZddl	Z
ddlZddlZddlZddlZddlZddlmZ ddlmZ ddlZddlmZ ddlmZmZ dd	lmZ ddlZddlZddlZd
 Z d Z!ddZ"ddZ#ddZ$d Z%d dZ&d Z' G d dejP                        Z)d!dZ*d Z+d Z,d Z-d Z.d Z/ G d d      Z0ddl1m2Z2m3Z3m4Z4m5Z5m6Z6 y)"z#
Utilities shared by dxpy modules.
    )print_functionunicode_literalsdivisionabsolute_importN)	cpu_count)Mapping   )logger)
basestringTHREAD_TIMEOUT_MAX)DXErrorc                 J    t        j                  t         j                         y N)os_exitEX_IOERR)signumframes     u/home/marpiech/ifpan-abm-pgxpred/analysis/marpiech-gwas-test/venv/lib/python3.12/site-packages/dxpy/utils/__init__.py_force_quitr   "   s    HHR[[    c                 B    t         j                  j                  |       S )Nmax_workers)
concurrentfuturesThreadPoolExecutorr   s    r   get_futures_threadpoolr   &   s    00[0IIr   c                 N   	 	 t        t        j                  j                  | t                    }	 |S # t        j                  j
                  $ r Y nSt        $ rH |rt        j                          nt        d       t        j                  t        j                         Y nw xY w)z
    Return the next future that completes.  If a KeyboardInterrupt is
    received, then the entire process is exited immediately.  See
    wait_for_all_futures for more notes.
    timeout )nextr   r   as_completedr   TimeoutErrorKeyboardInterrupt	tracebackprint_stackprintr   r   r   )r   print_tracebackfutures      r   wait_for_a_futurer,   )   s     
	"*,,99'K]9^_F M !!.. 	  	"%%'b	HHR[[!	" s   .4 B#AB#"B#c                 $   	 	 t         j                  j                  | d      }t        |j                        dk(  ry;# t
        $ rH |rt        j                          nt        d       t        j                  t        j                         Y yw xY w)a  
    Wait indefinitely for all futures in the input iterable to complete.
    Use a timeout to enable interrupt handling.
    Call os._exit() in case of KeyboardInterrupt. Otherwise, the atexit registered handler in concurrent.futures.thread
    will run, and issue blocking join() on all worker threads, requiring us to listen to events in worker threads
    in order to enable timely exit in response to Ctrl-C.

    Note: This still doesn't handle situations where Ctrl-C is pressed elsewhere in the code and there are worker
    threads with long-running tasks.

    Note: os._exit() doesn't work well with interactive mode (e.g. ipython). This may help:
    import __main__ as main; if hasattr(main, '__file__'): os._exit() else: os.exit()
    <   r    r   r"   N)r   r   waitlennot_doner&   r'   r(   r)   r   r   r   )r   r*   waited_futuress      r   wait_for_all_futuresr3   >   sw    
'//44Wb4IN>**+q0   !!#"I
s   :> > ABBc              #   4  K   t        j                         }|
t               }d }d }|rt        |       \  }}}	 ||i |	 t	        |      D ]  }
 || ||      }|r n t        |      dkD  r' ||      } || ||       | ~t        |      dkD  r&yyw)a  
    :param request_iterator:
        An iterator producing inputs for consumption by the worker pool.
    :type request_iterator: iterator of callable, args, kwargs
    :param thread_pool: thread pool to submit the requests to
    :type thread_pool: concurrent.futures.thread.ThreadPoolExecutor
    :param max_active_tasks:
        The maximum number of tasks that may be either running or
        waiting for consumption of their result. If not given, defaults
        to the number of CPU cores on the machine.
    :type max_active_tasks: int
    :param do_first_task_sequentially:
        If True, executes (and returns the result of) the first request
        before submitting any other requests (the subsequent requests
        are submitted with *max_active_tasks* parallelism).
    :type do_first_task_sequentially: bool

    Rate-limited asynchronous multithreaded task runner. Consumes tasks
    from *request_iterator*. Yields their results in order, while
    allowing up to *max_active_tasks* to run simultaneously. Unlike
    concurrent.futures.Executor.map, prevents new tasks from starting
    while there are *max_active_tasks* or more unconsumed results.

    Nc                 |    t        | d       }|y|\  }}} |j                  |g|i |}|j                  |       y)NFT)r#   submitappend)task_iteratorexecutorfutures_queueretvaltask_callable	task_argstask_kwargstask_futures           r   submit_taskz&response_iterator.<locals>.submit_taskw   sM    }d+>06-y+%hoomOiO;O[)r   c                     | j                         }	 |j                  t              }|S # t        $ r2 t	        d       t        j                  t
        j                         Y S w xY w)Nr    r"   )popleftresultr   r&   r)   r   r   r   )tasks_in_progressr+   rC   s      r   next_resultz&response_iterator.<locals>.next_result   sZ    "**,	"]]+=]>F  ! 	""IHHR[[!	"s   * 7A%$A%r   )collectionsdequer   r#   ranger0   )request_iteratorthread_poolmax_active_tasksdo_first_task_sequentiallyrD   r@   rE   r<   r=   r>   _ir;   rC   s                r   response_iteratorrN   Y   s     2 $))+$; "045E0F-y+Y6+66$% -{<MN
 
 1
$./$k3DE	 
 1
$s   AB6BBc                     | j                         }| j                  dt        j                         | j                         }| j                  |       |S )Nr   )tellseekr   SEEK_END)buforig_posbuf_lens      r   string_buffer_lengthrV      s;    xxzHHHQhhjGHHXNr   c                    d}t        | t              r| j                         rt        |       } t        | t              r	 t	        |       } nRt        | t        j                         rddd}||vrt        d      | ||   z  } nt        |j                  |             t        t        j                         dz        }| dk  s|r
| |k  r| |z  } | S # t
        $ r 	 t        t        j                  t        j                  j                  |       j                               dz        } | dkD  sJ n3# t
        t        t        f$ r t        |j                  |             w xY wY w xY w)a  
    :param default_unit: units of the input time *t*; must be one of "s" or
        "ms". This param is only respected if *t* looks like an int (e.g.
        "12345", 12345).
    :type default_unit: string

    Converts inputs such as:
       "2012-05-01"
       "-5d"
       1352863174
       "1352863174"
    to milliseconds since epoch. See http://labix.org/python-dateutil and :meth:`normalize_timedelta`.
    zError: Expected an int timestamp, a date format (e.g. YYYY-MM-DD), or an int with a single-letter suffix (s=seconds, m=minutes, h=hours, d=days, w=weeks, M=months, y=years; e.g. "-10d" indicates 10 days ago); but got {t}  r   )t   )mssz.Expected default_unit to be one of 's' or 'ms')
isinstancer   isdigitintnormalize_timedelta
ValueErrortimemktimedateutilparserparse	timetupleOverflowErrorAssertionErrorformatnumbersIntegral)rY   r+   default_unit	error_msgunits_multipliersnows         r   normalize_time_inputrq      sP    oI!Z QYY[F!Z 	8#A&A 
Aw''	(#$4000MNN!,//))A).//
diik$
C1uAG	SH!  	88HOO$9$9!$<$F$F$HI$NO1uu~> 8 !1!1A!1!6778 	8s*   C 	E$AD.-E$.0EE$#E$c           	          	 t        |       dz  S # t        $ r=}| dd | dd }}ddddddd	d
}||vr
t               t        |      ||   z  cY d}~S d}~ww xY w)z
    Given a string like "1w" or "-5d", convert it to an integer in milliseconds.
    Integers without a suffix are interpreted as seconds.
    Note: not related to the datetime timedelta class.
    rX   Ni`  i6 i \&i $l    H4 l    ,b/ )r\   mhdwMy)r_   ra   )	timedeltaerY   suffixsuffix_multiplierss        r   r`   r`      s|    39~$$ 3crNIbcN6#'gJ]ap#3:KM++,1v*62223s    	A2AAAc                   *     e Zd Z fdZd Zd Z xZS )OrderedDefaultdictc                     d }d}|r"|d   }|t        |      st        d      |dd  }|| _        t        | j                  |   |i | y )N r   z'first argument must be callable or NonerZ   )callable	TypeErrordefault_factorysuper	__class____init__)selfargskwargs
newdefaultnewargsr   s        r   r   zOrderedDefaultdict.__init__   s]    
aJ&(:*> IJJ12hG)dnnd,g@@r   c                 ^    | j                   t        |      | j                         x| |<   }|S r   )r   KeyError)r   keyvalues      r   __missing__zOrderedDefaultdict.__missing__   s4    '3- 0022S	Er   c                     | j                   r| j                   n	t               }t        |       |d d | j                         fS r   )r   tupletypeitems)r   r   s     r   
__reduce__zOrderedDefaultdict.__reduce__   s4    '+';';t##Dz4tTZZ\99r   )__name__
__module____qualname__r   r   r   __classcell__)r   s   @r   r   r      s    	A:r   r   c                     t        t              }| D ]2  }||vr	d |vrg |d <   ||j                  |         j                  |       4 |S r   )r   listgetr7   )arrayfieldgroupsitems       r   group_array_by_fieldr      sU    %F -V!3F4Ltxx&&t,- Mr   c                     |j                         D ]?  \  }}t        |t              r"t        | j	                  |i       |      }|| |<   8||   | |<   A | S )z
    Recursively updates a dictionary.
    Example: merge({"a": {"b": 1, "c": 2}}, {"a": {"b": 3}}) = {"a": {"b": 3, "c": 2}}
    )r   r]   r   merger   )rv   ukvrs        r   r   r      sZ    
 	 1a!aeeArlA&AAaDQ4AaD Hr   c                 L    i }| D ]  \  }}||v rt        d|      |||<    |S )z 
    Reject duplicate keys.
    zduplicate key: )ra   )ordered_pairsrv   r   r   s       r   _dict_raise_on_duplicatesr     sA     	A 1616771Q4	
 Hr   c                  >    t         |d<   t        j                  | i |S )zB
    Like json.load(), but raises an error on duplicate keys.
    object_pairs_hook)r   jsonloadr   r   s     r   json_load_raise_on_duplicatesr     s#     #<F99d%f%%r   c                  >    t         |d<   t        j                  | i |S )zC
    Like json.loads(), but raises an error on duplicate keys.
    r   )r   r   loadsr   s     r   json_loads_raise_on_duplicatesr     s#     #<F::t&v&&r   c                  :    t        | dt        j                  i| y )Nfile)r)   sysstderrr   s     r   warnr     s    	4+cjj+F+r   c                   ,    e Zd ZdZd Zd Zed        Zy)Noncez
    Generates a nonce by using the system's random number generator. If it fails
    it uses python's random library to generate a random long integer.
    The nonce is the random number concatenated with the time.
    c                 j   	 dt        j                  t        j                  d            j	                  d      t        j
                         fz  | _        y #  t        j                  t        j
                                dt        j                  d      t        j
                         fz  | _        Y y xY w)Nz%s%f    zutf-8   )
binasciihexlifyr   urandomdecoderb   noncerandomseedgetrandbitsr   s    r   r   zNonce.__init__&  s}    	J8#3#3BJJrN#C#J#J7#SUYU^U^U`"aaDJ	JKK		$6#5#5d#;TYY["IIDJs   AA AB2c                     | j                   S r   )r   r   s    r   __str__zNonce.__str__-  s    zzr   c                     | j                         }t        |j                  dd            dk(  rt        t	                     |d<   |S )a  
        Static method to return a copy of the input dictionary with an
        additional unique nonce
        :param input: an input dictionary that may be empty
        :type input: dict
        :returns an extended copy of the input with an additional nonce field

        The input dictionary is updated with a nonce only if does not already
        have a non empty nonce
        r   r"   r   )copyr0   r   strr   )input_paramsinput_cps     r   update_noncezNonce.update_nonce0  s?      $$&x||GR()Q. #EGHWr   N)r   r   r   __doc__r   r   staticmethodr   r   r   r   r   r      s&    
J  r   r   rZ   )runconvert_handlers_to_dxlinksparse_args_as_job_inputentry_pointDXJSONEncoder)F)NT)Fr[   )group)7r   
__future__r   r   r   r   r   r   rF   concurrent.futuresr   r'   r   rb   gcplatformmultiprocessingr   collections.abcr   dateutil.parserrd   r"   r
   compatr   r   
exceptionsr   rk   r   r   r   r   r,   r3   rN   rV   rq   r`   OrderedDictr   r   r   r   r   r   r   r   
exec_utilsr   r   r   r   r   r   r   r   <module>r      s   " S R T T T T T % #   3     J*6=~%N3":00 :,
&', D n mr   