
    AVh                        d 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
Z
ddlm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 dd	lmZ dd
lmZ ddlmZ ej<                  Z	 ddlZ	 ddl!Z!	 ddl"Z#e#jH                  jK                           ejL                  dg d      Z' ejL                  dddg      Z( ejL                  dg d      Z) ejL                  dg d      Z*dZ+dZ, G d de-      Z. G d dej^                        Z0 G d de-      Z1 e
jd                         Z3d Z4d Z5 G d d e-      Z6d! Z7d" Z8 ed#g $       G d% d&e9             Z: ed'g $       G d( d)e9             Z; ed*g $       G d+ d,e9             Z<d- Z=d7d.Z> ed/g $      ddd0e+ddfd1       Z?da@ ed2g $      d3        ZAdaB ej                         ZDd4 ZE ed5g $      d6        ZFy# e $ r dZY xw xY w# e $ r dZ!Y w xY w# e $ r Y nw xY w)8z)Multi-process runner for testing purpose.    N)logging)queue)tf2)	v2_compat)multi_worker_util)multi_process_lib)context)	test_util)	tf_export_ProcessStatusInfo	task_typetask_idis_successfulexc_inforeturn_valueMultiProcessRunnerResultr   stdoutTestEnvironmentr   r   cluster_spec	rpc_layergrpc_fail_fast
v2_enabledexecuting_eagerlyvisible_gpus	Resourcesprocess_status_queueparent_to_sub_queuestreaming_pipe_wbarrier      c                       e Zd ZdZ	 	 	 	 	 	 	 	 	 	 	 	 ddZddZd Z	 	 	 	 ddZd Zd Z		 	 	 	 dd	Z
d
 Zd Zd Zd Zd Zd Zd ZefdZd Zd ZddZddZy)MultiProcessRunnera  A utility class to start multiple processes to simulate a cluster.

  We need to use multiple processes to simulate a cluster in TF 2.0 tests
  because TF 2.0 has some process-global data structures that have to be
  separated by processes. We also need child processes to test out our fault
  tolerance because shutting down a standard TensorFlow server within its
  process is not supported.

  Note: the main test program that uses this runner class must run main program
  via `test_main` defined in this file. Using this runner in non-test binaries
  is not supported yet.

  This class is not thread-safe. Child processes will inherit TF2 behavior flag.
  Nc                    t        j                         rt        j                  d      |J d|v r7t	        |d         dkD  r&t        dj                  t	        |d                     t                t        |      st        d      || _	        || _
        |xs d| _        || _        || _        || _        || _        |
| _        || _        |	| _        || _        |xs d| _        |xs i | _        || _        t	        t/        j.                         j1                  d	            | _        t5        j6                         | _        t/        j:                         | _        d
| _        tA        jB                         | _"        i | _#        tI               | _%        g | _&        tO               | _(        | jP                  jS                         | _*        | jP                  jS                         | _+        tY        d | j                  j[                         D              }| jP                  j]                  |      | _/        | jP                  jS                         | _0        d| _1        y)a  Instantiation of a `MultiProcessRunner`.

    Args:
      fn: Function to be run on child processes. This will be run on processes
        for all task types.
      cluster_spec: Dict for cluster spec. The utility function
        `tf.__internal__.distribute.multi_process_runner.create_cluster_spec`
        can be conveniently used to create such dict. The following is an
        example of cluster with three workers and two ps's.
        {"worker": ["worker0.example.com:2222",
                    "worker1.example.com:2222",
                    "worker2.example.com:2222"],
         "ps": ["ps0.example.com:2222",
                "ps1.example.com:2222"]}
      rpc_layer: RPC layer to use. Default value is 'grpc'.
      max_run_time: `None` or integer. If not `None`, child processes are forced
        to exit at approximately this many seconds after this utility is called.
        We achieve this through `signal.alarm()` api. Note that this is best
        effort at Python level since Python signal handler does not get executed
        when it runs lower level C/C++ code. So it can be delayed for
        arbitrarily long time. If any of the child process is still running when
        `max_run_time` is up, they will be force-terminated and an
        `UnexpectedSubprocessExitError` may be raised. If `None`, child
        processes are not forced to exit.
      grpc_fail_fast: Whether GRPC connection between processes should fail
        without retrying. Defaults to None, in which case the environment
        variable is not explicitly set.
      stream_output: True if the output/error from the subprocesses should be
        streamed to be printed in parent process' log. Defaults to True.
      return_output: If True, the output/error from the subprocesses should be
        collected to be attached to the resulting namedtuple returned from
        `join()`. The list of output can be retrieved via `stdout` attribute.
        Defaults to False.
      use_dill_for_args: Whether to use dill to pickle `args` and `kwargs`. dill
        can pickle more objects, but doesn't work with types in
        `multiprocessing` library like `Mutex`.
      daemon: Whether to start processes as daemons.
      dependence_on_chief: Whether to terminates the cluster if the chief exits.
        If auto_restart is True, it only terminates the cluster if the chief
        exits with a zero exit code.
      auto_restart: Whether to automatically restart processes that exit with
        non-zero exit code.
      share_gpu: Whether to share GPUs among workers. If False, each worker is
        assigned different GPUs in a roundrobin fashion. This should be True
        whenever possible for better test execution coverage; some situations
        that need it to be False are tests that runs NCCL.
      args: Positional arguments to be sent to `fn` run on subprocesses.
      kwargs: Keyword arguments to be sent to `fn` run on subprocesses.

    Raises:
      RuntimeError: if `multi_process_runner.test_main()` is not called.
      ValueError: if there are more than one chief in the `cluster_spec`.
      SkipTest: if thread sanitizer is enabled (which is incompatible with MPR).
    z:ThreadSanitizer is not compatible with MultiProcessRunner.Nchief   zfIf chief exists in the cluster, there must be at most one chief. Current `cluster_spec` has {} chiefs.zfn is not a callablegrpc GPUFc              3   2   K   | ]  }t        |        y wN)len).0	addressess     a/home/dcms/DCMS/lib/python3.12/site-packages/tensorflow/python/distribute/multi_process_runner.py	<genexpr>z.MultiProcessRunner.__init__.<locals>.<genexpr>   s     NY#i.Ns   )2r
   is_tsan_enabledunittestSkipTestr/   
ValueErrorformat_check_initializationcallable_fn_cluster_spec
_rpc_layer_max_run_time_grpc_fail_fast_stream_output_return_output_dependence_on_chief_use_dill_for_args_daemon_auto_restart_args_kwargs
_share_gpur	   list_physical_devices
_total_gpur   enabled_v2_enabledr   _executing_eagerly_joined	threadingLock_process_lock
_processesset_terminated_reading_threadsmanager_managerQueue_process_status_queue_parent_to_sub_queuesumvaluesBarrier_barrier_streaming_queue_watchdog_thread)selffnr   r   max_run_timer   stream_outputreturn_outputuse_dill_for_argsdaemondependence_on_chiefauto_restart	share_gpuargskwargspartiess                   r2   __init__zMultiProcessRunner.__init__{   s    J   "
FH H ###,3|G'<#=#A Js<#89:< < B<-..DH%D)6DO%D)D'D'D 3D/DDL%DDJ<RDLDO'//+AA%HIDO {{}D%779DDL")DDO uDDIDM!%!4!4!6D $ 3 3 5DN$2D2D2K2K2MNNGMM))'2DM !MM//1D D    c                 X    |xs | j                   | _         |xs | j                  | _        y r.   )rF   rG   )ra   rk   rl   s      r2   set_argszMultiProcessRunner.set_args   s"    #DJ)T\\DLro   c                 t   t        j                  |j                         dd      5 }|D ]w  }dj                  ||      }dj                  |j	                  d      |      }| j
                  rt        |dd	       | j                  s]| j                  j                  |       y 	 d
d
d
       y
# 1 sw Y   y
xY w)z6Function to continuously read lines from subprocesses.rF)closefdz[{}-{}]:z{} {}    T)endflushN)
osfdopenfilenor8   ljustr@   printrA   r_   put)ra   pipe_rr   r   readerlinetask_stringformatted_lines           r2   _continuously_readline_from_subz2MultiProcessRunner._continuously_readline_from_sub   s    	6==?C	7 	46 4$ ''	7; (9(9"(=tD Bd
3



#
#N
34	4 	4 	4s   AB.B..B7c           
         t         t        j                  d      |xs | j                  }d}| j                  s\| j
                  dkD  rMt        j                  |||      }t        j                  ||      }	t        t        || j
                  |	            }t        |||| j                  | j                  | j                  | j                  |      }
t         j#                  d      \  }}t%        | j&                  | j(                  || j*                        }|$| j,                  | j.                  | j0                  }}}t        j2                  |t         j4                        }| j6                  rHt        j2                  |t         j4                        }t        j2                  |t         j4                        }t9        |
t;               ||
|||| j6                  f| j<                        }|j?                          || j@                  ||f<   | jB                  jE                  ||f       tG        jH                  | jJ                  |||f	      }|j?                          | jL                  jO                  |       | jP                  | jP                  jS                         s@tG        jH                  | jT                  
      | _(        | jP                  j?                          yy)zDStart a subprocess and a thread the reads lines from the subprocess.N1TODO(b/150264776): Resolve dependency issue in CIr   r   Fduplexr   )test_envtargetrk   rg   )r   rk   )r   )+dillr5   r6   r<   rH   rJ   r   id_in_clusterworker_countlistranger   r=   r?   rL   rM   multiprocessingPiper   rY   rZ   r^   r;   rF   rG   dumpsHIGHEST_PROTOCOLrC   _Process	_ProcFuncrD   startrR   rT   discardrO   Threadr   rU   appendr`   is_alive_process_watchdog)ra   r   r   r   rb   rk   rl   r   r   r   r   r   pipe_w	resourcespthreads                   r2   $_start_subprocess_and_reading_threadz7MultiProcessRunner._start_subprocess_and_reading_thread  sd    |
=? ?  54#5#5LL??t2'55lI6=?m&33L)Ll%tMNl!//++##11!	H %)))7NFF!77 55	I 
z4::t||$b 
B--	.BZZd334dzz&$"7"78f{2tVT5L5LM||		A
 GGI,-DOOY()i12 33i)+F LLN  ($D,A,A,J,J,L'..d6L6LMd
!!# -Mro   c                      j                   5   j                  rt        d       j                  rt        d       j                  j                         D ]*  \  }}t        |      D ]  \  }} j                  ||        , 	 ddd        j                  I fd}t        j                  t        j                  |       t        j                   j                         yy# 1 sw Y   _xY w)zStarts processes, one for each task in `cluster_spec`.

    Note that this is best effort by the applicable multiprocessing library,
    and it may take up to seconds for a subprocess to be successfully started.
    #MultiProcessRunner already started.Ccannot start new processes afterMultiProcessRunner.join() is calledNc                 *    ~ ~j                          y r.   )terminate_all)signumframera   s     r2   handlerz)MultiProcessRunner.start.<locals>.handlerf  s    Ero   )rQ   rR   r7   rN   r<   items	enumerater   r>   signalSIGALRMalarm)ra   r   r1   r   _r   s   `     r2   r   zMultiProcessRunner.startQ  s     
		 	H	>??	 ? @ 	@ #'"4"4":":"< H
)Y#I. 	HJGQ

3
3Iw
G	HH	H % mmFNNG,ll4%%& &	H 	Hs   A6C##C,c                    | j                   rt        d      | j                  5  | j                  rt        d      | j                  j                         D ]5  \  }}t        |      D ]"  \  }}||k(  r||k(  r| j                  ||       $ 7 	 ddd       t        ||| j                  | j                          | j                  | j                  i | j                   y# 1 sw Y   RxY w)a  Start the processes, with the specified task run in main process.

    This is similar to `start()` except that the task with task_type
    `as_task_type` and task_id `as_task_id` is run in the main process.
    This method is particularly useful when debugging tool such as `pdb` is
    needed in some specific task. Note that since this method is blocking until
    that specific task exits, additional actions would need a thread to be
    called:

    ```python
    def fn():
      # user code to be run
      import pdb; pdb.set_trace()

    def follow_ups():
      time.sleep(5)
      mpr.start_single_process(
          task_type='evaluator',
          task_id=0)

    mpr = multi_process_runner.MultiProcessRunner(
        fn,
        multi_worker_test_base.create_cluster_spec(
            has_chief=True, num_workers=1))
    threading.Thread(target=follow_ups).start()
    mpr.start_in_process_as(as_task_type='chief', as_task_id=0)
    mpr.join()
    ```

    Note that if `return_output=True`, the logs/stdout by task
    run by the main process is not available in result.stdout.

    Args:
      as_task_type: The task type to be run in the main process.
      as_task_id: The task id to be run in the main process.
    r   r   N)rR   r7   rQ   rN   r<   r   r   r   _set_tf_configr=   r;   rF   rG   )ra   as_task_type
as_task_idr   r1   r   r   s          r2   start_in_process_asz&MultiProcessRunner.start_in_process_asm  s    J <==			 J	 ? @ 	@"&"4"4":":"< J
)Y#I. 	JJGQ|+:0E55iI	JJ	J <T-?-???$DHHdjj)DLL)J Js   AC 8C  C)c           
          | j                   5  | j                  rt        d      | j                  |||||xs d|xs i        ddd       y# 1 sw Y   yxY w)aC  Starts a single process.

    This starts a process in the cluster with the task type, task id, and the
    process function (`fn`). If process function is `None`, the function
    provided at `__init__` will be used. If `cluster_spec` is `None`, the
    cluster spec provided at `__init__` will be used.

    TODO(rchao): It is meant that all subprocesses will be updated with the new
    cluster spec, but this has yet to be implemented. At this time only the
    newly started subprocess picks up this updated cluster spec.

    Args:
      task_type: The task type.
      task_id: The task id.
      cluster_spec: The cluster spec to be used on the newly started
        process. If `None`, the cluster spec provided at `__init__` will be
        used.
      fn: The process function to be run on the newly started
        process. If specified, specify `args` and `kwargs` as well. If `None`,
        the function provided at `__init__` will be used.
      args: Optional positional arguments to be supplied in `fn`.
      kwargs: Optional keyword arguments to be supplied in `fn`.
    r   r+   )r   rb   rk   rl   N)rQ   rN   r7   r   )ra   r   r   r   rb   rk   rl   s          r2   start_single_processz'MultiProcessRunner.start_single_process  sj    < 
		 
	 ? @ 	@
//

#zr2 0 	
 
 
s   7AAc                     g }	 	 |j                  |j                  d             ## t        j                  $ r Y |S w xY w)z Convert `queue.Queue` to `list`.Fblock)r   getrX   Empty)ra   queue_to_convertlist_to_returns      r2   _queue_to_listz!MultiProcessRunner._queue_to_list  sO    N
.222?@  [[ s   !' >>c                     i }| j                  | j                        D ]  }|||j                  |j                  f<    |S r.   )r   rY   r   r   )ra   statusesstatuss      r2   _get_process_statusesz(MultiProcessRunner._get_process_statuses  sE    H%%d&@&@A <5;h  &..12<Oro   c                     | j                   5  | j                  j                  ||fd      }ddd       r|j                  S dS # 1 sw Y   xY w)z:Returns the subprocess id given the task type and task id.N)rQ   rR   r   pidra   r   r   r   s       r2   get_process_idz!MultiProcessRunner.get_process_id  sL    			 :
//

y'2D
9a:1554: :s   AAc                     | j                   5  | j                  ||f   }ddd       r|j                  S dS # 1 sw Y   xY w)aZ  Returns the subprocess exit code given the task type and task id.

    Args:
      task_type: The task type.
      task_id: The task id.

    Returns:
      The subprocess exit code; `None` if the subprocess has not exited yet.

    Raises:
      KeyError: If the corresponding subprocess is not found with `task_type`
        and `task_id`.
    N)rQ   rR   exitcoder   s       r2   get_process_exit_codez(MultiProcessRunner.get_process_exit_code  sF     
		 0
//9g.
/a01::$$0 0s	   7A c                 *    | j                  ||      du S )a  Returns whether the subprocess still exists given the task type and id.

    Args:
      task_type: The task type.
      task_id: The task id.

    Returns:
      Boolean; whether the subprocess still exists. If the subprocess has
      exited, this returns False.
    N)r   )ra   r   r   s      r2   process_existsz!MultiProcessRunner.process_exists  s     %%i9TAAro   c                    	 t        j                  d       | j                  5  | j                  j	                  dd      }|r| j
                  r|j                  |j                  dk(  s| j                  sz| j                  j                         D ]  }|j                  d        | j                          | j                  j                         D ]  }|j                           	 ddd       y| j                  r|d}| j                  j                         D ]P  \  \  }}}|j                  |j                  dk7  s&d}t        j                  d	||       | j                  ||       R |r	 ddd       vt        d
 | j                  j                         D              r
	 ddd       y	 ddd       # 1 sw Y   xY w)a  Simulates a cluster management system.

    - If auto_restart is True, it restarts processes that exit with a non-zero
      exit code. Note that when join() times out it overrides auto_restart to
      False.
    - If dependence_on_chief is True, it terminates all processes once the chief
      exits. If auto_restart is also True, it only terminates all processes if
      the chief exit with a zero exit code, otherwise it restarts the chief.

    This runs in self._watchdog_thread.
    Tr)   )r(   r   Nr      )timeoutFzRestarting failed %s-%dc              3   8   K   | ]  }|j                   d u  y wr.   )r   )r0   r   s     r2   r3   z7MultiProcessRunner._process_watchdog.<locals>.<genexpr>,  s     H!qzz%Hs   )timesleeprQ   rR   r   rB   r   rE   r\   join_terminate_allr   r   infor   all)ra   r(   r   has_failurer   r   s         r2   r   z$MultiProcessRunner._process_watchdog  s    
jjm ##L$7
 T..5>>3M^^q ););__++-  ffQf  !__++- ffh " +)-)>)>)@ L%"y'Azz%!**/ kll4iI77	7K	L
 3 8 Ht/E/E/GHH
; 8 I9  s$   CF6/=F6-F6=0F67+F66F?c                     |j                         D ]`  }t        |t              sJ |j                  r"| j	                  |      |j
                  d   _        t        j                  |j
                    b y )Nr)   )	r\   
isinstancer   r   _get_mpr_resultr   
mpr_resultsixreraise)ra   process_statusesprocess_statuss      r2   _reraise_if_subprocess_errorz/MultiProcessRunner._reraise_if_subprocess_error/  sg    *113 .(:;;;))040D0D1"-^,,-.ro   c                    |rt        |t              st        d      | j                  5  | j                  rt        d      d| _        ddd       | j
                  j                  |       | j
                  j                         r| j                  5  d| _        ddd       t        j                  d       | j                  t        j                         | j
                  j                  t               | j
                  j                         r?t        j                  d       | j                          | j
                  j                          | j                         }| j!                  |       t#        d	j%                  |      | j'                  |            | j(                  j+                         D ]*  \  \  }}}t        j,                  d
|||j.                         , | j                         }| j!                  |       | j(                  j+                         D ]a  \  \  }}}|j.                  J |j.                  dkD  s'||f| j0                  vs8t3        d|||j.                  fz  | j'                  |             t        j,                  d       | j4                  D ]  }|j                           t        j,                  d       t        j6                  d       | j'                  |      S # 1 sw Y   xY w# 1 sw Y   fxY w)a	  Joins all the processes with timeout.

    If any of the subprocesses does not exit approximately after `timeout`
    seconds has passed after `join` call, this raises a
    `SubprocessTimeoutError`.

    Note: At timeout, it uses SIGTERM to terminate the subprocesses, in order to
    log the stack traces of the subprocesses when they exit. However, this
    results in timeout when the test runs with tsan (thread sanitizer); if tsan
    is being run on the test targets that rely on timeout to assert information,
    `MultiProcessRunner.terminate_all()` must be called after `join()`, before
    the test exits, so the subprocesses are terminated with SIGKILL, and data
    race is removed.

    Args:
      timeout: optional integer or `None`. If provided as an integer, and not
      all processes report status within roughly `timeout` seconds, a
      `SubprocessTimeoutError` exception will be raised. If `None`, `join` never
      times out.

    Returns:
      A `MultiProcessRunnerResult` object, which has two attributes,
      `return_value` and `stdout`. `return_value` always contains a list of
      return values from the subprocesses, although the order is not meaningful.
      If `return_output` argument is True at `__init__`, `stdout` is available
      that contains a list of all messages from subprocesses' stdout and stderr.

    Raises:
      SubprocessTimeoutError: if not all processes report status approximately
        within `timeout` seconds. When this is raised, a
        `MultiProcessRunnerResult` object can be retrieved by
        `SubprocessTimeoutError`'s mpr_result attribute, which has the same
        structure as above 'Returns' section describes.
      UnexpectedSubprocessExitError: If any of the subprocesses did not exit
        properly (for example, they exit on SIGTERM or SIGKILL signal). When
        this is raised, a `MultiProcessRunnerResult` object can be retrieved by
        `UnexpectedSubprocessExitError`'s mpr_result attribute, which has the
        same structure as above 'Returns' section describes. If `max_run_time`
        is not `None`, it is expected that some subprocesses may be
        force-killed when `max_run_time` is up, and this is raised in those
        cases.
      Exception: if there is an Exception propagated from any subprocess. When
        this is raised, a `MultiProcessRunnerResult` object can be retrieved by
        `UnexpectedSubprocessExitError`'s mpr_result attribute, which has the
        same structure as above 'Returns' section describes.
    z'`timeout` must be an integer or `None`.z)MultiProcessRunner can't be joined twice.TNFz8Timeout when joining for child processes. Terminating...)sigzPTimeout when waiting for child processes to print stacktrace. Sending SIGKILL...zOne or more subprocesses timed out, where timeout was set to {}s. Please change the `timeout` argument for `MultiProcessRunner.join()` or `multi_process_runner.run()` if it should be adjusted.z%s-%d exit code: %sr   z@Subprocess %s-%d exited with exit code %s. See logs for details.zJoining log reading threads.zJoined log reading threads.)r   intr7   rQ   rN   r`   r   r   rE   r   errorr   r   SIGTERM_FORCE_KILL_WAIT_SECr   r   SubprocessTimeoutErrorr8   r   rR   r   r   r   rT   UnexpectedSubprocessExitErrorrU   r   )ra   r   r   r   r   r   r   s          r2   r   zMultiProcessRunner.join7  s   ^ z'3/@AA			 	DEEdl
 	w'%%' #"#mmNO
V^^,   !56				'	'	) = 	>""$335
''(89"& '-fWo


/
02 2 $(??#8#8#: JGall()WajjIJ 113%%&67 $(??#8#8#: 4Ga ZZ###
**q.i19I9II+N'1::./  !124 	44 LL/0'' kkmLL./ LLO 011m # #s   K2K?2K<?L	c                     | j                  | j                        }g }|j                         D ]*  }|j                  |j	                  |j                         , t        ||      S )N)r   r   )r   r_   r\   r   r   r   )ra   r   r   return_valuesr   s        r2   r   z"MultiProcessRunner._get_mpr_result  sd      !6!67FM*113 :		$	$	0^889: $6NNro   c                 l   | j                   5  | j                  j                  ||fd      }|t        dj	                  ||            | j
                  j                  ||f       | j                  j                  dj	                  ||             |j                          ddd       y# 1 sw Y   yxY w)a   Terminates the process with `task_type` and `task_id`.

    If auto_retart=True, the terminated task will be restarted unless the chief
    has already exited with zero exit code.

    Args:
      task_type: the task type.
      task_id: the task id.

    Nz{}-{} does not existterminate {} {})
rQ   rR   r   r7   r8   rT   addrZ   r~   r   r   s       r2   	terminatezMultiProcessRunner.terminate  s     
		 
//

y'2D
9a	
/66y'JKK
Iw/0
##$5$<$<
W% ffh  s   BB**B3c                    |xs t        t        dt        j                        }| j                  j	                         D ]  \  \  }}}|j
                  t        j                  d||       -	 t        j                  |j                  |       | j                  j                  ||f       t        j                  d|||        y# t        $ r t        j                  d||       Y w xY w)zTerminates all subprocesses.

    The caller is required to hold self._process_lock.

    Args:
      sig: the signal used to terminate the process. The default is SIGKILL.
    SIGKILLNz*%s-%d has already exited. Not terminating.z %s-%d terminated with signal %r.z/Attempting to kill %s-%d but it does not exist.)getattrr   r   rR   r   r   r   r   ry   killr   rT   r   ProcessLookupError)ra   r   r   r   r   s        r2   r   z!MultiProcessRunner._terminate_all  s     
;FNN;C#'??#8#8#: )Ga	
	A9	)
si127G	)   )F	))s   ,AC C'&C'c                 h    | j                   5  | j                  |       ddd       y# 1 sw Y   yxY w)zTerminates all subprocesses.N)rQ   r   )ra   r   s     r2   r   z MultiProcessRunner.terminate_all  s.    			 
#  s   (1)NNNTFTFTFTNNNN)NNNNr.   )__name__
__module____qualname____doc__rn   rq   r   r   r   r   r   r   r   r   r   r   r   r   _DEFAULT_TIMEOUT_SECr   r   r   r   r   r+   ro   r2   r&   r&   k   s    $  "!"!%#'!~!@*4  9=.20426C$J'82*n )-" $"&(T	 %$B+Z. . g2RO*)4ro   r&   c                   (     e Zd ZdZ fdZd Z xZS )r   zKA modified `multiprocessing.Process` that can set up environment variables.c                 ~    t        t        | 
  di | || _        t	        | d      | _        | j                  | _        y )Nrunr+   )superr   rn   	_test_envr   _actual_run_run_with_setenvr   )ra   r   rl   	__class__s      r2   rn   z_Process.__init__  s8    	(D",V,DNtU+D$$DHro   c                    | j                   }|j                  &t        |j                        t        j                  d<   |j
                  rCdj                  |j
                  D cg c]  }t        |       c}      t        j                  d<   t        |j                  |j                  |j                  |j                         | j                         S c c}w )NGRPC_FAIL_FAST,CUDA_VISIBLE_DEVICES)r   r   strry   environr   r   r   r   r   r   r   r   )ra   r   is      r2   r   z_Process._run_with_setenv  s     ~~H*%()@)@%Abjj!"+.88#00
1a3q6
1,3bjj'(8%%x'7'79N9N%%' 2s   $C)r   r   r   r   rn   r   __classcell__r   s   @r2   r   r     s    S%ro   r   c                   F    e Zd ZdZej
                  d        Zd Zd Zd Z	y)r   z-Represents a callable to run in a subprocess.c              #      K   |r"t        j                         5  d  d d d        y t        j                         5  d  d d d        y # 1 sw Y   y xY w# 1 sw Y   y xY wwr.   )r	   
eager_mode
graph_mode)ra   r   s     r2   _runtime_modez_ProcFunc._runtime_mode  s\            s2   A!A	A!A 	A!	AA!AA!c           	      :   	 	 | j                   j                  j                  d      }|j                  d      st	        dj                  |            |dj                  ||      k(  rn<| j                   j                  j                  |       t        j                  d       	 | j                   j                  j                  t        ||dd	d	
             t        j                  d       y	# t        j                  $ r t        j                  d       Y tw xY w)z>A function that regularly checks messages from parent process.TFr   r   zUnrecognized message: {}r   r)   g?Nr   )
_resourcesr    r   
startswithr7   r8   r~   r   r   rX   r   r   r   ry   _exit)ra   r   r   messages       r2   _message_checking_funcz _ProcFunc._message_checking_func  s     //55999F !!+.5<<WEF
F'..y'BB
 //
-
-
1
1'
:
**Q- " 	OO((,,	  HHQK [[ 

3s   A&C/ *:C/ /(DDc                 <   t         j                  j                          t         j                  j                          t         j                  j	                          t         j                  j	                          | j
                  j                  j	                          y)zClose stdout, stderr and streaming pipe.

    We need to explicitly close them since Tensorflow may take a while to exit,
    so that the reading threads in the main process can exit more quickly.
    N)sysr   rx   stderrcloser  r!   ra   s    r2   _close_streamingz_ProcFunc._close_streaming   sZ     JJJJJJJJOO$$**,ro   c                    || _         | j                   j                  at        j                  |      }|r*t        j                  |      }t        j                  |      }t
        9t        j                          t        j                  t        j                  d       t        j                  t        j                         t        j                  |j                  j!                         t"        j$                  j!                                t        j                  |j                  j!                         t"        j&                  j!                                t        j(                         }t        j*                  d||j,                  |j.                         t        j*                  dt        j0                  d          t3        j4                  | j6                  |j,                  |j.                  fd      j9                          |j:                  rt=        j>                          | jA                  |jB                        5  tE        |j,                  |j.                  |||      }| j                   jF                  jI                  |       |jJ                  stM        jN                  |jP                    | jS                          ddd       t#        jT                  d       y# 1 sw Y   xY w)	zAThe wrapper function that actually gets run in child process(es).NT)chainz5Subprocess with PID %d (%s, %d) is now being started.zTF_CONFIG: %r	TF_CONFIG)r   rk   rg   r   )+r  r"   r^   r   loadsfaulthandlerenableregisterr   r   r   set_stderrthresholdDEBUGry   dup2r!   r{   r  r   r  getpidr   r   r   r  rO   r   r  r   r   r   enable_v2_behaviorr  r   _run_containedr   r~   r   r   r   r   r  exit)	ra   r   r   rb   rk   rl   rf   r   r   s	            r2   __call__z_ProcFunc.__call__,  s   
  DO&&H	BBZZdzz&!fFNN$7 . GGI&&--/1B1B1DEGGI&&--/1B1B1DE
))+CLLH###X%5%57LL"**["9: **  ("2"23 UW""$			H66	7 H..0@0@"d"$d
oo**..t4 T]]#
$ HHQK% s   =BKK%N)
r   r   r   r   
contextlibcontextmanagerr  r  r  r)  r+   ro   r2   r   r     s,    5 <
-;ro   r   c                  :    t         D ]  } | j                           y r.   )_active_pool_runnersshutdown)pools    r2   _shutdown_all_pool_runnersr0  p  s    " dMMOro   c                  h    t        t        j                        dk\  xr dt        j                  d   v S )z*Returns whether the test is run under OSS.r)   bazelr   )r/   r  argvr+   ro   r2   is_ossr4  u  s'    	SXX!		6388A; 66ro   c                   2    e Zd ZdZddZd Zd Zd Zd	dZy)
MultiProcessPoolRunnerzA utility class to start a process pool to simulate a cluster.

  It's similar to MultiProcessRunner, but uses a pool of processes to avoid the
  expensive initialization cost of Tensorflow.
  Nc                 t    t         j                  |        || _        || _        || _        i | _        d| _        y)a  Creates a multi-process pool runner.

    Args:
      cluster_spec: Dict for cluster spec. The following is an example of
        cluster with three workers.
        {"worker": ["worker0.example.com:2222",
                    "worker1.example.com:2222",
                    "worker2.example.com:2222"]}
      initializer: a callable to called at the startup of worker processes.
      share_gpu: Whether to share GPUs among workers. If False, each worker is
        assigned different GPUs in a roundrobin fashion.

    Raises:
      RuntimeError: if `multi_process_runner.test_main()` is not called.
      ValueError: if there are more than one chief in the `cluster_spec`.
    N)r-  r   r<   _initializerrH   _conn_runner)ra   r   initializerrj   s       r2   rn   zMultiProcessPoolRunner.__init__  s7    " T"%D#DDODJDLro   c                 $    | j                          y r.   )r.  r  s    r2   __del__zMultiProcessPoolRunner.__del__  s    MMOro   c                 L   | j                   j                         D ]  }|j                           i | _         | j                  #	 | j                  j	                          d| _        yy# t
        j                  $ r  t        $ r }t        j                  d|       Y d}~@d}~ww xY w)zShuts down the worker pool.Nz@Ignoring exception when shutting down MultiProcessPoolRunner: %s)
r9  r\   r  r:  r   r5   r6   	Exceptionr   	exception)ra   connes      r2   r.  zMultiProcessPoolRunner.shutdown  s    

!!# 
jjlDJ||
 dl     
N	
 	

s   A' 'B#BB#c                    t         t        j                  d      t        d | j                  d| j
                        | _        | j                  r/t        j                  | j                  t         j                        }nd}| j                  j                         D ]i  \  }}t        |      D ]V  \  }}t        j                  d      \  }}|| j                  ||f<   | j                  j                  ||t         ||||f       X k y)	zStarts the worker pool.Nr   c                       y r.   r+   r+   ro   r2   <lambda>z/MultiProcessPoolRunner._start.<locals>.<lambda>  s    ro   F)rb   r   rf   rj   Tr   )rb   rk   )r   r5   r6   r&   r<   rH   r:  r8  r   r   r   r   r   r   r9  r   _pool_runner_worker)ra   r;  r   r1   r   r   conn1conn2s           r2   _startzMultiProcessPoolRunner._start  s    
 |
=? ? &''//	#DL
 JJt00$2G2GHkk $ 2 2 8 8 : ;	9!), ;*'1&++4+8u+0

Iw'())"Wk59	 	* 	;;;ro   c                    t                t        j                          | j                  | j	                          t        j                  |t
        j                        }| j                  j                         D ]  }|j                  ||xs g |xs i f         g }| j                  j                         D ]?  \  \  }}}t        j                  d||       	 |j                  |j                                A g }|D ]d  }	t'        |	t(              sJ |	j*                  st-        j.                  |	j0                    |	j2                  J|j                  |	j2                         f |S # t         $ r | j#                          t%        d      w xY w)a  Runs `fn` with `args` and `kwargs` on all jobs.

    Args:
      fn: The function to be run.
      args: Optional positional arguments to be supplied in `fn`.
      kwargs: Optional keyword arguments to be supplied in `fn`.

    Returns:
      A list of return values.
    z!Waiting for the result from %s-%dzAUnexpected EOF. Worker process may have died. Please report a bug)r9   r   Processr:  rI  r   r   r   r9  r\   sendr   r   r   r   recvEOFErrorr.  RuntimeErrorr   r   r   r   r   r   r   )
ra   rb   rk   rl   rA  r   r   r   r   r   s
             r2   r   zMultiProcessPoolRunner.run  sf    ||
kkm	B--	.B

!!# 0
iiTZR2./0 &*jj&6&6&8 	2"Gdll6	7K2		,	2 M* :(:;;;))^,,-		$	$	0^889:   2 	 1 2 	2	2s   E%%%F
)NTr   )	r   r   r   r   rn   r=  r.  rI  r   r+   ro   r2   r6  r6  z  s     0";8)ro   r6  c                 l   |rt        j                  |      } |        	 	 |j                         \  }}}t        j                  |      }t	        | ||||      }t
        j                  j                          t
        j                  j                          |j                  |       # t        $ r Y yw xY w)a  Function that runs on the workers in a pool.

  It listens for callables to run and returns the result until `conn` is closed.
  It captures the exceptions during executing the callable and return it through
  `conn`.

  Args:
    task_type: the task type.
    task_id: the task index.
    initializer: a callable to execute during startup.
    conn: a multiprocessing.Connection object to listen for tasks and send
      results.
  N)
r   r  rM  rN  r'  r  r   rx   r  rL  )r   r   r;  rA  rb   rk   rl   r   s           r2   rF  rF    s     **[)KMb$ 
BB)Wb$?DJJJJIIdO 	  s   B' '	B32B3c                     d}d}d}	  ||i |}d}t        | ||||      S # t        $ r' t        j                         }t        | ||||      cY S w xY w)a  Runs `fn` with `args` and `kwargs`.

  The function returns _ProcessStatusInfo which captures the return value and
  the exception.

  Args:
    task_type: the task type.
    task_id: the task index.
    fn: the function to be run.
    args: optional positional arguments to be supplied in `fn`.
    kwargs: optional keyword arguments to be supplied in `fn`.

  Returns:
    a _ProcessStatusInfo.

  FNTr   )r   r?  r  r   )r   r   rb   rk   rl   r   r   r   s           r2   r'  r'    s    " -,(#t&v&LM#!# # 
 #||~H#!# ##s   " -AAzC__internal__.distribute.multi_process_runner.SubprocessTimeoutError)v1c                   "     e Zd ZdZ fdZ xZS )r   al  An error that indicates there is at least one subprocess timing out.

  When this is raised, a namedtuple object representing the multi-process run
  result can be retrieved by
  `tf.__internal__.distribute.multi_process_runner.SubprocessTimeoutError`'s
  `mpr_result` attribute. See
  `tf.__internal__.distribute.multi_process_runner.run` for more information.
  c                 :    t         t        |   |       || _        y r.   )r   r   rn   r   ra   msgr   r   s      r2   rn   zSubprocessTimeoutError.__init__I  s    	
 $05 DOro   r   r   r   r   rn   r  r	  s   @r2   r   r   <  s    ! !ro   r   zJ__internal__.distribute.multi_process_runner.UnexpectedSubprocessExitErrorc                   "     e Zd ZdZ fdZ xZS )r   a|  An error indicating there is at least one subprocess with unexpected exit.

  When this is raised, a namedtuple object representing the multi-process run
  result can be retrieved by
  `tf.__internal__.distribute.multi_process_runner
  .UnexpectedSubprocessExitError`'s
  `mpr_result` attribute. See
  `tf.__internal__.distribute.multi_process_runner.run` for more information.
  c                 :    t         t        |   |       || _        y r.   )r   r   rn   r   rU  s      r2   rn   z&UnexpectedSubprocessExitError.__init__\  s    	
'7< DOro   rW  r	  s   @r2   r   r   N  s    ! !ro   r   z@__internal__.distribute.multi_process_runner.NotInitializedErrorc                       e Zd ZdZy)NotInitializedErrora  An error indicating `multi_process_runner.run` is used without init.

  When this is raised, user is supposed to call
  `tf.__internal__.distribute.multi_process_runner.test_main()` within
  `if __name__ == '__main__':` block to properly initialize
  `multi_process_runner.run`.
  N)r   r   r   r   r+   ro   r2   r[  r[  a  s     ro   r[  c                  B    t        j                         st        d      y )Nz`multi_process_runner` is not initialized. Please call `tf.__internal__.distribute.multi_process_runner.test_main()` within `if __name__ == '__main__':` block in your python module to properly initialize `multi_process_runner`.)r   initializedr[  r+   ro   r2   r9   r9   n  s%    		&	&	(
	"# # 
)ro   c                 n    || |dd}|||d<   t        j                  |      t        j                  d<   y)z#Set TF_CONFIG environment variable.)typeindex)clustertaskNr   r  )jsonr   ry   r  )r   r   r   r   tf_config_dicts        r2   r   r   x  sB     . "+N; JJ~6"**[ro   z0__internal__.distribute.multi_process_runner.runFc           	      h    t        | ||||||      }|j                          |j                  |      S )a  Run `fn` in multiple processes according to `cluster_spec`.

  Given a callable `fn`, `tf.__internal__.distribute.multi_process_runner.run`
  launches multiple processes, each of which runs `fn`. These processes are
  referred to as "subprocesses" or "child processes". Each of those subprocesses
  will have their `TF_CONFIG` environment variable set, according to
  `cluster_spec` and their task types. The stdout of the subprocesses are
  streamed to the main process' and thus available in logs (if `stream_output`
  is True), with [type-id] prefix.

  `tf.__internal__.distribute.multi_process_runner.run` will block until all
  subprocesses have successfully exited, and return a namedtuple object that
  represents the run result. This object has a `return_value` attribute, which
  is a list that contains subprocesses `fn`'s return values, for those
  subprocesses that successfully returned from `fn`. The order of `return_value`
  list is not meaningful. If an optional arg `return_output` (default to False)
  is set to True, the namedtuple object will have an additional attribute
  `stdout`, which is a list containing the stdout of the subprocesses. If any
  subprocess' `fn` ends up raising an error, that error will be reraised from
  `tf.__internal__.distribute.multi_process_runner.run`, and the aforementioned
  namedtuple object will be available through the exception's
  `mpr_result` attribute.

  This utility is used for simulating running TensorFlow programs across
  multiple task types, and each of the task type may contain more than one task
  (except for "chief" where more than one task is prohibited). Test coverage of
  multi-worker training is the main application of this utility, where code
  written for multi-worker training can be realistically covered in unit tests.

  Any test module that uses
  `tf.__internal__.distribute.multi_process_runner.run()` must call
  `tf.__internal__.distribute.multi_process_runner.test_main()` instead of
  regular `test.main()` inside `if __name__ == '__main__':` block for proper
  initialization.

  Args:
    fn: Function to be run on child processes. This will be run on processes for
      all task types.
    cluster_spec: Dict for cluster spec. The utility function
      `tf.__internal__.distribute.multi_process_runner.create_cluster_spec` can
      be conveniently used to create such dict. The following is an example of
      cluster with three workers and two ps's.
      {"worker": ["worker0.example.com:2222",
                  "worker1.example.com:2222",
                  "worker2.example.com:2222"],
       "ps": ["ps0.example.com:2222",
              "ps1.example.com:2222"]}
    rpc_layer: RPC layer to use. Default value is 'grpc'.
    max_run_time: `None` or integer. If not `None`, child processes are forced
      to exit at approximately this many seconds after this utility is called.
      We achieve this through `signal.alarm()` api. Note that this is best
      effort at Python level since Python signal handler does not get executed
      when it runs lower level C/C++ code. So it can be delayed for arbitrarily
      long time. If any of the child process is still running when
      `max_run_time` is up, they will be force-terminated and an
      `tf.__internal__.distribute.multi_process_runner
      .UnexpectedSubprocessExitError`
      may be raised. If `None`, child processes are not forced to exit.
    return_output: If True, the output/error from the subprocesses should be
      collected to be attached to the resulting namedtuple returned from this
      utility. The list of output can be retrieved via `stdout` attribute.
      Defaults to False.
    timeout: optional integer or `None`. If provided as an integer, and not all
      processes report status within roughly `timeout` seconds, a
      `tf.__internal__.distribute.multi_process_runner.SubprocessTimeoutError`
      exception will be raised. If `None`,
      `tf.__internal__.distribute.multi_process_runner.run` never times out.
      Defaults to the constant `_DEFAULT_TIMEOUT_SEC` defined in
      `multi_process_runner` module.
    args: Positional arguments to be sent to `fn` run on subprocesses.
    kwargs: Keyword arguments to be sent to `fn` run on subprocesses.

  Returns:
      A namedtuple object, which has two attributes,
      `return_value` and `stdout`. `return_value` always contains a list of
      returnvalues from the subprocesses, although the order is not meaningful.
      If `return_output` argument is True, `stdout` is available that contains a
      list of all messages from subprocesses' stdout and stderr, and the order
      is mostly chronological.

  Raises:
    RuntimeError: if
    `tf.__internal__.distribute.multi_process_runner.test_main()` is
      not called in test's `if __name__ == '__main__':` block.
    ValueError: if there are more than one chief in the `cluster_spec`.
    tf.__internal__.distribute.multi_process_runner.SubprocessTimeoutError: if
      not all processes report status approximately
      within `timeout` seconds. When this is raised, a
      namedtuple object can be retrieved by
      `tf.__internal__.distribute.multi_process_runner.SubprocessTimeoutError`'s
      `mpr_result` attribute, which has the same
      structure as above 'Returns' section describes.
    tf.__internal__.distribute.multi_process_runner
    .UnexpectedSubprocessExitError:
      If any of the subprocesses did not exit
      properly (for example, they exit on SIGTERM or SIGKILL signal). When
      this is raised, a namedtuple object can be retrieved by
      `tf.__internal__.distribute.multi_process_runner
      .UnexpectedSubprocessExitError`'s
      `mpr_result` attribute, which has the
      same structure as above 'Returns' section describes. If `max_run_time`
      is not `None`, it is expected that some subprocesses may be
      force-killed when `max_run_time` is up, and this is raised in those
      cases.
    Exception: if there is an Exception propagated from any subprocess. When
      this is raised, a namedtuple object can be retrieved by
      `tf.__internal__.distribute.multi_process_runner
      .UnexpectedSubprocessExitError`
      `mpr_result` attribute, which has the
      same structure as above 'Returns' section describes.

  Examples:

  ```python
  class SimpleMultiProcessTest(tf.test.TestCase):

    def test_simple_printing_and_return(self):

      def fn():
        resolver = tf.distribute.cluster_resolver.TFConfigClusterResolver()

        # This will print "[chief-0]:     Task type: chief , task id: 0"
        # for chief, for example.
        logging.info('Task type: %s, task id: %d',
                     resolver.task_type, resolver.task_id)

        return resolver.task_type

      result = tf.__internal__.distribute.multi_process_runner.run(
          fn=fn,
          cluster_spec=(
              tf.__internal__
              .distribute.multi_process_runner.create_cluster_spec(
                  has_chief=True, num_workers=2)))
      assert sorted(result.return_value) == ['chief', 'worker', 'worker']

    def test_error_from_fn(self):

      def fn():
        resolver = tf.distribute.cluster_resolver.TFConfigClusterResolver()
        raise ValueError('Task type {}, task id {} is errors out'.format(
            resolver.task_type, resolver.task_id))

      with self.assertRaisesRegex(ValueError,
                                   'Task type worker, task id 0 is errors out'):
        cluster_spec = (
            tf.__internal__.distribute.multi_process_runner.create_cluster_spec(
                num_workers=1))
        tf.__internal__.distribute.multi_process_runner.run(
            fn=fn, cluster_spec=cluster_spec)


  if __name__ == '__main__':
    tf.__internal__.distribute.multi_process_runner.test_main()
  ```
  )rc   re   rk   rl   )r&   r   r   )	rb   r   r   rc   re   r   rk   rl   runners	            r2   r   r     s>    J !& 	,,.	W	ro   z8__internal__.distribute.multi_process_runner.get_barrierc                  0    t         t        d      t         S )a  Returns a `multiprocessing.Barrier` for `multi_process_runner.run`.

  `tf.__internal__.distribute.multi_process_runner.get_barrier()` returns
  a `multiprocessing.Barrier` object which can be used within `fn` of
  `tf.__internal__.distribute.multi_process_runner` to wait with
  `barrier.wait()` call until all other tasks have also reached the
  `barrier.wait()` call, before they can proceed individually.

  Note that all tasks (subprocesses) have to reach `barrier.wait()` call to
  proceed. Currently it is not supported to block on only a subset of tasks
  in the cluster.

  Example:
  ```python

  def fn():
    some_work_to_be_done_by_all_tasks()

    tf.__internal__.distribute.multi_process_runner.get_barrier().wait()

    # The barrier guarantees that at this point, all tasks have finished
    # `some_work_to_be_done_by_all_tasks()`
    some_other_work_to_be_done_by_all_tasks()

  result = tf.__internal__.distribute.multi_process_runner.run(
      fn=fn,
      cluster_spec=(
          tf.__internal__
          .distribute.multi_process_runner.create_cluster_spec(
              num_workers=2)))
  ```


  Returns:
    A `multiprocessing.Barrier` for `multi_process_runner.run`.
  zbarrier is not defined. It is likely because you are calling get_barrier() in the main process. get_barrier() can only be called in the subprocesses.)r^   r7   r+   ro   r2   get_barrierrh  ;  s$    L 
	 
 
/ro   c                      t                t        5  t        t        j	                         at        cddd       S # 1 sw Y   yxY w)aj  Returns the multiprocessing manager object for concurrency tools.

  The manager object is useful as it controls a server process that holds
  the python objects that can be shared across processes. This can be used
  for parent-subprocess communication:

  ```python
  manager = multi_process_runner.manager()
  some_event_happening_in_subprocess = manager.Event()
  mpr = multi_process_runner.MultiProcessRunner(fn, cluster_spec,
      args=(some_event_happening_in_subprocess,))
  mpr.start()
  some_event_happening_in_subprocess.wait()
  # Do something that only should after some event happens in subprocess.
  ```

  Note that the user of multi_process_runner should not create additional
  `multiprocessing.Manager()` objects; doing so can result in segfault in
  some cases.

  This method should only be called after multi_process_runner.test_main() is
  called.
  N)r9   _manager_lockrW   r   Managerr+   ro   r2   rV   rV   n  s9    0   ((*h  s	    ;Az6__internal__.distribute.multi_process_runner.test_mainc                      t        t        j                  d   dd      fd} t        t        j                  d   d|        t	        j
                          y)a  Main function to be called within `__main__` of a test file.

  Any test module that uses
  `tf.__internal__.distribute.multi_process_runner.run()`
  must call this instead of regular `test.main()` inside
  `if __name__ == '__main__':` block, or an error will be raised when
  `tf.__internal__.distribute.multi_process_runner.run()` is used. This method
  takes
  care of needed initialization for launching multiple subprocesses.

  Example:
  ```python
  class MyTestClass(tf.test.TestCase):
    def testSomething(self):
      # Testing code making use of
      # `tf.__internal__.distribute.multi_process_runner.run()`.

  if __name__ == '__main__':
    tf.__internal__.distribute.multi_process_runner.test_main()
  ```
  __main__tearDownModuleNc                  .    t                           y y r.   )r0  )old_tear_down_modules   r2   tear_down_modulez#test_main.<locals>.tear_down_module  s     ' (ro   )r   r  modulessetattrr   	test_main)rq  rp  s    @r2   rt  rt    sK    6 !Z!8:J!%'
 
#++j
!#35EFro   r.   )Gr   collectionsr*  rc  ry   r   r  rO   r   r5   weakrefabslr   r   	six.movesr   rX   tensorflow.pythonr   tensorflow.python.compatr   tensorflow.python.distributer   r   tensorflow.python.eagerr	   tensorflow.python.frameworkr
    tensorflow.python.util.tf_exportr   r   r  ImportErrorr   tblib.pickling_supporttblibpickling_supportinstall
namedtupler   r   r   r   r   r   objectr&   rK  r   r   WeakSetr-  r0  r4  r6  rF  r'  rO  r   r   r[  r9   r   r   r^   rh  rW   rP   rj  rV   rt  r+   ro   r2   <module>r     s   0    	  
      
 $ ! . : : + 1 6#33

  " ,[++IK 
 2;112L3A82LN  )+((): =   #K""; 1 	  
  o	 o	d (( 2q qn 'w( 
7
xV xv:'#T  %!\ !!  ,!L !!  F2O, O#7 ="E $m Fmb  E"M+ N+\ 	 @ CK#  L# C+  ,  	$  s6   2G$ 7G2 <H  $G/.G/2G=<G= H	H	