1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
|
#------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
#------------------------------------------------------------------------------
""" Generate the Example Documentation for the Enaml Examples
Run as part of the documentation build script. Requires PyQt4.
Look for example enaml files with the line:
<< autodoc-me >>
Generate an rst file, then open the app and take a snapshot.
Note: Many examples have dependencies outside of the normal Enaml dependencies.
These will need to be installed for the snapshots to be generated.
"""
from __future__ import print_function
import argparse
import os
import re
import shutil
import sys
from textwrap import dedent
from atom.api import Atom, Str, Value
import enaml
from enaml.qt.qt_application import QtApplication
from enaml.qt.QtWidgets import QApplication
from enaml.qt.QtTest import QTest
class SnapShot(Atom):
""" Generate a snapshot of an enaml view.
"""
#: The snapshot save path.
path = Str()
#: The enaml view object.
view = Value()
def _observe_view(self, change):
""" Move window and allow it to draw before taking the snapshot.
"""
if change['type'] == 'create':
self.view.initial_position = (10, 10)
self.view.always_on_top = True
def snapshot(self):
""" Take a snapshot of the window and close it.
"""
widget = self.view.proxy.widget
framesize = widget.window().frameSize()
screen = QApplication.primaryScreen()
screen.grabWindow(
QApplication.desktop().winId(),
widget.x(), widget.y(),
framesize.width(), framesize.height()
).save(self.path)
self.view.close()
def save_snapshot_of_module(mod, image_path):
view = mod.Main()
snapshot = SnapShot(path=image_path, view=view)
view.show()
QTest.qWaitForWindowExposed(view.proxy.widget)
QTest.qWait(500)
snapshot.snapshot()
QTest.qWait(100)
def extract_docstring(script_text):
""" Extract the docstring from an example Enaml script. """
# The docstring is found between the first two '"""' strings.
return script_text.split('"""')[1]
def clean_docstring(docstring):
""" Convert a docstring into ReStructuredText format. """
docstring = docstring.replace('<< autodoc-me >>\n', '').strip()
# Find backquoted identifiers, and double the backquotes to match RST.
docstring = re.sub(
pattern=r"`(?P<identifier>[A-Za-z_]+)`", # Backquoted identifiers
repl=r"``\g<identifier>``", # Double backquoted identifiers
string=docstring)
return docstring
SCREENSHOT_RST_TEMPLATE = dedent("""
Screenshot
-------------------------------------------------------------------------------
.. image:: images/{name}
""")
EXAMPLE_DOC_RST_TEMPLATE = dedent("""
..
NOTE: This RST file was generated by `make examples`.
Do not edit it directly.
See docs/source/examples/example_doc_generator.py
{title} Example
===============================================================================
{docstring_rst}
.. TIP:: To see this example in action, download it from
:download:`{name} <../../../{path}>`
and run::
$ enaml-run {name}.enaml
{screenshot_rst}
Example Enaml Code
-------------------------------------------------------------------------------
.. literalinclude:: ../../../{path}
:language: enaml
""")
def generate_example_doc(docs_path, script_path):
""" Generate an RST and a PNG for an example file.
Parameters
----------
docs_path : str
Full path to enaml/docs/source/examples
script_path : str
Full path to the example enaml file
"""
script_name = os.path.basename(script_path)
script_name = script_name[:script_name.find('.')]
print('generating doc for %s' % script_name)
script_title = script_name.replace('_', ' ').title()
script_image_name = 'ex_' + script_name + '.png'
image_path = os.path.join(docs_path, 'images', script_image_name)
rst_path = os.path.join(
docs_path, 'ex_' + script_name + '.rst')
relative_script_path = script_path[
script_path.find('examples'):].replace('\\', '/')
# Add the script to the Python Path
old_python_path = sys.path
sys.path = sys.path + [os.path.dirname(script_path)]
snapshot_success = False
with enaml.imports():
try:
mod = __import__(script_name)
save_snapshot_of_module(mod, image_path)
snapshot_success = True
except Exception as err:
print('Could not snapshot: %s' % script_name)
print(' %s' % err)
finally:
# The import leaves behind a cache. Clean it up.
enaml_cache_dir = os.path.join(
os.path.dirname(script_path), '__enamlcache__')
if os.path.exists(enaml_cache_dir):
shutil.rmtree(enaml_cache_dir)
# Restore Python path.
sys.path = old_python_path
with open(os.path.join(script_path)) as fid:
script_text = fid.read()
docstring = clean_docstring(extract_docstring(script_text))
screenshot_rst = '' if not snapshot_success else (
SCREENSHOT_RST_TEMPLATE.format(name=script_image_name))
example_doc_rst = EXAMPLE_DOC_RST_TEMPLATE.format(
title=script_title,
name=script_name,
path=relative_script_path,
docstring_rst=docstring,
screenshot_rst=screenshot_rst)
with open(rst_path, 'wb') as rst_output_file:
rst_output_file.write(example_doc_rst.lstrip().encode())
def main(white_list):
""" Generate documentation for all enaml examples requesting autodoc.
Looks in enaml/examples for all enaml files, then looks for the line:
<< auto-doc >>
If the line appears in the script, generate an RST and PNG for the example.
"""
app = QtApplication()
docs_path = os.path.dirname(__file__)
base_path = '../../../examples'
base_path = os.path.realpath(os.path.join(docs_path, base_path))
# Find all the files in the examples directory with a .enaml extension
# that contain the pragma '<< autodoc-me >>', and generate .rst files for
# them.
for dirname, dirnames, filenames in os.walk(base_path):
files = [os.path.join(dirname, f)
for f in filenames if f.endswith('.enaml')]
if white_list:
files = [f for f in files
if any([f.endswith(w) for w in white_list])]
for fname in files:
with open(fname, 'rb') as fid:
data = fid.read()
if b'<< autodoc-me >>' in data.splitlines():
generate_example_doc(docs_path, fname)
if __name__ == '__main__':
doc = ("Enaml example documentation generator.\n\n"
"By default all examples with the << auto-doc >> directive are "
"processed.")
parser = argparse.ArgumentParser(description=doc)
parser.add_argument('-f', '--filenames',
help='Files for which to generate the docs',
action='store', nargs='+')
args = parser.parse_args()
main(args.filenames)
|