HEX
Server: Apache/2.4.65 (Ubuntu)
System: Linux ielts-store-v2 6.8.0-1036-gcp #38~22.04.1-Ubuntu SMP Thu Aug 14 01:19:18 UTC 2025 x86_64
User: root (0)
PHP: 7.2.34-54+ubuntu20.04.1+deb.sury.org+1
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
Upload Files
File: //snap/google-cloud-cli/394/lib/googlecloudsdk/calliope/concepts/deps_map_util.py
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Utilities for generating and updating fallthrough maps."""

import copy

from googlecloudsdk.calliope.concepts import deps as deps_lib


def AddFlagFallthroughs(
    base_fallthroughs_map, attributes, attribute_to_args_map):
  """Adds flag fallthroughs to fallthrough map.

  Iterates through each attribute and prepends a flag fallthrough.
  This allows resource attributes to be resolved to flag first. For example:

    {'book': [deps.ValueFallthrough('foo')]}

  will update to something like...

    {
        'book': [
            deps.ArgFallthrough('--foo'),
            deps.ValueFallthrough('foo')
        ]
    }

  Args:
    base_fallthroughs_map: {str: [deps._FallthroughBase]}, A map of attribute
      names to fallthroughs
    attributes: list[concepts.Attribute], list of attributes associated
      with the resource
    attribute_to_args_map: {str: str}, A map of attribute names to the names
      of their associated flags.
  """
  for attribute in attributes:
    current_fallthroughs = base_fallthroughs_map.get(attribute.name, [])

    if arg_name := attribute_to_args_map.get(attribute.name):
      arg_fallthrough = deps_lib.ArgFallthrough(arg_name)
    else:
      arg_fallthrough = None

    if arg_fallthrough:
      filtered_fallthroughs = [
          f for f in current_fallthroughs if f != arg_fallthrough]
      fallthroughs = [arg_fallthrough] + filtered_fallthroughs
    else:
      fallthroughs = current_fallthroughs
    base_fallthroughs_map[attribute.name] = fallthroughs


def AddAnchorFallthroughs(
    base_fallthroughs_map, attributes, anchor, collection_info,
    anchor_fallthroughs):
  """Adds fully specified fallthroughs to fallthrough map.

  Iterates through each attribute and prepends a fully specified fallthrough.
  This allows resource attributes to resolve to the fully specified anchor
  value first. For example:

    {'book': [deps.ValueFallthrough('foo')]}

  will udpate to something like...

    {
        'book': [
            deps.FullySpecifiedAnchorFallthrough(anchor_fallthroughs),
            deps.ValueFallthrough('foo')
        ]
    }

  Args:
    base_fallthroughs_map: {str: [deps._FallthroughBase]}, A map of attribute
      names to fallthroughs
    attributes: list[concepts.Attribute], list of attributes associated
      with the resource
    anchor: concepts.Attribute, attribute that the other attributes should
      resolve to if fully specified
    collection_info: the info of the collection to parse the anchor as
    anchor_fallthroughs: list[deps._FallthroughBase], fallthroughs used to
      resolve the anchor value
  """
  for attribute in attributes:
    current_fallthroughs = base_fallthroughs_map.get(attribute.name, [])
    anchor_based_fallthrough = deps_lib.FullySpecifiedAnchorFallthrough(
        anchor_fallthroughs, collection_info, attribute.param_name)

    if attribute != anchor:
      filtered_fallthroughs = [
          f for f in current_fallthroughs if f != anchor_based_fallthrough]
      fallthroughs = [anchor_based_fallthrough] + filtered_fallthroughs
    else:
      fallthroughs = current_fallthroughs
    base_fallthroughs_map[attribute.name] = fallthroughs


def UpdateWithValueFallthrough(
    base_fallthroughs_map, attribute_name, parsed_args):
  """Shortens fallthrough list to a single deps.ValueFallthrough.

  Used to replace the attribute_name entry in a fallthrough map to a
  single ValueFallthrough. For example:

    {'book': [deps.Fallthrough(lambda: 'foo')]}

  will update to something like...

    {'book': [deps.ValueFallthrough('foo')]}

  Args:
    base_fallthroughs_map: {str: [deps._FallthroughBase]}, A map of attribute
      names to fallthroughs we are updating
    attribute_name: str, entry in fallthrough map we are updating
    parsed_args: Namespace | None, used to derive the value for ValueFallthrough
  """
  if not parsed_args:
    return

  attribute_value, attribute_fallthrough = _GetFallthroughAndValue(
      attribute_name, base_fallthroughs_map, parsed_args)

  if attribute_fallthrough:
    _UpdateMapWithValueFallthrough(
        base_fallthroughs_map, attribute_value, attribute_name,
        attribute_fallthrough)


def CreateValueFallthroughMapList(
    base_fallthroughs_map, attribute_name, parsed_args):
  """Generates a list of fallthrough maps for each anchor value in a list.

  For each anchor value, generate a fallthrough map. For example, if user
  provides anchor values ['foo', 'bar'] and a base fallthrough like...

    {'book': [deps.ArgFallthrough('--book')]}

  will generate something like...

    [
        {'book': [deps.ValueFallthrough('foo')]},
        {'book': [deps.ValueFallthrough('bar')]}
    ]

  Args:
    base_fallthroughs_map: {str: [deps._FallthroughBase]}, A map of attribute
      names to fallthroughs we are updating
    attribute_name: str, entry in fallthrough map we are updating
    parsed_args: Namespace | None, used to derive the value for ValueFallthrough

  Returns:
    list[{str: deps._FallthroughBase}], a list of fallthrough maps for
    each parsed anchor value
  """
  attribute_values, attribute_fallthrough = _GetFallthroughAndValue(
      attribute_name, base_fallthroughs_map, parsed_args)

  map_list = []
  if not attribute_fallthrough:
    return map_list

  for value in attribute_values:
    new_map = {**base_fallthroughs_map}
    _UpdateMapWithValueFallthrough(
        new_map, value, attribute_name, attribute_fallthrough)
    map_list.append(new_map)
  return map_list


def PluralizeFallthroughs(base_fallthroughs_map, attribute_name):
  """Updates fallthrough map entry to make fallthroughs plural.

  For example:

    {'book': [deps.ArgFallthrough('--foo')]}

  will update to something like...

    {'book': [deps.ArgFallthrough('--foo'), plural=True]}

  Args:
    base_fallthroughs_map: {str: [deps.Fallthrough]}, A map of attribute
      names to fallthroughs we are updating
    attribute_name: str, entry in fallthrough map we are updating
  """
  given_fallthroughs = base_fallthroughs_map.get(attribute_name, [])

  base_fallthroughs_map[attribute_name] = [
      _PluralizeFallthrough(fallthrough)
      for fallthrough in given_fallthroughs
  ]


def _PluralizeFallthrough(fallthrough):
  plural_fallthrough = copy.deepcopy(fallthrough)
  plural_fallthrough.plural = True
  return plural_fallthrough


def _UpdateMapWithValueFallthrough(
    base_fallthroughs_map, value, attribute_name, attribute_fallthrough):
  value_fallthrough = deps_lib.ValueFallthrough(
      value,
      attribute_fallthrough.hint,
      active=attribute_fallthrough.active)
  base_fallthroughs_map[attribute_name] = [value_fallthrough]


def _GetFallthroughAndValue(attribute_name, fallthroughs_map, parsed_args):
  """Derives value and fallthrough used to derives value from map."""
  for possible_fallthrough in fallthroughs_map.get(attribute_name, []):
    try:
      value = possible_fallthrough.GetValue(parsed_args)
      return (value, possible_fallthrough)
    except deps_lib.FallthroughNotFoundError:
      continue
  else:
    return (None, None)


def ValidateFallthroughMap(fallthroughs_map):
  """Validates fallthrough map to ensure fallthrough map is not invalid.

  Fallthrough maps are only invalid if an inactive fallthrough comes before
  an active fallthrough. It could result in an active fallthrough that can
  never be reached.

  Args:
    fallthroughs_map: {str: [deps._FallthroughBase]}, A map of attribute
      names to fallthroughs we are validating

  Returns:
    (bool, str), bool for whether fallthrough map is valid and str for
      the error message
  """

  for attr, fallthroughs in fallthroughs_map.items():
    inactive_fallthrough = None
    for fallthrough in fallthroughs:
      if inactive_fallthrough and fallthrough.active:
        active_str = fallthrough.__class__.__name__
        inactive_str = inactive_fallthrough.__class__.__name__
        msg = (f'Invalid Fallthrough Map: Fallthrough map at [{attr}] contains '
               f'inactive fallthrough [{inactive_str}] before active '
               f'fallthrough [{active_str}]. Fix the order so that active '
               f'fallthrough [{active_str}] is reachable or remove active '
               f'fallthrough [{active_str}].')
        return False, msg

      if not fallthrough.active:
        inactive_fallthrough = fallthrough
    else:
      return True, None