o
    lQ                     @   s   d Z ddlmZ ddlmZ ddlmZ ddl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Zdd
lmZ dd ZG dd deZ		dddZG dd deZdS )a  A class for projecting and transforming JSON-serializable objects.

From the Cloud SDK doc "DD: gcloud resource projection algorithm":

  Algorithm

  The algorithm represents a resource R and projection P as trees. P is used
  to color the nodes of R (with the colors {0, 1, 2, 3}) as follows:

  1. Initialize the nodes in R to (id, 0, identity).
  2. Do a DFS on P. Let p be the projection subtree and r be the resource
     subtree at each level. Let f be a flag value at each level, and initialize
     f to the flag value of the root node of P.
     2.1. For each id i in p that is also in r, set r[i].flag |= p[i].flag | f,
          and r[i].transform = p[i].transform if  r[i].transform != identity and
          p[i].transform != identity.
     2.2. If p contains a slice then repeat step 2.1 with i = slice.
     2.3. If r[i].flag is 0 then prune the search at this node, otherwise
     2.4. descend to the next level with r = r[i], p = p[i], and f = r[i].flag.
  3. At the end of the search the nodes of R will be colored with the values
     {0, 1, 2, 3}. The projected keys are the set of the longest paths from the
     root of R ending with a flag value >= 2.

  Remarks

  If the initial value of f is PROJECT or PROJECT* (2 or 3) then all keys in R
  are projected. Non-leaf keys may be projected in this model, resulting in dict
  or list values instead of scalars.

Example usage:

  projector = resource_projector.Compile(expression)
  for resource in resources:
    obj = projector.Evaluate(resource)
    OperateOnProjectedResource(obj)
    )absolute_import)division)unicode_literalsN)messages)encoding)resource_projection_parser)resource_property)rangec                 C   s   t  | S )zReturns resource or a JSON-serializable copy of resource.

  Args:
    resource: The resource object.

  Returns:
    The original resource if it is a primitive type object, otherwise a
    JSON-serializable copy of resource.
  )CompileEvaluate)resource r   L/tmp/google-cloud-sdk/lib/googlecloudsdk/core/resource/resource_projector.pyMakeSerializableG   s   
r   c                   @   sx   e Zd ZdZ		dddZdd Zdd Zd	d
 Z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S )	Projectora
  Projects a resource using a ProjectionSpec.

  A projector is a method that takes an object and a projection as input and
  produces a new JSON-serializable object containing only the values
  corresponding to the keys in the projection. Optional projection key
  attributes may transform the values in the resulting JSON-serializable object.

  Attributes:
    _projection: The projection object.
    _been_here_done_that: A set of the current object id()'s being projected.
      Used to catch recursive objects like datetime.datetime.max.
    _by_columns: True if Projector projects to a list of columns.
    _columns: self._projection.Columns() column attributes.
    _ignore_default_transforms: Ignore default projection transforms if True.
    _retain_none_values: Retain dict entries with None values.
    _transforms_enabled_attribute: The projection.Attributes()
      transforms_enabled setting.
    _transforms_enabled: Projection attribute transforms enabled if True,
      disabled if False, or set by each Evaluate().
  Fc                 C   sh   || _ || _| j  | _|| _|| _t | _| }d|v r"d| _	nd|v r*d| _	nd| _	d|v | _
dS )a0  Constructor.

    Args:
      projection: A ProjectionSpec (parsed resource projection expression).
      by_columns: Project to a list of columns if True.
      ignore_default_transforms: Ignore default projection transforms if True.
      retain_none_values: project dict entries with None values.
    
transformsTzno-transformsFNzjson-decode)_projection_by_columnsColumns_columns_ignore_default_transforms_retain_none_valuesset_been_here_done_that
Attributes_transforms_enabled_attribute_json_decode)self
projection
by_columnsignore_default_transformsretain_none_values
attributesr   r   r   __init__j   s   
zProjector.__init__c                 C   s"   | j dur| j S |jd| jjfv S )zReturns True if transform is enabled.

    Args:
      transform: The resource_projection_parser._Transform object.

    Returns:
      True if the transform is enabled, False if not.
    N)_transforms_enabledactiver   )r   	transformr   r   r   _TransformIsEnabled   s   
	zProjector._TransformIsEnabledc                 C   sP   || j jk rdS |r|jr|jjr| |jjr|jj|S | j|||ddS )a"  Applies projection.attribute.transform in projection if any to obj.

    Args:
      obj: An object.
      projection: Projection _Tree node.
      flag: A bitmask of DEFAULT, INNER, PROJECT.

    Returns:
      The transformed obj if there was a transform, otherwise the original obj.
    NTleaf)r   PROJECT	attributer&   r'   r   _Project)r   objr   flagr   r   r   _ProjectAttribute   s   zProjector._ProjectAttributec           
      C   s  i }t  }t|tjrt||d< |d nz|dd t|jD  W n	 ty0   Y nw t|D ]O}|	dr=q5||v rBq5zt
||}W n   Y q5t|drUq5|}||jv rx|j| }	||	jjO }|| jjk rnq5| ||	|||< q5| || j |||< q5|S )a-  Converts class object to a dict.

    Private and callable attributes are omitted in the dict.

    Args:
      obj: The class object to convert.
      projection: Projection _Tree node.
      flag: A bitmask of DEFAULT, INNER, PROJECT.

    Returns:
      The dict representing the class object.
    datetime)maxmin
resolutiontzinfoc                 S   s   g | ]}|  r|qS r   )isupper).0ar   r   r   
<listcomp>   s    z+Projector._ProjectClass.<locals>.<listcomp>___call__)r   
isinstancer0   six	text_typeupdatedir	__class__AttributeError
startswithgetattrhasattrtreer+   r.   r   INNERr,   r/   GetEmpty)
r   r-   r   r.   rexcludeattrvaluefchild_projectionr   r   r   _ProjectClass   s<   



zProjector._ProjectClassc           	   	   C   s   |s|S i }zt | W n
 ty   Y dS w t |D ]V\}}|}||jv rB|j| }||jjO }|| jjk r:q| |||}n
| 	|| j
 |}|dus\| js\|| jjkrs| jrsz	||t|< W q tyr   |||< Y qw q|pwdS )zProjects a dictionary object.

    Args:
      obj: A dict.
      projection: Projection _Tree node.
      flag: A bitmask of DEFAULT, INNER, PROJECT.

    Returns:
      The projected obj.
    N)r<   	iteritems
ValueErrorrE   r+   r.   r   rF   r,   r/   rG   r   r*   r   r   DecodeUnicodeError)	r   r-   r   r.   reskeyvalrL   rM   r   r   r   _ProjectDict   s4   

zProjector._ProjectDictc                    sR   du rdS  s
g S zt  }z d }W n ty"   t  Y nw W n ty?   zt  W n ty<   Y Y dS w Y nw tg }d}|jsR|| jjk rQdS n4|jD ]0}|du rn|| jjksh|j| jj	rm|j| }qUt
|tjr|tt   t  v r|| qU|| jjkr|s| j }|s|sdS d}|rdgt   }	ndgt fdd|D d  }	|rtt  n|D ]U} | }
|
du rq|}||jv r|j| }|r||jj	O }n|}|r||jj	O }|| jjkr| |
||}
nd}
|
du rq|dk r	|t  7 }||k r|}|
|	|< q|dk rdS |r'|	d|d  S |	S )zProjects a list, tuple or set object.

    Args:
      obj: A list, tuple or set.
      projection: Projection _Tree node.
      flag: A bitmask of DEFAULT, INNER, PROJECT.

    Returns:
      The projected obj.
    Nr   c                 3   s(    | ]}|d k r|t   n|V  qdS )r   N)len)r6   xr-   r   r   	<genexpr>F  s   & z)Projector._ProjectList.<locals>.<genexpr>   )rX   	TypeErrorsortedlistr   rE   r   r*   r+   r.   r;   r<   integer_typesr	   addrG   r1   rF   r,   )r   r-   r   r.   r9   indicesslicedindexmaxindexrS   rU   rL   rM   r   rZ   r   _ProjectList  s   



 





zProjector._ProjectListc           	   	   C   s|  t |}|| jv rdS |du rn!t|tjst|tjrWt|tjr(t|}| jrV|	dr5|
ds?|	drV|
drVz| jt||||dW S  tyU   Y nw nt|tttfset|tjrfnt|trstt|}nt|tjr}|j}n| j| ddlm} ddl}t|tjrt|}n0t||jrdd	lm} ||}nt||jr|j !|}nt"|d
rt"|dr| #|||}|r|j$r|j$j%r| &|j$j%r|j$j%'|}nI|| j(j)ks|r*|j*r*t"|d
r*t"|drz	| +|||}W n& t,t-fy   d}Y nw z	| .|||}W n t,t-fy)   d}Y nw | j/| |S |r7|S | 0|||S )a  Evaluate() helper function.

    This function takes a resource obj and a preprocessed projection. obj
    is a dense subtree of the resource schema (some keys values may be missing)
    and projection is a sparse, possibly improper, subtree of the resource
    schema. Improper in that it may contain paths that do not exist in the
    resource schema or object. _Project() traverses both trees simultaneously,
    guided by the projection tree. When a projection tree path reaches an
    non-existent obj tree path the projection tree traversal is pruned. When a
    projection tree path terminates with an existing obj tree path, that obj
    tree value is projected and the obj tree traversal is pruned.

    Since resources can be sparse a projection can reference values not present
    in a particular resource. Because of this the code is lenient on out of
    bound conditions that would normally be errors.

    Args:
      obj: An object.
      projection: Projection _Tree node.
      flag: A bitmask of DEFAULT, INNER, PROJECT.
      leaf: Do not call _ProjectAttribute() if True.

    Returns:
      An object containing only the key:values selected by projection, or obj if
      the projection is None or empty.
    Nz{"}[]r(   r   )message)json_format__iter___fieldsitems)1idr   r;   r<   r=   binary_typer   rQ   r   rB   endswithr,   jsonloadsrP   boolfloatcomplexr`   	bytearraybytesprotorpc_messageEnumnamera   cloudsdk.google.protobufrj   protoMessageprotorpc_encodingMessageToDictrk   r@   to_dictrD   rN   r+   r&   r'   r   r   r*   rE   rV   IOErrorr]   rf   discardr/   )	r   r-   r   r.   r)   objidprotobuf_messager}   protobuf_encodingr   r   r   r,   u  s   




zProjector._Projectc                 C   
   || _ dS )z{Sets the projection to list-of-columns mode.

    Args:
      enable: Enables projection to a list-of-columns if True.
    N)r   r   enabler   r   r   SetByColumns     
zProjector.SetByColumnsc                 C   r   )zuSets the ignore default transforms mode.

    Args:
      enable: Disable default projection transforms if True.
    N)r   r   r   r   r   SetIgnoreDefaultTransforms  r   z$Projector.SetIgnoreDefaultTransformsc                 C   r   )zSets the projection to retain-none-values mode.

    Args:
      enable: Enables projection to a retain-none-values if True.
    N)r   r   r   r   r   SetRetainNoneValues  r   zProjector.SetRetainNoneValuesc                 C   s   | j | _| jr
| js,| jrd| _| jj}n| jj}t|dr"|	 }| 
|| j |S | 
|| j | jj}| j du rB| j | _g }| jD ]&}|jrSt||jn|}|jjrh| |jjrh|jj||}|| qG|S )a  Serializes/projects/transforms obj.

    A default or empty projection expression simply converts a resource object
    to a JSON-serializable copy of the object.

    Args:
      obj: An object.

    Returns:
      A JSON-serializeable object containing only the key values selected by
        the projection. The return value is a deep copy of the object: changes
        to the input object do not affect the JSON-serializable copy.
    Fr   N)r   r$   r   r   r   r   DEFAULTr*   rD   r   r,   TreerG   r   rT   r   Getr+   r&   r'   r   append)r   r-   r.   obj_serializedcolumnscolumnrU   r   r   r   r     s4   




zProjector.Evaluatec                 C   s   | j S )zzReturns the ProjectionSpec object for the projector.

    Returns:
      The ProjectionSpec object for the projector.
    )r   r   r   r   r   
Projection  s   zProjector.ProjectionN)FFF)F)__name__
__module____qualname____doc__r#   r'   r/   rN   rV   rf   r,   r   r   r   r   r   r   r   r   r   r   T   s     
9(
oa,r    Fc                 C   s    t j| ||td}t|||dS )a  Compiles a resource projection expression.

  Args:
    expression: The resource projection string.
    defaults: resource_projection_spec.ProjectionSpec defaults.
    symbols: Transform function symbol table dict indexed by function name.
    by_columns: Project to a list of columns if True.
    retain_none_values: Retain dict entries with None values.

  Returns:
    A Projector containing the compiled expression ready for Evaluate().
  )defaultssymbolscompiler)r   r!   )r   Parser
   r   )
expressionr   r   r   r!   r   r   r   r   r
   #  s   r
   c                       s(   e Zd ZdZ fddZdd Z  ZS )IdentityProjectorz>A no-op resource projector that preserves the original object.c                    s   t t| t  d S N)superr   r#   r   r   r   r@   r   r   r#   :  s   zIdentityProjector.__init__c                 C   s   |S r   r   )r   r-   r   r   r   r   =  s   zIdentityProjector.Evaluate)r   r   r   r   r#   r   __classcell__r   r   r   r   r   7  s    r   )r   NNFF)r   
__future__r   r   r   r0   rr   apitools.base.protorpcliter   ry   apitools.base.pyr   r   googlecloudsdk.core.resourcer   r   googlecloudsdk.core.utilr<   	six.movesr	   r   objectr   r
   r   r   r   r   r   <module>   s,   %   R
