
    *                         S r SSKJr  SSKrSSKJr  S rS rS rS r	 " S	 S
\
5      r " S S\5      r " S S\5      r " S S\5      r " S S\5      rg)aP  Collection of classes for converting and transforming an input dictionary.

Conversions are defined statically using subclasses of SchemaField (Message,
Value, RepeatedField) which transform a source dictionary input to the target
schema. The source dictionary is expected to be parsed from a JSON
representation.

Only fields listed in the schema will be converted (i.e. an allowlist).
A SchemaField is a recursive structure and employs the visitor pattern to
convert an input structure.

# Schema to use for transformation
SAMPLE_SCHEMA = Message(
    foo=Value(target_name='bar'),
    list_of_things=RepeatedField(target_name='bar_list_of_things',
                                 element=Value()))

# Input dictionary:
input_dict = {
    'foo': '1234',
    'list_of_things': [1, 4, 5],
    'some_other_field': "hello"
}

# To convert:
result = SAMPLE_SCHEMA.ConvertValue(input_dict)

# The resulting dictionary will be:
{
    'bar': '1234',
    'bar_list_of_things': [1, 4, 5]
}

Note that both fields were renamed according to the rules in the schema. Fields
not listed in the schema will not be copied. In this example, "some_other_field"
was not copied.

If further transformation is required on the value itself, a converter can be
specified, which is simply a function which takes an input value and transforms
it according to whatever logic it wants.

For example, to convert a string value to an integer value, one could construct
a schema as follows:
CONVERTER_SCHEMA = Message(
    foo=Value(target_name='bar', converter=int))

Using the above input dictionary, the result would be:
{
    'bar': 1234
}
    )absolute_importN)
convertersc                     U R                  5       R                  S5      nUS   SR                  S USS  5       5      -   $ )zFConvert underscores to lower camel case (e.g. 'foo_bar' --> 'fooBar')._r    c              3   @   #    U  H  oR                  5       v   M     g 7fN)
capitalize).0parts     =lib/googlecloudsdk/appengine/admin/tools/conversion/schema.py	<genexpr>-UnderscoreToLowerCamelCase.<locals>.<genexpr>N   s     D)$OO--)s      N)lowersplitjoin)textpartss     r   UnderscoreToLowerCamelCaser   K   s<    
**,

S
!%	qBGGD%)DD	DD    c                 d    [        X5      (       d   [        SU< S[        U 5      < SU < 35      eg )NzExpected a z
, but got  for value )
isinstance
ValueErrortype)source_valueexpected_types     r   ValidateTyper   Q   s5    	L	0	0
4A484F4@	BC C 
1r   c                 J    [        X5      (       a  [        SU< SU < 35      eg )NzDid not expect r   )r   r   )r   non_expected_types     r   ValidateNotTyper"   Y   s)    00
,=|LN N 1r   c                   ^ ^ [        T 5      [        T5      -  nU(       aI  [        UU 4S jU 5       5      nU(       a,  UU 4S jnU H  n[        R                  " U" U5      5        M!     T R                  5       nUR	                  T5        U$ )ag  Attempts to merge the given dictionaries.

Warns if a key exists with different values in both dictionaries. In this
case, the new_dict value trumps the previous value.

Args:
  old_dict: Existing dictionary.
  new_dict: New dictionary.

Returns:
  Result of merging the two dictionaries.

Raises:
  ValueError: If the keys in each dictionary are not unique.
c              3   D   >#    U  H  nTU   TU   :w  d  M  Uv   M     g 7fr	    )r   keynew_dictold_dicts     r   r   (MergeDictionaryValues.<locals>.<genexpr>q   s(      >+3&smx}< 3+s    	 c                 2   > SR                  U TU    TU    S9$ )NzB'{key}' has conflicting values '{old}' and '{new}'. Using '{new}'.)r&   oldnew)format)r&   r'   r(   s    r   	FormatKey(MergeDictionaryValues.<locals>.FormatKeyt   s,    #$*Fs/7}/7} %+ %>	>r   )setloggingwarningcopyupdate)r(   r'   common_keysconflicting_keysr.   conflicting_keyresults   ``     r   MergeDictionaryValuesr9   _   sr      HH-+ >+ > >>
 ./	/23 .==?&--	-r   c                   4    \ rS rSrSrS	S jrS rS rS rSr	g)
SchemaField   a_  Transformation strategy from input dictionary to an output dictionary.

Each subclass defines a different strategy for how an input value is converted
to an output value. ConvertValue() makes a copy of the input with the proper
transformations applied. Additionally, constraints about the input structure
are validated while doing the transformation.
Nc                     Xl         X l        g)zConstructor.

Args:
  target_name: New field name to use when creating an output dictionary. If
    None is specified, then the original name is used.
  converter: A function which performs a transformation on the value of the
    field.
N)target_name	converter)selfr>   r?   s      r   __init__SchemaField.__init__   s     #Nr   c                 F    U R                  U5      nU R                  U5      $ )a&  Convert an input value using the given schema and converter.

This method is not meant to be overwritten. Update _VisitInternal to change
the behavior.

Args:
  value: Input value.

Returns:
  Output which has been transformed using the given schema for renaming and
  converter, if specified.
)_VisitInternal_PerformConversion)r@   valuer8   s      r   ConvertValueSchemaField.ConvertValue   s%       'F""6**r   c                     [        5       e)a  Shuffles the input value using the renames specified in the schema.

Only structural changes are made (e.g. renaming keys, copying lists, etc.).
Subclasses are expected to override this.

Args:
  value: Input value.

Returns:
  Output which has been transformed using the given schema.
)NotImplementedErrorr@   rF   s     r   rD   SchemaField._VisitInternal   s     
r   c                 J    U R                   (       a  U R                  U5      $ U$ )z8Transforms the result value if a converter is specified.)r?   )r@   r8   s     r   rE   SchemaField._PerformConversion   s    %)^^4>>&!??r   )r?   r>   NN)
__name__
__module____qualname____firstlineno____doc__rA   rG   rD   rE   __static_attributes__r%   r   r   r;   r;      s    
+  @r   r;   c                   6   ^  \ rS rSrSrSU 4S jjrS rSrU =r$ )Message   zyA message has a collection of fields which should be converted.

Expected input type: Dictionary
Output type: Dictionary
c                 p   > [         [        U ]  X5        X0l        U R                  (       d  [	        S5      eg)a  Constructor.

Args:
  target_name: New field name to use when creating an output dictionary. If
    None is specified, then the original name is used.
  converter: A function which performs a transformation on the value of the
    field.
  **kwargs: Kwargs where the keys are names of the fields and values are
    FieldSchemas for each child field.

Raises:
  ValueError: If the message has no child fields specified.
zMessage must contain fieldsN)superrW   rA   fieldsr   )r@   r>   r?   kwargs	__class__s       r   rA   Message.__init__   s1     
'4!+9K;;455 r   c                    [        U[        5        0 nU R                  R                  5        H  u  p4X1;  a  M  X   nUR                  =(       d    Un[        U5      nUR                  U5      nXb;  a  XrU'   ML  [        X&   [        5      (       a'  [        U[        5      (       a  [        X&   U5      X&'   M  [        SU-  5      e   U$ )z@Convert each child field and put the result in a new dictionary.zTarget key "%s" already exists.)
r   dictr[   itemsr>   r   rG   r   r9   r   )r@   rF   r8   
source_keyfield_schemar   
target_keyresult_values           r   rD   Message._VisitInternal   s    F$(KK$5$5$7 
		 &l++9zj-j9j!..|<l		!)zf($//J|?C5E 5E263E3?A :ZGHH# %8& Mr   )r[   rO   	rP   rQ   rR   rS   rT   rA   rD   rU   __classcell__r]   s   @r   rW   rW      s    6& r   rW   c                       \ rS rSrSrS rSrg)Value   a  Represents a leaf node. Only the value itself is copied.

A primitive value corresponds to any non-string, non-dictionary value which
can be represented in JSON.

Expected input type: Primitive value type (int, string, boolean, etc.).
Output type: Same primitive value type.
c                 F    [        U[        5        [        U[        5        U$ r	   )r"   listr`   rK   s     r   rD   Value._VisitInternal   s    E4 E4 Lr   r%   N)rP   rQ   rR   rS   rT   rD   rU   r%   r   r   rk   rk      s    r   rk   c                   f   ^  \ rS rSrSrSS\R                  \R                  4U 4S jjrS rSr	U =r
$ )Map   zqRepresents a leaf node where the value itself is a map.

Expected input type: Dictionary
Output type: Dictionary
Nc                 D   > [         [        U ]  X5        X0l        X@l        g)a  Constructor.

Args:
  target_name: New field name to use when creating an output dictionary. If
    None is specified, then the original name is used.
  converter: A function which performs a transformation on the value of the
    field.
  key_converter: A function which performs a transformation on the keys.
  value_converter: A function which performs a transformation on the values.
N)rZ   rq   rA   key_convertervalue_converter)r@   r>   r?   rt   ru   r]   s        r   rA   Map.__init__  s      
#tk5&*r   c                     [        U[        5        0 nUR                  5        HM  u  p4U R                  (       a  U R                  U5      nU R                  (       a  U R	                  U5      nXBU'   MO     U$ r	   )r   r`   ra   rt   ru   )r@   rF   r8   r&   
dict_values        r   rD   Map._VisitInternal  sc    F ;;=			  %			))*5
Sk ) Mr   )rt   ru   )rP   rQ   rR   rS   rT   r   ToJsonStringrA   rD   rU   rh   ri   s   @r   rq   rq      s0     "&'44)66+"
 
r   rq   c                   6   ^  \ rS rSrSrSU 4S jjrS rSrU =r$ )RepeatedFieldi!  zRepresents a list of nested elements. Each item in the list is copied.

The type of each element in the list is specified in the constructor.

Expected input type: List
Output type: List
c                    > [         [        U ]  X5        X0l        U R                  (       d  [	        S5      e[        U R                  [        5      (       a  [	        S5      eg)a  Constructor.

Args:
  target_name: New field name to use when creating an output dictionary. If
    None is specified, then the original name is used.
  converter: A function which performs a transformation on the value of the
    field.
  element: A SchemaField element defining the type of every element in the
    list. The input structure is expected to be homogenous.

Raises:
  ValueError: If an element has not been specified or if the element type is
  incompatible with a repeated field.
z%Element required for a repeated fieldzRepeated maps are not supportedN)rZ   r|   rA   elementr   r   rq   )r@   r>   r?   r~   r]   s       r   rA   RepeatedField.__init__*  sN     
-'?L<<>??$,,$$899 %r   c                     [        U[        5        / nU H-  nUR                  U R                  R	                  U5      5        M/     U$ r	   )r   rn   appendr~   rG   )r@   rF   r8   items       r   rD   RepeatedField._VisitInternalB  s;    FmmDLL--d34 Mr   )r~   )NNNrg   ri   s   @r   r|   r|   !  s    :0 r   r|   )rT   
__future__r   r1   /googlecloudsdk.appengine.admin.tools.conversionr   r   r   r"   r9   objectr;   rW   rk   rq   r|   r%   r   r   <module>r      so   2h '  F
ECNB5@& 5@p1k 1hK  "+ "J&K &r   