code: def _get_submodules(
      ds,
      paths,
      fulfilled,
      recursive,
      recursion_limit,
      contains,
      bottomup,
      set_property,
      delete_property,
      refds_path,
  ):
      lookup_cache = {}
      # it should be OK to skip the extra check, because _parse_git_submodules()
      # we specifically look for .gitmodules and the rest of the function
      # is on its results
      # if not GitRepo.is_valid_repo(dspath):
      #    return
      # put in giant for-loop to be able to yield results before completion
      for sm in _parse_git_submodules(ds, paths, lookup_cache):
          repo = lookup_cache["repo"]
          sm_path = sm["path"]
          contains_hits = None
          if contains:
              contains_hits = [c[0] for c in contains if sm_path in c]
              if not contains_hits:
                  # we are not looking for this subds, because it doesn't
                  # match the target path
                  continue
          # the following used to be done by _parse_git_submodules()
          # but is expensive and does not need to be done for submodules
          # not matching `contains`
          if not sm_path.exists() or not GitRepo.is_valid_repo(sm_path):
              sm["state"] = "absent"
          else:
              assert "state" not in sm
              sm["state"] = "present"
          # do we just need this to recurse into subdatasets, or is this a
          # real results?
          to_report = paths is None or any(
              p == sm_path or p in sm_path.parents for p in paths
          )
          if to_report and (set_property or delete_property):
              # first deletions
              for dprop in ensure_list(delete_property):
                  try:
                      repo.call_git(
                          [
                              "config",
                              "--file",
                              ".gitmodules",
                              "--unset-all",
                              "submodule.{}.{}".format(
                                  sm["gitmodule_name"], dprop
                              ),
                          ]
                      )
                  except CommandError:
                      yield get_status_dict(
                          "subdataset",
                          status="impossible",
                          message=(
                              "Deleting subdataset property '%s' failed for "
                              "subdataset '%s', possibly did "
                              "not exist",
                              dprop,
                              sm["gitmodule_name"],
                          ),
                          logger=lgr,
                          **sm,
                      )
                  # also kick from the info we just read above
                  sm.pop("gitmodule_{}".format(dprop), None)
              # and now setting values
              for sprop in ensure_list(set_property):
                  prop, val = sprop
                  if (
                      val.startswith("<")
                      and val.endswith(">")
                      and "{" in val
                  ):
                      # expand template string
                      val = val[1:-1].format(
                          **dict(
                              sm,
                              refds_relpath=sm_path.relative_to(
                                  refds_path
                              ),
                              refds_relname=str(
                                  sm_path.relative_to(refds_path)
                              ).replace(os.sep, "-"),
                          )
                      )
                  try:
                      repo.call_git(
                          [
                              "config",
                              "--file",
                              ".gitmodules",
                              "--replace-all",
                              "submodule.{}.{}".format(
                                  sm["gitmodule_name"], prop
                              ),
                              str(val),
                          ]
                      )
                  except CommandError as e:  # pragma: no cover
                      # this conditional may not be possible to reach, as
                      # variable name validity is checked before and Git
                      # replaces the file completely, resolving any permission
                      # issues, if the file could be read (already done above)
                      ce = CapturedException(e)
                      yield get_status_dict(
                          "subdataset",
                          status="error",
                          message=(
                              "Failed to set property '%s': %s",
                              prop,
                              ce,
                          ),
                          exception=ce,
                          type="dataset",
                          logger=lgr,
                          **sm,
                      )
                      # it is up to parent code to decide whether we would continue
                      # after this
  
                  # also add to the info we just read above
                  sm["gitmodule_{}".format(prop)] = val
              yield from ds.save(
                  ".gitmodules",
                  to_git=True,
                  message="[DATALAD] modified subdataset properties",
                  result_renderer="disabled",
                  return_type="generator",
              )
  
          # common = commonprefix((with_pathsep(subds), with_pathsep(path)))
          # if common.endswith(sep) and common == with_pathsep(subds):
          #    candidates.append(common)
          subdsres = get_status_dict(
              "subdataset", status="ok", type="dataset", logger=lgr
          )
          subdsres.update(sm)
          subdsres["parentds"] = ds.path
          if to_report:
              if contains_hits:
                  subdsres["contains"] = contains_hits
              if not bottomup and (
                  fulfilled is None
                  or GitRepo.is_valid_repo(sm_path) == fulfilled
              ):
                  yield subdsres
  
          # expand list with child submodules. keep all paths relative to parent
          # and convert jointly at the end
          if recursive and (
              recursion_limit in (None, "existing")
              or (
                  isinstance(recursion_limit, int)
                  and recursion_limit > 1
              )
          ):
              for r in _get_submodules(
                  Dataset(sm_path),
                  paths,
                  fulfilled,
                  recursive,
                  (recursion_limit - 1)
                  if isinstance(recursion_limit, int)
                  else recursion_limit,
                  contains,
                  bottomup,
                  set_property,
                  delete_property,
                  refds_path,
              ):
                  yield r
          if to_report and (
              bottomup
              and (
                  fulfilled is None
                  or GitRepo.is_valid_repo(sm_path) == fulfilled
              )
          ):
              yield subdsres
  
imports: import logging
  import re
  import os
  import warnings
  from datalad.interface.utils import generic_result_renderer
  from datalad.interface.base import Interface, build_doc, eval_results
  from datalad.interface.results import get_status_dict
  from datalad.support.constraints import EnsureStr, EnsureNone, NoneDeprecated
  from datalad.support.param import Parameter
  from datalad.support.exceptions import CapturedException, CommandError
  from datalad.interface.common_opts import contains, dataset_state, fulfilled, recursion_flag, recursion_limit
  from datalad.distribution.dataset import Dataset, require_dataset
  from datalad.support.gitrepo import GitRepo
  from datalad.utils import ensure_list, getpwd, partition, Path
  from datalad.distribution.dataset import EnsureDataset, datasetmethod, resolve_path
label_desc: Use image node_id 2 for calling a global function with example usage: _get_submodules(ds, paths, fulfilled, recursive, recursion_limit, contains, bottomup, set_property, delete_property, refds_path) without return types