docs: def field(self, name, from_study=None):
          """
          Gets the field named 'name' produced by the Study named 'study' if
          provided. If a spec is passed instead of a str to the name argument,
          then the study will be set from the spec iff it is derived
  
          Parameters
          ----------
          name : str | BaseField
              The name of the field or a spec matching the given name
          study : str | None
              Name of the study that produced the field if derived. If None
              and a spec is passed instaed of string to the name argument then
              the study name will be taken from the spec instead.
          """
          if isinstance(name, BaseField):
              if from_study is None and name.derived:
                  from_study = name.study.name
              name = name.name
          try:
              return self._fields[(name, from_study)]
          except KeyError:
              available = [d.name for d in self.fields
                           if d.from_study == from_study]
              other_studies = [(d.from_study if d.from_study is not None
                                else '<root>')
                               for d in self.fields
                               if d.name == name]
              if other_studies:
                  msg = (". NB: matching field(s) found for '{}' study(ies) "
                         "('{}')".format(name, "', '".join(other_studies)))
              else:
                  msg = ''
              raise ArcanaNameError(
                  name, ("{} doesn't have a field named '{}'{} "
                         "(available '{}')"
                         .format(
                             self, name,
                             (" from study '{}'".format(from_study)
                              if from_study is not None else ''),
                             "', '".join(available), msg)))