Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update model docs to reStructuredText format #1374

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
886a32d
added tags to UserDocs extraction
terhorstd Nov 24, 2019
d748644
added hierarchy generator for tagged docs
terhorstd Nov 24, 2019
3580b0c
added index page generator stub
terhorstd Nov 24, 2019
a929fec
auto-generate tag indices
terhorstd Nov 24, 2019
3687942
refactor userdoc extractor
terhorstd Nov 24, 2019
34ee8e5
fix rst references in user docs
terhorstd Nov 24, 2019
b9abdad
split extracted rst files and wrappers
terhorstd Nov 24, 2019
f03fd22
removed tag backlinks and tag-index heading
terhorstd Nov 24, 2019
762c028
add errors if beginuserdocs enduserdocs not found, incorrect
jessica-mitchell Nov 28, 2019
4580305
remove breathe options in config
jessica-mitchell Nov 28, 2019
e3186e2
remove verbatim rst, change math to rst, add userdocs label
jessica-mitchell Dec 17, 2019
200e7ab
add rst undertitle markup
jessica-mitchell Dec 17, 2019
232b417
update in line reference to rst format and remove sli examples
jessica-mitchell Dec 17, 2019
1a8b8d2
fix up rst formatting
jessica-mitchell Dec 18, 2019
a809589
minor fixes to rst
jessica-mitchell Dec 18, 2019
9cab0b4
remove models files used for the breath doxygen generator
jessica-mitchell Dec 20, 2019
9a2999c
Merge remote-tracking branch 'upstream/nest-3' into update_models_rst…
jessica-mitchell Dec 20, 2019
452c998
Merge remote-tracking branch 'upstream/nest-3' into update_models_rst…
jessica-mitchell Jan 9, 2020
bb9d57c
pep 8
jessica-mitchell Jan 9, 2020
d0c86c6
pep8
jessica-mitchell Jan 9, 2020
081c73e
updated BeginUserDocs keywords
sarakonradi Jan 13, 2020
a116ea5
removed authors
sarakonradi Jan 14, 2020
83e355e
Changed SeeAlso: to See also
sarakonradi Jan 14, 2020
8ec6717
made length of # and + consistent with text above
sarakonradi Jan 15, 2020
cd5be12
updated blank lines before subheadings
sarakonradi Jan 16, 2020
777940e
updated subheadings of models/voltmeter.h
sarakonradi Jan 16, 2020
ebbec13
Merge pull request #11 from sarakonradi/update_models_rst_format
jessica-mitchell Jan 19, 2020
78b9b92
Update models/volume_transmitter.h
jessica-mitchell Jan 22, 2020
c3fa4eb
Update nestkernel/recording_backend_screen.h
jessica-mitchell Jan 22, 2020
a50ed08
Update nestkernel/recording_backend_memory.h
jessica-mitchell Jan 22, 2020
e7da88e
Update nestkernel/recording_backend_sionlib.h
jessica-mitchell Jan 22, 2020
d2a9ea1
Update models/hh_cond_beta_gap_traub.h
jessica-mitchell Jan 22, 2020
419ce68
Update models/hh_psc_alpha_clopath.h
jessica-mitchell Jan 22, 2020
ddd7d25
Update models/hh_psc_alpha_clopath.h
jessica-mitchell Jan 22, 2020
719321b
Update models/ac_generator.h
jessica-mitchell Jan 22, 2020
e6a5c52
Update models/dc_generator.h
jessica-mitchell Jan 22, 2020
a5c3bbc
Update models/dc_generator.h
jessica-mitchell Jan 22, 2020
63ca400
Change keywords to singular form
sarakonradi Jan 23, 2020
473f9b8
Add empty line between EndUserDocs and class definition
sarakonradi Jan 23, 2020
5262a3e
Update keywords in BeginUserDocs
sarakonradi Jan 28, 2020
31c6717
Correct iaf_psc_exp_htum model name
sarakonradi Jan 29, 2020
fda8f01
Write out AC abbreviation
sarakonradi Jan 29, 2020
f9a0acc
Add device name to devices
sarakonradi Jan 31, 2020
18012fa
Add description to devices
sarakonradi Jan 31, 2020
0ac566e
Add model name and dash
sarakonradi Jan 31, 2020
0422520
Remove synopsis
sarakonradi Jan 31, 2020
b0b766d
Write out DC abbreviation
sarakonradi Jan 31, 2020
dbe6920
Reformulate definition
sarakonradi Jan 31, 2020
c6699c1
Replace Transmits with Sends
sarakonradi Jan 31, 2020
49efbc2
Change AC current to AC input
sarakonradi Feb 12, 2020
f0b595b
Change DC Input to DC input
sarakonradi Feb 12, 2020
9032703
Retain Transmits in _connection.h files
sarakonradi Feb 12, 2020
cd9d33a
Fix ac_generator, dc_generator and volume_transmitter files
sarakonradi Feb 14, 2020
3899f2e
Merge branch 'update_models_rst_format' into update_models_rst_format
sarakonradi Feb 14, 2020
60caee6
Merge pull request #12 from sarakonradi/update_models_rst_format
jessica-mitchell Mar 4, 2020
d7e3a45
Update documentation of iaf_chxk_2008 in the "update_models_rst_forma…
heplesser Mar 18, 2020
94913f0
Merge pull request #13 from heplesser/jm-update_models_rst_format
jessica-mitchell Mar 18, 2020
941f515
Merge branch 'master' into update_models_rst_format
jessica-mitchell Mar 19, 2020
b00d3d5
Merge branch 'update_models_rst_format' of https://github.com/jessica…
jessica-mitchell Mar 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 15 additions & 29 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
sys.path.insert(0, os.path.abspath(root_path + '/pynest/nest'))
sys.path.insert(0, os.path.abspath(doc_path))


# -- Mock pynestkernel ----------------------------------------------------
# The mock_kernel has to be imported after setting the correct sys paths.
from mock_kernel import convert # noqa
Expand Down Expand Up @@ -101,8 +100,6 @@

breathe_default_project = "EXTRACT_MODELS"

subprocess.call('doxygen', shell=True)

mathjax_path = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS-MML_HTMLorMML" # noqa

# Add any paths that contain templates here, relative to this directory.
Expand Down Expand Up @@ -197,6 +194,17 @@

intersphinx_mapping = {'https://docs.python.org/': None}

from doc.extractor_userdocs import ExtractUserDocs, relative_glob # noqa


def config_inited_handler(app, config):
ExtractUserDocs(
relative_glob("models/*.h", "nestkernel/*.h", basedir='..'),
outdir="from_cpp/"
)
ConvertMarkdownFiles()


nitpick_ignore = [('py:class', 'None'),
('py:class', 'optional'),
('py:class', 's'),
Expand All @@ -215,6 +223,10 @@ def setup(app):
app.add_stylesheet('css/pygments.css')
app.add_javascript("js/copybutton.js")
app.add_javascript("js/custom.js")
# for events see
# https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx-core-events
app.connect('config-inited', config_inited_handler)


# -- Options for LaTeX output ---------------------------------------------

Expand Down Expand Up @@ -268,29 +280,3 @@ def setup(app):
]

# -- Options for readthedocs ----------------------------------------------

models_with_documentation = (
"models/multimeter",
"models/spike_detector",
"models/weight_recorder",
"nestkernel/recording_backend_ascii",
"nestkernel/recording_backend_memory",
"nestkernel/recording_backend_screen",
"nestkernel/recording_backend_sionlib",
)

pattern = r'BeginDocumentation((?:.|\n)*)EndDocumentation'
for model in models_with_documentation:
with open("../%s.h" % model) as f:
match = re.search(pattern, f.read())
if match:
rst_dir = "from_cpp/"
if not os.path.exists(rst_dir):
os.mkdir(rst_dir)
rst_fname = rst_dir + os.path.basename(model) + ".rst"
rst_file = open(rst_fname, "w")
rst_file.write(match.group(1))
rst_file.close()
print("Wrote model documentation for model " + model)
else:
print("No documentation found for model " + model)
272 changes: 272 additions & 0 deletions doc/extractor_userdocs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# -*- coding: utf-8 -*-
#
# extractor_userdocs.py
#
# This file is part of NEST.
#
# Copyright (C) 2004 The NEST Initiative
#
# NEST is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# NEST is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see <http://www.gnu.org/licenses/>.

import re
from pprint import pformat
import os
import glob
import json
from itertools import chain, combinations
import logging
logging.basicConfig(level=logging.INFO)
log = logging.getLogger()


def relative_glob(*pattern, basedir=os.curdir, **kwargs):
tobase = os.path.relpath(basedir, os.curdir)
tohere = os.path.relpath(os.curdir, basedir)
# prefix all patterns with basedir and expand
names = chain(*[glob.glob(os.path.join(tobase, pat), **kwargs) for pat in pattern])
# remove prefix from all expanded names
return [name[len(tobase)+1:] for name in names]


def UserDocExtractor(
filenames,
basedir="..",
replace_ext='.rst',
outdir="from_cpp/"
):
"""
Extract all user documentation from given files.

This method searches for "BeginUserDocs" and "EndUserDocs" keywords and
extracts all text inbetween as user-level documentation. The keyword
"BeginUserDocs" may optionally be followed by a colon ":" and a comma
separated list of tags till the end of the line.

Example
-------

/* BeginUserDocs: example, user documentation generator

[...]

EndUserDocs */

This will extract "[...]" as documentation for the file and tag it with
'example' and 'user documentation generator'.

The extracted documentation is written to a file in `basedir` named after
the sourcefile with ".rst" replacing the original extension.

Parameters
----------

filenames
: Any iterable with input file names (relative to `basedir`).
basedir
: Directory to which input `filenames` are relative.
replace_ext
: Replacement for the extension of the original filename when writing to `outdir`.
outdir
: Directory where output files are created.

Returns
-------
Dictionary mapping tags to lists of documentation filenames (relative to
`outdir`).
"""
if not os.path.exists(outdir):
log.info("creating output directory "+outdir)
os.mkdir(outdir)
userdoc_re = re.compile(r'BeginUserDocs:?\s*(?P<tags>(\w+(,\s*)?)*)\n+(?P<doc>(.|\n)*)EndUserDocs')
tagdict = dict() # map tags to lists of documents
nfiles_total = 0
for filename in filenames:
log.info("extracting user documentation from %s...", filename)
nfiles_total += 1
match = None
with open(os.path.join(basedir, filename)) as infile:
match = userdoc_re.search(infile.read())
if not match:
log.warning("No user documentation found in " + filename)
continue
outname = os.path.basename(os.path.splitext(filename)[0]) + replace_ext
tags = [t.strip() for t in match.group('tags').split(',')]
for tag in tags:
tagdict.setdefault(tag, list()).append(outname)
write_rst_files(match.group('doc'), tags, outdir, outname)

log.info("%4d tags found", len(tagdict))
log.info(" "+pformat(list(tagdict.keys())))
nfiles = len(set.union(*[set(x) for x in tagdict.values()]))
log.info("%4d files in input", nfiles_total)
log.info("%4d files with documentation", nfiles)
return tagdict


def write_rst_files(doc, tags, outdir, outname):
"""
Write raw rst to a file and generate a wrapper with index
"""
with open(os.path.join(outdir, outname), "w") as outfile:
outfile.write(doc)


def make_hierarchy(tags, *basetags):
"""
This method adds a single level of hierachy to the given dictionary.

First a list of items with given basetags is created (intersection). Then
this list is subdivided into sections by creating intersections with all
remaining tags.

Parameters
----------
tags
: flat dictionary of tag to entry

basetags
: iterable of a subset of tags.keys(), if no basetags are given the
original tags list is returned unmodified.

Returns a hierarchical dictionary of (dict or set) with items in the
intersection of basetag.
"""
if not basetags:
return tags

# items having all given basetags
baseitems = set.intersection(*[set(items) for tag, items in tags.items() if tag in basetags])
tree = dict()
subtags = [t for t in tags.keys() if t not in basetags]
for subtag in subtags:
docs = set(tags[subtag]).intersection(baseitems)
if docs:
tree[subtag] = docs
remaining = None
if tree.values():
remaining = baseitems.difference(set.union(*tree.values()))
if remaining:
tree[''] = remaining
return {basetags: tree}


def rst_index(hierarchy, underlines='=-~'):
"""
Create an index page from a given hierarchical dict of documents.

The given `hierarchy` is pretty-printed and returned as a string.

Parameters
----------
hierarchy
: any dict or dict-of-dict returned from `make_hierarchy()`
underlines
: list of characters to use for underlining deeper levels of the generated
index.

Returns
-------
String with pretty index.
"""
def mktitle(t, ul):
return t+'\n'+ul*len(t)+'\n'

def mkitem(t):
return "* :doc:`%s`" % os.path.splitext(t)[0]

output = list()
for tags, items in sorted(hierarchy.items()):
if isinstance(tags, str):
title = tags
else:
title = " & ".join(tags)
if title:
if title != title.upper():
title = title.title() # title-case any tag that is not an acronym
output.append(mktitle(title, underlines[0]))
if isinstance(items, dict):
output.append(rst_index(items, underlines[1:]))
else:
for item in items:
output.append(mkitem(item))
output.append("")
return "\n".join(output)


def reverse_dict(tags):
"""
return the reversed dict-of-list
"""
revdict = dict()
for tag, items in tags.items():
for item in items:
revdict.setdefault(item, list()).append(tag)
return revdict


def CreateTagIndices(tags, outdir="from_cpp/"):
taglist = list(tags.keys())
if "" in taglist:
taglist.remove('')
indexfiles = list()
for current_tags in chain(*[combinations(taglist, L) for L in range(len(taglist)-1)]):
current_tags = sorted(current_tags)
indexname = "index%s.rst" % "".join(["_"+x for x in current_tags])

hier = make_hierarchy(tags.copy(), *current_tags)
if not any(hier.values()):
log.debug("index %s is empyt!", str(current_tags))
continue
log.debug("generating index for %s...", str(current_tags))
indextext = rst_index(hier)
with open(os.path.join(outdir, indexname), 'w') as outfile:
outfile.write(indextext)
indexfiles.append(indexname)
log.info("%4d index files generated", len(indexfiles))
return indexfiles


class JsonWriter(object):
"""
Helper class to have a unified data output interface.
"""
def __init__(self, outdir):
self.outdir = outdir
log.info("writing JSON files to %s", self.outdir)

def write(self, obj, name):
"""
Store the given object with the given name.
"""
outname = os.path.join(self.outdir, name + ".json")
with open(outname, 'w') as outfile:
json.dump(obj, outfile)
log.info("data saved as " + outname)


def ExtractUserDocs(listoffiles, basedir='..', outdir='from_cpp'):
"""
Extract and build all user documentation and build tag indices.
"""
data = JsonWriter(outdir)
# Gather all information and write RSTs
tags = UserDocExtractor(listoffiles, basedir=basedir, outdir=outdir)
data.write(tags, "tags")

indexfiles = CreateTagIndices(tags, outdir=outdir)
data.write(indexfiles, "indexfiles")


if __name__ == '__main__':
ExtractUserDocs(relative_glob("models/*.h", "nestkernel/*.h", basedir='..'), outdir="from_cpp/")
5 changes: 0 additions & 5 deletions doc/models/aeif.rst

This file was deleted.

5 changes: 0 additions & 5 deletions doc/models/binary.rst

This file was deleted.

5 changes: 0 additions & 5 deletions doc/models/clopath_neuron.rst

This file was deleted.

5 changes: 0 additions & 5 deletions doc/models/clopath_synapse.rst

This file was deleted.

5 changes: 0 additions & 5 deletions doc/models/conductance.rst

This file was deleted.

5 changes: 0 additions & 5 deletions doc/models/cont_delay.rst

This file was deleted.

12 changes: 0 additions & 12 deletions doc/models/create_model.rst

This file was deleted.

Loading