Skip to content

Commit 5b57ac4

Browse files
docs: add format docs (#516)
Signed-off-by: Jinzhe Zeng <jinzhe.zeng@rutgers.edu> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 47619b7 commit 5b57ac4

4 files changed

Lines changed: 263 additions & 19 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@ _version.py
2424
__pycache__
2525
docs/_build
2626
docs/formats.csv
27+
docs/drivers.csv
28+
docs/minimizers.csv
2729
docs/api/
30+
docs/formats/

docs/formats.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,10 @@ dpdata supports the following formats:
66
.. csv-table:: Supported Formats
77
:file: formats.csv
88
:header-rows: 1
9+
10+
11+
.. toctree::
12+
:maxdepth: 1
13+
:glob:
14+
15+
formats/*

docs/make_format.py

Lines changed: 252 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import csv
2+
import os
23
from collections import defaultdict
4+
from inspect import Parameter, Signature, cleandoc, signature
5+
from typing import Literal
6+
7+
from numpydoc.docscrape import Parameter as numpydoc_Parameter
8+
from numpydoc.docscrape_sphinx import SphinxDocString
9+
10+
from dpdata.bond_order_system import BondOrderSystem
311

412
# ensure all plugins are loaded!
513
from dpdata.driver import Driver, Minimizer
614
from dpdata.format import Format
15+
from dpdata.system import LabeledSystem, MultiSystems, System
716

817

918
def get_formats() -> dict:
@@ -64,7 +73,7 @@ def get_cls_link(cls: object) -> str:
6473

6574

6675
def check_supported(fmt: Format):
67-
methods = set()
76+
methods = {}
6877
for mtd in [
6978
"from_system",
7079
"to_system",
@@ -76,44 +85,267 @@ def check_supported(fmt: Format):
7685
"to_multi_systems",
7786
]:
7887
if detect_overridden(fmt, mtd):
79-
methods.add(mtd)
88+
methods[mtd] = None
8089
if mtd == "to_system":
81-
methods.add("to_labeled_system")
90+
methods["to_labeled_system"] = None
8291
if fmt.MultiMode != fmt.MultiModes.NotImplemented:
83-
methods.add("from_multi_systems")
84-
methods.add("to_multi_systems")
85-
return methods
92+
methods["from_multi_systems"] = None
93+
methods["to_multi_systems"] = None
94+
return list(methods.keys())
8695

8796

8897
method_links = {
89-
"from_system": ":func:`System() <dpdata.system.System>`",
90-
"to_system": ":func:`System.to() <dpdata.system.System.to>`",
91-
"from_labeled_system": ":func:`LabeledSystem() <dpdata.system.LabeledSystem>`",
92-
"to_labeled_system": ":func:`LabeledSystem.to() <dpdata.system.System.to>`",
93-
"from_bond_order_system": ":func:`BondOrderSystem() <dpdata.bond_order_system.BondOrderSystem>`",
94-
"to_bond_order_system": ":func:`BondOrderSystem.to() <dpdata.system.System.to>`",
95-
"from_multi_systems": ":func:`MultiSystems.load_systems_from_file() <dpdata.system.MultiSystems.load_systems_from_file>`",
96-
"to_multi_systems": ":func:`MultiSystems.to() <dpdata.system.MultiSystems.to>`",
98+
"from_system": ":ref:`System() <{}_{}>`",
99+
"to_system": ":ref:`System.to() <{}_{}>`",
100+
"from_labeled_system": ":ref:`LabeledSystem() <{}_{}>`",
101+
"to_labeled_system": ":ref:`LabeledSystem.to() <{}_{}>`",
102+
"from_bond_order_system": ":ref:`BondOrderSystem() <{}_{}>`",
103+
"to_bond_order_system": ":ref:`BondOrderSystem.to() <{}_{}>`",
104+
"from_multi_systems": ":ref:`MultiSystems.load_systems_from_file() <{}_{}>`",
105+
"to_multi_systems": ":ref:`MultiSystems.to() <{}_{}>`",
106+
}
107+
108+
method_classes = {
109+
"from_system": "System",
110+
"to_system": "System",
111+
"from_labeled_system": "LabeledSystem",
112+
"to_labeled_system": "LabeledSystem",
113+
"from_bond_order_system": "BondOrderSystem",
114+
"to_bond_order_system": "BondOrderSystem",
115+
"from_multi_systems": "MultiSystems",
116+
"to_multi_systems": "MultiSystems",
97117
}
98118

119+
method_cls_obj = {
120+
"from_system": System,
121+
"to_system": System,
122+
"from_labeled_system": LabeledSystem,
123+
"to_labeled_system": LabeledSystem,
124+
"from_bond_order_system": BondOrderSystem,
125+
"to_bond_order_system": BondOrderSystem,
126+
"from_multi_systems": MultiSystems,
127+
"to_multi_systems": MultiSystems,
128+
}
129+
130+
131+
def generate_sub_format_pages(formats: dict):
132+
"""Generate sub format pages."""
133+
os.makedirs("formats", exist_ok=True)
134+
for format, alias in formats.items():
135+
# format: Format, alias: list[str]
136+
buff = []
137+
buff.append(".. _%s:" % format.__name__)
138+
buff.append("")
139+
for aa in alias:
140+
buff.append("%s format" % aa)
141+
buff.append("=" * len(buff[-1]))
142+
buff.append("")
143+
buff.append("Class: %s" % get_cls_link(format))
144+
buff.append("")
145+
146+
docstring = format.__doc__
147+
if docstring is not None:
148+
docstring = cleandoc(docstring)
149+
rst = str(SphinxDocString(docstring))
150+
buff.append(rst)
151+
buff.append("")
152+
153+
buff.append("Conversions")
154+
buff.append("-----------")
155+
methods = check_supported(format)
156+
for method in methods:
157+
buff.append("")
158+
buff.append(f".. _{format.__name__}_{method}:")
159+
buff.append("")
160+
if method.startswith("from_"):
161+
buff.append("Convert from this format to %s" % method_classes[method])
162+
buff.append("`" * len(buff[-1]))
163+
elif method.startswith("to_"):
164+
buff.append("Convert from %s to this format" % method_classes[method])
165+
buff.append("`" * len(buff[-1]))
166+
buff.append("")
167+
method_obj = getattr(format, method)
168+
if (
169+
method == "to_labeled_system"
170+
and method not in format.__dict__
171+
and "to_system" in format.__dict__
172+
):
173+
method_obj = getattr(format, "to_system")
174+
docstring = method_obj.__doc__
175+
if docstring is not None:
176+
docstring = cleandoc(docstring)
177+
sig = signature(method_obj)
178+
parameters = dict(sig.parameters)
179+
return_annotation = sig.return_annotation
180+
# del self
181+
del parameters[list(parameters)[0]]
182+
# del data
183+
if method.startswith("to_"):
184+
del parameters[list(parameters)[0]]
185+
if "args" in parameters:
186+
del parameters["args"]
187+
if "kwargs" in parameters:
188+
del parameters["kwargs"]
189+
if method == "to_multi_systems" or method.startswith("from_"):
190+
sig = Signature(
191+
list(parameters.values()), return_annotation=method_cls_obj[method]
192+
)
193+
else:
194+
sig = Signature(
195+
list(parameters.values()), return_annotation=return_annotation
196+
)
197+
sig = str(sig)
198+
if method.startswith("from_"):
199+
if method != "from_multi_systems":
200+
for aa in alias:
201+
parameters["fmt"] = Parameter(
202+
"fmt",
203+
Parameter.POSITIONAL_OR_KEYWORD,
204+
default=None,
205+
annotation=Literal[aa],
206+
)
207+
sig_fmt = Signature(
208+
list(parameters.values()),
209+
return_annotation=method_cls_obj[method],
210+
)
211+
sig_fmt = str(sig_fmt)
212+
buff.append(
213+
f""".. py:function:: dpdata.{method_classes[method]}{sig_fmt}"""
214+
)
215+
buff.append(""" :noindex:""")
216+
for aa in alias:
217+
buff.append(
218+
""".. py:function:: dpdata.{}.from_{}{}""".format(
219+
method_classes[method],
220+
aa.replace("/", "_").replace(".", ""),
221+
sig,
222+
)
223+
)
224+
buff.append(""" :noindex:""")
225+
buff.append("")
226+
if docstring is None or method not in format.__dict__:
227+
docstring = """ Convert this format to :class:`%s`.""" % (
228+
method_classes[method]
229+
)
230+
doc_obj = SphinxDocString(docstring)
231+
if len(doc_obj["Parameters"]) > 0:
232+
doc_obj["Parameters"] = [
233+
xx
234+
for xx in doc_obj["Parameters"]
235+
if xx.name not in ("*args", "**kwargs")
236+
]
237+
else:
238+
if method == "from_multi_systems":
239+
doc_obj["Parameters"] = [
240+
numpydoc_Parameter(
241+
"directory",
242+
"str",
243+
["directory of systems"],
244+
)
245+
]
246+
doc_obj["Yields"] = []
247+
doc_obj["Returns"] = [
248+
numpydoc_Parameter("", method_classes[method], ["converted system"])
249+
]
250+
rst = " " + str(doc_obj)
251+
buff.append(rst)
252+
buff.append("")
253+
elif method.startswith("to_"):
254+
for aa in alias:
255+
parameters = {
256+
"fmt": Parameter(
257+
"fmt",
258+
Parameter.POSITIONAL_OR_KEYWORD,
259+
annotation=Literal[aa],
260+
),
261+
**parameters,
262+
}
263+
if method == "to_multi_systems":
264+
sig_fmt = Signature(
265+
list(parameters.values()),
266+
return_annotation=method_cls_obj[method],
267+
)
268+
else:
269+
sig_fmt = Signature(
270+
list(parameters.values()),
271+
return_annotation=return_annotation,
272+
)
273+
sig_fmt = str(sig_fmt)
274+
buff.append(
275+
f""".. py:function:: dpdata.{method_classes[method]}.to{sig_fmt}"""
276+
)
277+
buff.append(""" :noindex:""")
278+
for aa in alias:
279+
buff.append(
280+
""".. py:function:: dpdata.{}.to_{}{}""".format(
281+
method_classes[method],
282+
aa.replace("/", "_").replace(".", ""),
283+
sig,
284+
)
285+
)
286+
buff.append(""" :noindex:""")
287+
buff.append("")
288+
if docstring is None or (
289+
method not in format.__dict__
290+
and not (
291+
method == "to_labeled_system"
292+
and method not in format.__dict__
293+
and "to_system" in format.__dict__
294+
)
295+
):
296+
docstring = "Convert :class:`%s` to this format." % (
297+
method_classes[method]
298+
)
299+
doc_obj = SphinxDocString(docstring)
300+
if len(doc_obj["Parameters"]) > 0:
301+
doc_obj["Parameters"] = [
302+
xx
303+
for xx in doc_obj["Parameters"][1:]
304+
if xx.name not in ("*args", "**kwargs")
305+
]
306+
else:
307+
if method == "to_multi_systems":
308+
doc_obj["Parameters"] = [
309+
numpydoc_Parameter(
310+
"directory",
311+
"str",
312+
["directory to save systems"],
313+
)
314+
]
315+
if method == "to_multi_systems":
316+
doc_obj["Yields"] = []
317+
doc_obj["Returns"] = [
318+
numpydoc_Parameter("", method_classes[method], ["this system"])
319+
]
320+
rst = " " + str(doc_obj)
321+
buff.append(rst)
322+
buff.append("")
323+
buff.append("")
324+
buff.append("")
325+
326+
with open("formats/%s.rst" % format.__name__, "w") as rstfile:
327+
rstfile.write("\n".join(buff))
328+
329+
99330
if __name__ == "__main__":
100331
formats = get_formats()
101332
with open("formats.csv", "w", newline="") as csvfile:
102333
fieldnames = [
103-
"Class",
334+
"Format",
104335
"Alias",
105-
"Supported Functions",
336+
"Supported Conversions",
106337
]
107338
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
108339

109340
writer.writeheader()
110341
for kk, vv in formats.items():
111342
writer.writerow(
112343
{
113-
"Class": get_cls_link(kk),
344+
"Format": ":ref:`%s`" % kk.__name__,
114345
"Alias": "\n".join("``%s``" % vvv for vvv in vv),
115-
"Supported Functions": "\n".join(
116-
method_links[mtd] for mtd in check_supported(kk)
346+
"Supported Conversions": "\n".join(
347+
method_links[mtd].format(kk.__name__, mtd)
348+
for mtd in check_supported(kk)
117349
),
118350
}
119351
)
@@ -151,3 +383,4 @@ def check_supported(fmt: Format):
151383
"Alias": "\n".join("``%s``" % vvv for vvv in vv),
152384
}
153385
)
386+
generate_sub_format_pages(formats)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ docs = [
5454
'm2r2',
5555
'deepmodeling-sphinx>=0.1.1',
5656
'sphinx-argparse',
57+
'rdkit',
5758
]
5859

5960
[tool.setuptools.packages.find]

0 commit comments

Comments
 (0)