class_docstr: Helper to figure out/use versions of the externals (modules, cmdline tools, etc).
  
  To avoid collision between names of python modules and command line tools,
  prepend names for command line tools with `cmd:`.
  
  It maintains a dictionary of `distuil.version.LooseVersion`s to make
  comparisons easy. Note that even if version string conform the StrictVersion
  "standard", LooseVersion will be used.  If version can't be deduced for the
  external, `UnknownVersion()` is assigned.  If external is not present (can't
  be imported, or custom check throws exception), None is returned without
  storing it, so later call will re-evaluate fully.
class_docstr_tok: ["Helper","to","figure","out\/use","versions","of","the","externals","(","modules",",","cmdline","tools",",","etc",")",".","To","avoid","collision","between","names","of","python","modules","and","command","line","tools",",","prepend","names","for","command","line","tools","with","`","cmd",":","`",".","It","maintains","a","dictionary","of","`","distuil.version.LooseVersion","`","s","to","make","comparisons","easy",".","Note","that","even","if","version","string","conform","the","StrictVersion","''","standard","''",",","LooseVersion","will","be","used",".","If","version","ca","n't","be","deduced","for","the","external",",","`","UnknownVersion","(",")","`","is","assigned",".","If","external","is","not","present","(","can't","be","imported",",","or","custom","check","throws","exception",")",",","None","is","returned","without","storing","it",",","so","later","call","will","re-evaluate","fully","."]
code: def _deduce_version(klass, value):
      version = None
  
      # see if it is something containing a version
      for attr in ("__version__", "version"):
          if hasattr(value, attr):
              version = getattr(value, attr)
              break
  
      # try importlib.metadata
      if version is None and hasattr(value, "__name__"):
          pkg_name = klass._PYTHON_PACKAGES.get(
              value.__name__, value.__name__
          )
          try:
              if sys.version_info < (3, 10):
                  import importlib_metadata as im
              else:
                  import importlib.metadata as im
  
              version = im.version(pkg_name)
          except Exception:
              pass
  
      # assume that value is the version
      if version is None:
          version = value
  
      # do type analysis
      if isinstance(version, (tuple, list)):
          #  Generate string representation
          version = ".".join(str(x) for x in version)
      elif isinstance(version, bytes):
          version = version.decode()
      elif isinstance(version, str):
          pass
      else:
          version = None
  
      if version:
          return LooseVersion(version)
      else:
          return klass.UNKNOWN
  
imports: import os.path
  import re
  import sys
  from itertools import chain
  from os import linesep
  from looseversion import LooseVersion
  from datalad.config import get_git_version
  from datalad.log import lgr
  from .exceptions import CapturedException, CommandError
  from datalad.cmd import GitWitlessRunner, StdOutErrCapture, WitlessRunner
  from datalad.support.exceptions import MissingExternalDependency, OutdatedExternalDependency
label_desc: Use image node_id 2 for calling the ExternalVersions obj's underlying member method code with example usage: obj._deduce_version(klass, value) and returns: LooseVersion, klass