diff --git a/src/Notebook_Processor/desired_state/cell_modifier.py b/src/Notebook_Processor/desired_state/cell_modifier.py index b54343779050790e72512cc0fa6f99a994d9637d..2e0f903765f6fca7a7fa7e7630e5cdb918bc8556 100644 --- a/src/Notebook_Processor/desired_state/cell_modifier.py +++ b/src/Notebook_Processor/desired_state/cell_modifier.py @@ -91,7 +91,7 @@ class CellModifier(BaseModel): """ @staticmethod - def delete(cell: Optional[NotebookCellAnnotation] = None, + def delete(cell: Optional[NotebookCellAnnotation] = None, # pylint: disable=unused-argument **kwargs, # pylint: disable=unused-argument ): """ @@ -125,11 +125,29 @@ class CellModifier(BaseModel): return cell_class(**kwargs) @staticmethod - def clear_outputs(cell: NotebookCellAnnotation, **kwargs) -> NotebookCellAnnotation: + def clear_outputs(cell: NotebookCellAnnotation, + **kwargs # pylint: disable=unused-argument + ) -> NotebookCellAnnotation: """ Clear the output of the cell + + :param cell NotebookCellAnnotation: + The cell to be modified + + :param kwargs dict: + Unused keyword arguments + + :return NotebookCellAnnotation: """ - cell.outputs = [] + if hasattr(cell, "outputs"): + return cell.model_copy( + deep=True, + update={ + "outputs": [] + } + ) + # The following could happen for cells that don't have an output such as Markdown Cells. + module_logger.warning("Cell '%s' has no field 'outputs'. Skipping clearing of outputs.", cell.id) return cell # I don't want to remove this yet but I am unsure of the value of this function. @@ -164,8 +182,12 @@ class CellModifier(BaseModel): """ Replace the given string in the source of the cell """ - cell.source = [line.replace(old, new) for line in cell.source] - return cell + return cell.model_copy( + deep=True, + update={ + "source": [line.replace(old, new) for line in cell.source] + } + ) @staticmethod def update_metadata(cell: NotebookCellAnnotation, *, @@ -175,8 +197,12 @@ class CellModifier(BaseModel): """ Update the metadata of the cell """ - cell.metadata.update(metadata) - return cell + return cell.model_copy( + deep=True, + update={ + "metadata": metadata + } + ) @staticmethod def report(cell: NotebookCellAnnotation, *, diff --git a/tests/desired_state/test_predefined_action.py b/tests/desired_state/test_predefined_action.py index b01dad4b2a7bd067f20848c8816365da9967ad86..6cefea760588b920a9fd01ec1ddd82780eab0de3 100644 --- a/tests/desired_state/test_predefined_action.py +++ b/tests/desired_state/test_predefined_action.py @@ -19,7 +19,16 @@ def code_cell(metadata) -> NotebookCodeCellModel: id="test_code", cell_type="code", metadata=metadata, - source=["print('Hello, World!')"] + source=["print('Hello, World!')"], + outputs=[ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World!\n" + ] + }, + ], ) @@ -117,6 +126,27 @@ def test_CLEAR_args(code_cell: NotebookCodeCellModel, markdown_cell: NotebookMar assert modified_markdown_cell.source == ["Goodnight, World!"] +def test_CLEAR_OUTPUTS_code(code_cell, metadata): + assert isinstance(CellModifier.Action.CLEAR_OUTPUTS, Callable) + modified_code_cell = CellModifier.Action.CLEAR_OUTPUTS(code_cell) + assert modified_code_cell.outputs == [] + assert modified_code_cell == NotebookCodeCellModel( + id="test_code", + cell_type="code", + metadata=metadata, + source=["print('Hello, World!')"], + outputs=[] + ) + + +def test_CLEAR_OUTPUTS_markdown(markdown_cell, metadata, caplog): + assert isinstance(CellModifier.Action.CLEAR_OUTPUTS, Callable) + caplog.clear() + modified_markdown_cell = CellModifier.Action.CLEAR_OUTPUTS(markdown_cell) + assert caplog.messages[0] == "Cell '" + markdown_cell.id + "' has no field 'outputs'. Skipping clearing of outputs." + assert modified_markdown_cell == markdown_cell + + def test_REPLACE_IN_SOURCE(code_cell, markdown_cell, metadata, extra_args): assert isinstance(CellModifier.Action.REPLACE_IN_SOURCE, Callable) modified_code_cell = CellModifier.Action.REPLACE_IN_SOURCE( @@ -137,7 +167,8 @@ def test_REPLACE_IN_SOURCE(code_cell, markdown_cell, metadata, extra_args): id="test_code", cell_type="code", metadata=metadata, - source=["print('Goodbye, World!')"] + source=["print('Goodbye, World!')"], + outputs=[{'name': 'stdout', 'output_type': 'stream', 'text': ['Hello, World!\n']}], ) assert modified_markdown_cell.source == [