def render(self, wf_module: WfModule,
               table: Optional[DataFrame]) -> ProcessResult:
        """Process `table` with module `render` method, for a ProcessResult.

        If the `render` method raises an exception, this method will return an
        error string. It is always an error for a module to raise an exception.
        """
        if table is None:
            return None  # TODO disallow?

        if not hasattr(self.module, 'render'):
            return self._default_render(wf_module, table)

        params = wf_module.create_parameter_dict(table)

        return self._call_method('render', table, params)
    def fetch(self, wf_module: WfModule) -> None:
        """Run `call_fetch(wf_module)` and write to `wf_module`.

        `wf_module` will be set to `busy` until the fetch completes. After,
        it will be either `ready` or `error`.
        """
        # FIXME database writes probably belong in dispatch.py. Right now,
        # here, half is dispatch stuff and half is database stuff.
        if not hasattr(self.module, 'fetch'):
            return

        params = wf_module.create_parameter_dict(None)

        wf_module.set_busy()

        result = self.call_fetch(params)
        result.truncate_in_place_if_too_big()
        result.sanitize_in_place()

        ModuleImpl.commit_result(wf_module, result)