クラス docutils.nodes.Node

Docutils では reStructuredText 形式のデータを処理するために、中間形式として木構造のオブジェクト群を組み立てる。この木のオブジェクトの型を定義する一連のクラスは Node というスーパークラスを継承の始点とする。これらのスーパークラスとサブクラスは全てモジュール docutils.nodes で定義されている。本節ではデータ構造を中心に、ノード群の設計を解読していく。ただし、各具象ノードの構造までは深く立ち入らないで、別の節で検討したい。例えばノードオブジェクトの処理方法については、次節 クラス docutils.nodes.NodeVisitor で解読していく。

クラス図

まずは抽象クラス Node を継承グラフの始点とする簡単なクラス図を示す。ただし、本来は具象クラスが 98 個もあるにもかかわらず、一枚の図に全てを掲載することは紙幅の都合上避ける。また、理解し易さを優先するべく、説明の目的とは外れた関係、属性の記載を省略している。

classDiagram direction TB Node <|-- Text Text <|-- ConcreteText Node <|-- Element Element <|-- ConcreteElement Root <|-- document Structural <|-- document Element <|-- document %% ロールを書きたい。 Node --> Node: parent Node --> document: document Element *-- Element: children class Node{ +str source +int line +copy()* Node +deepcopy()* Node +astext()* str #setup_child(Node) +walk(NodeVisitor) +walkabout(NodeVisitor) +traverse(...) +next_node(...) Node } class Text{ +shortexpr(int) str +copy() Text +deepcopy() Text } class Element{ +astext() str +shortexpr(int) str +starttag(...) str +endtag(...) str +emptytag() str +copy() Element +deepcopy() Element } class document{ +copy() document }
  • クラス名が普通なもの (CamelCase) は抽象クラスまたは補助クラス。

  • クラス名が小文字ベースなもの (snake_case) は対応する名前の XML 要素的なものにマッチする。

  • ノードの位置は抽象クラスにより表現される?

  • 全ての具象ノードクラスの名前はリストオブジェクト doutils.nodes.node_class_names で確認可能。クラス NodeVisitor の宣言直前に書いてある。

クラス Node

クラス Node はドキュメントツリーの全ノードクラスの抽象クラスだ。

メンバーデータ

これらはクラススコープで宣言されているが、実際はむしろオブジェクトデータとして参照されるもよう。

Node.parent

このノードを直下に含むノードへの参照。メソッド setup_child を見ると、実体はノードオブジェクトらしい。

Node.document

このノードの所属するドキュメントツリーのルートノードということだ。実体はクラス document のオブジェクトだ。

Node.source

このノードを生成した入力元のファイルパスまたは説明文を示す文字列。

Node.line

このノードに対応する文字列の開始位置が source の何行目にあるかを示す正の整数。

メンバーメソッド

__bool__(self)

ノードオブジェクトを if 文に置いた時に真を返すようにしたいので、これがある。メソッド traverse を見ればわかる。

copy(self), deepcopy(self)

オーバーライド専用メソッド。デフォルトの実装はいつもの raise NotImplementedError だ。

  • copy をオーバーライドするのはサブクラス Text, Element, document, pending だけ。

  • deepcopy をオーバーライドするのはサブクラス Text, Element だけ。

walk(self, visitor), walkabout(self, visitor)

これらは クラス docutils.nodes.NodeVisitor で解説する。

traverse(self, condition=None, include_self=True, descend=True, siblings=False, ascend=False)

このノードツリーに対して、引数で指示したようなノードオブジェクトを含むリストオブジェクトを返す。キーワード引数の意味は、型が bool なものはその名前の示すとおり。

  • condition が特殊。述語関数を渡す場合と、ノードのサブクラスを型として渡す場合がある。いずれにせよ、このメソッドは condition(n) が真となるノード n をかき集める。

next_node(self, condition=None, include_self=False, descend=True, siblings=False, ascend=False)

self.traverse(...)[0] または None を返す。

  • include_self=False がデフォルト値なので注意。

サブクラス

  • 差し当たり Text, Element, document が Docutils の設計を読み解くのに特に重要なサブクラスだろう。

    • クラス Text はノードツリーの末端オブジェクトになる。子ノードも属性も存在しない。オブジェクトのコピーが可能。

    • クラス Element がノードツリーのオブジェクトの基本機能、子ノードと属性値へのアクセスのためのインターフェイスを提供する。オブジェクトのコピーが可能。

      このクラスそれ自体もいずれ解読したい。

    • クラス document はドキュメントツリーのルートノードのための型だ。これは解読するなら別ページにしたい。

  • クラス Element の下には大量のサブクラスが存在するが、インターフェイスが新規に追加されていることはほぼない。代わりに Visitor がその辺を賄うという設計だ。

  • 一般に、ノードオブジェクトを生成するのはクラス State のサブクラスのそれぞれのメソッドとなる。入力の reStructuredText データを一行一行構文解析すると同時に、順次オブジェクトを生成する。

    例えば doctest_block オブジェクトを生成するのは、クラス docutils.parsers.rst.states.Body のメソッド doctest による。生成したオブジェクトをメンバーデータであるオブジェクト parent に収納する。

  • クラス Element の継承グラフの最下層クラス群は名前が snake_case になっている。固有のコードが存在しないクラスの多重継承を多用している。

クラス Admonition

  • 直接のスーパークラスは Body ひとつ。

  • このクラス固有のコードがない。例外クラスの定義よろしく単に pass してあるだけだ。

  • 直接のサブクラスは次のものがある: attention, caution, danger, error, important, note, tip, hint, warning, admonition 以上。

    • これらのサブクラスにも固有のコードがない。

    • これらのサブクラスにはもう一つだけスーパークラスを持つ。すなわち Element だ。

クラス BackLinkable

  • このクラスにはスーパークラスがない。

  • このクラス固有のコードがない。

  • 直接のサブクラスは次のものがある: footnote, citation そして system_message だ。

    • これらのサブクラスは必ず Element をもスーパークラスに持つ。

    • クラス footnotecitation には固有のコードがない。

    • クラス system_message は少々特殊な用途があるようだ。

クラス Bibliographic

  • このクラスにはスーパークラスがない。

  • 固有のコードもない。

  • 直接のサブクラスは次のものがある:docinfo, author, authors, organization, address, contact, version, revision, status, date, copyright 以上。

    • いずれのサブクラスも固有のコードがない。

    • いずれもあと一つスーパークラスを持つ。Element, TextElement または FixedTextElement に限る。

クラス Body

  • このクラスにはスーパークラスがない。

  • 固有のコードもない。

  • 直接のサブクラスは次のものがある: General, Sequential, Admonition, Special 以上。

    • いずれのサブクラスも固有のコードがない。

    • いずれのサブクラスも Body 以外のスーパークラスがない。

クラス Decorative

  • 直接のスーパークラスは PreBibliographic ただ一つだ。

  • 固有のコードがない。

  • 直接のサブクラスは次のとおり: decoration, header, footer の三つ。

    • どのサブクラスも、もう一つのスーパークラスとして Element を継承する。

    • クラス decoration は小文字クラスにしては珍しく固有のメソッドがある。すなわち get_headerget_footer だ。どちらも子ノード列の先頭と末尾要素へのアクセスに他ならない。一方、クラス headerfooter には固有のコードがない。

クラス FixedTextElement

クラス FixedTextElement は書式化済みのテキストを直接含む要素ということだ。

  • スーパークラスは TextElement ただ一つ。

  • サブクラスとしては次のものがある: address, literal_block, doctest_block, math_block, comment, raw 以上。

    • 各サブクラスの他のスーパークラスとしては色々なパターンがある。

    • どのサブクラスにも固有のコードがない。

  • メンバーとしては __init__ がオーバーライド定義されていて、常に xml:space 属性値が preserve に設定済みになる。

クラス General

  • スーパークラスは Body ただ一つ。

  • 固有のコードがない。

  • サブクラスはかなりのものがある: paragraph, compound, container, literal_block, doctest_block, math_block, line_block, block_quote, footnote, citation, figure, table, reference, image 以上。

    • これらのサブクラスは複数のスーパークラスから継承しており、必ず Element またはそのサブクラスを派生元リストに含んでいる。

クラス Inline

文書中のインライン要素をマークアップするためのクラス群の共通スーパークラス。

  • スーパークラスおよび固有のコードがない。

  • サブクラスが多数ある。

    • クラス image を除いて、いずれもクラス TextElement からも派生している。

    • クラス image を除いて、まともなコードがない。image にはメソッド astextElement からオーバーライドされている。

クラス Invisible

出力には現れることのない内部要素のためのスーパークラス。

  • スーパークラスは PreBibliographic だ。

  • このクラスには固有のコードがない。

  • サブクラスは次のとおり: comment, substitution_definition, target, pending の以上だ。

    • いずれのサブクラスも複数のスーパークラスから派生している。

    • pending を除いてクラスに固有のコードがない。

クラス Labeled

これは最初の要素として label を含む要素を表すスーパークラスだ。

  • スーパークラスもコードもない空のクラス。

  • サブクラスは footnotecitation の二つ。

    • いずれも同じスーパークラス群から派生しており、固有のコードがないことも共通している。

クラス Part

  • スーパークラスも固有のコードもない空のクラス。

  • サブクラスが大量にある。一例を挙げると list_item, definition_list_item, term, classifier, definition, field, field_name, field_body, option, option_argument, option_group, option_list_item, option_string, description, line, attribution, label, caption, legend, tgroup, colspec, thead, tbody, row, entry といったところだ。

    • いずれももう一つのスーパークラスから派生している。型は Element または TextElement に限られている。

    • 基本的にはサブクラス固有のコードはないものが多いが、一部 option 系と line には若干のコードがある。

クラス PreBibliographic

これは Bibliographic ノードよりも前の位置に出現する文書要素のためのスーパークラスだ。

  • スーパークラスもコードもない空のクラス。

  • サブクラスは次のとおり: Decorative, Invisible, title, subtitle, system_message, raw 以上。

    • 最初の二つのサブクラスは先述のとおり。

クラス Referential

  • スーパークラスは Resolvable ただ一つ。

  • コードはない。

  • サブクラスは次の三つ: reference, footnote_reference, citation_reference 以上。

    • いずれもスーパークラス InlineTextElement から派生している。 reference だけはさらに General からも派生している。

    • サブクラス固有のコードはない。

クラス Resolvable

  • スーパークラスはない。

  • コードとしてはクラス属性 resolved = 0 が定義されているのみ。

  • サブクラスは ReferentialTargetable の二つがある。いずれのサブクラスも Resolvable 以外のスーパークラスからは派生していない。

クラス Root

これは構文木の根要素のためだけに存在するクラスと言える。

  • スーパークラスも固有のコードもない。

  • サブクラスは document ただ一つ。

    • document に関しては別の箇所で詳しく見てきたい。

クラス Sequential

これは配列のような要素を表すためのスーパークラスとして存在する。

  • スーパークラスは Body ただ一つ。

  • クラス固有のコードはない。

  • サブクラスは次のとおり: bullet_list, enumerated_list, definition_list, field_list, option_list 以上。

    • いずれもクラス Element からも派生。

    • いずれもクラス固有のコードがない。

クラス Special

何か特別な要素のためのスーパークラスということだろう。

  • スーパークラスは Body ただ一つ。

  • クラス固有のコードはない。

  • サブクラスは comment, substitution_definition, target, system_message, pending, raw だ。

クラス Structural

  • スーパークラスも固有のコードもない。

  • サブクラスは次のとおり: document, section, topic, sidebar, transition 以上。

    • いずれもクラス Element からも派生している。

    • document 以外のいずれのサブクラスにもクラス固有のコードがない。せいぜい docstring が書いてあるだけ。

クラス Targetable

  • 直接のスーパークラスは Resolvable ただ一つ。

  • クラス属性 referencedindirect_reference_name を定義する。

  • サブクラスは target, footnote, citation の三つ。

    • 複数のスーパークラスがある。

    • クラス固有のコードはない。

クラス Titular

これは見出しに関係する要素のためのスーパークラスだ。

  • スーパークラスも固有のコードもない。

  • サブクラスは title, subtitle, rubric の三つ。

    • 必ず TextElement をスーパークラスとする。

    • クラス固有のコードはない。

感想

  • 文字列 line を grep するのがたいへん難しい。

  • メソッド Node.traverse 系は generator にできるだろうか。

  • クラス Element における演算子のオーバーロードは注意したい。もしかすると elem += x で「要素 x を子要素とする」の意味があるかもしれない。

  • オブジェクト生成は構文解析時になされるので、ノートが本節で完結しない。

  • 実はクラス Element 以下の継承グラフを DOT で描いてみたのだが、何の省略の工夫もせずにグラフを定義したところ、幅 6000 ピクセル超の画像になってしまった。ブラウザーの画面幅に収まらない図表は使い物にならないので、ボツにした。