class_docstr: Export an archive of a local annex object store for the ORA remote.
  
  Keys in the local annex object store are reorganized in a temporary
  directory (using links to avoid storage duplication) to use the
  'hashdirlower' setup used by git-annex for bare repositories and
  the directory-type special remote. This alternative object store is
  then moved into a 7zip archive that is suitable for use in a
  ORA remote dataset store. Placing such an archive into::
  
    <dataset location>/archives/archive.7z
  
  Enables the ORA special remote to locate and retrieve all keys contained
  in the archive.
class_docstr_tok: ["Export","an","archive","of","a","local","annex","object","store","for","the","ORA","remote",".","Keys","in","the","local","annex","object","store","are","reorganized","in","a","temporary","directory","(","using","links","to","avoid","storage","duplication",")","to","use","the","'hashdirlower","'","setup","used","by","git-annex","for","bare","repositories","and","the","directory-type","special","remote",".","This","alternative","object","store","is","then","moved","into","a","7zip","archive","that","is","suitable","for","use","in","a","ORA","remote","dataset","store",".","Placing","such","an","archive","into",":",":","<","dataset","location",">","\/archives\/archive.7z","Enables","the","ORA","special","remote","to","locate","and","retrieve","all","keys","contained","in","the","archive","."]
code: def __call__(
      target,
      opts=None,
      *,  # opts is positional but optional in CLI
      dataset=None,
      remote=None,
      annex_wanted=None,
      froms=None,
      missing_content="error",
  ):
      # only non-bare repos have hashdirmixed, so require one
      ds = require_dataset(
          dataset, check_installed=True, purpose="export to ORA archive"
      )
      ds_repo = ds.repo
  
      annex_objs = ds_repo.dot_git / "annex" / "objects"
  
      archive = resolve_path(target, dataset)
      if archive.is_dir():
          archive = archive / "archive.7z"
      else:
          archive.parent.mkdir(exist_ok=True, parents=True)
  
      froms = ensure_list(froms)
  
      if not opts:
          # uncompressed by default
          opts = ["-mx0"]
  
      res_kwargs = dict(
          action="export-archive-ora",
          logger=lgr,
      )
  
      if not annex_objs.is_dir():
          yield get_status_dict(
              ds=ds,
              status="notneeded",
              message="no annex keys present",
              **res_kwargs,
          )
          return
  
      exportdir = ds_repo.dot_git / "datalad" / "tmp" / "ora_archive"
      if exportdir.exists():
          yield get_status_dict(
              ds=ds,
              status="error",
              message=(
                  "export directory already exists, please remove first: %s",
                  str(exportdir),
              ),
              **res_kwargs,
          )
          return
  
      def expr_to_opts(expr):
          opts = []
          expr = expr.replace("(", " ( ").replace(")", " ) ")
          for sub_expr in expr.split(" "):
              if len(sub_expr):
                  if sub_expr in "()":
                      opts.append(f"-{sub_expr}")
                  else:
                      opts.append(f"--{sub_expr}")
          return opts
  
      find_filters = []
      if remote:
          find_filters = (
              ["-("]
              + expr_to_opts(
                  ds_repo.get_preferred_content("wanted", remote)
              )
              + ["-)"]
          )
      if annex_wanted:
          find_filters.extend(expr_to_opts(annex_wanted))
      # git-annex find results need to be uniqued with set, as git-annex find
      # will return duplicates if multiple symlinks point to the same key.
      #
      # TODO: use --json which was already added, checked with 10.20230407+git131-gb90c2156a6
      if froms:
          keypaths = set(
              [
                  annex_objs.joinpath(k)
                  for treeish in froms
                  for k in ds_repo.call_annex_items_(
                      [
                          "find",
                          *find_filters,
                          f"--branch={treeish}",
                          "--format=${hashdirmixed}${key}/${key}\\n",
                      ]
                  )
              ]
          )
      else:
          keypaths = set(
              annex_objs.joinpath(k)
              for k in ds_repo.call_annex_items_(
                  [
                      "find",
                      *find_filters,
                      "--format=${hashdirmixed}${key}/${key}\\n",
                  ]
              )
          )
  
      log_progress(
          lgr.info,
          "oraarchiveexport",
          "Start ORA archive export %s",
          ds,
          total=len(keypaths),
          label="ORA archive export",
          unit=" Keys",
      )
  
      if missing_content == "continue":
          missing_file_lgr_func = lgr.warning
      elif missing_content == "ignore":
          missing_file_lgr_func = lgr.debug
  
      link_fx = os.link
      for keypath in keypaths:
          key = keypath.name
          hashdir = op.join(keypath.parts[-4], keypath.parts[-3])
          log_progress(
              lgr.info,
              "oraarchiveexport",
              "Export key %s to %s",
              key,
              hashdir,
              update=1,
              increment=True,
          )
          keydir = exportdir / hashdir / key
          keydir.mkdir(parents=True, exist_ok=True)
          try:
              link_fx(str(keypath), str(keydir / key))
          except FileNotFoundError as e:
              if missing_content == "error":
                  raise IOError(
                      "Key %s has no content available" % keypath
                  )
              missing_file_lgr_func(
                  "Key %s has no content available", str(keypath)
              )
          except OSError:
              lgr.warning(
                  "No hard links supported at %s, will copy files instead",
                  str(keypath),
              )
              # no hard links supported
              # switch function after first error
              link_fx = shutil.copyfile
              link_fx(str(keypath), str(keydir / key))
  
      log_progress(
          lgr.info,
          "oraarchiveexport",
          "Finished RIA archive export from %s",
          ds,
      )
      try:
          subprocess.run(
              ["7z", "u", str(archive), "."] + opts,
              cwd=str(exportdir),
          )
          yield get_status_dict(
              path=str(archive), type="file", status="ok", **res_kwargs
          )
      except Exception as e:
          ce = CapturedException(e)
          yield get_status_dict(
              path=str(archive),
              type="file",
              status="error",
              message=("7z failed: %s", ce),
              exception=ce,
              **res_kwargs,
          )
          return
      finally:
          rmtree(str(exportdir))
  
file_path: datalad/datalad/distributed/export_archive_ora.py
imports: import logging
  import os
  import os.path
  import shutil
  import subprocess
  from argparse import REMAINDER
  from datalad.utils import ensure_list, rmtree
  from datalad.interface.base import Interface, build_doc, eval_results
  from datalad.interface.results import get_status_dict
  from datalad.support.param import Parameter
  from datalad.support.constraints import EnsureChoice, EnsureNone, EnsureStr
  from datalad.support.exceptions import CapturedException
  from datalad.distribution.dataset import EnsureDataset, datasetmethod, require_dataset, resolve_path
  from datalad.log import log_progress
label_desc: Use image node_id 1 for calling the ExportArchiveORA obj's underlying member method code with example usage: obj.__call__(target, opts) and returns: opts