docs: def save_project(self, fname=None, pwd=None, pack=False, **kwargs):
          """
          Save this project to a file
  
          Parameters
          ----------
          fname: str or None
              If None, the dictionary will be returned. Otherwise the necessary
              information to load this project via the :meth:`load` method is
              saved to `fname` using the :mod:`pickle` module
          pwd: str or None, optional
              Path to the working directory from where the data can be imported.
              If None and `fname` is the path to a file, `pwd` is set to the
              directory of this file. Otherwise the current working directory is
              used.
          pack: bool
              If True, all datasets are packed into the folder of `fname`
              and will be used if the data is loaded
          %(ArrayList.array_info.parameters.no_pwd|copy)s
  
          Notes
          -----
          You can also store the entire data in the pickled file by setting
          ``ds_description={'ds'}``"""
          # store the figure informatoptions and array informations
          if fname is not None and pwd is None and not pack:
              pwd = os.path.dirname(fname)
          if pack and fname is not None:
              target_dir = os.path.dirname(fname)
              if not os.path.exists(target_dir):
                  os.makedirs(target_dir)
  
              def tmp_it():
                  from tempfile import NamedTemporaryFile
                  while True:
                      yield NamedTemporaryFile(
                          dir=target_dir, suffix='.nc').name
  
              kwargs.setdefault('paths', tmp_it())
          if fname is not None:
              kwargs['copy'] = True
  
          _update_versions()
          ret = {'figs': dict(map(_ProjectLoader.inspect_figure, self.figs)),
                 'arrays': self.array_info(pwd=pwd, **kwargs),
                 'versions': _deepcopy(_versions)}
          if pack and fname is not None:
              # we get the filenames out of the results and copy the datasets
              # there. After that we check the filenames again and force them
              # to the desired directory
              from shutil import copyfile
              fnames = (f[0] for f in self._get_dsnames(ret['arrays']))
              alternative_paths = kwargs.pop('alternative_paths', {})
              counters = defaultdict(int)
              if kwargs.get('use_rel_paths', True):
                  get_path = partial(os.path.relpath, start=target_dir)
              else:
                  get_path = os.path.abspath
              for ds_fname in unique_everseen(chain(alternative_paths, fnames)):
                  if ds_fname is None or utils.is_remote_url(ds_fname):
                      continue
                  dst_file = alternative_paths.get(
                      ds_fname, os.path.join(target_dir, os.path.basename(
                          ds_fname)))
                  orig_dst_file = dst_file
                  if counters[dst_file] and (
                          not os.path.exists(dst_file) or
                          not os.path.samefile(ds_fname, dst_file)):
                      dst_file, ext = os.path.splitext(dst_file)
                      dst_file += '-' + str(counters[orig_dst_file]) + ext
                  if (not os.path.exists(dst_file) or
                          not os.path.samefile(ds_fname, dst_file)):
                      copyfile(ds_fname, dst_file)
                      counters[orig_dst_file] += 1
                  alternative_paths.setdefault(ds_fname, get_path(dst_file))
              ret['arrays'] = self.array_info(
                  pwd=pwd, alternative_paths=alternative_paths, **kwargs)
          # store the plotter settings
          for arr, d in zip(self, six.itervalues(ret['arrays'])):
              if arr.psy.plotter is None:
                  continue
              plotter = arr.psy.plotter
              d['plotter'] = {
                  'ax': _ProjectLoader.inspect_axes(plotter.ax),
                  'fmt': {key: getattr(plotter, key).value2pickle
                          for key in plotter},
                  'cls': (plotter.__class__.__module__,
                          plotter.__class__.__name__),
                  'shared': {}}
              d['plotter']['ax']['shared'] = set(
                  other.psy.arr_name for other in self
                  if other.psy.ax == plotter.ax)
              if plotter.ax._sharex:
                  d['plotter']['ax']['sharex'] = next(
                      (other.psy.arr_name for other in self
                       if other.psy.ax == plotter.ax._sharex), None)
              if plotter.ax._sharey:
                  d['plotter']['ax']['sharey'] = next(
                      (other.psy.arr_name for other in self
                       if other.psy.ax == plotter.ax._sharey), None)
              shared = d['plotter']['shared']
              for fmto in plotter._fmtos:
                  if fmto.shared:
                      shared[fmto.key] = [other_fmto.plotter.data.psy.arr_name
                                          for other_fmto in fmto.shared]
          if fname is not None:
              with open(fname, 'wb') as f:
                  pickle.dump(ret, f)
              return None
  
          return ret