o
    B                     @   s\  d 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
Z
d	d
 Zdd Zdd Zdd ZdZdZdZdZeeeZeeeZ		d;ddZd<ddZ		d=ddZd<ddZd d! Z		d>d"d#Zd$d% Zd&d' Zd(d) Zd*d+ Zd,d- Z 	d?d/d0Z!G d1d2 d2e"Z#G d3d4 d4e"Z$d5d6 Z%d<d7d8Z&G d9d: d:e$Z'dS )@a"  Module for labels API support.

Typical usage (create command):

  # When defining arguments
  labels_util.AddCreateLabelsFlags(parser)
  # When running the command
  new_resource.labels = labels_util.ParseCreateArgs(args, labels_cls)
  Create(..., new_resource)

Typical usage (update command):

  # When defining arguments
  labels_util.AddUpdateLabelsFlags(parser)

  # When running the command
  labels_diff = labels_util.Diff.FromUpdateArgs(args)
  if labels_diff.MayHaveUpdates():
    orig_resource = Get(...)  # to prevent unnecessary Get calls
    labels_update = labels_diff.Apply(labels_cls, orig_resource.labels)
    if labels_update.needs_update:
      new_resource.labels = labels_update.labels
      field_mask.append('labels')
  Update(..., new_resource)

  # Or alternatively, when running the command
  labels_update = labels_util.ProcessUpdateArgsLazy(
    args, labels_cls, lambda: Get(...).labels)
  if labels_update.needs_update:
    new_resource.labels = labels_update.labels
    field_mask.append('labels')
  Update(..., new_resource)
    )absolute_import)division)unicode_literals)arg_parsers)base)
exceptionsNc                 C   s   |   o|  p|   S )z8Returns True if c is lower case or a caseless ideograph.)isalphaislowerisupperc r   M/tmp/google-cloud-sdk/lib/googlecloudsdk/command_lib/util/args/labels_util.py_IsLower<   s   r   c                 C   s   | dv p|   pt| S )zGReturns True if c is a valid value or subsequent (not first) character.)_-)isdigitr   r   r   r   r   _IsValueOrSubsequentA   s   r   c                 C   s*   | du s
t | dkrdS tdd | D S )a  Implements the PCRE r'[\p{Ll}\p{Lo}\p{N}_-]{0,63}'.

  Only hyphens (-), underscores (_), lowercase characters, and numbers are
  allowed. International characters are allowed.

  Args:
    value: The label value, a string.

  Returns:
    True is the value is valid; False if not.
  N?   Fc                 s   s    | ]}t |V  qd S N)r   ).0r   r   r   r   	<genexpr>T   s    z$IsValidLabelValue.<locals>.<genexpr>)lenall)valuer   r   r   IsValidLabelValueF   s   r   c                 C   s   | rt | d s
dS t| S )zImplements the PCRE r'[\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{N}_-]{0,62}'.

  The key must start with a lowercase character and must be a valid label value.

  Args:
    key: The label key, a string.

  Returns:
    True if the key is valid; False if not.
  r   F)r   r   )keyr   r   r   IsValidLabelKeyW   s   r   zOnly hyphens (-), underscores (_), lowercase characters, and numbers are allowed. Keys must start with a lowercase character. International characters are allowed. Key length must not exceed 63 characters.zKeys must start with a lowercase character and contain only hyphens (`-`), underscores (```_```), lowercase characters, and numbers.zwOnly hyphens (-), underscores (_), lowercase characters, and numbers are allowed. International characters are allowed.zaValues must contain only hyphens (`-`), underscores (```_```), lowercase characters, and numbers. labelsTc                 C      |rt nd}|r
tnd}g }|r|t |r|t dg}|r)|d| | r0||  tjd|dt	j
||dt	jd|dS )	z*Makes the base.Argument for --labels flag.Nz%List of label KEY=VALUE pairs to add. z--{}	KEY=VALUEkey_type
value_type

metavartypeactionhelpKEY_FORMAT_VALIDATORVALUE_FORMAT_VALIDATORappendKEY_FORMAT_HELPVALUE_FORMAT_HELPjoinr   Argumentformatr   ArgDictUpdateActionextra_messagelabels_namevalidate_keysvalidate_valuesr$   r%   format_help
help_partsr   r   r   GetCreateLabelsFlag}   s*   


r>   c                 C   s   t jd| ddj| ddS )Nz
--clear-{}
store_truea            Remove all labels. If `--update-{labels}` is also specified then
          `--clear-{labels}` is applied first.

          For example, to remove all labels:

              $ {{command}} --clear-{labels}

          To remove all existing labels and create two new labels,
          ``foo'' and ``baz'':

              $ {{command}} --clear-{labels} --update-{labels} foo=bar,baz=qux
          r   )r*   r+   )r   r3   r4   )r9   r   r   r   GetClearLabelsFlag   s   rA   c                 C   r    )	z5Makes a base.Argument for the `--update-labels` flag.NzuList of label KEY=VALUE pairs to update. If a label exists, its value is modified. Otherwise, a new label is created.r!   z--update-{}r"   r#   r&   r'   r,   r7   r   r   r   GetUpdateLabelsFlag   s,   


rB   c                 C   s,   t jd|dt tjdj|d|  dS )Nz--remove-{}KEYz      List of label keys to remove. If a label does not exist it is
      silently ignored. If `--update-{labels}` is also specified then
      `--update-{labels}` is applied first.r@   r'   )r   r3   r4   r   ArgListr6   )r8   r9   r   r   r   GetRemoveLabelsFlag   s   rE   c                 C   s   t  |  dS )zxAdds create command labels flags to an argparse parser.

  Args:
    parser: The argparse parser to add the flags to.
  N)r>   AddToParser)parserr   r   r   AddCreateLabelsFlags   s   rH   c                 C   sJ   t ||  |r|  }t | t|| dS t||  dS )a  Adds update command labels flags to an argparse parser.

  Args:
    parser: The argparse parser to add the flags to.
    extra_update_message: str, extra message to append to help text for
                          --update-labels flag.
    extra_remove_message: str, extra message to append to help text for
                          --delete-labels flag.
    enable_clear: bool, whether to include the --clear-labels flag.
  N)rB   rF   add_mutually_exclusive_grouprA   rE   )rG   extra_update_messageextra_remove_messageenable_clearremove_groupr   r   r   AddUpdateLabelsFlags   s   rN   c                 C   s   t | dr| jS | jS )zReturns the update labels dict from the parsed args.

  Args:
    args: The parsed args.

  Returns:
    The update labels dict from the parsed args.
  r   )hasattrr   update_labelsargsr   r   r   GetUpdateLabelsDictFromArgs   s   	rS   c                 C   s   | j S )zReturns the remove labels list from the parsed args.

  Args:
    args: The parsed args.

  Returns:
    The remove labels list from the parsed args.
  )remove_labelsrQ   r   r   r   GetRemoveLabelsListFromArgs   s   	rU   c                 C   s"   t | }| stdd|S )a{  Validates and returns labels specific args for update.

  At least one of --update-labels, --clear-labels or --remove-labels must be
  provided. The --clear-labels flag *must* be a declared argument, whether it
  was specified on the command line or not.

  Args:
    parsed_args: The parsed args.

  Returns:
    (update_labels, remove_labels)
    update_labels contains values from --labels and --update-labels flags
    respectively.
    remove_labels contains values from --remove-labels flag

  Raises:
    RequiredArgumentException: if all labels arguments are absent.
    AttributeError: if the --clear-labels flag is absent.
  LABELSzVAt least one of --update-labels, --remove-labels, or --clear-labels must be specified.)DiffFromUpdateArgsMayHaveUpdatescalliope_exceptionsRequiredArgumentException)parsed_argsdiffr   r   r   GetAndValidateOpsFromArgs  s   
r^   c                    s"     fddt t|D dS )Nc                    s   g | ]\}} j ||d qS )r   r   )AdditionalProperty)r   r   r   
labels_clsr   r   
<listcomp>(  s    z"_PackageLabels.<locals>.<listcomp>additionalProperties)sortedsix	iteritems)rb   r   r   ra   r   _PackageLabels&  s   
ri   c                 C   s   | si S dd | j D S )Nc                 S   s   i | ]}|j |jqS r   r_   )r   lr   r   r   
<dictcomp>0  s    z*_GetExistingLabelsDict.<locals>.<dictcomp>rd   r@   r   r   r   _GetExistingLabelsDict-  s   rl   =c              
   C   s   | sdS i }| D ]O}z	| |\}}W n ty#   tdd|w |r5t|s5tddj|td|rFt|sFtddj|td||v rStdd||||< q|S )	a  Validates and returns labels in dictionary format.

  Args:
    list_of_labels: List of labels in format ["K1=V1", "K2=V2", ...].
    delimiter: delimiters which separates key and its corresponding values.
    validate_keys: if true, performs regex validation.
    validate_values: if true, performs regex validation.

  Returns:
    None: if list_of_labels is empty.
    Otheriwse: dictionary of labels {"K1": "V1", "K2": "V2", ...}.

  Raises:
    InvalidArgumentException: if invalid format.
  Nz--labelszInvalid label format: {}z-Invalid key format: {key}
{_KEY_FORMAT_ERROR})r   _KEY_FORMAT_ERRORz3Invalid value format: {value}
{_VALUE_FORMAT_ERROR})r   _VALUE_FORMAT_ERRORzDuplicate key: {})	split
ValueErrorrZ   InvalidArgumentExceptionr4   r   rn   r   ro   )list_of_labels	delimiterr:   r;   dict_of_labelslabelr   r   r   r   r   ValidateAndParseLabels3  s>   


rw   c                   @   s,   e Zd ZdZdd Zedd Zdd ZdS )	UpdateResulta-  Result type for Diff application.

  Attributes:
    needs_update: bool, whether the diff resulted in any changes to the existing
      labels proto.
    _labels: LabelsValue, the new populated LabelsValue object. If needs_update
      is False, this is identical to the original LabelValue object.
  c                 C   s   || _ || _d S r   )needs_update_labels)selfry   r   r   r   r   __init__o  s   
zUpdateResult.__init__c                 C   s   | j std| jS )zUReturns the new labels.

    Raises:
      ValueError: if needs_update is False.
    zTIf no update is needed (self.needs_update == False), checking labels is unnecessary.)ry   rq   rz   r{   r   r   r   r   s  s
   zUpdateResult.labelsc                 C   s   z| j W S  ty   Y dS w )a  Returns the new labels if an update is needed or None otherwise.

    NOTE: If this function returns None, make sure not to include the labels
    field in the field mask of the update command. Otherwise, it's possible to
    inadvertently clear the labels on the resource.
    N)r   rq   r}   r   r   r   	GetOrNone  s
   zUpdateResult.GetOrNoneN)__name__
__module____qualname____doc__r|   propertyr   r~   r   r   r   r   rx   e  s    	
rx   c                   @   sR   e Zd ZdZdddZdd Zdd	 Zd
d ZdddZdd Z	e
dddZdS )rW   z%A change to the labels on a resource.NFc                 C   s6   |pi | _ |pg | _|| _| jr| jrtddS dS )ae  Initialize a Diff.

    Only one of [subtractions, clear] may be specified.

    Args:
      additions: {str: str}, any label values to be updated
      subtractions: List[str], any labels to be removed
      clear: bool, whether to clear the labels

    Returns:
      Diff.

    Raises:
      ValueError: if both subtractions and clear are specified.
    z3Only one of [subtractions, clear] may be specified.N)
_additions_subtractions_clearrq   )r{   	additionssubtractionsclearr   r   r   r|     s   

zDiff.__init__c                 C   s&   ~|  }| jD ]}||d q|S zRemove labels.N)copyr   popr{   existing_labels
new_labelsr   r   r   r   _RemoveLabels  s
   
zDiff._RemoveLabelsc                 C   s   ~i S r   r   r{   r   r   r   r   _ClearLabels  s   zDiff._ClearLabelsc                 C   s   |  }|| j |S r   )r   updater   )r{   r   r   r   r   
_AddLabels  s   zDiff._AddLabelsc                 C   sZ   t |}| }| jr| |}| jr| |}| jr!| ||}||k}t|t	||S )a  Apply this Diff to the (possibly non-existing) labels.

    First, makes any additions. Then, removes any labels.

    Args:
      labels_cls: type, the LabelsValue class for the resource.
      labels: LabelsValue, the existing LabelsValue object for the original
        resource (or None, which is treated the same as empty labels)

    Returns:
      labels_cls, the instantiated LabelsValue message with the new set up
        labels, or None if there are no changes.
    )
rl   r   r   r   r   r   r   r   rx   ri   )r{   rb   r   r   r   ry   r   r   r   Apply  s   

z
Diff.Applyc                 C   s   t | j| j| jgS )z'Returns true if this Diff is non-empty.)anyr   r   r   r}   r   r   r   rY     s   zDiff.MayHaveUpdatesTc                 C   s    |r|j }nd}| |j|j|S )zBInitializes a Diff based on the arguments in AddUpdateLabelsFlags.N)clear_labelsrP   rT   )clsrR   rL   r   r   r   r   rX     s   zDiff.FromUpdateArgs)NNFr   )T)r   r   r   r   r|   r   r   r   r   rY   classmethodrX   r   r   r   r   rW     s    

rW   c                 C   s(   t | }| r| nd}|||S )a  Returns the result of applying the diff constructed from args.

  Lazily fetches the original labels value if needed.

  Args:
    args: argparse.Namespace, the parsed arguments with update_labels,
      remove_labels, and clear_labels
    labels_cls: type, the LabelsValue class for the new labels.
    orig_labels_thunk: callable, a thunk which will return the original labels
      object (of type LabelsValue) when evaluated.

  Returns:
    UpdateResult: the result of applying the diff.

  N)rW   rX   rY   r   )rR   rb   orig_labels_thunkr]   orig_labelsr   r   r   ProcessUpdateArgsLazy  s   
r   c                 C   s    t | |}|du rdS t||S )z5Initializes labels based on args and the given class.N)getattrri   )rR   rb   labels_destr   r   r   r   ParseCreateArgs  s   

r   c                   @   s    e Zd ZdZdd Zdd ZdS )ExplicitNullificationDiffzA change to labels for resources where API requires explicit nullification.

  That is, to clear a label {'foo': 'bar'}, you must pass {'foo': None} to the
  API.
  c                 C   s8   |  }| jD ]}||v rd||< q||v r||= q|S r   )r   r   r   r   r   r   r     s   

z'ExplicitNullificationDiff._RemoveLabelsc                 C   s   dd |D S )Nc                 S   s   i | ]}|d qS r   r   )r   r   r   r   r   rk     s    z:ExplicitNullificationDiff._ClearLabels.<locals>.<dictcomp>r   r   r   r   r   r     s   z&ExplicitNullificationDiff._ClearLabelsN)r   r   r   r   r   r   r   r   r   r   r      s    
r   )r   r   TTr@   )r   TT)r   r   T)Nrm   TT)(r   
__future__r   r   r   googlecloudsdk.callioper   r   r   rZ   rg   r   r   r   r   rn   r0   ro   r1   CustomFunctionValidatorr-   r.   r>   rA   rB   rE   rH   rN   rS   rU   r^   ri   rl   rw   objectrx   rW   r   r   r   r   r   r   r   <module>   sd   "






2(V
