utils.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. """Utility functions for Jupyter Book.
  2. This is a mini-module to make testing easier."""
  3. import string
  4. import nbformat as nbf
  5. import os
  6. ALLOWED_CHARACTERS = string.ascii_letters + '-_/.' + string.digits
  7. def _split_yaml(lines):
  8. yaml0 = None
  9. for ii, iline in enumerate(lines):
  10. iline = iline.strip()
  11. if yaml0 is None:
  12. if iline == '---':
  13. yaml0 = ii
  14. elif iline:
  15. break
  16. elif iline == '---':
  17. return lines[yaml0 + 1:ii], lines[ii + 1:]
  18. return [], lines
  19. def _check_url_page(url_page, content_folder_name):
  20. """Check that the page URL matches certain conditions."""
  21. if not all(ii in ALLOWED_CHARACTERS for ii in url_page):
  22. raise ValueError("Found unsupported character in filename: {}".format(url_page))
  23. if '.' in os.path.splitext(url_page)[-1]:
  24. raise _error("A toc.yml entry links to a file directly. You should strip the file suffix.\n"
  25. "Please change {} to {}".format(url_page, os.path.splitext(url_page)[0]))
  26. if any(url_page.startswith(ii) for ii in [content_folder_name, os.sep+content_folder_name]):
  27. raise ValueError("It looks like you have a page URL that starts with your content folder's name."
  28. "page URLs should be *relative* to the content folder. Here is the page URL: {}".format(url_page))
  29. def _prepare_toc(toc):
  30. """Prepare the TOC for processing."""
  31. # Drop toc items w/o links
  32. toc = [ii for ii in toc if ii.get('url', None) is not None]
  33. # Un-nest the TOC so it's a flat list
  34. new_toc = []
  35. for ii in toc:
  36. sections = ii.pop('sections', None)
  37. new_toc.append(ii)
  38. if sections is None:
  39. continue
  40. for jj in sections:
  41. subsections = jj.pop('subsections', None)
  42. new_toc.append(jj)
  43. if subsections is None:
  44. continue
  45. for kk in subsections:
  46. new_toc.append(kk)
  47. return new_toc
  48. def _prepare_url(url):
  49. """Prep the formatting for a url."""
  50. # Strip suffixes and prefixes of the URL
  51. if not url.startswith('/'):
  52. url = '/' + url
  53. # Standardize the quotes character
  54. url = url.replace('"', "'")
  55. return url
  56. def _clean_notebook_cells(path_ntbk):
  57. """Clean up cell text of an nbformat NotebookNode."""
  58. ntbk = nbf.read(path_ntbk, nbf.NO_CONVERT)
  59. # Remove '#' from the end of markdown headers
  60. for cell in ntbk.cells:
  61. if cell.cell_type == "markdown":
  62. cell_lines = cell.source.split('\n')
  63. for ii, line in enumerate(cell_lines):
  64. if line.startswith('#'):
  65. cell_lines[ii] = line.rstrip('#').rstrip()
  66. cell.source = '\n'.join(cell_lines)
  67. nbf.write(ntbk, path_ntbk)
  68. def _error(msg):
  69. msg = '\n\n==========\n{}\n==========\n'.format(msg)
  70. raise ValueError(msg)