clearmetadata.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. """Module containing a preprocessor that removes metadata from code cells"""
  2. # Copyright (c) IPython Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. from traitlets import Bool, Set
  5. from .base import Preprocessor
  6. class ClearMetadataPreprocessor(Preprocessor):
  7. """
  8. Removes all the metadata from all code cells in a notebook.
  9. """
  10. clear_cell_metadata = Bool(
  11. True,
  12. help=("Flag to choose if cell metadata is to be cleared in addition to notebook metadata."),
  13. ).tag(config=True)
  14. clear_notebook_metadata = Bool(
  15. True,
  16. help=("Flag to choose if notebook metadata is to be cleared in addition to cell metadata."),
  17. ).tag(config=True)
  18. preserve_nb_metadata_mask = Set(
  19. [("language_info", "name")],
  20. help=(
  21. "Indicates the key paths to preserve when deleting metadata "
  22. "across both cells and notebook metadata fields. Tuples of "
  23. "keys can be passed to preserved specific nested values"
  24. ),
  25. ).tag(config=True)
  26. preserve_cell_metadata_mask = Set(
  27. help=(
  28. "Indicates the key paths to preserve when deleting metadata "
  29. "across both cells and notebook metadata fields. Tuples of "
  30. "keys can be passed to preserved specific nested values"
  31. )
  32. ).tag(config=True)
  33. def current_key(self, mask_key):
  34. """Get the current key for a mask key."""
  35. if isinstance(mask_key, str):
  36. return mask_key
  37. if len(mask_key) == 0:
  38. # Safeguard
  39. return None
  40. return mask_key[0]
  41. def current_mask(self, mask):
  42. """Get the current mask for a mask."""
  43. return {self.current_key(k) for k in mask if self.current_key(k) is not None}
  44. def nested_masks(self, mask):
  45. """Get the nested masks for a mask."""
  46. return {
  47. self.current_key(k[0]): k[1:]
  48. for k in mask
  49. if k and not isinstance(k, str) and len(k) > 1
  50. }
  51. def nested_filter(self, items, mask):
  52. """Get the nested filter for items given a mask."""
  53. keep_current = self.current_mask(mask)
  54. keep_nested_lookup = self.nested_masks(mask)
  55. for k, v in items:
  56. keep_nested = keep_nested_lookup.get(k)
  57. if k in keep_current:
  58. if keep_nested is not None:
  59. if isinstance(v, dict):
  60. yield k, dict(self.nested_filter(v.items(), keep_nested))
  61. else:
  62. yield k, v
  63. def preprocess_cell(self, cell, resources, cell_index):
  64. """
  65. All the code cells are returned with an empty metadata field.
  66. """
  67. if self.clear_cell_metadata and cell.cell_type == "code": # noqa: SIM102
  68. # Remove metadata
  69. if "metadata" in cell:
  70. cell.metadata = dict(
  71. self.nested_filter(cell.metadata.items(), self.preserve_cell_metadata_mask)
  72. )
  73. return cell, resources
  74. def preprocess(self, nb, resources):
  75. """
  76. Preprocessing to apply on each notebook.
  77. Must return modified nb, resources.
  78. Parameters
  79. ----------
  80. nb : NotebookNode
  81. Notebook being converted
  82. resources : dictionary
  83. Additional resources used in the conversion process. Allows
  84. preprocessors to pass variables into the Jinja engine.
  85. """
  86. nb, resources = super().preprocess(nb, resources)
  87. if self.clear_notebook_metadata and "metadata" in nb:
  88. nb.metadata = dict(
  89. self.nested_filter(nb.metadata.items(), self.preserve_nb_metadata_mask)
  90. )
  91. return nb, resources