class_docstr: Thin wrapper around `git-config` with support for a dataset configuration.
  
  The general idea is to have an object that is primarily used to read/query
  configuration option.  Upon creation, current configuration is read via one
  (or max two, in the case of the presence of dataset-specific configuration)
  calls to `git config`.  If this class is initialized with a Dataset
  instance, it supports reading and writing configuration from
  ``.datalad/config`` inside a dataset too. This file is committed to Git and
  hence useful to ship certain configuration items with a dataset.
  
  The API aims to provide the most significant read-access API of a
  dictionary, the Python ConfigParser, and GitPython's config parser
  implementations.
  
  This class is presently not capable of efficiently writing multiple
  configurations items at once.  Instead, each modification results in a
  dedicated call to `git config`. This author thinks this is OK, as he
  cannot think of a situation where a large number of items need to be
  written during normal operation.
  
  Each instance carries a public `overrides` attribute. This dictionary
  contains variables that override any setting read from a file. The overrides
  are persistent across reloads.
  
  Any DATALAD_* environment variable is also presented as a configuration
  item. Settings read from environment variables are not stored in any of the
  configuration files, but are read dynamically from the environment at each
  `reload()` call. Their values take precedence over any specification in
  configuration files, and even overrides.
  
  Parameters
  ----------
  dataset : Dataset, optional
    If provided, all `git config` calls are executed in this dataset's
    directory. Moreover, any modifications are, by default, directed to
    this dataset's configuration file (which will be created on demand)
  overrides : dict, optional
    Variable overrides, see general class documentation for details.
  source : {'any', 'local', 'branch', 'branch-local'}, optional
    Which sources of configuration setting to consider. If 'branch',
    configuration items are only read from a dataset's persistent
    configuration file in current branch, if any is present
    (the one in ``.datalad/config``, not
    ``.git/config``); if 'local', any non-committed source is considered
    (local and global configuration in Git config's terminology);
    if 'branch-local', persistent configuration in current dataset branch
    and local, but not global or system configuration are considered; if 'any'
    all possible sources of configuration are considered.
    Note: 'dataset' and 'dataset-local' are deprecated in favor of 'branch'
    and 'branch-local'.
class_docstr_tok: ["Thin","wrapper","around","`","git-config","`","with","support","for","a","dataset","configuration",".","The","general","idea","is","to","have","an","object","that","is","primarily","used","to","read\/query","configuration","option",".","Upon","creation",",","current","configuration","is","read","via","one","(","or","max","two",",","in","the","case","of","the","presence","of","dataset-specific","configuration",")","calls","to","`","git","config","`",".","If","this","class","is","initialized","with","a","Dataset","instance",",","it","supports","reading","and","writing","configuration","from","``",".datalad\/config","``","inside","a","dataset","too",".","This","file","is","committed","to","Git","and","hence","useful","to","ship","certain","configuration","items","with","a","dataset",".","The","API","aims","to","provide","the","most","significant","read-access","API","of","a","dictionary",",","the","Python","ConfigParser",",","and","GitPython","'s","config","parser","implementations",".","This","class","is","presently","not","capable","of","efficiently","writing","multiple","configurations","items","at","once",".","Instead",",","each","modification","results","in","a","dedicated","call","to","`","git","config","`",".","This","author","thinks","this","is","OK",",","as","he","can","not","think","of","a","situation","where","a","large","number","of","items","need","to","be","written","during","normal","operation",".","Each","instance","carries","a","public","`","overrides","`","attribute",".","This","dictionary","contains","variables","that","override","any","setting","read","from","a","file",".","The","overrides","are","persistent","across","reloads",".","Any","DATALAD_","*","environment","variable","is","also","presented","as","a","configuration","item",".","Settings","read","from","environment","variables","are","not","stored","in","any","of","the","configuration","files",",","but","are","read","dynamically","from","the","environment","at","each","`","reload","(",")","`","call",".","Their","values","take","precedence","over","any","specification","in","configuration","files",",","and","even","overrides",".","Parameters","--","--","--","--","--","dataset",":","Dataset",",","optional","If","provided",",","all","`","git","config","`","calls","are","executed","in","this","dataset's","directory",".","Moreover",",","any","modifications","are",",","by","default",",","directed","to","this","dataset","'s","configuration","file","(","which","will","be","created","on","demand",")","overrides",":","dict",",","optional","Variable","overrides",",","see","general","class","documentation","for","details",".","source",":","{","'any","'",",","'local","'",",","'branch","'",",","'branch-local","'","}",",","optional","Which","sources","of","configuration","setting","to","consider",".","If","'branch","'",",","configuration","items","are","only","read","from","a","dataset","'s","persistent","configuration","file","in","current","branch",",","if","any","is","present","(","the","one","in","``",".datalad\/config","``",",","not","``",".git\/config","``",")",";","if","'local","'",",","any","non-committed","source","is","considered","(","local","and","global","configuration","in","Git","config","'s","terminology",")",";","if","'branch-local","'",",","persistent","configuration","in","current","dataset","branch","and","local",",","but","not","global","or","system","configuration","are","considered",";","if","'any'","all","possible","sources","of","configuration","are","considered",".","Note",":","'dataset","'","and","'dataset-local","'","are","deprecated","in","favor","of","'branch'","and","'branch-local","'","."]
docstr: Convenience method to obtain settings interactively, if needed
  
  A UI will be used to ask for user input in interactive sessions.
  Questions to ask, and additional explanations can be passed directly
  as arguments, or retrieved from a list of pre-configured items.
  
  Additionally, this method allows for type conversion and storage
  of obtained settings. Both aspects can also be pre-configured.
  
  Parameters
  ----------
  var : str
    Variable name including any section like `git config` expects them,
    e.g. 'core.editor'
  default : any type
    In interactive sessions and if `store` is True, this default value
    will be presented to the user for confirmation (or modification).
    In all other cases, this value will be silently assigned unless
    there is an existing configuration setting.
  dialog_type : {'question', 'yesno', None}
    Which dialog type to use in interactive sessions. If `None`,
    pre-configured UI options are used.
  store : bool
    Whether to store the obtained value (or default)
  %s
  `**kwargs`
    Additional arguments for the UI function call, such as a question
    `text`.
docstr_tok: ["Convenience","method","to","obtain","settings","interactively",",","if","needed","A","UI","will","be","used","to","ask","for","user","input","in","interactive","sessions",".","Questions","to","ask",",","and","additional","explanations","can","be","passed","directly","as","arguments",",","or","retrieved","from","a","list","of","pre-configured","items",".","Additionally",",","this","method","allows","for","type","conversion","and","storage","of","obtained","settings",".","Both","aspects","can","also","be","pre-configured",".","Parameters","--","--","--","--","--","var",":","str","Variable","name","including","any","section","like","`","git","config","`","expects","them",",","e.g",".","'core.editor'","default",":","any","type","In","interactive","sessions","and","if","`","store","`","is","True",",","this","default","value","will","be","presented","to","the","user","for","confirmation","(","or","modification",")",".","In","all","other","cases",",","this","value","will","be","silently","assigned","unless","there","is","an","existing","configuration","setting",".","dialog_type",":","{","'question","'",",","'yesno","'",",","None","}","Which","dialog","type","to","use","in","interactive","sessions",".","If","`","None","`",",","pre-configured","UI","options","are","used",".","store",":","bool","Whether","to","store","the","obtained","value","(","or","default",")","%","s","`","*","*","kwargs","`","Additional","arguments","for","the","UI","function","call",",","such","as","a","question","`","text","`","."]
code: def obtain(
      self,
      var,
      default=None,
      dialog_type=None,
      valtype=None,
      store=False,
      scope=None,
      reload=True,
      **kwargs,
  ):
      """
      Convenience method to obtain settings interactively, if needed
  
      A UI will be used to ask for user input in interactive sessions.
      Questions to ask, and additional explanations can be passed directly
      as arguments, or retrieved from a list of pre-configured items.
  
      Additionally, this method allows for type conversion and storage
      of obtained settings. Both aspects can also be pre-configured.
  
      Parameters
      ----------
      var : str
        Variable name including any section like `git config` expects them,
        e.g. 'core.editor'
      default : any type
        In interactive sessions and if `store` is True, this default value
        will be presented to the user for confirmation (or modification).
        In all other cases, this value will be silently assigned unless
        there is an existing configuration setting.
      dialog_type : {'question', 'yesno', None}
        Which dialog type to use in interactive sessions. If `None`,
        pre-configured UI options are used.
      store : bool
        Whether to store the obtained value (or default)
      %s
      `**kwargs`
        Additional arguments for the UI function call, such as a question
        `text`.
      """
      # do local import, as this module is import prominently and the
      # could theoretically import all kind of weird things for type
      # conversion
      from datalad.interface.common_cfg import definitions as cfg_defs
  
      # fetch what we know about this variable
      cdef = cfg_defs.get(var, {})
      # type conversion setup
      if valtype is None and "type" in cdef:
          valtype = cdef["type"]
      if valtype is None:
          valtype = lambda x: x
  
      # any default?
      if default is None and "default" in cdef:
          default = cdef["default"]
  
      _value = None
      if var in self:
          # nothing needs to be obtained, it is all here already
          _value = self[var]
      elif store is False and default is not None:
          # nothing will be stored, and we have a default -> no user confirmation
          # we cannot use logging, because we want to use the config to configure
          # the logging
          # lgr.debug('using default {} for config setting {}'.format(default, var))
          _value = default
  
      if _value is not None:
          # we got everything we need and can exit early
          try:
              return valtype(_value)
          except Exception as e:
              raise ValueError(
                  "value '{}' of existing configuration for '{}' cannot be "
                  "converted to the desired type '{}' ({})".format(
                      _value, var, valtype, e
                  )
              ) from e
  
      # now we need to try to obtain something from the user
      from datalad.ui import ui
  
      # configure UI
      dialog_opts = kwargs
      if dialog_type is None:  # no override
          # check for common knowledge on how to obtain a value
          if "ui" in cdef:
              dialog_type = cdef["ui"][0]
              # pull standard dialog settings
              dialog_opts = cdef["ui"][1]
              # update with input
              dialog_opts.update(kwargs)
  
      if (
          not ui.is_interactive or dialog_type is None
      ) and default is None:
          raise RuntimeError(
              "cannot obtain value for configuration item '{}', "
              "not preconfigured, no default, no UI available".format(
                  var
              )
          )
  
      if not hasattr(ui, dialog_type):
          raise ValueError(
              "UI '{}' does not support dialog type '{}'".format(
                  ui, dialog_type
              )
          )
  
      # configure storage destination, if needed
      if store:
          if scope is None and "destination" in cdef:
              scope = cdef["destination"]
          if scope is None:
              raise ValueError(
                  "request to store configuration item '{}', but no "
                  "storage destination specified".format(var)
              )
  
      # obtain via UI
      dialog = getattr(ui, dialog_type)
      _value = dialog(default=default, **dialog_opts)
  
      if _value is None:
          # we got nothing
          if default is None:
              raise RuntimeError(
                  "could not obtain value for configuration item '{}', "
                  "not preconfigured, no default".format(var)
              )
          # XXX maybe we should return default here, even it was returned
          # from the UI -- if that is even possible
  
      # execute type conversion before storing to check that we got
      # something that looks like what we want
      try:
          value = valtype(_value)
      except Exception as e:
          raise ValueError(
              "cannot convert user input `{}` to desired type ({})".format(
                  _value, e
              )
          ) from e
          # XXX we could consider "looping" until we have a value of proper
          # type in case of a user typo...
  
      if store:
          # store value as it was before any conversion, needs to be str
          # anyway
          # needs string conversion nevertheless, because default could come
          # in as something else
          self.add(var, "{}".format(_value), scope=scope, reload=reload)
      return value
  
imports: import json
  import logging
  import os
  import re
  import threading
  import warnings
  from collections import namedtuple
  from fasteners import InterProcessLock
  from functools import lru_cache, wraps
  from pathlib import Path
  import datalad
  from datalad.consts import DATASET_CONFIG_FILE
  from datalad.runner import CommandError, GitRunner, KillOutput, StdOutErrCapture
  from datalad.utils import getpwd, on_windows
label_desc: Use image node_id 6 for calling the ConfigManager obj's underlying member method code with example usage: obj.obtain(var, default, dialog_type, valtype, store, scope, reload) and returns: value, valtype