예제 #1
0
        def save(self, model, path=""):
            """
            This is called when a file is saved
            """
            if self.manager and path in self.manager:
                out = self.manager.overwrite(model, path)
                return out
            else:
                check_metadata_filter(self.log, model)
                # not sure what's the difference between model['path'] and path
                # but path has leading "/", _model_in_dag strips it
                key = self._model_in_dag(model, path)

                if key:
                    content = model['content']
                    metadata = content.get('metadata', {}).get('ploomber', {})

                    if not metadata.get('injected_manually'):
                        self.log.info(
                            '[Ploomber] Cleaning up injected cell in {}...'.
                            format(model.get('name') or ''))
                        model['content'] = _cleanup_rendered_nb(content)

                    self.log.info("[Ploomber] Deleting product's metadata...")
                    self.dag_mapping.delete_metadata(key)

                return super().save(model, path)
예제 #2
0
def test_cleanup_rendered_nb(nb, expected_n, expected_source):
    out = _cleanup_rendered_nb(jupytext.reads(nb))

    assert len(out['cells']) == expected_n
    assert [c['source'] for c in out['cells']] == expected_source
예제 #3
0
    def develop(self, app='notebook', args=None):
        """
        Opens the rendered notebook (with injected parameters) and adds a
        "debugging-settings" cell to the that changes directory to the current
        active directory. This will reflect conditions when callign
        `DAG.build()`. This modified notebook is saved in the same location as
        the source with a "-tmp" added to the filename. Changes to this
        notebook can be exported to the original notebook after the notebook
        process is shut down. The "injected-parameters" and
        "debugging-settings" cells are deleted before saving.

        Parameters
        ----------
        app : {'notebook', 'lab'}, default: 'notebook'
            Which Jupyter application to use
        args : str
            Extra parameters passed to the jupyter application

        Notes
        -----
        Be careful when developing tasks interacively. If the task has run
        successfully, you overwrite products but don't save the
        updated source code, your DAG will enter an inconsistent state where
        the metadata won't match the overwritten product.

        If you modify the source code and call develop again, the source
        code will be updated only if the ``hot_reload option`` is turned on.
        See :class:`ploomber.DAGConfigurator` for details.
        """
        # TODO: this code needs refactoring, should be a context manager
        # like the one we have for PythonCallable.develop that abstracts
        # the handling of the temporary notebook while editing

        apps = {'notebook', 'lab'}

        if app not in apps:
            raise ValueError('"app" must be one of {}, got: "{}"'.format(
                apps, app))

        if self.source.language != 'python':
            raise NotImplementedError(
                'develop is not implemented for "{}" '
                'notebooks, only python is supported'.format(
                    self.source.language))

        if self.source.loc is None:
            raise ValueError('Can only use develop in notebooks loaded '
                             'from files, not from str')

        nb = _read_rendered_notebook(self.source.nb_str_rendered)

        name = self.source.loc.name
        suffix = self.source.loc.suffix
        name_new = name.replace(suffix, '-tmp.ipynb')
        tmp = self.source.loc.with_name(name_new)
        content = nbformat.writes(nb, version=nbformat.NO_CONVERT)
        tmp.write_text(content)

        # open notebook with injected debugging cell
        try:
            subprocess.run(['jupyter', app, str(tmp)] +
                           shlex.split(args or ''),
                           check=True)
        except KeyboardInterrupt:
            print(f'Jupyter {app} application closed...')

        # read tmp file again, to see if the user made any changes
        content_new = Path(tmp).read_text()

        # maybe exclude changes in tmp cells?
        if content == content_new:
            print('No changes found...')
        else:
            # save changes
            if _save():
                nb = nbformat.reads(content_new,
                                    as_version=nbformat.NO_CONVERT)

                # remove injected-parameters and debugging-settings cells if
                # they exist
                _cleanup_rendered_nb(nb)

                # write back in the same format and original location
                ext_source = Path(self.source.loc).suffix[1:]
                print('Saving notebook to: ', self.source.loc)
                jupytext.write(nb, self.source.loc, fmt=ext_source)
            else:
                print('Not saving changes...')

        # remove tmp file
        Path(tmp).unlink()