
    BVh?                     f   d Z ddlm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  edg        G d	 d
             Z edg        G d d             Z edg        G d de             ZddZd Z edg       d        Z edg       d        Z edg        G d d             Zd Zy)ag  Facilities for creating multiple test combinations.

Here is a simple example for testing various optimizers in Eager and Graph:

class AdditionExample(test.TestCase, parameterized.TestCase):
  @combinations.generate(
     combinations.combine(mode=["graph", "eager"],
                          optimizer=[AdamOptimizer(),
                                     GradientDescentOptimizer()]))
  def testOptimizer(self, optimizer):
    ... f(optimizer)...

This will run `testOptimizer` 4 times with the specified optimizers: 2 in
Eager and 2 in Graph mode.
The test is going to accept the same parameters as the ones used in `combine()`.
The parameters need to match by name between the `combine()` call and the test
signature.  It is necessary to accept all parameters. See `OptionalParameter`
for a way to implement optional parameters.

`combine()` function is available for creating a cross product of various
options.  `times()` function exists for creating a product of N `combine()`-ed
results.

The execution of generated tests can be customized in a number of ways:
-  The test can be skipped if it is not running in the correct environment.
-  The arguments that are passed to the test can be additionally transformed.
-  The test can be run with specific Python context managers.
These behaviors can be customized by providing instances of `TestCombination` to
`generate()`.
    )OrderedDictN)parameterized)
tf_inspect)	tf_exportz.__internal__.test.combinations.TestCombination)v1c                   "    e Zd ZdZd Zd Zd Zy)TestCombinationa  Customize the behavior of `generate()` and the tests that it executes.

  Here is sequence of steps for executing a test combination:
    1. The test combination is evaluated for whether it should be executed in
       the given environment by calling `should_execute_combination`.
    2. If the test combination is going to be executed, then the arguments for
       all combined parameters are validated.  Some arguments can be handled in
       a special way.  This is achieved by implementing that logic in
       `ParameterModifier` instances that returned from `parameter_modifiers`.
    3. Before executing the test, `context_managers` are installed
       around it.
  c                      ~y)a*  Indicates whether the combination of test arguments should be executed.

    If the environment doesn't satisfy the dependencies of the test
    combination, then it can be skipped.

    Args:
      kwargs:  Arguments that are passed to the test combination.

    Returns:
      A tuple boolean and an optional string.  The boolean False indicates
    that the test should be skipped.  The string would indicate a textual
    description of the reason.  If the test is going to be executed, then
    this method returns `None` instead of the string.
    )TN selfkwargss     ]/home/dcms/DCMS/lib/python3.12/site-packages/tensorflow/python/framework/test_combinations.pyshould_execute_combinationz*TestCombination.should_execute_combinationI   s
     	    c                     g S )zCReturns `ParameterModifier` instances that customize the arguments.r   r   s    r   parameter_modifiersz#TestCombination.parameter_modifiers[   s    Ir   c                     ~g S )aP  Return context managers for running the test combination.

    The test combination will run under all context managers that all
    `TestCombination` instances return.

    Args:
      kwargs:  Arguments and their values that are passed to the test
        combination.

    Returns:
      A list of instantiated context managers.
    r   r   s     r   context_managersz TestCombination.context_managers_   s     	Ir   N)__name__
__module____qualname____doc__r   r   r   r   r   r   r	   r	   :   s    $r   r	   z0__internal__.test.combinations.ParameterModifierc                   >    e Zd ZdZ e       ZddZd Zd Zd Z	d Z
y)	ParameterModifierai  Customizes the behavior of a particular parameter.

  Users should override `modified_arguments()` to modify the parameter they
  want, eg: change the value of certain parameter or filter it from the params
  passed to the test case.

  See the sample usage below, it will change any negative parameters to zero
  before it gets passed to test case.
  ```
  class NonNegativeParameterModifier(ParameterModifier):

    def modified_arguments(self, kwargs, requested_parameters):
      updates = {}
      for name, value in kwargs.items():
        if value < 0:
          updates[name] = 0
      return updates
  ```
  Nc                     || _         y)a  Construct a parameter modifier that may be specific to a parameter.

    Args:
      parameter_name:  A `ParameterModifier` instance may operate on a class of
        parameters or on a parameter with a particular name.  Only
        `ParameterModifier` instances that are of a unique type or were
        initialized with a unique `parameter_name` will be executed.
        See `__eq__` and `__hash__`.
    N)_parameter_name)r   parameter_names     r   __init__zParameterModifier.__init__   s     *Dr   c                 
    ~~i S )a  Replace user-provided arguments before they are passed to a test.

    This makes it possible to adjust user-provided arguments before passing
    them to the test method.

    Args:
      kwargs:  The combined arguments for the test.
      requested_parameters: The set of parameters that are defined in the
        signature of the test method.

    Returns:
      A dictionary with updates to `kwargs`.  Keys with values set to
      `ParameterModifier.DO_NOT_PASS_TO_THE_TEST` are going to be deleted and
      not passed to the test.
    r   r   r   requested_parameterss      r   modified_argumentsz$ParameterModifier.modified_arguments   s      	$Ir   c                 l    | |u ryt        |       t        |      u r| j                  |j                  k(  S y)z9Compare `ParameterModifier` by type and `parameter_name`.TF)typer   r   others     r   __eq__zParameterModifier.__eq__   s6    u}	dtE{	"!!U%:%:::r   c                 &    | j                  |       S N)r)   r'   s     r   __ne__zParameterModifier.__ne__   s    {{5!!!r   c                 n    | j                   rt        | j                         S t        | j                        S )z8Compare `ParameterModifier` by type or `parameter_name`.)r   hashid	__class__r   s    r   __hash__zParameterModifier.__hash__   s+    $&&''r   r+   )r   r   r   r   objectDO_NOT_PASS_TO_THE_TESTr    r$   r)   r,   r1   r   r   r   r   r   p   s*    ( #H
*&" r   r   z0__internal__.test.combinations.OptionalParameterc                       e Zd ZdZd Zy)OptionalParameteraG  A parameter that is optional in `combine()` and in the test signature.

  `OptionalParameter` is usually used with `TestCombination` in the
  `parameter_modifiers()`. It allows `TestCombination` to skip certain
  parameters when passing them to `combine()`, since the `TestCombination` might
  consume the param and create some context based on the value it gets.

  See the sample usage below:

  ```
  class EagerGraphCombination(TestCombination):

    def context_managers(self, kwargs):
      mode = kwargs.pop("mode", None)
      if mode is None:
        return []
      elif mode == "eager":
        return [context.eager_mode()]
      elif mode == "graph":
        return [ops.Graph().as_default(), context.graph_mode()]
      else:
        raise ValueError(
            "'mode' has to be either 'eager' or 'graph', got {}".format(mode))

    def parameter_modifiers(self):
      return [test_combinations.OptionalParameter("mode")]
  ```

  When the test case is generated, the param "mode" will not be passed to the
  test method, since it is consumed by the `EagerGraphCombination`.
  c                 Z    | j                   |v ri S | j                   t        j                  iS r+   )r   r   r3   r"   s      r   r$   z$OptionalParameter.modified_arguments   s.    33i""$5$M$MNNr   N)r   r   r   r   r$   r   r   r   r5   r5      s    @Or   r5   c                       fd}|S )a  A decorator for generating combinations of a test method or a test class.

  Parameters of the test method must match by name to get the corresponding
  value of the combination.  Tests must accept all parameters that are passed
  other than the ones that are `OptionalParameter`.

  Args:
    combinations: a list of dictionaries created using combine() and times().
    test_combinations: a tuple of `TestCombination` instances that customize
      the execution of generated tests.

  Returns:
    a decorator that will cause the test method or the test class to be run
    under the specified conditions.

  Raises:
    ValueError: if any parameters were not accepted by the test method
  c                    g }D ]  }t        |t              sJ dj                  t        |j	                               D cg c]r  \  }\  }}dj                  dj                  t        t        j                  |            dj                  t        t        j                  t        ||                        t c}}}      }|j                  t        t        |j	                               ddj                  |      fgz                 t        | t              r| }i x|_        }|j                  j                         j	                         D ]  \  }}	|j!                  t"        j$                  j&                        s0t        |	t(        j*                        sKt-        ||       i }
t/        j0                  |j2                  |
||t/        j4                  t7        |	      |t.        j8                  |             |
j	                         D ]  \  }}t;        |||         |S t7        |       }	 t/        j<                  | |	      S c c}}}w )zThe decorator to be returned. z_{}_{}testcase_namez_test{})test_combinations)
isinstancer   join	enumerateitemsformatfilterstrisalnum	_get_nameappendlistr&   _test_method_ids__dict__copy
startswithunittest
TestLoadertestMethodPrefixtypesFunctionTypedelattrr   &_update_class_dict_for_param_test_caser   _ParameterizedTestIter_augment_with_special_arguments_NAMEDsetattrnamed_parameters)test_method_or_classnamed_combinationscombinationikeyvaluenameclass_objecttest_method_idstest_methodmethodsmethod_namemethodcombinationsr;   s                r   	decoratorzgenerate.<locals>.decorator   s    # < [111WW "+;+<+<+>!?  a#u //"''&c":;''&iq6I"JKM 	d
 
;$$&'!1!1$!789:;<< &-)l8::l#o+4499;AAC 7
$OOH//@@A{E$6$67
,
%'

>
>##Wot221!5FH$m&:&:DBC &-]]_ 7!k6L+v677 3
2CEk@]++-?@MM?s   A7I
r   )rd   r;   re   s   `` r   generaterf      s    &(NT 
r   c                       fd}|S )Nc                    
 j                         g }D ];  }|j                  j                               \  }}|r(|j                  d|z          = |r  j                  dj	                  |             g 
D ]!  }
j                  |j                                # t        
      

 fd}g }D ]7  }|j                  j                               D ]  }|j                  |        9 t        t        d      r#t        j                  | 5   |        ddd       yt        j                         5 }	|D ]  }|	j                  |         |        ddd       y# 1 sw Y   yxY w# 1 sw Y   yxY w)zEA wrapped test method that can treat some arguments in a special way.z - 
c                     t        j                        j                  } D ]b  }|j                  
j	                         |       j                         D ]/  \  }}|t        j                  u r	j                  |d        +|	|<   1 d t        |       j                  t        t        	j                               dgz               }|rt        dj                  |            t        t        	j                               dgz         j                  t        |             }|rt        dj                  |            i }| D ]  }|dk(  r||<   	|   ||<     di | y )Nr   zBThe test requires parameters whose arguments were not passed: {} .z9The test does not take parameters that were passed : {} .r   )r   getfullargspecargsr$   rI   r?   r   r3   popset
differencerF   keys
ValueErrorr@   )r#   customized_parameterargumentr\   omitted_argumentsmissing_argumentskwargs_to_pass	parametercustomized_parametersr   original_kwargsr   r`   s           r   execute_test_methodzO_augment_with_special_arguments.<locals>.decorated.<locals>.execute_test_method<  so   '66{CHH"7 %
3FF  "$8 ::?%'	%OHe'???JJx&$F8	%% 23>>
d6;;=!VH,
-/	 1178I1JL 	Ld6;;=1VH<=HH
"
#%	 ""(&):";= 	= n+ 8)&*.
#&,Y&7.
#	8
 #N#r   nestedN)rI   r   rE   skipTestr=   extendr   rn   r   hasattr
contextlibr{   	ExitStackenter_context)r   r   reasons_to_skiprY   should_executereasonrz   r   managercontext_stackrx   ry   r;   r`   s   ``        @@r   	decoratedz2_augment_with_special_arguments.<locals>.decorated%  s   kkmO O( /*EE



  "nfuv~.	/ 
mmDIIo./( F"";#B#B#DEF 56$ $< ( ) 11



 " )'())
 z8$./   ! ]' 	/G

%
%g
.	/   s   E$; E0$E-0E9r   )r`   r;   r   s   `` r   rS   rS   $  s    CJ 
r   z&__internal__.test.combinations.combinec                     | st               gS d }t        t        | j                         |            } t        | j                               d   }t	        t        | j                               dd       }t        di |}|d   }|d   }t        |t              s|g}|D cg c]<  }|D ]5  }t        t        t        |j                               ||fgz   |            7 > c}}S c c}}w )a  Generate combinations based on its keyword arguments.

  Two sets of returned combinations can be concatenated using +.  Their product
  can be computed using `times()`.

  Args:
    **kwargs: keyword arguments of form `option=[possibilities, ...]`
         or `option=the_only_possibility`.

  Returns:
    a list of dictionaries for each combination. Keys in the dictionaries are
    the keyword argument names.  Each key has one value - one of the
    corresponding keyword argument values.
  c                     | d   S )Nr   r   )ks    r   <lambda>zcombine.<locals>.<lambda>  s
    !A$ r   )r[   r      Nr   )r   sortedr?   rF   dictcombiner<   )	r   sort_by_keyfirstrestrest_combinedr[   valuesvcombineds	            r   r   r   m  s      
M?+vflln+>?&
v||~
q
!%	d6<<>"12&	'$/D/-a#8&	FD	!XF 

#
  &hnn./C8*<+NO
O
  
s   #AC(z$__internal__.test.combinations.timesc                     | sJ t        |       dk(  r| d   S | d   }t        | dd  }g }|D ]  }|D ]  }t        |j                               j	                  t        |j                                     r7t        dj                  |j                         |j                                     |j                  t        t        |j                               t        |j                               z                  |S )aJ  Generate a product of N sets of combinations.

  times(combine(a=[1,2]), combine(b=[3,4])) == combine(a=[1,2], b=[3,4])

  Args:
    *combined: N lists of dictionaries that specify combinations.

  Returns:
    a list of dictionaries for each combination.

  Raises:
    ValueError: if some of the inputs have overlapping keys.
  r   r   Nz"Keys need to not overlap: {} vs {})lentimesrn   rp   intersectionrq   r@   rE   r   rF   r?   )r   r   r   combined_resultsabs         r   r   r     s     
/]aA;
1+%!"&- Na N	QVVX	#	#CM	2=DDFFHaffh  ! 	! k$qwwy/DO*KLMNN 
r   z*__internal__.test.combinations.NamedObjectc                   .    e Zd ZdZd Zd Zd Zd Zd Zy)NamedObjectz8A class that translates an object into a good test name.c                      || _         || _        y r+   )_name_obj)r   r]   objs      r   r    zNamedObject.__init__  s    DJDIr   c                 .    t        | j                  |      S r+   )getattrr   )r   r]   s     r   __getattr__zNamedObject.__getattr__  s    499d##r   c                 &     | j                   |i |S r+   )r   )r   rl   r   s      r   __call__zNamedObject.__call__  s    499d%f%%r   c                 6    | j                   j                         S r+   )r   __iter__r   s    r   r   zNamedObject.__iter__  s    99r   c                     | j                   S r+   )r   r   s    r   __repr__zNamedObject.__repr__  s    ::r   N)	r   r   r   r   r    r   r   r   r   r   r   r   r   r     s    @$& r   r   c                 T    t        j                  dt        |      t        |             S )Nz0[xX][0-9a-fA-F]+)resubrB   )r\   indexs     r   rD   rD     s    	#SZU	<<r   )r   )r   collectionsr   r   r   rN   rK   absl.testingr   tensorflow.python.utilr    tensorflow.python.util.tf_exportr   r	   r   r5   rf   rS   r   r   r   rD   r   r   r   <module>r      s  > $  	   & - 6 ;C2 2 D2j ="EG  G  FG T ="E%O) %O F%OP=@FR 3;" <"J 1b9 :B 7B?  @(=r   