U
    ҥcG                     @   st  d Z ddlZddlZddlZddlmZ ddlZddlmZ	 ddl
mZ dd ZddddddddZd	d
 Zdd Zeddddd ZeZejdd Zdd Zeddd Zejdd Zeddd Zejdd Zed d!d" Zejd#d$ Zed%d&d' Zejd(d) Zed*d+d, Zejd-d. Zed/d0d1 Zejd2d3 Z d4d5 Z!d6d7 Z"d:d8d9Z#dS );z4Event loop integration for the ZeroMQ-based kernels.    N)partial)Version)Applicationc                   C   s"   t jdko tt d tdkS )zhShould we use appnope for dealing with OS X app nap?

    Checks if we are on OS X 10.9 or greater.
    darwinr   z10.9)sysplatformVZmac_ver r	   r	   3lib/python3.8/site-packages/ipykernel/eventloops.py_use_appnope   s    r   )ZinlineZnbaggZwebaggZnotebookZipymplZwidgetNc                     s    fdd}|S )a0  Decorator to register an event loop to integrate with the IPython kernel

    The decorator takes names to register the event loop as for the %gui magic.
    You can provide alternative names for the same toolkit.

    The decorated function should take a single argument, the IPython kernel
    instance, arrange for the event loop to call ``kernel.do_one_iteration()``
    at least every ``kernel._poll_interval`` seconds, and start the event loop.

    :mod:`ipykernel.eventloops` provides and registers such functions
    for a few common event loops.
    c                    s2   D ]} t |< qdd  _ fdd}| _ S )z#Integration registration decorator.c                 S   s   d S Nr	   kernelr	   r	   r
   <lambda>7       z9register_integration.<locals>.decorator.<locals>.<lambda>c                    s
   |  _ | S )zc@func.exit is now a decorator

            to register a function to be called on exit
            )	exit_hook)Z	exit_funcfuncr	   r
   exit_decorator9   s    z?register_integration.<locals>.decorator.<locals>.exit_decorator)loop_mapr   exit)r   namer   toolkitnamesr   r
   	decorator2   s    

z'register_integration.<locals>.decoratorr	   )r   r   r	   r   r
   register_integration$   s    r   c                    s   dd l ddlm} ddlm  zddlm} W n* tk
rZ   |d  fdd}Y nX fdd}td	sj	t
j} ||d
jjj_jj| njd tds j_jd jj| jd d S )Nr   )	lru_cache)QtCore)enum_helperc                    s     | dd tj j S )N.r   )
attrgetter
rpartitionr   modules__package__)r   )r   operatorr	   r
   r   Q   s    z&_notify_stream_qt.<locals>.enum_helperc                      s*    j jddr& jd  jj  dS )2fall back to main loop when there's a socket event   limitFN)shell_streamflush_qt_notifier
setEnabledappqt_event_loopquitr	   r   r	   r
   process_stream_eventsU   s    z0_notify_stream_qt.<locals>.process_stream_eventsr+   zQtCore.QSocketNotifier.TypeT	_qt_timer)r$   	functoolsr   IPython.external.qt_for_kernelr   r   ImportErrorhasattrr)   
getsockoptzmqFDZQSocketNotifierZReadr-   r.   r+   Z	activatedZconnectr,   ZQTimerr1   ZsetSingleShottimeoutstart)r   r   r   r0   fdr	   )r   r   r$   r
   _notify_stream_qtG   s.    

  	
r<   qtqt5qt6c                 C   s>   t |  d| j_| jj}t|dr*| n|  d| j_dS )z,Event loop for all supported versions of Qt.TexecFN)r<   r-   _in_event_loopr.   r5   r@   Zexec_)r   elr	   r	   r
   loop_qtv   s
    rC   c                 C   s   | j   d S r   )r-   r   r   r	   r	   r
   loop_qt_exit   s    rD   c                 C   s   d| _ |   d| _ dS )zInner-loop for running the Wx eventloop

    Pulled from guisupport.start_event_loop in IPython < 5.2,
    since IPython 5.2 only checks `get_ipython().active_eventloop` is defined,
    rather than if the eventloop is actually running.
    TFN)rA   ZMainLoop)r-   r	   r	   r
   _loop_wx   s    rE   wxc                    s   ddl tdj fddG fdddj G  fdd	d	j}td
drltjjsx|dd_ddl}t	|
|js||j|j tj dS )z*Start a kernel with wx event loop support.r   N  c                      s     j jddr j  dS dS )zwake from wxr&   r'   N)r)   r*   r-   ZExitMainLoopr	   r   r	   r
   wake   s    
zloop_wx.<locals>.wakec                       s"   e Zd Z fddZdd ZdS )zloop_wx.<locals>.TimerFramec                    sB   j | d d | | _| j  | j| j || _d S )N)	Frame__init__TimerZtimerZStartZBindZ	EVT_TIMERon_timerr   )selfr   poll_intervalrF   r	   r
   rK      s
    z$loop_wx.<locals>.TimerFrame.__init__c                 S   s   |    d S r   r   )rN   Zeventr	   r	   r
   rM      s    z$loop_wx.<locals>.TimerFrame.on_timerN)__name__
__module____qualname__rK   rM   r	   rO   r	   r
   
TimerFrame   s   rT   c                       s   e Zd Z fddZdS )zloop_wx.<locals>.IPWxAppc                    s    | _ | j d dS )NFT)frameZShowrN   rT   rH   r	   r
   OnInit   s    
zloop_wx.<locals>.IPWxApp.OnInitN)rQ   rR   rS   rX   r	   rW   r	   r
   IPWxApp   s   rY   r-   F)Zredirect)rF   int_poll_intervalrJ   ZAppgetattr
isinstancer-   signalcallable	getsignalSIGINTdefault_int_handlerrE   )r   rY   r^   r	   )rT   r   rP   rH   rF   r
   loop_wx   s    rc   c                 C   s   ddl }|  dS )zExit the wx loop.r   N)rF   ZExit)r   rF   r	   r	   r
   loop_wx_exit   s    rd   tkc           	         s   ddl m}m} |  t drG dd d} fdd}| _t|j}d|_ j	j
tj||  d|    nXdd	ldd	l}|  j}td
j G fddd}| |_j  d	S )z&Start a kernel with the Tk event loop.r   )READABLETkcreatefilehandlerc                   @   s   e Zd Zdd ZdS )z loop_tk.<locals>.BasicAppWrapperc                 S   s   || _ | j   d S r   )r-   withdraw)rN   r-   r	   r	   r
   rK      s    z)loop_tk.<locals>.BasicAppWrapper.__init__N)rQ   rR   rS   rK   r	   r	   r	   r
   BasicAppWrapper   s   rj   c                    s8   | j ddr4 j| tj       `dS r%   r&   r'   N)	r*   re   Zdeletefilehandlerr6   r7   r8   r/   destroyapp_wrapper)streamakw)r-   r   r	   r
   r0      s
    z&loop_tk.<locals>.process_stream_eventsnotifierNrG   c                       s,   e Zd Zdd Z fddZdd ZdS )z loop_tk.<locals>.TimedAppWrapperc                 S   s   || _ | j   || _d S r   )r-   ri   r   )rN   r-   r   r	   r	   r
   rK     s    
z)loop_tk.<locals>.TimedAppWrapper.__init__c                    sP      }z||   W n  tk
r:   jd Y nX | j| j d S )NzError in message handler)	get_event_looprun_until_completer   	ExceptionlogZ	exceptionr-   afterrM   )rN   loopasyncior   rP   r	   r
   rM     s    z)loop_tk.<locals>.TimedAppWrapper.on_timerc                 S   s   |    | j  d S r   )rM   r-   mainlooprV   r	   r	   r
   r:     s    z&loop_tk.<locals>.TimedAppWrapper.startN)rQ   rR   rS   rK   rM   r:   r	   rx   r	   r
   TimedAppWrapper  s   r{   )Ztkinterrf   rg   r5   rm   r   r)   rQ   re   rh   r6   r7   r8   rv   rz   ry   nest_asyncioZapplyZdo_one_iterationrZ   r[   r:   )	r   rf   rg   rj   r0   rq   r|   Zdoir{   r	   )r-   ry   r   rP   r
   loop_tk   s&    
	

r}   c              	   C   s2   z| j j  | ` W n ttfk
r,   Y nX dS )zExit the tk loop.N)rm   r-   rl   RuntimeErrorAttributeErrorr   r	   r	   r
   loop_tk_exit  s
    r   Zgtkc                 C   s&   ddl m} || }|  || _dS z6Start the kernel, coordinating with the GTK event loopr&   )GTKEmbedN)Zgui.gtkembedr   r:   _gtkr   r   Z
gtk_kernelr	   r	   r
   loop_gtk'  s    r   c                 C   s   | j   dS )zExit the gtk loop.Nr   stopr   r	   r	   r
   loop_gtk_exit1  s    r   Zgtk3c                 C   s&   ddl m} || }|  || _dS r   )Zgui.gtk3embedr   r:   r   r   r	   r	   r
   	loop_gtk37  s    r   c                 C   s   | j   dS )zExit the gtk3 loop.Nr   r   r	   r	   r
   loop_gtk3_exitA  s    r   Zosxc                    s   ddl m}m tj  fdd}| jjszrzJz.|t_|| j | jj	ddr\W W W HdS W n t
k
rt    Y nX W n" tk
r   tdtjd Y nX W 5  t_X q$dS )	znStart the kernel, coordinating with the Cocoa CFRunLoop event loop
    via the matplotlib MacOSX backend.
    r&   )rz   r   c                    s.     | t krtdtjd n | || dS )z.don't let KeyboardInterrupts look like crashesz%KeyboardInterrupt caught in CFRunLoopfileN)KeyboardInterruptprintr   
__stdout__)etypevaluetbZreal_excepthookr   r	   r
   
handle_intP  s    zloop_cocoa.<locals>.handle_intr'   Nz"KeyboardInterrupt caught in kernelr   )_eventloop_macosrz   r   r   
excepthookshellZexit_nowr[   r)   r*   BaseExceptionr   r   r   )r   rz   r   r	   r   r
   
loop_cocoaG  s    	
r   c                 C   s   ddl m} |  dS )zExit the cocoa loop.r&   )r   N)r   r   )r   r   r	   r	   r
   loop_cocoa_exitn  s    r   ry   c              
      s   ddl }|    rdS   r6|  |  d _ fdd}t|| j} 	| j
tj|  | d}z   W n< tk
r   Y qtY n& tk
r } z|}W 5 d}~X Y nX  jrЈ   |dk	r|qqtdS )z/Start a kernel with asyncio event loop support.r   NFc                    s   | j ddr   dS rk   )r*   r   )rn   rw   r	   r
   r0     s    z+loop_asyncio.<locals>.process_stream_events)ry   rr   
is_running	is_closedZnew_event_loopZset_event_loop_should_closer   r)   Z
add_readerr6   r7   r8   Z	call_soonZrun_foreverr   rt   close)r   ry   r0   rq   errorer	   r   r
   loop_asynciov  s0    

r   c                    sP   ddl }|  |j fdd}  r2|  n  sL |    dS )zExit hook for asyncior   Nc                   3   s*   t  dr  E d H  d _   d S )Nshutdown_asyncgensT)r5   r   r   r   r	   r   r	   r
   
close_loop  s    
z%loop_asyncio_exit.<locals>.close_loop)ry   rr   	coroutiner   r   rs   r   )r   ry   r   r	   r   r
   loop_asyncio_exit  s    
r   c                 C   s0  t jdd}ddlm}m}m}m}m} | }|d|d|d|di}|dk	rx| dkrx|| | krxt	d||  d	 dS |dk	r| dkr|| | krt	d
|  d| d dS n| dkr$zddl
}	dt jd< W nN tk
r    zddl}
dt jd< W n  tk
r   dt jd< Y nX Y nX n| dkrzddl}dt jd< W nN tk
r   zddl}dt jd< W n  tk
r   dt jd< Y nX Y nX n4| dkrdt jkrt jd= nt	d|  d dS zddlm}m} W nL tk
r* } z,dt jkrt jd= t	d|  W Y dS d}~X Y nX dS )a  
    Sets the QT_API environment variable by trying to import PyQtx or PySidex.

    The user can generically request `qt` or a specific Qt version, e.g. `qt6`.
    For a generic Qt request, we let the mechanism in IPython choose the best
    available version by leaving the `QT_API` environment variable blank.

    For specific versions, we check to see whether the PyQt or PySide
    implementations are present and set `QT_API` accordingly to indicate to
    IPython which version we want. If neither implementation is present, we
    leave the environment variable set so IPython will generate a helpful error
    message.

    Notes
    -----
    - If the environment variable is already set, it will be used unchanged,
      regardless of what the user requested.
    ZQT_APINr   )QT_API_PYQT5QT_API_PYQT6QT_API_PYSIDE2QT_API_PYSIDE6
loaded_apir>   r?   r=   z9Cannot switch Qt versions for this session; you must use r   zRequest for "zC" will be ignored because `QT_API` environment variable is set to ""Zpyqt5Zpyside2Zpyqt6Zpyside6zUnrecognized Qt version: z". Should be "qt5", "qt6", or "qt".r   QtGuiz$QT_API couldn't be set due to error )osenvirongetZIPython.external.qt_loadersr   r   r   r   r   r   PyQt5r4   PySide2PyQt6PySide6r3   r   r   rt   )guiZqt_apir   r   r   r   r   ZloadedZ
qt_env2guir   r   r   r   r   r   r   r	   r	   r
   set_qt_api_env_from_gui  sj        



r   c                 C   sl   t |drdS t|  ddlm}m} ddlm} |dg|_t|j|j	rX|j
d ||j|j_dS )z?Sets the `QT_API` environment variable if it isn't already set.r-   Nr   r   )get_app_qt4 F)r5   r   r3   r   r   ZIPython.lib.guisupportr   r-   r]   ZQApplicationZsetQuitOnLastWindowClosedZ
QEventLoopr.   )r   r   r   r   r   r	   r	   r
   make_qt_app_for_kernel  s    
r   c                 C   s   | t kr$d| dt   }t||dkrXt rDtt dd}|dkrXd}t|| dkrvt|drt	|d n| 
drt| | t |  }|r|jdk	r|j|k	rd}t|||_dS )	z#Enable integration with a given GUIzInvalid GUI request z, valid ones are:Nr   z\You didn't specify a kernel, and no IPython Application with a kernel appears to be running.r-   r=   z'Cannot activate multiple GUI eventloops)r   keys
ValueErrorr   Zinitializedr\   instancer~   r5   delattr
startswithr   Z	eventloop)r   r   r   msgrw   r	   r	   r
   
enable_gui%  s(    


r   )N)$__doc__r   r   r   r2   r   r7   Zpackaging.versionr   r   Ztraitlets.config.applicationr   r   r   r   r<   rC   Zloop_qt5r   rD   rE   rc   rd   r}   r   r   r   r   r   r   r   r   r   r   r   r   r	   r	   r	   r
   <module>   sf   
#/



4

E
	
	

	

&

(
\