Source code for docfly.doctree

# -*- coding: utf-8 -*-

"""
Create doc tree if you follows

:ref:`Sanhe Sphinx standard <en_sphinx_doc_style_guide>`.
"""

from __future__ import print_function
import json

from pathlib_mate import PathCls as Path

from .template import TC
from .pkg import textfile


[docs]class ArticleFolder(object): """ Represent an ``index.rst`` or ``index.ipynb`` file with a Title in a directory. :param index_file: the index file name (no file extension) :param dir_path: A folder contains single rst file. The rst file path **中文文档** 一篇 Article 代表着文件夹中有一个 ``index.rst`` 或 ``index.ipynb`` 文件的文件夹. 其中必然有至少一个标题元素. """ DEFAULT_INDEX_FILE = "index" def __init__(self, index_file=None, dir_path=None): if index_file is None: index_file = self.DEFAULT_INDEX_FILE self.index_file = index_file self.dir_path = dir_path self._title = None @property def rst_path(self): """ The actual rst file absolute path. """ return Path(self.dir_path, self.index_file + ".rst").abspath @property def ipynb_path(self): """ The actual ipynb file absolute path. """ return Path(self.dir_path, self.index_file + ".ipynb").abspath @property def rel_path(self): """ File relative path from the folder. """ return "{}/{}".format(Path(self.dir_path).basename, self.index_file) @property def title(self): """ Title for the first header. """ if self._title is None: if Path(self.rst_path).exists(): self._title = self.get_title_from_rst() elif Path(self.ipynb_path).exists(): self._title = self.get_title_from_ipynb() else: pass return self._title
[docs] def get_title_from_rst(self): """ Get title line from .rst file. **中文文档** 从一个 ``_filename`` 所指定的 .rst 文件中, 找到顶级标题. 也就是第一个 ``====`` 或 ``----`` 或 ``~~~~`` 上面一行. """ header_bar_char_list = "=-~+*#^" lines = list() for cursor_line in textfile.readlines( self.rst_path, strip="both", encoding="utf-8" ): if cursor_line.startswith(".. include::"): relative_path = cursor_line.split("::")[-1].strip() included_path = Path(Path(self.rst_path).parent.abspath, relative_path) if included_path.exists(): cursor_line = included_path.read_text(encoding="utf-8") lines.append(cursor_line) rst_content = "\n".join(lines) cursor_previous_line = None for cursor_line in rst_content.split("\n"): for header_bar_char in header_bar_char_list: if cursor_line.startswith(header_bar_char): flag_full_bar_char = cursor_line == header_bar_char * len( cursor_line ) flag_line_length_greather_than_1 = len(cursor_line) >= 1 flag_previous_line_not_empty = bool(cursor_previous_line) if ( flag_full_bar_char and flag_line_length_greather_than_1 and flag_previous_line_not_empty ): return cursor_previous_line.strip() cursor_previous_line = cursor_line msg = ( "Warning, this document doesn't have any %s header!" % header_bar_char_list ) return None
[docs] def get_title_from_ipynb(self): """ Get title line from .ipynb file. **中文文档** 从一个 ``_filename`` 所指定的 .ipynb 文件中, 找到顶级标题. 也就是第一个 ``#`` 后面的部分. 有的时候我们会用 raw RestructuredText 来做顶级标题. """ header_bar_char_list = "=-~+*#^" data = json.loads(Path(self.ipynb_path).read_text()) for row in data["cells"]: if len(row["source"]): if row.get("cell_type") == "markdown": content = row["source"][0] line = content.split("\n")[0] if "# " in line: return line[2:].strip() elif ( row.get("cell_type") == "raw" and row.get("metadata", {}).get("raw_mimetype", "unknown") == "text/restructuredtext" ): try: line = row["source"][3].strip() except IndexError: continue try: title_line = row["source"][2].strip() except IndexError: continue for header_bar_char in header_bar_char_list: if line.startswith(header_bar_char): flag_full_bar_char = line == header_bar_char * len(line) flag_line_length_greather_than_1 = len(line) >= 1 flag_previous_line_not_empty = bool(title_line) if ( flag_full_bar_char and flag_line_length_greather_than_1 and flag_previous_line_not_empty ): return title_line else: pass msg = "Warning, this document doesn't have any level 1 header!" return None
@property def sub_article_folders(self): """ Returns all valid ArticleFolder sitting inside of :attr:`ArticleFolder.dir_path`. """ l = list() for p in Path.sort_by_fname(Path(self.dir_path).select_dir(recursive=False)): af = ArticleFolder(index_file=self.index_file, dir_path=p.abspath) try: if af.title is not None: l.append(af) except: pass return l
[docs] def toc_directive(self, maxdepth=1): """ Generate toctree directive text. :param table_of_content_header: :param header_bar_char: :param header_line_length: :param maxdepth: :return: """ articles_directive_content = TC.toc.render( maxdepth=maxdepth, article_list=self.sub_article_folders, ) return articles_directive_content
def __repr__(self): return "Article(index_file=%r, title=%r)" % ( self.index_file, self.title, )