class_docstr: A Jina YAML parser supports loading and dumping and substituting variables.
  
  To use it:
  
  .. highlight:: python
  .. code-block:: python
  
      from jina.jaml import JAML
  
      JAML.load(...)
      JAML.dump(...)
  
  
      class DummyClass:
          pass
  
  
      JAML.register(DummyClass)
  
  You can use expressions to programmatically set variables in YAML files and access contexts.
  An expression can be any combination of literal values, references to a context, or functions.
  You can combine literals, context references, and functions using operators.
  
  You need to use specific syntax to tell Jina to evaluate an expression rather than treat it as a string,
  which is based on GitHub actions syntax, and looks like this:
  
  .. highlight:: yaml
  .. code-block:: yaml
  
      ${{ <expression> }}
  
  This expression can be evaluated directly (i.e. substituted by the real value) when being loaded,
  by using :meth:`load(substitute=True)`
  
  JAML supports three different kinds of variables to be used as expressions: `Environment variables`
  (coming form the environment itself), `context variables` (being passed as a dict),
  and `internal references` (included in the .yaml file itself).
  
  An environment variable `var` is accessed through the following syntax:
  
  .. highlight:: yaml
  .. code-block:: yaml
  
      ${{ env.var }}
  
  Note the mandatory spaces before and after the variable denotation.
  
  Context variables can be accessed using the following syntax:
  
  .. highlight:: yaml
  .. code-block:: yaml
  
      ${{ context_var }}
  
  Or, if you want to be explicit:
  
  .. highlight:: yaml
  .. code-block:: yaml
  
      ${{ context.context_var }}
  
  These context variables are passed as a dict:
  
  .. highlight:: python
  .. code-block:: python
  
      obj = JAML.load(
          fp, substitute=True, context={'context_var': 3.14, 'context_var2': 'hello-world'}
      )
  
  Internal references point to other variables in the yaml file itself, and can be accessed using the following syntax:
  
  .. highlight:: yaml
  .. code-block:: yaml
  
      ${{root.path.to.var}}
  
  Note omission of spaces in this syntax.
  
  
  .. note::
      :class:`BaseFlow`, :class:`BaseExecutor`, :class:`BaseGateway`
      and all their subclasses have already implemented JAML interfaces,
      to load YAML config into objects, please use :meth:`Flow.load_config`,
      :meth:`BaseExecutor.load_config`, etc.
docstr: Expand variables from YAML file.
  
  :param d: yaml file loaded as python dict
  :param context: context replacement variables in a dict, the value of the dict is the replacement.
  :param resolve_cycle_ref: resolve internal reference if True.
  :param resolve_passes: number of rounds to resolve internal reference.
  :return: expanded dict.
docstr_tok: ["Expand","variables","from","YAML","file",".",":","param","d",":","yaml","file","loaded","as","python","dict",":","param","context",":","context","replacement","variables","in","a","dict",",","the","value","of","the","dict","is","the","replacement",".",":","param","resolve_cycle_ref",":","resolve","internal","reference","if","True",".",":","param","resolve_passes",":","number","of","rounds","to","resolve","internal","reference",".",":","return",":","expanded","dict","."]
code: def expand_dict(
      d: Dict,
      context: Optional[Union[Dict, SimpleNamespace]] = None,
      resolve_cycle_ref=True,
      resolve_passes: int = 3,
  ) -> Dict[str, Any]:
      """
      Expand variables from YAML file.
  
      :param d: yaml file loaded as python dict
      :param context: context replacement variables in a dict, the value of the dict is the replacement.
      :param resolve_cycle_ref: resolve internal reference if True.
      :param resolve_passes: number of rounds to resolve internal reference.
      :return: expanded dict.
      """
  
      from jina.helper import parse_arg
  
      expand_map = SimpleNamespace()
      env_map = SimpleNamespace()
  
      def _scan(sub_d, p):
          if isinstance(sub_d, dict):
              for k, v in sub_d.items():
                  if isinstance(v, dict):
                      p.__dict__[k] = SimpleNamespace()
                      _scan(v, p.__dict__[k])
                  elif isinstance(v, list):
                      p.__dict__[k] = list()
                      _scan(v, p.__dict__[k])
                  else:
                      p.__dict__[k] = v
          elif isinstance(sub_d, list):
              for idx, v in enumerate(sub_d):
                  if isinstance(v, dict):
                      p.append(SimpleNamespace())
                      _scan(v, p[idx])
                  elif isinstance(v, list):
                      p.append(list())
                      _scan(v, p[idx])
                  else:
                      p.append(v)
  
      def _replace(sub_d, p, resolve_ref=False):
          if isinstance(sub_d, dict):
              for k, v in sub_d.items():
                  if isinstance(v, (dict, list)):
                      _replace(v, p.__dict__[k], resolve_ref)
                  else:
                      if isinstance(v, str):
                          if resolve_ref and yaml_ref_regex.findall(v):
                              sub_d[k] = _resolve_yaml_reference(v, p)
                          else:
                              sub_d[k] = _sub(v)
          elif isinstance(sub_d, list):
              for idx, v in enumerate(sub_d):
                  if isinstance(v, (dict, list)):
                      _replace(v, p[idx], resolve_ref)
                  else:
                      if isinstance(v, str):
                          if resolve_ref and yaml_ref_regex.findall(v):
                              sub_d[idx] = _resolve_yaml_reference(v, p)
                          else:
                              sub_d[idx] = _sub(v)
  
      def _var_to_substitutable(v, exp=context_var_regex):
          def repl_fn(matchobj):
              return "$$" + matchobj.group(0)[4:-3]
  
          return re.sub(exp, repl_fn, v)
  
      def _to_env_var_synatx(v):
          v = _var_to_substitutable(v, new_env_var_regex)
  
          def repl_fn(matchobj):
              match_str = matchobj.group(0)
              match_str = match_str.replace("ENV.", "")
              match_str = match_str.replace("env.", "")
              return match_str[1:]
  
          return re.sub(r"\$\$[a-zA-Z0-9_.]*", repl_fn, v)
  
      def _to_normal_context_var(v):
          def repl_fn(matchobj):
              match_str = matchobj.group(0)
              match_str = match_str.replace("CONTEXT.", "")
              match_str = match_str.replace("context.", "")
              return match_str
  
          return re.sub(context_dot_regex, repl_fn, v)
  
      def _sub(v):
          # substitute template with actual value either from context or env variable
          # v could contain template of the form
          #
          # 1)    ${{ var }},${{ context.var }},${{ CONTEXT.var }} when need to be parsed with the context dict
          # or
          # 2 )   ${{ ENV.var }},${{ env.var }},$var ( deprecated) when need to be parsed with env
          #
          #
          # internally env var (1) and context var (2) are treated differently, both of them are cast to a unique and
          # normalize template format and then are parsed
          # 1) context variables placeholder are cast to $$var then we use the ContextVarTemplate to parse the context
          # variables
          # 2) env variables placeholder are cast to $var then we leverage the os.path.expandvars to replace by
          # environment variables.
  
          if env_var_deprecated_regex.findall(
              v
          ) and not env_var_regex.findall(
              v
          ):  # catch expressions of form '$var'
              warnings.warn(
                  "Specifying environment variables via the syntax `$var` is deprecated."
                  "Use `${{ ENV.var }}` instead.",
                  category=DeprecationWarning,
              )
          if new_env_var_regex.findall(
              v
          ):  # handle expressions of form '${{ ENV.var}}',
              v = _to_env_var_synatx(v)
          if context_dot_regex.findall(v):
              v = _to_normal_context_var(v)
          if context_var_regex.findall(
              v
          ):  # handle expressions of form '${{ var }}'
              v = _var_to_substitutable(v)
              if context:
                  v = ContextVarTemplate(v).safe_substitute(
                      context
                  )  # use vars provided in context
          v = os.path.expandvars(
              v
          )  # gets env var and parses to python objects if neededd
          return parse_arg(v)
  
      def _resolve_yaml_reference(v, p):
          org_v = v
          # internal references are of the form ${{path}} where path is a yaml path like root.executors[0].name
  
          def repl_fn(matchobj):
              match_str = matchobj.group(0)
              match_str_origin = match_str
  
              match_str = re.sub(
                  yaml_ref_regex, "{\\1}", match_str
              )  # from ${{var}} to {var} to leverage python formatter
  
              try:
                  # "root" context is now the global namespace
                  # "this" context is now the current node namespace
                  match_str = match_str.format(
                      root=expand_map, this=p, ENV=env_map
                  )
              except AttributeError as ex:
                  raise AttributeError(
                      "variable replacement is failed, please check your YAML file."
                  ) from ex
              except KeyError:
                  return match_str_origin
  
              return match_str
  
          v = re.sub(yaml_ref_regex, repl_fn, v)
  
          return parse_arg(v)
  
      _scan(d, expand_map)
      _scan(dict(os.environ), env_map)
  
      # first do var replacement
      _replace(d, expand_map)
  
      # do `resolve_passes` rounds of scan-replace to resolve internal references
      for _ in range(resolve_passes):
          # rebuild expand_map
          expand_map = SimpleNamespace()
          _scan(d, expand_map)
  
          # resolve internal reference
          if resolve_cycle_ref:
              _replace(d, expand_map, resolve_ref=True)
  
      return d
  
imports: import copy
  import os
  import re
  import string
  import tempfile
  import warnings
  from types import SimpleNamespace
  from typing import Any, Dict, List, Optional, Set, TextIO, Tuple, Union
  import yaml
  from yaml.constructor import FullConstructor
  from jina.jaml.helper import JinaLoader, JinaResolver, get_jina_loader_with_runtime, load_py_modules, parse_config_source
  from jina.excepts import BadConfigSource
label_desc: Use image node_id 8 for calling the JAML obj's underlying member method code with example usage: obj.expand_dict(d, context, resolve_cycle_ref, resolve_passes) and returns: d, re, re, re, parse_arg, parse_arg, unknown, match_str, match_str, match_str, match_str_origin