mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2026-01-06 03:50:06 +01:00
- Initial commit of the Python scripts to ReSTify the XML documentation.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@70205 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
0
sphinxtools/__init__.py
Normal file
0
sphinxtools/__init__.py
Normal file
142
sphinxtools/constants.py
Normal file
142
sphinxtools/constants.py
Normal file
@@ -0,0 +1,142 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: sphinxtools/constants.py
|
||||
# Author: Andrea Gavana
|
||||
#
|
||||
# Created: 30-Nov-2010
|
||||
# Copyright: (c) 2011 by Total Control Software
|
||||
# License: wxWindows License
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# Standard library imports
|
||||
import os
|
||||
import re
|
||||
import datetime
|
||||
|
||||
# Phoenix-specific imports
|
||||
import buildtools.version as version
|
||||
|
||||
# List of strings that should be ignored when creating inline literals
|
||||
# such as ``ID_ANY`` or ``HORIZONtAL``, with double backticks
|
||||
IGNORE = ['wxPython', 'wxWidgets', 'wxOSX', 'wxMGL', 'wxDFB', 'wxMAC', 'wxGTK', 'wxGTK2', 'wxUniversal',
|
||||
'OS', 'X', 'OSX', 'DFB', 'MAC', 'GTK', 'GTK2', 'MSW', 'wxMSW', 'X11', 'OS2', 'MS', 'XP', 'GTK+',
|
||||
'UI', 'GUI', '--', 'OTOH', 'GDI+', 'API', 'NT', 'RTL', 'GDI', '3D', 'MDI']
|
||||
|
||||
# C++ stuff to Python/ReST stuff
|
||||
VALUE_MAP = {'true': '``True``',
|
||||
'false': '``False``',
|
||||
'``NULL``': '``None``',
|
||||
'NULL': '``None``',
|
||||
'L{OSX}': '`OSX`',
|
||||
'ctor': 'constructor',
|
||||
}
|
||||
|
||||
# This is a list of instances in Phoenix (i.e., without documentation strings), and
|
||||
# For the sake of beauty of the docs they get the inline literal treatment (double backticks)
|
||||
CONSTANT_INSTANCES = ['NullAcceleratorTable', 'TheApp', 'DefaultPosition', 'DefaultSize',
|
||||
'DefaultCoord', 'Coord', 'TheBrushList', 'TheColourDatabase',
|
||||
'NullFont', 'NullBrush', 'NullPalette', 'NullPen', 'EmptyString',
|
||||
'TheFontList', 'NullIcon', 'NullBitmap', 'constructor', 'ThePenList',
|
||||
'DefaultValidator', 'String.Capitalize']
|
||||
|
||||
# Phoenix full version
|
||||
VERSION = '%d.%d.%d' % (version.VER_MAJOR, version.VER_MINOR, version.VER_RELEASE)
|
||||
|
||||
# Things to chop away when ReST-ifying the docstrings
|
||||
PUNCTUATION = '!"#$%\'()*,./:;<=>?@\\^{|}~'
|
||||
|
||||
# Conversion between XML sections and ReST sections
|
||||
SECTIONS = [('return' , ':returns:'),
|
||||
('since' , '.. versionadded::'),
|
||||
('deprecated', '.. deprecated::'),
|
||||
('warning' , '.. warning::'),
|
||||
('remarks' , '.. note::'),
|
||||
('remark' , '.. note::'),
|
||||
('available' , '.. availability::'),
|
||||
('note' , '.. note::'),
|
||||
('see' , '.. seealso::'),
|
||||
('todo' , '.. todo::')]
|
||||
|
||||
|
||||
# List of things to remove/ignore (there may be more)
|
||||
REMOVED_LINKS = ['Library:', 'Category:', 'Predefined objects/pointers:']
|
||||
|
||||
# Dictionary mapping the etg module name to the real Phoenix module name
|
||||
# This needs to be kept up to date when other stuff comes in (i.e., wx.grid,
|
||||
# wx.html and so on)
|
||||
MODULENAME_REPLACE = {'_core' : '',
|
||||
'_dataview': 'dataview.'
|
||||
}
|
||||
|
||||
# Other C++ specific things to strip away
|
||||
CPP_ITEMS = ['*', '&', 'const', 'unsigned', '(size_t)', 'size_t', 'void']
|
||||
|
||||
# Serie of paths containing the input data for Sphinx and for the scripts
|
||||
# building the ReST docs:
|
||||
|
||||
# The location of the Phoenix main folder
|
||||
PHOENIXROOT = os.path.abspath(os.path.split(__file__)[0] + '/..')
|
||||
|
||||
# The location of the Sphinx main folder
|
||||
SPHINXROOT = os.path.join(PHOENIXROOT, 'docs', 'sphinx')
|
||||
|
||||
# Where the snippets found in the XML docstrings live (There are C++, unconverted and
|
||||
# converted Python snippets in 3 sub-folders
|
||||
SNIPPETROOT = os.path.join(SPHINXROOT, 'rest_substitutions', 'snippets')
|
||||
|
||||
# A folder where some of the difficult-to-translate-to-ReST tables are. There are 3 of
|
||||
# them up to now, for various reasons:
|
||||
# 1. The wx.Sizer flags table is a grid table, very difficult to ReSTify automatically
|
||||
# 2. The wx.ColourDatabase table of colour comes up all messy when ReSTified from XML
|
||||
# 3. The "wxWidgets 2.8 Compatibility Functions" table for wx.VScrolledWindow
|
||||
TABLEROOT = os.path.join(SPHINXROOT, 'rest_substitutions', 'tables')
|
||||
|
||||
# Folder where to save the inheritance diagrams for the classes
|
||||
INHERITANCEROOT = os.path.join(SPHINXROOT, '_static', 'images', 'inheritance')
|
||||
|
||||
# Folder where to save the images found in the wxWidgets overviews or in the XML
|
||||
# docstrings
|
||||
OVERVIEW_IMAGES_ROOT = os.path.join(SPHINXROOT, '_static', 'images', 'overviews')
|
||||
|
||||
# Folder where to save the widgets screenshots (full-size, no thumbnails here)
|
||||
WIDGETS_IMAGES_ROOT = os.path.join(SPHINXROOT, '_static', 'images', 'widgets', 'fullsize')
|
||||
|
||||
# Folder for the icons used for titles, sub-titles and so on for the Sphinx documentation
|
||||
SPHINX_IMAGES_ROOT = os.path.join(SPHINXROOT, '_static', 'images', 'sphinxdocs')
|
||||
|
||||
# The Doxygen root for the XML docstrings
|
||||
DOXYROOT = os.path.join(os.environ['WXWIN'], 'docs', 'doxygen')
|
||||
|
||||
# Dictionary copied over from tweaker_tools
|
||||
MAGIC_METHODS = {
|
||||
'operator!=' : '__ne__',
|
||||
'operator==' : '__eq__',
|
||||
'operator+' : '__add__',
|
||||
'operator-' : '__sub__',
|
||||
'operator*' : '__mul__',
|
||||
'operator/' : '__div__',
|
||||
'operator+=' : '__iadd__',
|
||||
'operator-=' : '__isub__',
|
||||
'operator*=' : '__imul__',
|
||||
'operator/=' : '__idiv__',
|
||||
'operator bool' : '__int__', # Why not __nonzero__?
|
||||
# TODO: add more
|
||||
}
|
||||
|
||||
# A regex to split a string keeping the whitespaces
|
||||
RE_KEEP_SPACES = re.compile(r'(\s+)')
|
||||
|
||||
# A list of things used in the post-processing of the HTML files generated by Sphinx
|
||||
# This list is used only to insert a HTML horizontal line (<hr>) after each method/function
|
||||
# description
|
||||
HTML_REPLACE = ['module', 'function', 'method', 'class', 'classmethod', 'staticmethod', 'attribute']
|
||||
|
||||
# The SVN revision of wxWidgets/Phoenix used to build the Sphinx docs.
|
||||
# There must be a more intelligent way to get this information automatically.
|
||||
SVN_REVISION = '70154'
|
||||
|
||||
# Today's date representation for the Sphinx HTML docs
|
||||
TODAY = datetime.date.today().strftime('%d %B %Y')
|
||||
|
||||
212
sphinxtools/inheritance.py
Normal file
212
sphinxtools/inheritance.py
Normal file
@@ -0,0 +1,212 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: sphinxtools/inheritance.py
|
||||
# Author: Andrea Gavana
|
||||
#
|
||||
# Created: 30-Nov-2010
|
||||
# Copyright: (c) 2011 by Total Control Software
|
||||
# License: wxWindows License
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# Standard library imports
|
||||
|
||||
import os
|
||||
import errno
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
# Phoenix-specific imports
|
||||
|
||||
from utilities import Wx2Sphinx
|
||||
from constants import INHERITANCEROOT
|
||||
|
||||
ENOENT = getattr(errno, 'ENOENT', 0)
|
||||
EPIPE = getattr(errno, 'EPIPE', 0)
|
||||
|
||||
|
||||
class InheritanceDiagram(object):
|
||||
"""
|
||||
Given a list of classes, determines the set of classes that they inherit
|
||||
from all the way to the root "object", and then is able to generate a
|
||||
graphviz dot graph from them.
|
||||
"""
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def __init__(self, class_info):
|
||||
|
||||
self.class_info, self.specials = class_info
|
||||
|
||||
|
||||
# These are the default attrs for graphviz
|
||||
default_graph_attrs = {
|
||||
'rankdir': 'LR',
|
||||
'size': '"8.0, 12.0"',
|
||||
}
|
||||
default_node_attrs = {
|
||||
'shape': 'box',
|
||||
'fontsize': 10,
|
||||
'height': 0.3,
|
||||
'fontname': 'Vera Sans, DejaVu Sans, Liberation Sans, '
|
||||
'Arial, Helvetica, sans',
|
||||
'style': '"setlinewidth(0.5)"',
|
||||
}
|
||||
default_edge_attrs = {
|
||||
'arrowsize': 0.5,
|
||||
'style': '"setlinewidth(0.5)"',
|
||||
}
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def FormatNodeAttrs(self, attrs):
|
||||
|
||||
return ','.join(['%s=%s' % x for x in attrs.items()])
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def FormatGraphAttrs(self, attrs):
|
||||
|
||||
return ''.join(['%s=%s;\n' % x for x in attrs.items()])
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def GenerateDot(self, name="dummy"):
|
||||
"""
|
||||
Generate a graphviz dot graph from the classes that were passed in to `__init__`.
|
||||
|
||||
:param string `name`: the name of the graph.
|
||||
|
||||
:rtype: `string`
|
||||
|
||||
:returns: A string representing the Graphviz dot diagram.
|
||||
"""
|
||||
|
||||
inheritance_graph_attrs = dict(fontsize=9, ratio='auto', size='""', rankdir="LR")
|
||||
inheritance_node_attrs = {"align": "center", 'shape': 'box',
|
||||
'fontsize': 10, 'height': 0.3,
|
||||
'fontname': 'Vera Sans, DejaVu Sans, Liberation Sans, '
|
||||
'Arial, Helvetica, sans', 'style': '"setlinewidth(0.5)"',
|
||||
'labelloc': 'c', 'fontcolor': 'grey45'}
|
||||
|
||||
inheritance_edge_attrs = {'arrowsize': 0.5, 'style': '"setlinewidth(0.5)"', "color": "black"}
|
||||
|
||||
g_attrs = self.default_graph_attrs.copy()
|
||||
n_attrs = self.default_node_attrs.copy()
|
||||
e_attrs = self.default_edge_attrs.copy()
|
||||
g_attrs.update(inheritance_graph_attrs)
|
||||
n_attrs.update(inheritance_node_attrs)
|
||||
e_attrs.update(inheritance_edge_attrs)
|
||||
|
||||
res = []
|
||||
res.append('digraph %s {\n' % name)
|
||||
res.append(self.FormatGraphAttrs(g_attrs))
|
||||
|
||||
for name, fullname, bases in self.class_info.values():
|
||||
# Write the node
|
||||
this_node_attrs = n_attrs.copy()
|
||||
|
||||
if name in self.specials:
|
||||
this_node_attrs['fontcolor'] = 'black'
|
||||
this_node_attrs['color'] = 'blue'
|
||||
this_node_attrs['style'] = 'bold'
|
||||
|
||||
newname, fullname = Wx2Sphinx(name)
|
||||
|
||||
this_node_attrs['URL'] = '"%s.html"'%fullname
|
||||
res.append(' "%s" [%s];\n' %
|
||||
(newname, self.FormatNodeAttrs(this_node_attrs)))
|
||||
|
||||
# Write the edges
|
||||
for base_name in bases:
|
||||
|
||||
this_edge_attrs = e_attrs.copy()
|
||||
if name in self.specials:
|
||||
this_edge_attrs['color'] = 'red'
|
||||
|
||||
base_name, dummy = Wx2Sphinx(base_name)
|
||||
|
||||
res.append(' "%s" -> "%s" [%s];\n' %
|
||||
(base_name, newname,
|
||||
self.FormatNodeAttrs(this_edge_attrs)))
|
||||
res.append('}\n')
|
||||
return ''.join(res)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def MakeInheritanceDiagram(self):
|
||||
"""
|
||||
Actually generates the inheritance diagram as a PNG file plus the corresponding
|
||||
MAP file for mouse navigation over the inheritance boxes.
|
||||
|
||||
These two files are saved into the ``INHERITANCEROOT`` folder (see `sphinxtools/constants.py`
|
||||
for more information).
|
||||
|
||||
:rtype: `tuple`
|
||||
|
||||
:returns: a tuple containing the PNG file name and a string representing the content
|
||||
of the MAP file (with newlines stripped away).
|
||||
|
||||
.. note:: The MAP file is deleted as soon as its content has been read.
|
||||
"""
|
||||
|
||||
code = self.GenerateDot()
|
||||
|
||||
# graphviz expects UTF-8 by default
|
||||
if isinstance(code, unicode):
|
||||
code = code.encode('utf-8')
|
||||
|
||||
static_root = INHERITANCEROOT
|
||||
|
||||
dot_args = ['dot']
|
||||
dummy, filename = Wx2Sphinx(self.specials[0])
|
||||
outfn = os.path.join(static_root, filename + '_inheritance.png')
|
||||
|
||||
if os.path.isfile(outfn):
|
||||
os.remove(outfn)
|
||||
if os.path.isfile(outfn + '.map'):
|
||||
os.remove(outfn + '.map')
|
||||
|
||||
dot_args.extend(['-Tpng', '-o' + outfn])
|
||||
dot_args.extend(['-Tcmapx', '-o%s.map' % outfn])
|
||||
|
||||
try:
|
||||
|
||||
p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE)
|
||||
|
||||
except OSError, err:
|
||||
|
||||
if err.errno != ENOENT: # No such file or directory
|
||||
raise
|
||||
|
||||
print '\nERROR: Graphviz command `dot` cannot be run (needed for Graphviz output), check your ``PATH`` setting'
|
||||
|
||||
try:
|
||||
# Graphviz may close standard input when an error occurs,
|
||||
# resulting in a broken pipe on communicate()
|
||||
stdout, stderr = p.communicate(code)
|
||||
|
||||
except OSError, err:
|
||||
|
||||
# in this case, read the standard output and standard error streams
|
||||
# directly, to get the error message(s)
|
||||
stdout, stderr = p.stdout.read(), p.stderr.read()
|
||||
p.wait()
|
||||
|
||||
if p.returncode != 0:
|
||||
print '\nERROR: Graphviz `dot` command exited with error:\n[stderr]\n%s\n[stdout]\n%s\n\n' % (stderr, stdout)
|
||||
|
||||
mapfile = outfn + '.map'
|
||||
|
||||
fid = open(mapfile, 'rt')
|
||||
map = fid.read()
|
||||
fid.close()
|
||||
|
||||
os.remove(mapfile)
|
||||
|
||||
return os.path.split(outfn)[1], map.replace('\n', ' ')
|
||||
|
||||
566
sphinxtools/postprocess.py
Normal file
566
sphinxtools/postprocess.py
Normal file
@@ -0,0 +1,566 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: sphinxtools/postprocess.py
|
||||
# Author: Andrea Gavana
|
||||
#
|
||||
# Created: 30-Nov-2010
|
||||
# Copyright: (c) 2011 by Total Control Software
|
||||
# License: wxWindows License
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# Standard library imports
|
||||
import os
|
||||
import re
|
||||
import cPickle
|
||||
import glob
|
||||
import random
|
||||
|
||||
# Phoenix-specific imports
|
||||
import templates
|
||||
|
||||
from utilities import Wx2Sphinx
|
||||
from constants import HTML_REPLACE, SVN_REVISION, TODAY, SPHINXROOT
|
||||
from constants import CONSTANT_INSTANCES, WIDGETS_IMAGES_ROOT, SPHINX_IMAGES_ROOT
|
||||
|
||||
|
||||
def MakeHeadings():
|
||||
"""
|
||||
Generates the "headings.inc" file containing the substitution reference
|
||||
for the small icons used in the Sphinx titles, sub-titles and so on.
|
||||
|
||||
The small icons are stored into the ``SPHINX_IMAGES_ROOT`` folder.
|
||||
|
||||
.. note:: The "headings.inc" file is created in the ``SPHINXROOT`` folder
|
||||
(see `sphinxtools/constants.py`).
|
||||
"""
|
||||
|
||||
images = glob.glob(SPHINX_IMAGES_ROOT + '/*.png')
|
||||
images.sort()
|
||||
|
||||
heading_file = os.path.join(SPHINXROOT, 'headings.inc')
|
||||
|
||||
fid = open(heading_file, 'wt')
|
||||
|
||||
for img in images:
|
||||
name = os.path.split(os.path.splitext(img)[0])[1]
|
||||
rel_path_index = img.find('_static')
|
||||
rel_path = img[rel_path_index:]
|
||||
|
||||
width = ('overload' in name and [16] or [32])[0]
|
||||
text = templates.TEMPLATE_HEADINGS % (name, os.path.normpath(rel_path), width)
|
||||
|
||||
fid.write(text)
|
||||
|
||||
fid.close()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def SphinxIndexes(sphinxDir):
|
||||
"""
|
||||
This is the main function called after the `etg` process has finished.
|
||||
|
||||
It class other functions to generate the standalone functions page, the
|
||||
main class index and some clean-up/maintenance of the generated ReST
|
||||
files.
|
||||
"""
|
||||
|
||||
pklfiles = glob.glob(sphinxDir + '/*.pkl')
|
||||
|
||||
for file in pklfiles:
|
||||
if file.endswith('functions.pkl'):
|
||||
ReformatFunctions(file)
|
||||
elif 'classindex' in file:
|
||||
MakeClassIndex(file)
|
||||
|
||||
BuildEnumsAndMethods(sphinxDir)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def BuildEnumsAndMethods(sphinxDir):
|
||||
"""
|
||||
This function does some clean-up/refactoring of the generated ReST files by:
|
||||
|
||||
1. Removing the `:meth:` reference to Enums, as they are not methods, and replacing
|
||||
it with the `:ref:` role. This information is unfortunately not known when the
|
||||
main `etgtools/sphinx_generator.py` runs.
|
||||
2. Removing the "Perl note" stuff from the text (and we should clean up the
|
||||
wxWidgets docs at the source to remove the wxPython notes as well).
|
||||
3. Substituting the `:ref:` role for unreferenced classes (these may be classes yet
|
||||
to be ported to Phoenix or C++-specific classes which will never be ported to
|
||||
Phoenix) with simple backticks.
|
||||
4. Some cleanup.
|
||||
"""
|
||||
|
||||
fid = open(os.path.join(sphinxDir, 'class_summary.lst'), 'rb')
|
||||
class_summary = cPickle.load(fid)
|
||||
fid.close()
|
||||
|
||||
unreferenced_classes = {}
|
||||
|
||||
textfiles = glob.glob(sphinxDir + '/*.txt')
|
||||
enum_files = glob.glob(sphinxDir + '/*.enumeration.txt')
|
||||
|
||||
enum_base = [os.path.split(os.path.splitext(enum)[0])[1] for enum in enum_files]
|
||||
enum_base = [enum.replace('.enumeration', '') for enum in enum_base]
|
||||
|
||||
enum_dict = {}
|
||||
|
||||
for enum in enum_base:
|
||||
enum_dict[':meth:`%s`'%enum] = ':ref:`%s`'%enum
|
||||
|
||||
for input in textfiles:
|
||||
|
||||
fid = open(input, 'rt')
|
||||
text = fid.read()
|
||||
fid.close()
|
||||
|
||||
for old, new in enum_dict.items():
|
||||
text = text.replace(old, new)
|
||||
|
||||
# Replace the "Perl Note" stuff, we don't need it
|
||||
newtext = ''
|
||||
for line in text.splitlines():
|
||||
if 'perl note' in line.lower():
|
||||
continue
|
||||
newtext += line + '\n'
|
||||
|
||||
text = newtext
|
||||
|
||||
text = FindInherited(input, class_summary, enum_base, text)
|
||||
text, unreferenced_classes = RemoveUnreferenced(input, class_summary, enum_base, unreferenced_classes, text)
|
||||
|
||||
text = text.replace('wx``', '``')
|
||||
text = text.replace('wx.``', '``')
|
||||
text = text.replace('non-NULL', 'not ``None``')
|
||||
text = text.replace(',,', ',').replace(', ,', ',')
|
||||
text = text.replace(':note:', '.. note::')
|
||||
|
||||
fid = open(input, 'wt')
|
||||
fid.write(text)
|
||||
fid.close()
|
||||
|
||||
if not unreferenced_classes:
|
||||
return
|
||||
|
||||
warn = '\n\nWARNING: there are %d instances of referenced classes/enums, via the `:ref:` role, which\n' \
|
||||
'are not in the list of available classes (these may be classes yet to be ported to Phoenix\n' \
|
||||
'or C++-specific classes which will never be ported to Phoenix).\n\n' \
|
||||
'*sphinxgenerator* has replaced the `:ref:` role for them with simple backticks, i.e.:\n\n' \
|
||||
' :ref:`MissingClass` ==> `MissingClass`\n\n' \
|
||||
'to avoid warning from Sphinx and Docutils, and saved a list of their occurrences into\n' \
|
||||
'the text file "unreferenced_classes.inc" together with the ReST file names where they\n' \
|
||||
'appear.\n\n'
|
||||
|
||||
keys = unreferenced_classes.keys()
|
||||
keys.sort()
|
||||
|
||||
fid = open(os.path.join(SPHINXROOT, 'unreferenced_classes.inc'), 'wt')
|
||||
fid.write('\n')
|
||||
fid.write('='*50 + ' ' + '='*50 + '\n')
|
||||
fid.write('%-50s %-50s\n'%('Reference', 'File Name(s)'))
|
||||
fid.write('='*50 + ' ' + '='*50 + '\n')
|
||||
|
||||
for key in keys:
|
||||
fid.write('%-50s %-50s\n'%(key, ', '.join(unreferenced_classes[key])))
|
||||
|
||||
fid.write('='*50 + ' ' + '='*50 + '\n')
|
||||
fid.close()
|
||||
|
||||
print warn%(len(keys))
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def FindInherited(input, class_summary, enum_base, text):
|
||||
|
||||
regex = re.findall(':meth:\S+', text)
|
||||
|
||||
for regs in regex:
|
||||
|
||||
hasdot = '.' in regs
|
||||
hastilde = '~' in regs
|
||||
|
||||
if regs.count('`') < 2:
|
||||
continue
|
||||
|
||||
full_name = regs[regs.index('`')+1:regs.rindex('`')]
|
||||
full_name = full_name.replace('~', '')
|
||||
|
||||
curr_class = dummy = os.path.split(os.path.splitext(input)[0])[1]
|
||||
|
||||
if hasdot:
|
||||
newstr = full_name.split('.')
|
||||
curr_class, meth_name = '.'.join(newstr[0:-1]), newstr[-1]
|
||||
else:
|
||||
meth_name = full_name
|
||||
|
||||
if meth_name == curr_class:
|
||||
newtext = ':ref:`%s`'%meth_name
|
||||
text = text.replace(regs, newtext, 1)
|
||||
continue
|
||||
|
||||
|
||||
## elif meth_name in enum_base:
|
||||
## newtext = ':ref:`%s`'%meth_name
|
||||
## text = text.replace(regs, newtext, 1)
|
||||
## continue
|
||||
|
||||
|
||||
if meth_name in CONSTANT_INSTANCES:
|
||||
text = text.replace(regs, '``%s``'%meth_name, 1)
|
||||
continue
|
||||
|
||||
if curr_class not in class_summary:
|
||||
continue
|
||||
|
||||
methods, bases = class_summary[curr_class]
|
||||
|
||||
if meth_name in methods:
|
||||
continue
|
||||
|
||||
newstr = ''
|
||||
|
||||
for cls in bases:
|
||||
if cls not in class_summary:
|
||||
continue
|
||||
|
||||
submethods, subbases = class_summary[cls]
|
||||
|
||||
if meth_name in submethods:
|
||||
if not hasdot:
|
||||
newstr = ':meth:`~%s.%s`'%(cls, meth_name)
|
||||
elif not hastilde:
|
||||
newstr = ':meth:`%s.%s`'%(cls, meth_name)
|
||||
elif hasdot:
|
||||
newstr = ':meth:`~%s.%s`'%(cls, meth_name)
|
||||
else:
|
||||
newstr = ':meth:`%s.%s`'%(cls, meth_name)
|
||||
|
||||
break
|
||||
|
||||
if newstr:
|
||||
text = text.replace(regs, newstr, 1)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def RemoveUnreferenced(input, class_summary, enum_base, unreferenced_classes, text):
|
||||
|
||||
regex = re.findall(':ref:`(.*?)`', text)
|
||||
|
||||
for reg in regex:
|
||||
if reg in class_summary or reg in enum_base:
|
||||
continue
|
||||
|
||||
if ' ' in reg or '-' in reg:
|
||||
# Leave the items with spaces/dash alone, as they are
|
||||
# Overview pages
|
||||
continue
|
||||
|
||||
if reg not in unreferenced_classes:
|
||||
unreferenced_classes[reg] = []
|
||||
|
||||
split = os.path.split(input)[1]
|
||||
if split not in unreferenced_classes[reg]:
|
||||
unreferenced_classes[reg].append(split)
|
||||
|
||||
text = text.replace(':ref:`%s`'%reg, '`%s`'%reg, 1)
|
||||
|
||||
return text, unreferenced_classes
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def ReformatFunctions(file):
|
||||
|
||||
text_file = os.path.splitext(file)[0] + '.txt'
|
||||
local_file = os.path.split(file)[1]
|
||||
|
||||
fid = open(file, 'rb')
|
||||
functions = cPickle.load(fid)
|
||||
fid.close()
|
||||
|
||||
if local_file.count('.') == 1:
|
||||
# Core functions
|
||||
label = 'Core'
|
||||
else:
|
||||
label = local_file.split('.')[0:-2][0]
|
||||
|
||||
names = functions.keys()
|
||||
names.sort()
|
||||
|
||||
text = templates.TEMPLATE_FUNCTION_SUMMARY % (label, label)
|
||||
|
||||
letters = []
|
||||
for fun in names:
|
||||
if fun[0] not in letters:
|
||||
letters.append(fun[0].upper())
|
||||
|
||||
text += ' | '.join(['`%s`_'%letter for letter in letters])
|
||||
text += '\n\n\n'
|
||||
|
||||
for letter in letters:
|
||||
text += '%s\n^\n\n'%letter
|
||||
for fun in names:
|
||||
if fun[0].upper() != letter:
|
||||
continue
|
||||
|
||||
text += '* :func:`%s`\n'%fun
|
||||
|
||||
text += '\n\n'
|
||||
|
||||
text += 'Functions\n=============\n\n'
|
||||
|
||||
for fun in names:
|
||||
text += functions[fun] + '\n'
|
||||
|
||||
fid = open(text_file, 'wt')
|
||||
fid.write(text)
|
||||
fid.close()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def MakeClassIndex(file):
|
||||
|
||||
text_file = os.path.splitext(file)[0] + '.txt'
|
||||
local_file = os.path.split(file)[1]
|
||||
|
||||
fid = open(file, 'rb')
|
||||
classes = cPickle.load(fid)
|
||||
fid.close()
|
||||
|
||||
if local_file.count('.') == 1:
|
||||
# Core functions
|
||||
label = 'Core'
|
||||
else:
|
||||
label = local_file.split('.')[0:-2][0]
|
||||
|
||||
names = classes.keys()
|
||||
names.sort()
|
||||
|
||||
text = templates.TEMPLATE_CLASS_INDEX % (label, label)
|
||||
|
||||
text += 80*'=' + ' ' + 80*'=' + '\n'
|
||||
text += '%-80s **Short Description**\n'%'**Class**'
|
||||
text += 80*'=' + ' ' + 80*'=' + '\n'
|
||||
|
||||
for cls in names:
|
||||
text += '%-80s %s\n'%(':ref:`%s`'%Wx2Sphinx(cls)[1], classes[cls])
|
||||
|
||||
text += 80*'=' + ' ' + 80*'=' + '\n'
|
||||
|
||||
fid = open(text_file, 'wt')
|
||||
fid.write(text)
|
||||
fid.close()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def GenGallery():
|
||||
|
||||
link = '<div class="gallery_class">'
|
||||
|
||||
link_template = """\
|
||||
<table><caption align="bottom"><a href="%s"<b>%s</b></a</caption>
|
||||
<tr>
|
||||
<td><a href="%s"><img src="_static/%s/%s" border="20" alt="%s"/></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
"""
|
||||
|
||||
image_folder = WIDGETS_IMAGES_ROOT
|
||||
platforms = ['wxmsw', 'wxgtk', 'wxmac']
|
||||
|
||||
image_files = {}
|
||||
|
||||
pwd = os.getcwd()
|
||||
|
||||
for folder in platforms:
|
||||
plat_folder = os.path.join(image_folder, folder)
|
||||
os.chdir(plat_folder)
|
||||
|
||||
image_files[folder] = glob.glob('*.png')
|
||||
|
||||
os.chdir(pwd)
|
||||
|
||||
txt_files = glob.glob(SPHINXROOT + '/*.txt')
|
||||
html_files = {}
|
||||
|
||||
for text in txt_files:
|
||||
simple = os.path.split(os.path.splitext(text)[0])[1]
|
||||
possible = simple.split('.')[-1]
|
||||
possible = possible.lower()
|
||||
html_files[possible + '.png'] = simple + '.html'
|
||||
|
||||
keys = html_files.keys()
|
||||
keys.sort()
|
||||
|
||||
text = ''
|
||||
|
||||
for key in keys:
|
||||
possible_png = key
|
||||
html = html_files[possible_png]
|
||||
|
||||
rand_list = range(3)
|
||||
random.shuffle(rand_list)
|
||||
|
||||
for plat_index in rand_list:
|
||||
platform = platforms[plat_index]
|
||||
plat_images = image_files[platform]
|
||||
|
||||
if possible_png in plat_images:
|
||||
text += link_template%(html, os.path.splitext(html)[0], html, platform, possible_png, os.path.splitext(html)[0])
|
||||
text += '\n'
|
||||
break
|
||||
|
||||
gallery = os.path.join(SPHINXROOT, '_templates', 'gallery.html')
|
||||
fid = open(gallery, 'wt')
|
||||
fid.write(templates.TEMPLATE_GALLERY % text)
|
||||
fid.close()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def AddPrettyTable(text):
|
||||
""" Unused at the moment. """
|
||||
|
||||
newtext = """<br>
|
||||
<table border="1" class="docutils"> """
|
||||
newtext2 = """<br>
|
||||
<table border="1" class="last docutils"> """
|
||||
|
||||
text = text.replace('<table border="1" class="docutils">', newtext)
|
||||
text = text.replace('<table border="1" class="last docutils">', newtext2)
|
||||
|
||||
othertext = """class="pretty-table">"""
|
||||
|
||||
text = text.replace('class="docutils">', othertext)
|
||||
text = text.replace('class="last docutils">', othertext)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def ClassToFile(line):
|
||||
|
||||
if '–' not in line:
|
||||
return line
|
||||
|
||||
if 'href' in line and '<li>' in line and '(' in line and ')' in line:
|
||||
indx1 = line.index('href=')
|
||||
if 'title=' in line:
|
||||
indx2 = line.rindex('title=')
|
||||
paramdesc = line[indx1+6:indx2-2]
|
||||
|
||||
if '.html#' in paramdesc:
|
||||
newparamdesc = paramdesc[:]
|
||||
lower = paramdesc.index('#') + 1
|
||||
|
||||
letter = paramdesc[lower]
|
||||
if letter.isupper():
|
||||
newparamdesc = newparamdesc[0:lower] + letter.lower() + newparamdesc[lower+1:]
|
||||
newparamdesc = newparamdesc.replace(letter, letter.lower(), 1)
|
||||
line = line.replace(paramdesc, newparamdesc)
|
||||
|
||||
return line
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def PostProcess(folder):
|
||||
|
||||
fileNames = glob.glob(folder + "/html/*.html")
|
||||
|
||||
phoenix_image = '<div class="floatcenter" style="background-color: white; text-align: middle; align: middle; padding: 40px 10px 15px 15px">\n' \
|
||||
'<img src="_static/phoenix_top.png" alt="Phoenix Logo" align="middle" />\n' \
|
||||
'</div>'
|
||||
|
||||
|
||||
enum_files = glob.glob(folder + '/html/*.enumeration.html')
|
||||
|
||||
enum_base = [os.path.split(os.path.splitext(enum)[0])[1] for enum in enum_files]
|
||||
enum_base = [enum.replace('.enumeration', '') for enum in enum_base]
|
||||
|
||||
enum_dict = {}
|
||||
# ENUMS
|
||||
|
||||
for indx, enum in enumerate(enum_base):
|
||||
html_file = os.path.split(enum_files[indx])[1]
|
||||
base = enum.split('.')[-1]
|
||||
new = '(<a class="reference internal" href="%s" title="%s"><em>%s</em></a>)'%(html_file, base, base)
|
||||
enum_dict['(<em>%s</em>)'%enum] = new
|
||||
|
||||
for files in fileNames:
|
||||
|
||||
if "genindex" in files or "modindex" in files:
|
||||
continue
|
||||
|
||||
methods_done = properties_done = False
|
||||
|
||||
fid = open(files, "rt")
|
||||
text = fid.read()
|
||||
fid.close()
|
||||
|
||||
text = text.replace('|SVN|', SVN_REVISION)
|
||||
text = text.replace('|TODAY|', TODAY)
|
||||
|
||||
split = os.path.split(files)[1]
|
||||
|
||||
if split not in ['index.html', 'main.html']:
|
||||
text = text.replace(phoenix_image, '')
|
||||
|
||||
## text = AddPrettyTable(text)
|
||||
text = text.replace('– <p>', '– ')
|
||||
text = text.replace('<p><img alt="overload"', '<br><p><img alt="overload"')
|
||||
text = text.replace('<strong>Overloaded Implementations</strong>', '<em><strong>Overloaded Implementations</strong></em>')
|
||||
text = text.replace('<strong>~~~</strong></p>', '<hr style="color:#0000FF;background-color:#0000FF;height:1px;border:none;width:50%;float:left" /></p><br>')
|
||||
|
||||
for item in HTML_REPLACE:
|
||||
text = text.replace('<dl class="%s">'%item, '<br><hr />\n<dl class="%s">'%item)
|
||||
|
||||
newtext = ''
|
||||
splitted_text = text.splitlines()
|
||||
len_split = len(splitted_text)
|
||||
|
||||
for index, line in enumerate(splitted_text):
|
||||
if '<div class="admonition-availability admonition' in line:
|
||||
line = '<div class="admonition-availability admonition availability">'
|
||||
|
||||
line = ClassToFile(line)
|
||||
|
||||
if index < len_split - 1:
|
||||
|
||||
if line.strip() == '<br><hr />':
|
||||
|
||||
next_line = splitted_text[index+1]
|
||||
stripline = next_line.strip()
|
||||
|
||||
if (stripline == '<dl class="staticmethod">' or stripline == '<dl class="method">') and not methods_done:
|
||||
line = '<br><h3>Methods<a class="headerlink" href="#methods" title="Permalink to this headline">¶</a></h3>' + '\n' + line
|
||||
methods_done = True
|
||||
|
||||
elif stripline == '<dl class="attribute">' and not properties_done:
|
||||
line = '<br><h3>Properties<a class="headerlink" href="#properties" title="Permalink to this headline">¶</a></h3>' + '\n' + line
|
||||
properties_done = True
|
||||
|
||||
newtext += line + '\n'
|
||||
|
||||
for old, new in enum_dict.items():
|
||||
newtext = newtext.replace(old, new)
|
||||
|
||||
fid = open(files, "wt")
|
||||
fid.write(newtext)
|
||||
fid.close()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
243
sphinxtools/templates.py
Normal file
243
sphinxtools/templates.py
Normal file
@@ -0,0 +1,243 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: sphinxtools/templates.py
|
||||
# Author: Andrea Gavana
|
||||
#
|
||||
# Created: 30-Nov-2010
|
||||
# Copyright: (c) 2011 by Total Control Software
|
||||
# License: wxWindows License
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# Main class description, with class name repeated twice
|
||||
TEMPLATE_DESCRIPTION = '''
|
||||
|
||||
.. _%s:
|
||||
|
||||
=====================================================================================
|
||||
|phoenix_title| **%s**
|
||||
=====================================================================================
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Inheritance diagram template, containing the class name, the PNG file representing
|
||||
# the inheritance diagram, the "ALT" HTML flag (the class name again) and the full
|
||||
# "MAP" HTML flag used for mouse navigation in the inheritance diagram boxes
|
||||
TEMPLATE_INHERITANCE = '''
|
||||
|
||||
|
|
||||
|
||||
|class_hierarchy| Inheritance Diagram
|
||||
=====================================
|
||||
|
||||
Inheritance diagram for: **%s**
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p class="graphviz">
|
||||
<img src="_static/images/inheritance/%s" alt="Inheritance diagram of %s" usemap="#dummy" class="inheritance"/>
|
||||
%s
|
||||
</p>
|
||||
|
||||
|
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template for the widget screenshots, with one screenshots image file for
|
||||
# each platform
|
||||
TEMPLATE_APPEARANCE = '''
|
||||
|appearance| Control Appearance
|
||||
===============================
|
||||
|
||||
|
|
||||
|
||||
.. figure:: _static/images/widgets/fullsize/wxmsw/%s
|
||||
:alt: wxMSW
|
||||
:figclass: floatleft
|
||||
|
||||
**wxMSW**
|
||||
|
||||
|
||||
.. figure:: _static/images/widgets/fullsize/wxmac/%s
|
||||
:alt: wxMAC
|
||||
:figclass: floatright
|
||||
|
||||
**wxMAC**
|
||||
|
||||
|
||||
.. figure:: _static/images/widgets/fullsize/wxgtk/%s
|
||||
:alt: wxGTK
|
||||
:figclass: floatcenter
|
||||
|
||||
**wxGTK**
|
||||
|
||||
|
||||
|
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template for the subclasses of a class, with a string containing a list
|
||||
# of comma separated class names with their ReST role as :ref: prepended
|
||||
TEMPLATE_SUBCLASSES = '''
|
||||
|sub_classes| Known Subclasses
|
||||
==============================
|
||||
|
||||
%s
|
||||
|
||||
|
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template for the method summary of a class, containing a table made of
|
||||
# ``method_name`` ``method description``
|
||||
TEMPLATE_METHOD_SUMMARY = '''
|
||||
|method_summary| Methods Summary
|
||||
================================
|
||||
|
||||
%s
|
||||
|
||||
|
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template for the property summary of a class, containing a table made of
|
||||
# ``property_name`` ``property description``
|
||||
TEMPLATE_PROPERTY_SUMMARY = '''
|
||||
|property_summary| Properties Summary
|
||||
=====================================
|
||||
|
||||
%s
|
||||
|
||||
|
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template for the Class API title, no input
|
||||
TEMPLATE_API = '''
|
||||
|api| Class API
|
||||
===============
|
||||
|
||||
'''
|
||||
|
||||
# Template for the standalone function summary for a module (wx, wx.dataview
|
||||
# and so on).
|
||||
|
||||
TEMPLATE_FUNCTION_SUMMARY = '''
|
||||
.. include:: headings.inc
|
||||
|
||||
=========================================================================
|
||||
**%s** Functions
|
||||
=========================================================================
|
||||
|
||||
The functions and macros defined in the **%s** module are described here: you can look up a function using the alphabetical listing of them.
|
||||
|
||||
Function Summary
|
||||
================
|
||||
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template for the main class index for a module (wx, wx.dataview and so on).
|
||||
TEMPLATE_CLASS_INDEX = '''
|
||||
.. include:: headings.inc
|
||||
|
||||
=========================================================================
|
||||
**%s** Classes
|
||||
=========================================================================
|
||||
|
||||
This is an alphabetical listing of all the classes defined in the **%s** module, together with a brief description of them (if available).
|
||||
|
||||
You can look up a class using the alphabetical listing of them.
|
||||
|
||||
|
||||
Class Summary
|
||||
=============
|
||||
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template for the class window styles, with the class name as input
|
||||
TEMPLATE_WINDOW_STYLES = '''
|
||||
|
||||
.. _%s-styles:
|
||||
|
||||
|styles| Window Styles
|
||||
================================
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template for the class window extra styles, with the class name as input
|
||||
TEMPLATE_WINDOW_EXTRASTYLES = '''
|
||||
|
||||
.. _%s-extra-styles:
|
||||
|
||||
|extra_styles| Window Extra Styles
|
||||
==================================
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template for the class events, with the class name as input
|
||||
TEMPLATE_EVENTS = '''
|
||||
|
||||
.. _%s-events:
|
||||
|
||||
|events| Events Emitted by this Class
|
||||
=====================================
|
||||
|
||||
'''
|
||||
|
||||
|
||||
# Template used to generate the widgets gallery (this needs some work)
|
||||
TEMPLATE_GALLERY = '''
|
||||
|
||||
{%% extends "layout.html" %%}
|
||||
{%% set title = "Thumbnail gallery" %%}
|
||||
|
||||
|
||||
{%% block body %%}
|
||||
|
||||
<h3>Click on any image to go to the relevant documentation</h3>
|
||||
<br/>
|
||||
The gallery is generated by randomly choosing a widget image between the 3 main
|
||||
available ports of wxPython, namely <tt>wxMSW</tt>, <tt>wxGTK</tt> and <tt>wxMAC</tt> every
|
||||
time the <b>Phoenix</b> documentation is built.
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div class="gallery_class">
|
||||
|
||||
%s
|
||||
|
||||
</div>
|
||||
<br clear="all">
|
||||
|
||||
{%% endblock %%}
|
||||
'''
|
||||
|
||||
|
||||
# Template to generate the "headings.inc" file containing the substitution reference
|
||||
# for the small icons used in the Sphinx titles, sub-titles and so on.
|
||||
TEMPLATE_HEADINGS = '''
|
||||
|
||||
.. |%s| image:: %s
|
||||
:align: middle
|
||||
:width: %dpx
|
||||
|
||||
'''
|
||||
|
||||
589
sphinxtools/utilities.py
Normal file
589
sphinxtools/utilities.py
Normal file
@@ -0,0 +1,589 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: sphinxtools/utilities.py
|
||||
# Author: Andrea Gavana
|
||||
#
|
||||
# Created: 30-Nov-2010
|
||||
# Copyright: (c) 2011 by Total Control Software
|
||||
# License: wxWindows License
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# Standard library imports
|
||||
import os
|
||||
import codecs
|
||||
import shutil
|
||||
import cPickle
|
||||
|
||||
from UserDict import UserDict
|
||||
|
||||
# Phoenix-specific imports
|
||||
from constants import IGNORE, PUNCTUATION
|
||||
from constants import CPP_ITEMS, VERSION, VALUE_MAP
|
||||
from constants import RE_KEEP_SPACES
|
||||
from constants import DOXYROOT, SPHINXROOT, WIDGETS_IMAGES_ROOT
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
class odict(UserDict):
|
||||
"""
|
||||
An ordered dict (odict). This is a dict which maintains an order to its items;
|
||||
the order is rather like that of a list, in that new items are, by default,
|
||||
added to the end, but items can be rearranged.
|
||||
|
||||
.. note:: Note that updating an item (setting a value where the key is already
|
||||
in the dict) is not considered to create a new item, and does not affect the
|
||||
position of that key in the order. However, if an item is deleted, then a
|
||||
new item with the same key is added, this is considered a new item.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, dict = None):
|
||||
self._keys = []
|
||||
UserDict.__init__(self, dict)
|
||||
|
||||
def __delitem__(self, key):
|
||||
UserDict.__delitem__(self, key)
|
||||
self._keys.remove(key)
|
||||
|
||||
def __setitem__(self, key, item):
|
||||
UserDict.__setitem__(self, key, item)
|
||||
if key not in self._keys: self._keys.append(key)
|
||||
|
||||
def clear(self):
|
||||
UserDict.clear(self)
|
||||
self._keys = []
|
||||
|
||||
def copy(self):
|
||||
dict = UserDict.copy(self)
|
||||
dict._keys = self._keys[:]
|
||||
return dict
|
||||
|
||||
def items(self):
|
||||
return zip(self._keys, self.values())
|
||||
|
||||
def keys(self):
|
||||
return self._keys
|
||||
|
||||
def popitem(self):
|
||||
try:
|
||||
key = self._keys[-1]
|
||||
except IndexError:
|
||||
raise KeyError('dictionary is empty')
|
||||
|
||||
val = self[key]
|
||||
del self[key]
|
||||
|
||||
return (key, val)
|
||||
|
||||
def setdefault(self, key, failobj = None):
|
||||
UserDict.setdefault(self, key, failobj)
|
||||
if key not in self._keys: self._keys.append(key)
|
||||
|
||||
def update(self, dict):
|
||||
UserDict.update(self, dict)
|
||||
for key in dict.keys():
|
||||
if key not in self._keys: self._keys.append(key)
|
||||
|
||||
def values(self):
|
||||
return map(self.get, self._keys)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def RemoveWxPrefix(name):
|
||||
"""
|
||||
Removes the `wx` prefix from a string.
|
||||
|
||||
:param string `name`: a string, possibly starting with "wx" or "``wx".
|
||||
|
||||
:rtype: `string`
|
||||
|
||||
.. note:: This function is similar to the one already present in `tweaker_tools`
|
||||
but I had to extend it a bit to suite the ReSTification of the XML docs.
|
||||
|
||||
"""
|
||||
|
||||
if name.startswith('wx') and not name.startswith('wxEVT_') and not name.startswith('wx.'):
|
||||
name = name[2:]
|
||||
|
||||
if name.startswith('``wx') and not name.startswith('``wxEVT_') and not name.startswith('``wx.'):
|
||||
name = name[0:2] + name[4:]
|
||||
|
||||
return name
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def IsNumeric(input_string):
|
||||
"""
|
||||
Checks if the string `input_string` actually represents a number.
|
||||
|
||||
:param string `input_string`: any string.
|
||||
|
||||
:rtype: `bool`
|
||||
|
||||
:returns: ``True`` if the `input_string` can be converted to a number (any number),
|
||||
``False`` otherwise.
|
||||
"""
|
||||
|
||||
try:
|
||||
float(input_string)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def CountSpaces(text):
|
||||
"""
|
||||
Counts the number of spaces before and after a string.
|
||||
|
||||
:param string `text`: any string.
|
||||
|
||||
:rtype: `tuple`
|
||||
|
||||
:returns: a tuple representing the number of spaces before and after the text.
|
||||
"""
|
||||
|
||||
space_before = ' '*(len(text) - len(text.lstrip(' ')))
|
||||
space_after = ' '*(len(text) - len(text.rstrip(' ')))
|
||||
|
||||
return space_before, space_after
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def Underscore2Capitals(string):
|
||||
"""
|
||||
Replaces the underscore letter in a string with the following letter capitalized.
|
||||
|
||||
:param string `string`: the string to be analyzed.
|
||||
|
||||
:rtype: `string`
|
||||
"""
|
||||
|
||||
items = string.split('_')[1:]
|
||||
newstr = ''
|
||||
|
||||
for item in items:
|
||||
newstr += item.capitalize()
|
||||
|
||||
return newstr
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def ReplaceCppItems(line):
|
||||
"""
|
||||
Replaces various C++ specific stuff with more Pythonized version of them.
|
||||
|
||||
:param string `line`: any string.
|
||||
|
||||
:rtype: `string`
|
||||
"""
|
||||
|
||||
newstr = []
|
||||
|
||||
for item in RE_KEEP_SPACES.split(line):
|
||||
|
||||
if item in CPP_ITEMS:
|
||||
continue
|
||||
|
||||
if 'wxString' in item:
|
||||
item = 'string'
|
||||
elif item == 'char':
|
||||
item = 'int'
|
||||
elif item == 'double':
|
||||
item = 'float'
|
||||
|
||||
if len(item.replace('``', '')) > 2:
|
||||
# Avoid replacing standalone '&&' and similar
|
||||
for cpp in CPP_ITEMS[0:2]:
|
||||
item = item.replace(cpp, '')
|
||||
|
||||
newstr.append(item)
|
||||
|
||||
newstr = ''.join(newstr)
|
||||
newstr = newstr.replace(' *)', ' )')
|
||||
return newstr
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def PythonizeType(ptype):
|
||||
"""
|
||||
Replaces various C++ specific stuff with more Pythonized version of them,
|
||||
for parameter lists and return types (i.e., the `:param:` and `:rtype:`
|
||||
ReST roles).
|
||||
|
||||
:param string `ptype`: any string.
|
||||
|
||||
:rtype: `string`
|
||||
"""
|
||||
|
||||
ptype = Wx2Sphinx(ReplaceCppItems(ptype))[1]
|
||||
ptype = ptype.replace('::', '.').replace('*&', '')
|
||||
ptype = ptype.replace('int const', 'int')
|
||||
ptype = ptype.replace('Uint32', 'int').replace('**', '').replace('Int32', 'int')
|
||||
ptype = ptype.replace('FILE', 'file')
|
||||
|
||||
for item in ['unsignedchar', 'unsignedint', 'unsignedlong', 'unsigned']:
|
||||
ptype = ptype.replace(item, 'int')
|
||||
|
||||
ptype = ptype.strip()
|
||||
ptype = RemoveWxPrefix(ptype)
|
||||
|
||||
if ptype.lower() == 'double':
|
||||
ptype = 'float'
|
||||
|
||||
if ptype.lower() in ['string', 'char']:
|
||||
ptype = 'string'
|
||||
|
||||
if 'Image.' in ptype:
|
||||
ptype = ptype.split('.')[-1]
|
||||
|
||||
return ptype
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def ConvertToPython(text):
|
||||
"""
|
||||
Converts the input `text` into a more ReSTified version of it.
|
||||
|
||||
This involves the following steps:
|
||||
|
||||
1. Any C++ specific declaration (like ``unsigned``, ``size_t`` and so on
|
||||
is removed.
|
||||
2. Lines starting with "Include file" or "#include" are ignored.
|
||||
3. Uppercase constants (i.e., like ID_ANY, HORIZONTAL and so on) are converted
|
||||
into inline literals (i.e., ``ID_ANY``, ``HORIZONTAL``).
|
||||
4. The "wx" prefix is removed from all the words in the input `text`.
|
||||
|
||||
:param string `text`: any string.
|
||||
|
||||
:rtype: `string`
|
||||
"""
|
||||
|
||||
newlines = []
|
||||
unwanted = ['Include file', '#include']
|
||||
|
||||
for line in text.splitlines():
|
||||
|
||||
newline = []
|
||||
|
||||
for remove in unwanted:
|
||||
if remove in line:
|
||||
line = line[0:line.index(remove)]
|
||||
break
|
||||
|
||||
spacer = ' '*(len(line) - len(line.lstrip()))
|
||||
|
||||
line = ReplaceCppItems(line)
|
||||
|
||||
for word in RE_KEEP_SPACES.split(line):
|
||||
|
||||
if word == VERSION:
|
||||
newline.append(word)
|
||||
continue
|
||||
|
||||
newword = word
|
||||
for s in PUNCTUATION:
|
||||
newword = newword.replace(s, "")
|
||||
|
||||
if newword in VALUE_MAP:
|
||||
word = word.replace(newword, VALUE_MAP[newword])
|
||||
newline.append(word)
|
||||
continue
|
||||
|
||||
if newword not in IGNORE and not newword.startswith('wx.'):
|
||||
word = RemoveWxPrefix(word)
|
||||
newword = RemoveWxPrefix(newword)
|
||||
|
||||
if "::" in word and not word.endswith("::"):
|
||||
word = word.replace("::", ".")
|
||||
word = "`%s`"%word
|
||||
newline.append(word)
|
||||
continue
|
||||
|
||||
if newword.upper() == newword and newword not in PUNCTUATION and \
|
||||
newword not in IGNORE and len(newword.strip()) > 1 and \
|
||||
not IsNumeric(newword) and newword not in ['DC', 'GCDC']:
|
||||
|
||||
if '``' not in newword and '()' not in word and '**' not in word:
|
||||
word = word.replace(newword, "``%s``"%newword)
|
||||
|
||||
newline.append(word)
|
||||
|
||||
newline = spacer + ''.join(newline)
|
||||
newline = newline.replace(':see:', '.. seealso::')
|
||||
newline = newline.replace(':note:', '.. note::')
|
||||
newlines.append(newline)
|
||||
|
||||
formatted = "\n".join(newlines)
|
||||
formatted = formatted.replace('\\', '\\\\')
|
||||
return formatted
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def FindDescendants(element, tag, descendants=None):
|
||||
"""
|
||||
Finds and returns all descendants of a specific `xml.etree.ElementTree.Element`
|
||||
whose tag matches the input `tag`.
|
||||
|
||||
:param xml.etree.ElementTree.Element `element`: the XML element we want to examine.
|
||||
:param string `tag`: the target tag we are looking for.
|
||||
:param list `descendants`: a list of already-found descendants or ``None`` if this
|
||||
is the first call to the function.
|
||||
|
||||
:rtype: `list`
|
||||
|
||||
.. note:: This is a recursive function, and it is only used in the `etgtools.extractors.py`
|
||||
script.
|
||||
|
||||
"""
|
||||
|
||||
if descendants is None:
|
||||
descendants = []
|
||||
|
||||
for childElement in element:
|
||||
if childElement.tag == tag:
|
||||
descendants.append(childElement)
|
||||
|
||||
descendants = FindDescendants(childElement, tag, descendants)
|
||||
|
||||
return descendants
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def FindControlImages(element):
|
||||
"""
|
||||
Given the input `element` (an instance of `xml.etree.ElementTree.Element`)
|
||||
representing a Phoenix class description, this function will scan the
|
||||
doxygen image folder ``DOXYROOT`` to look for a widget screenshot.
|
||||
|
||||
If this class indeed represents a widget and a screenshot is found, it is
|
||||
then copied to the appropriate Sphinx input folder ``WIDGETS_IMAGES_ROOT``
|
||||
in one of its sub-folders (``wxmsw``, ``wxgtk``, ``wxmac``) depending on
|
||||
which platform the screenshot was taken.
|
||||
|
||||
:param xml.etree.ElementTree.Element `element`: the XML element we want to examine.
|
||||
|
||||
:rtype: `list`
|
||||
|
||||
:returns: A list of image paths, every element of it representing a screenshot on
|
||||
a different platform. An empty list if returned if no screenshots have been found.
|
||||
|
||||
.. note:: If a screenshot doesn't exist for one (or more) platform but it
|
||||
exists for others, the missing images will be replaced by the "no_appearance.png"
|
||||
file (you can find it inside the ``WIDGETS_IMAGES_ROOT`` folder.
|
||||
|
||||
"""
|
||||
|
||||
class_name = RemoveWxPrefix(element.name) or element.pyName
|
||||
py_class_name = Wx2Sphinx(class_name)[1]
|
||||
|
||||
class_name = class_name.lower()
|
||||
py_class_name = py_class_name.lower()
|
||||
|
||||
image_folder = os.path.join(DOXYROOT, 'images')
|
||||
|
||||
appearance = odict()
|
||||
|
||||
for sub_folder in ['wxmsw', 'wxmac', 'wxgtk']:
|
||||
|
||||
png_file = class_name + '.png'
|
||||
appearance[sub_folder] = ''
|
||||
|
||||
possible_image = os.path.join(image_folder, sub_folder, png_file)
|
||||
new_path = os.path.join(WIDGETS_IMAGES_ROOT, sub_folder)
|
||||
|
||||
py_png_file = py_class_name + '.png'
|
||||
new_file = os.path.join(new_path, py_png_file)
|
||||
|
||||
if os.path.isfile(new_file):
|
||||
|
||||
appearance[sub_folder] = py_png_file
|
||||
|
||||
elif os.path.isfile(possible_image):
|
||||
|
||||
if not os.path.isdir(new_path):
|
||||
os.makedirs(new_path)
|
||||
|
||||
if not os.path.isfile(new_file):
|
||||
shutil.copyfile(possible_image, new_file)
|
||||
|
||||
appearance[sub_folder] = py_png_file
|
||||
|
||||
if not any(appearance.values()):
|
||||
return []
|
||||
|
||||
for sub_folder, image in appearance.items():
|
||||
if not image:
|
||||
appearance[sub_folder] = '../no_appearance.png'
|
||||
|
||||
return appearance.values()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def MakeSummary(name, item_list, template, kind):
|
||||
"""
|
||||
This function generates a table containing a method/property name
|
||||
and a shortened version of its docstrings.
|
||||
|
||||
:param string `name`: the method/property class name.
|
||||
:param list `item_list`: a list of tuples like `(method/property name, short docstrings)`.
|
||||
:param string `template`: the template to use (from `sphinxtools/templates.py`, can
|
||||
be the ``TEMPLATE_METHOD_SUMMARY`` or the ``TEMPLATE_PROPERTY_SUMMARY``.
|
||||
:param string `kind`: can be ":meth:" or ":attr:".
|
||||
|
||||
:rtype: `string`
|
||||
"""
|
||||
|
||||
summary = '='*80 + ' ' + '='*80 + "\n"
|
||||
|
||||
for method, simple_docs in item_list:
|
||||
summary += '%-80s %s'%(':%s:`~%s.%s`'%(kind, name, method), simple_docs) + '\n'
|
||||
|
||||
summary += '='*80 + ' ' + '='*80 + "\n"
|
||||
|
||||
return template % summary
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def WriteSphinxOutput(stream, filename):
|
||||
"""
|
||||
Writes the text contained in the `stream` to the `filename` output file.
|
||||
|
||||
:param StringIO.StringIO `stream`: the stream where the text lives.
|
||||
:param string `filename`: the output file we want to write the text in.
|
||||
"""
|
||||
|
||||
text_file = os.path.join(SPHINXROOT, filename)
|
||||
text = stream.getvalue()
|
||||
|
||||
if not os.path.isfile(text_file):
|
||||
text = '.. include:: headings.inc\n\n' + text
|
||||
|
||||
fid = codecs.open(text_file, "a", encoding='utf-8')
|
||||
fid.write(text)
|
||||
fid.close()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def ChopDescription(text):
|
||||
"""
|
||||
Given the (possibly multiline) input text, this function will get the
|
||||
first non-blank line up to the next newline character.
|
||||
|
||||
:param string `text`: any string.
|
||||
|
||||
:rtype: `string`
|
||||
"""
|
||||
|
||||
description = ''
|
||||
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
|
||||
if not line or line.startswith('..') or line.startswith('|'):
|
||||
continue
|
||||
|
||||
description = line
|
||||
break
|
||||
|
||||
return description
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def PickleItem(description, current_module, name, kind):
|
||||
"""
|
||||
This function pickles/unpickles a dictionary containing class names as keys
|
||||
and class brief description (chopped docstrings) as values to build the
|
||||
main class index for Sphinx **or** the Phoenix standalone function names
|
||||
as keys and their full description as values to build the function page.
|
||||
|
||||
This step is necessary as the function names/description do not come out
|
||||
in alphabetical order from the ``etg`` process.
|
||||
|
||||
:param string `description`: the function/class description.
|
||||
:param string `current_module`: the harmonized module name for this class
|
||||
or function (see ``MODULENAME_REPLACE`` in `sphinxtools/constants.py`).
|
||||
:param string `name`: the function/class name.
|
||||
:param string `kind`: can be `function` or `class`.
|
||||
"""
|
||||
|
||||
if kind == 'function':
|
||||
pickle_file = os.path.join(SPHINXROOT, current_module + 'functions.pkl')
|
||||
else:
|
||||
pickle_file = os.path.join(SPHINXROOT, current_module + '1classindex.pkl')
|
||||
|
||||
if os.path.isfile(pickle_file):
|
||||
fid = open(pickle_file, 'rb')
|
||||
items = cPickle.load(fid)
|
||||
fid.close()
|
||||
else:
|
||||
items = {}
|
||||
|
||||
items[name] = description
|
||||
fid = open(pickle_file, 'wb')
|
||||
cPickle.dump(items, fid)
|
||||
fid.close()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def PickleClassInfo(class_name, element):
|
||||
|
||||
pickle_file = os.path.join(SPHINXROOT, 'class_summary.lst')
|
||||
|
||||
if os.path.isfile(pickle_file):
|
||||
fid = open(pickle_file, 'rb')
|
||||
items = cPickle.load(fid)
|
||||
fid.close()
|
||||
else:
|
||||
items = {}
|
||||
|
||||
method_list, bases = [], []
|
||||
for method, description in element.method_list:
|
||||
method_list.append(method)
|
||||
|
||||
for base in element.bases:
|
||||
bases.append(Wx2Sphinx(base)[1])
|
||||
|
||||
items[class_name] = (method_list, bases)
|
||||
fid = open(pickle_file, 'wb')
|
||||
cPickle.dump(items, fid)
|
||||
fid.close()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
def Wx2Sphinx(name):
|
||||
"""
|
||||
Converts a wxWidgets specific string into a Phoenix-ReST-ready string.
|
||||
|
||||
:param string `name`: any string.
|
||||
"""
|
||||
|
||||
if '<' in name:
|
||||
name = name[0:name.index('<')].strip()
|
||||
|
||||
newname = fullname = RemoveWxPrefix(name)
|
||||
|
||||
if 'DataView' in name:
|
||||
fullname = 'dataview.' + fullname
|
||||
|
||||
return newname, fullname
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
Reference in New Issue
Block a user