code: def construct_arg(
      class_name: str,
      argument_name: str,
      popped_params: Params,
      annotation: Type,
      default: Any,
      **extras,
  ) -> Any:
      """
      The first two parameters here are only used for logging if we encounter an error.
      """
      origin = getattr(annotation, "__origin__", None)
      args = getattr(annotation, "__args__", [])
  
      # The parameter is optional if its default value is not the "no default" sentinel.
      optional = default != _NO_DEFAULT
  
      if hasattr(annotation, "from_params"):
          if popped_params is default:
              return default
          elif popped_params is not None:
              # Our params have an entry for this, so we use that.
  
              subextras = create_extras(annotation, extras)
  
              # In some cases we allow a string instead of a param dict, so
              # we need to handle that case separately.
              if isinstance(popped_params, str):
                  popped_params = Params({"type": popped_params})
              elif isinstance(popped_params, dict):
                  popped_params = Params(popped_params)
              result = annotation.from_params(
                  params=popped_params, **subextras
              )
  
              return result
          elif not optional:
              # Not optional and not supplied, that's an error!
              raise ConfigurationError(
                  f"expected key {argument_name} for {class_name}"
              )
          else:
              return default
  
      # If the parameter type is a Python primitive, just pop it off
      # using the correct casting pop_xyz operation.
      elif annotation in {int, bool}:
          if type(popped_params) in {int, bool}:
              return annotation(popped_params)
          else:
              raise TypeError(
                  f"Expected {argument_name} to be a {annotation.__name__}."
              )
      elif annotation == str:
          # Strings are special because we allow casting from Path to str.
          if type(popped_params) == str or isinstance(
              popped_params, Path
          ):
              return str(popped_params)  # type: ignore
          else:
              raise TypeError(
                  f"Expected {argument_name} to be a string."
              )
      elif annotation == float:
          # Floats are special because in Python, you can put an int wherever you can put a float.
          # https://mypy.readthedocs.io/en/stable/duck_type_compatibility.html
          if type(popped_params) in {int, float}:
              return popped_params
          else:
              raise TypeError(
                  f"Expected {argument_name} to be numeric."
              )
  
      # This is special logic for handling types like Dict[str, TokenIndexer],
      # List[TokenIndexer], Tuple[TokenIndexer, Tokenizer], and Set[TokenIndexer],
      # which it creates by instantiating each value from_params and returning the resulting structure.
      elif (
          origin in {collections.abc.Mapping, Mapping, Dict, dict}
          and len(args) == 2
          and can_construct_from_params(args[-1])
      ):
          value_cls = annotation.__args__[-1]
          value_dict = {}
          if not isinstance(popped_params, Mapping):
              raise TypeError(
                  f"Expected {argument_name} to be a Mapping (probably a dict or a Params object)."
              )
  
          for key, value_params in popped_params.items():
              value_dict[key] = construct_arg(
                  str(value_cls),
                  argument_name + "." + key,
                  value_params,
                  value_cls,
                  _NO_DEFAULT,
                  **extras,
              )
  
          return value_dict
  
      elif origin in (Tuple, tuple) and all(
          can_construct_from_params(arg) for arg in args
      ):
          value_list = []
  
          for i, (value_cls, value_params) in enumerate(
              zip(annotation.__args__, popped_params)
          ):
              value = construct_arg(
                  str(value_cls),
                  argument_name + f".{i}",
                  value_params,
                  value_cls,
                  _NO_DEFAULT,
                  **extras,
              )
              value_list.append(value)
  
          return tuple(value_list)
  
      elif (
          origin in (Set, set)
          and len(args) == 1
          and can_construct_from_params(args[0])
      ):
          value_cls = annotation.__args__[0]
  
          value_set = set()
  
          for i, value_params in enumerate(popped_params):
              value = construct_arg(
                  str(value_cls),
                  argument_name + f".{i}",
                  value_params,
                  value_cls,
                  _NO_DEFAULT,
                  **extras,
              )
              value_set.add(value)
  
          return value_set
  
      elif origin == Union:
          # Storing this so we can recover it later if we need to.
          backup_params = deepcopy(popped_params)
  
          # We'll try each of the given types in the union sequentially, returning the first one that
          # succeeds.
          error_chain: Optional[Exception] = None
          for arg_annotation in args:
              try:
                  return construct_arg(
                      str(arg_annotation),
                      argument_name,
                      popped_params,
                      arg_annotation,
                      default,
                      **extras,
                  )
              except (
                  ValueError,
                  TypeError,
                  ConfigurationError,
                  AttributeError,
              ) as e:
                  # Our attempt to construct the argument may have modified popped_params, so we
                  # restore it here.
                  popped_params = deepcopy(backup_params)
                  e.args = (
                      f"While constructing an argument of type {arg_annotation}",
                  ) + e.args
                  e.__cause__ = error_chain
                  error_chain = e
  
          # If none of them succeeded, we crash.
          config_error = ConfigurationError(
              f"Failed to construct argument {argument_name} with type {annotation}."
          )
          config_error.__cause__ = error_chain
          raise config_error
      elif origin == Lazy:
          if popped_params is default:
              return default
  
          value_cls = args[0]
          subextras = create_extras(value_cls, extras)
          return Lazy(value_cls, params=deepcopy(popped_params), constructor_extras=subextras)  # type: ignore
  
      # For any other kind of iterable, we will just assume that a list is good enough, and treat
      # it the same as List. This condition needs to be at the end, so we don't catch other kinds
      # of Iterables with this branch.
      elif (
          origin in {collections.abc.Iterable, Iterable, List, list}
          and len(args) == 1
          and can_construct_from_params(args[0])
      ):
          value_cls = annotation.__args__[0]
  
          value_list = []
  
          for i, value_params in enumerate(popped_params):
              value = construct_arg(
                  str(value_cls),
                  argument_name + f".{i}",
                  value_params,
                  value_cls,
                  _NO_DEFAULT,
                  **extras,
              )
              value_list.append(value)
  
          return value_list
  
      else:
          # Pass it on as is and hope for the best.   ¯\_(ツ)_/¯
          if isinstance(popped_params, Params):
              return popped_params.as_dict()
          return popped_params
  
imports: import collections.abc
  from copy import deepcopy
  from pathlib import Path
  from typing import Any, Callable, cast, Dict, Iterable, List, Mapping, Set, Tuple, Type, TypeVar, Union, Optional
  import inspect
  import logging
  from allennlp.common.checks import ConfigurationError
  from allennlp.common.lazy import Lazy
  from allennlp.common.params import Params
label_desc: Use image node_id 11 for calling a global function with example usage: construct_arg(class_name, argument_name, popped_params, annotation, default) and returns: default, result, annotation, default, str, popped_params, value_dict, tuple, value_set, Lazy, construct_arg, default, value_list, popped_params, popped_params