docs: def export(package, output_path='.', force=False, symlinks=False):
      """Export package file data.
  
      Exports specified node (or its children) to files.
  
      `symlinks`
      **Warning** This is an advanced feature, use at your own risk.
                  You must have a thorough understanding of permissions,
                  and it is your responsibility to ensure that the linked
                  files are not modified.  If they do become modified, it
                  may corrupt your package store, and you may lose data,
                  or you may use or distribute incorrect data.
      If `symlinks` is `True`:
        * Nodes that point to binary data will be symlinked instead of copied.
        * All other specified nodes (columnar data) will be exported as normal.
  
      :param package: package/subpackage name, e.g., user/foo or user/foo/bar
      :param output_path: distination folder
      :param symlinks: Use at your own risk.  See full description above.
      :param force: if True, overwrite existing files
      """
      # TODO: (future) Support other tags/versions (via load(), probably)
      # TODO: (future) This would be *drastically* simplified by a 1:1 source-file to node-name correlation
      # TODO: (future) This would be significantly simplified if node objects with useful accessors existed
      #       (nodes.Node and subclasses are being phased out, per Kevin)
  
      if symlinks is True:
          from quilt import _DEV_MODE
          if not _DEV_MODE:
              response = input("Warning: Exporting using symlinks to the package store.\n"
                  "\tThis is an advanced feature.\n"
                  "\tThe package store must not be written to directly, or it may be corrupted.\n"
                  "\tManaging permissions and usage are your responsibility.\n\n"
                  "Are you sure you want to continue? (yes/No) ")
              if response != 'yes':
                  raise CommandException("No action taken: 'yes' not given")
      ## Helpers
      # Perhaps better as Node.iteritems()
      def iteritems(node, base_path=None, recursive=False):
          if base_path is None:
              base_path = pathlib.PureWindowsPath()
          assert isinstance(node, nodes.GroupNode)
  
          for name, child_node in node._items():
              child_path = base_path / name
              yield child_path, child_node
              if recursive and isinstance(child_node, nodes.GroupNode):
                  for subpath, subnode in iteritems(child_node, child_path, recursive):
                      yield subpath, subnode
  
      # Perhaps better as Node.export_path
      def get_export_path(node, node_path):
          # If filepath is not present, generate fake path based on node parentage.
          filepath = node._meta[SYSTEM_METADATA]['filepath']
          if filepath:
              dest = pathlib.PureWindowsPath(filepath)  # PureWindowsPath handles all win/lin/osx separators
          else:
              assert isinstance(node_path, pathlib.PureWindowsPath)
              assert not node_path.anchor
              print("Warning:  Missing export path in metadata.  Using node path: {}"
                    .format('/'.join(node_path.parts)))
              dest = node_path
  
          # When exporting dataframes, excel files are to be converted to csv.
          # check also occurs in export_node(), but done here prevents filename conflicts
          if node._target() == TargetType.PANDAS:
              if dest.suffix != '.csv':
                  # avoid name collisions from files with same name but different source,
                  # as we shift all to being csv for export.
                  # foo.tsv -> foo_tsv.csv
                  # foo.xls -> foo_xls.csv
                  # ..etc.
                  dest = dest.with_name(dest.stem + dest.suffix.replace('.', '_')).with_suffix('.csv')
          # if filepath isn't absolute
          if not dest.anchor:
              return pathlib.Path(*dest.parts)  # return a native path
  
          # filepath is absolute, convert to relative.
          dest = pathlib.Path(*dest.parts[1:])  # Issue warning as native path, and return it
          print("Warning:  Converted export path to relative path: {}".format(str(dest)))
          return dest
  
      def iter_filename_map(node, base_path):
          """Yields (<node>, <export path>) pairs for given `node`.
  
          If `node` is a group node, yield pairs for children of `node`.
          Otherwise, yield the path to export to for that node.
  
          :returns: Iterator of (<node>, <export path>) pairs
          """
          # Handle singular node export
          if not isinstance(node, nodes.GroupNode):
              yield (node, get_export_path(node, base_path))
              return
  
          for node_path, found_node in iteritems(node, base_path=base_path, recursive=True):
              if not isinstance(found_node, nodes.GroupNode):
                  yield (found_node, get_export_path(found_node, node_path))
  
      # perhaps better as Node.export()
      def export_node(node, dest, use_symlinks=False):
          if not dest.parent.exists():
              dest.parent.mkdir(parents=True, exist_ok=True)
          if node._target() == TargetType.FILE:
              if use_symlinks is True:
                  fs_link(node(), dest)
              else:
                  copyfile(node(), str(dest))
          elif node._target() == TargetType.PANDAS:
              df = node()
              # 100 decimal places of pi will allow you to draw a circle the size of the known
              # universe, and only vary by approximately the width of a proton.
              # ..so, hopefully 78 decimal places (256 bits) is ok for float precision in CSV exports.
              # If not, and someone complains, we can up it or add a parameter.
              df.to_csv(str(dest), index=False, float_format='%r')
          else:
              assert False
  
      def resolve_dirpath(dirpath):
          """Checks the dirpath and ensures it exists and is writable
          :returns: absolute, resolved dirpath
          """
          # ensure output path is writable.  I'd just check stat, but this is fully portable.
          try:
              dirpath.mkdir(exist_ok=True)  # could be '.'
              with tempfile.TemporaryFile(dir=str(dirpath), prefix="quilt-export-write-test-", suffix='.tmp'):
                  pass
          except OSError as error:
              raise CommandException("Invalid export path: not writable: " + str(error))
          return output_path.resolve()    # this gets the true absolute path, but requires the path exists.
  
      def finalize(outpath, exports):
          """Finalize exports
  
          This performs the following tasks:
              * Ensure destination doesn't exist
              * Remove absolute anchor in dest, if any
              * Prefix destination with target dir
  
          :returns: (<source Path / dest Path pairs list>, <set of zero-byte files>)
          """
          # We return list instead of yielding, so that all prep logic is done before write is attempted.
          final_export_map = []
  
          for node, dest in exports:
              dest = pathlib.PureWindowsPath(dest)
              dest = outpath.joinpath(*dest.parts[1:]) if dest.anchor else outpath / dest
  
              if dest.parent != outpath and dest.parent.exists() and not force:
                  raise CommandException("Invalid export path: subdir already exists: {!r}"
                                         .format(str(dest.parent)))
              if dest.exists() and not force:
                  raise CommandException("Invalid export path: file already exists: {!r}".format(str(dest)))
  
              final_export_map.append((node, dest))
          return final_export_map
  
      def check_for_conflicts(export_list):
          """Checks for conflicting exports in the final export map of (src, dest) pairs
  
          Export conflicts can be introduced in various ways -- for example:
              * export-time mapping -- user maps two files to the same name
              * coded builds -- user creates two files with the same path
              * re-rooting absolute paths -- user entered absolute paths, which are re-rooted to the export dir
              * build-time duplication -- user enters the same file path twice under different nodes
  
          This checks for these conflicts and raises an error if they have occurred.
  
          :raises: CommandException
          """
          results = {}
          conflicts = set()
  
          # Export conflicts..
          for src, dest in export_list:
              if dest in conflicts:
                  continue    # already a known conflict
              if dest not in results:
                  results[dest] = src
                  continue    # not a conflict..
              if src._target() == TargetType.FILE and src() == results[dest]():
                  continue    # not a conflict (same src filename, same dest)..
              # ..add other conditions that prevent this from being a conflict here..
  
              # dest is a conflict.
              conflicts.add(dest)
  
          if conflicts:
              conflict_strings = (os.linesep + '\t').join(str(c) for c in conflicts)
              conflict_error = CommandException(
                  "Invalid export: Identical filename(s) with conflicting contents cannot be exported:\n\t"
                  + conflict_strings
                  )
              conflict_error.file_conflicts = conflicts
              raise conflict_error
  
          # Check for filenames that conflict with folder names
          exports = set(results)
          dirs = set()
          for dest in results:
              dirs.update(dest.parents)
          file_dir_conflicts = exports & dirs
  
          if file_dir_conflicts:
              conflict_strings = (os.linesep + '\t').join(str(c) for c in file_dir_conflicts)
              conflict_error = CommandException(
                  "Invalid Export: Filename(s) conflict with folder name(s):\n\t" + conflict_strings
                  )
              conflict_error.dir_file_conflicts = file_dir_conflicts
              raise conflict_error
          # TODO: return abbreviated list of exports based on found non-conflicting duplicates
  
      ## Export Logic
      output_path = pathlib.Path(output_path)
      node, _, info = _load(package)
  
      if info.subpath:
          subpath = pathlib.PureWindowsPath(*info.subpath)
          for name in info.subpath:
              node = node._get(name)
      else:
          subpath = pathlib.PureWindowsPath()
  
      resolved_output = resolve_dirpath(output_path)  # resolve/create output path
      exports = iter_filename_map(node, subpath)      # Create src / dest map iterator
      exports = finalize(resolved_output, exports)    # Fix absolutes, check dest nonexistent, prefix dest dir
      check_for_conflicts(exports)                    # Prevent various potential dir/naming conflicts
  
      # Skip it if there's nothing to do
      if not exports:
          # Technically successful, but with nothing to do.
          # package may have no file nodes, or user may have filtered out all applicable targets.
          # -- should we consider it an error and raise?
          print("No files to export.")
          return
  
      # All prep done, let's export..
      try:
          fmt = "Exporting file {n_fmt} of {total_fmt} [{elapsed}]"
          sys.stdout.flush()   # flush prior text before making progress bar
  
          # bar_format is not respected unless both ncols and total are set.
          exports_bar = tqdm(exports, desc="Exporting: ", ncols=1, total=len(exports), bar_format=fmt)
          # tqdm is threaded, and its display may not specify the exact file currently being exported.
          with exports_bar:
              for node, dest in exports_bar:
                  # Escape { and } in filenames for .format called by tqdm
                  fname = str(dest.relative_to(resolved_output)).replace('{', "{{").replace('}', '}}')
                  exports_bar.bar_format = fmt + ": " + fname
                  exports_bar.update(0)
                  export_node(node, dest, use_symlinks=symlinks)
      except OSError as error:
          commandex = CommandException("Unexpected error during export: " + str(error))
          commandex.original_error = error
          raise commandex