mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2025-09-04 17:00:22 +02:00
Pulls out the data into a separate class, `tweaker_tools.Signature`. This simplifies stringizing the args string, and allows for some checks to be written in a much less complex way. The motivator for this was adding unions to type-hints (for the upcoming automatically converted python->wx types). This made parsing the already stringized args string very complex to be able to handle the potential for commas in `Union`.
3606 lines
117 KiB
Python
3606 lines
117 KiB
Python
# -*- coding: utf-8 -*-
|
|
#---------------------------------------------------------------------------
|
|
# Name: etgtools/sphinx_generator.py
|
|
# Author: Andrea Gavana
|
|
#
|
|
# Created: 30-Nov-2010
|
|
# Copyright: (c) 2010-2020 by Total Control Software
|
|
# License: wxWindows License
|
|
#---------------------------------------------------------------------------
|
|
|
|
"""
|
|
This generator will create the docstrings for Sphinx to process, by refactoring
|
|
the various XML elements passed by the Phoenix extractors into ReST format.
|
|
"""
|
|
|
|
# Standard library stuff
|
|
import keyword
|
|
import os
|
|
import operator
|
|
import sys
|
|
import shutil
|
|
import textwrap
|
|
|
|
from io import StringIO
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
# Phoenix-specific stuff
|
|
import etgtools.extractors as extractors
|
|
import etgtools.generators as generators
|
|
from etgtools.item_module_map import ItemModuleMap
|
|
from etgtools.tweaker_tools import removeWxPrefix, ParameterType
|
|
|
|
# Sphinx-Phoenix specific stuff
|
|
from sphinxtools.inheritance import InheritanceDiagram
|
|
from sphinxtools import templates
|
|
|
|
from sphinxtools.utilities import ODict
|
|
from sphinxtools.utilities import convertToPython
|
|
from sphinxtools.utilities import writeSphinxOutput
|
|
from sphinxtools.utilities import findControlImages, makeSummary, pickleItem
|
|
from sphinxtools.utilities import chopDescription, pythonizeType, wx2Sphinx
|
|
from sphinxtools.utilities import pickleClassInfo, pickleFunctionInfo, isNumeric
|
|
from sphinxtools.utilities import underscore2Capitals, countSpaces
|
|
from sphinxtools.utilities import formatContributedSnippets
|
|
from sphinxtools.utilities import PickleFile
|
|
from sphinxtools.utilities import textfile_open
|
|
|
|
from sphinxtools.constants import VERSION, REMOVED_LINKS, SECTIONS
|
|
from sphinxtools.constants import MAGIC_METHODS, MODULENAME_REPLACE
|
|
from sphinxtools.constants import IGNORE
|
|
from sphinxtools.constants import SPHINXROOT, DOXYROOT
|
|
from sphinxtools.constants import SNIPPETROOT, TABLEROOT, OVERVIEW_IMAGES_ROOT
|
|
from sphinxtools.constants import DOCSTRING_KEY
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Node(object):
|
|
"""
|
|
This is the base class of all the subsequent classes in this script, and it
|
|
holds information about a XML element coming from doxygen and this `Node`
|
|
parent element (another `Node`).
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen.
|
|
:param Node `parent`: the parent node, or ``None``.
|
|
"""
|
|
|
|
self.element = element
|
|
self.parent = parent
|
|
|
|
self.children = []
|
|
|
|
if parent is not None:
|
|
parent.Add(self)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Add(self, node):
|
|
"""
|
|
Adds a node to its children list.
|
|
|
|
:param Node `node`: another `Node` class.
|
|
"""
|
|
|
|
self.children.append(node)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def GetParent(self):
|
|
"""
|
|
Returns this node parent or ``None`` if it has no parent.
|
|
|
|
:rtype: :class:`Node`
|
|
"""
|
|
|
|
return self.parent
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def GetTopLevelParent(self):
|
|
"""
|
|
Returns the top level ancestor for this node or ``None``. If the ancestor
|
|
is not ``None``, then it should be an instance of :class:`Root`.
|
|
|
|
:rtype: :class:`Node`
|
|
"""
|
|
|
|
parent = self.parent
|
|
|
|
while 1:
|
|
|
|
if parent is None:
|
|
return
|
|
|
|
if isinstance(parent, Root):
|
|
return parent
|
|
|
|
parent = parent.parent
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def GetTag(self, tag_name):
|
|
"""
|
|
Given a `tag_name` for the element stored in this node, return the content
|
|
of that tag (or ``None`` if this element has no such tag).
|
|
|
|
:param string `tag_name`: the element tag name.
|
|
|
|
:returns: The element text for the input `tag_name` or ``None``.
|
|
"""
|
|
|
|
if isinstance(self.element, str):
|
|
return None
|
|
|
|
return self.element.get(tag_name)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def GetHierarchy(self):
|
|
"""
|
|
Returns a list of strings representing this node hierarchy up to the :class:`Root`
|
|
node.
|
|
|
|
:rtype: `list`
|
|
"""
|
|
|
|
hierarchy = [self.__class__.__name__]
|
|
parent = self.parent
|
|
|
|
while parent:
|
|
hierarchy.append(parent.__class__.__name__)
|
|
parent = parent.parent
|
|
|
|
return hierarchy
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def IsClassDescription(self):
|
|
"""
|
|
Returns a non-empty string if this node holds information about a class general description
|
|
(i.e., its `element` attribute does not contain information on a method, a property,
|
|
and so on).
|
|
|
|
This is needed to resolve ReST link conflicts in the :class:`XRef` below.
|
|
|
|
:rtype: `string`.
|
|
"""
|
|
|
|
top_level = self.GetTopLevelParent()
|
|
|
|
if top_level is None:
|
|
return ''
|
|
|
|
xml_docs = top_level.xml_docs
|
|
|
|
if xml_docs.kind != 'class':
|
|
return ''
|
|
|
|
dummy, class_name = wx2Sphinx(xml_docs.class_name)
|
|
return class_name
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Find(self, klass, node=None):
|
|
"""
|
|
This method returns ``True`` if this node contains a specific class into its
|
|
descendants.
|
|
|
|
:param `klass`: can be any of the classes defined in this script except :class:`XMLDocString`.
|
|
:param `node`: another `Node` instance or ``None`` if this is the first invocation of
|
|
this function.
|
|
|
|
:rtype: `bool`
|
|
|
|
.. note:: This is a recursive method.
|
|
|
|
"""
|
|
|
|
|
|
if node is None:
|
|
node = self
|
|
|
|
for child in node.children:
|
|
if isinstance(child, klass):
|
|
return True
|
|
|
|
return self.Find(klass, child)
|
|
|
|
return False
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def GetSpacing(self):
|
|
|
|
hierarchy = self.GetHierarchy()
|
|
if 'ParameterList' in hierarchy:
|
|
return ' '
|
|
|
|
elif not isinstance(self, ListItem) and 'List' in hierarchy:
|
|
return ' '*2
|
|
|
|
return ''
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
text = tail = ''
|
|
|
|
if self.element is None:
|
|
return text
|
|
|
|
if isinstance(self.element, str):
|
|
text = self.element
|
|
else:
|
|
text, tail = self.element.text, self.element.tail
|
|
text = (text is not None and [text] or [''])[0]
|
|
tail = (tail is not None and [tail] or [''])[0]
|
|
|
|
for link in REMOVED_LINKS:
|
|
if link in text.strip():
|
|
return ''
|
|
|
|
text = convertToPython(text)
|
|
|
|
for child in self.children:
|
|
text += child.Join(with_tail)
|
|
|
|
if with_tail and tail:
|
|
text += convertToPython(tail)
|
|
|
|
if text.strip() and not text.endswith('\n'):
|
|
text += ' '
|
|
|
|
return text
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Root(Node):
|
|
"""
|
|
This is the root class which has as its children all the other classes in
|
|
this script (excluding :class:`XMLDocString`). It is used to hold information
|
|
about an XML Doxygen item describing a class, a method or a function.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, xml_docs, is_overload, share_docstrings):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param XMLDocString `xml_docs`: an instance of :class:`XMLDocString`.
|
|
:param bool `is_overload`: ``True`` if the root node describes an overloaded
|
|
method/function, ``False`` otherwise.
|
|
:param bool `share_docstrings`: ``True`` if all the overloaded methods/functions
|
|
share the same docstrings.
|
|
"""
|
|
|
|
Node.__init__(self, '', None)
|
|
|
|
self.xml_docs = xml_docs
|
|
self.is_overload = is_overload
|
|
self.share_docstrings = share_docstrings
|
|
|
|
self.sections = ODict()
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Insert(self, node, before=None, after=None, dontcare=True):
|
|
"""
|
|
Inserts a node into the root children, depending of the `before` and `after`
|
|
input arguments.
|
|
"""
|
|
|
|
insert_at = -1
|
|
|
|
for index, child in enumerate(self.children):
|
|
if (before and child.Find(before)) or (after and child.Find(after)):
|
|
insert_at = index
|
|
break
|
|
|
|
node.parent = self
|
|
|
|
if insert_at >= 0:
|
|
if before:
|
|
self.children.insert(insert_at, node)
|
|
else:
|
|
self.children.insert(insert_at+1, node)
|
|
else:
|
|
if dontcare:
|
|
self.children.append(node)
|
|
else:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
if self.is_overload and self.share_docstrings:
|
|
# If it is an overloaded method and the docstrings are shared, we only return
|
|
# information about the parameter list and admonition sections
|
|
return self.CommonJoin(self, '')
|
|
|
|
text = Node.Join(self, with_tail)
|
|
|
|
# Health check
|
|
existing_sections = list(self.sections)
|
|
|
|
for section_name, dummy in SECTIONS:
|
|
if section_name not in self.sections:
|
|
continue
|
|
|
|
existing_sections.remove(section_name)
|
|
for section in self.sections[section_name]:
|
|
text += '\n\n%s\n\n' % section.Join()
|
|
|
|
# Health check
|
|
if any(existing_sections):
|
|
raise Exception('Unconverted sections remain: %s'%(', '.join(existing_sections)))
|
|
|
|
return text
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def CommonJoin(self, node, docstrings):
|
|
"""
|
|
Selectively join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile but only if they are instances of :class:`ParameterList`
|
|
or :class:`Section`.
|
|
|
|
:param `node`: either `self` or a child node.
|
|
:param string `docstrings`: the resulting docstrings obtained (they will go as output as well).
|
|
|
|
:rtype: `string`
|
|
|
|
.. note:: This is a recursive method.
|
|
"""
|
|
|
|
for child in node.children:
|
|
if isinstance(child, (ParameterList, Section)):
|
|
docstrings += child.Join()
|
|
|
|
docstrings = self.CommonJoin(child, docstrings)
|
|
|
|
return docstrings
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def AddSection(self, section):
|
|
"""
|
|
Adds an admonition section to the root node (i.e., `.. seealso::`, `.. note::`,
|
|
`:returns:` and so on).
|
|
|
|
Admonitions are somewhat different from other nodes as they need to be refactored and
|
|
handled differently when, for example, the return value of a method is different in Phoenix
|
|
than in wxWidgets or when the XML docs are a mess and an admonition ends up into
|
|
a tail of an xml element...
|
|
|
|
:param Section `section`: an instance of :class:`Section`.
|
|
|
|
"""
|
|
|
|
kind = section.section_type
|
|
|
|
if kind == 'return':
|
|
self.sections[kind] = [section]
|
|
|
|
elif kind == 'available':
|
|
|
|
if kind not in self.sections:
|
|
|
|
text = section.element.text
|
|
text = text.split(',')
|
|
newtext = []
|
|
for t in text:
|
|
newtext.append(t[2:].upper())
|
|
|
|
newtext = ', '.join(newtext)
|
|
newtext = 'Only available for %s'%newtext
|
|
|
|
if section.element.tail and section.element.tail.strip():
|
|
newtext += ' ' + section.element.tail.strip() + ' '
|
|
else:
|
|
newtext += '. '
|
|
|
|
section.element.text = newtext
|
|
self.sections[kind] = [section]
|
|
|
|
else:
|
|
|
|
prevsection = self.sections[kind][0]
|
|
prevtext = prevsection.element.text
|
|
|
|
currtext = section.element.text
|
|
|
|
pos = 1000
|
|
if '.' in currtext:
|
|
pos = currtext.index('.')
|
|
|
|
if currtext and currtext.strip():
|
|
prevtext = prevtext + currtext[pos+1:].strip()
|
|
|
|
prevsection.element.text = prevtext
|
|
self.sections[kind] = [prevsection]
|
|
|
|
|
|
else:
|
|
if kind not in self.sections:
|
|
self.sections[kind] = []
|
|
|
|
self.sections[kind].append(section)
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class ParameterList(Node):
|
|
"""
|
|
This class holds information about XML elements with a ``<parameterlist>`` tag.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent, xml_item, kind):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the ``<parameterlist>`` tag
|
|
:param Node `parent`: the parent node, must not be ``None``
|
|
:param `xml_item`: one of the classes available in `etgtools/extractors.py`, such as
|
|
`PyMethodDef`, `PyFunctionDef` and so on
|
|
:param string `kind`: one of `class`, `method`, `function`.
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
self.xml_item = xml_item
|
|
self.kind = kind
|
|
|
|
self.checked = False
|
|
self.py_parameters = ODict()
|
|
|
|
for pdef in xml_item.items:
|
|
name = pdef.name
|
|
parameter = Parameter(self, pdef)
|
|
self.py_parameters[name] = parameter
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Get(self, element_name):
|
|
"""
|
|
Returns an instance of :class:`Parameter` if this parameter name (retrieved using
|
|
the input `element_name`) is actually in the list of parameters held by this class.
|
|
|
|
:param string `element_name`: the parameter name.
|
|
|
|
:rtype: :class:`Parameter` or ``None``
|
|
|
|
.. note:: Very often the list of parameters in wxWidgets does not match the Phoenix Python
|
|
signature, as some of the parameters in Python get merged into one or removed altogether.
|
|
|
|
"""
|
|
|
|
name = element_name.strip()
|
|
|
|
if name in self.py_parameters:
|
|
return self.py_parameters[name]
|
|
|
|
if '_' in name:
|
|
name = name[0:name.index('_')]
|
|
if name in self.py_parameters:
|
|
return self.py_parameters[name]
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def CheckSignature(self):
|
|
"""
|
|
Checks if the function/method signature for items coming from pure C++ implementation
|
|
matches the parameter list itself.
|
|
|
|
This is mostly done as health check, as there are some instances (like `wx.Locale.Init`)
|
|
for which the function signature does not match the parameter list (see, for example,
|
|
the `shortName` parameter in the signature against the `name` in the parameter list).
|
|
|
|
These kind of mismatches can sometimes break the ReST docstrings.
|
|
"""
|
|
|
|
if self.checked:
|
|
return
|
|
|
|
self.checked = True
|
|
xml_item = self.xml_item
|
|
|
|
if isinstance(xml_item, (extractors.PyFunctionDef, extractors.CppMethodDef)):
|
|
return
|
|
|
|
name = xml_item.pyName if xml_item.pyName else removeWxPrefix(xml_item.name)
|
|
|
|
parent = self.GetTopLevelParent()
|
|
is_overload = parent.is_overload if parent else False
|
|
|
|
if xml_item.hasOverloads() and not is_overload:
|
|
return
|
|
|
|
if xml_item.signature is None:
|
|
xml_item.makePyArgsString()
|
|
assert xml_item.signature is not None
|
|
signature = xml_item.signature.signature()
|
|
arguments = list(xml_item.signature)
|
|
if not arguments:
|
|
return
|
|
|
|
py_parameters = []
|
|
for key, parameter in self.py_parameters.items():
|
|
pdef = parameter.pdef
|
|
if pdef.out or pdef.ignored or pdef.docsIgnored:
|
|
continue
|
|
|
|
py_parameters.append(key)
|
|
|
|
message = '\nSEVERE: Incompatibility between function/method signature and list of parameters in `%s`:\n\n' \
|
|
'The parameter `%s` appears in the method signature but could not be found in the parameter list.\n\n' \
|
|
' ==> Function/Method signature from `extractors`: %s\n' \
|
|
' ==> Parameter list from wxWidgets XML items: %s\n\n' \
|
|
'This may be a documentation bug in wxWidgets or a side-effect of removing the `wx` prefix from signatures.\n\n'
|
|
|
|
for arg in arguments:
|
|
arg_name = arg.name
|
|
if arg.position_type in (ParameterType.VAR_ARGS, ParameterType.KWARGS):
|
|
continue
|
|
if arg_name.startswith('_') and keyword.iskeyword(arg_name[1:]): # Reserved Python keywords we've had to rename
|
|
arg_name = arg_name[1:]
|
|
|
|
#if '*' in arg_name:
|
|
# continue
|
|
|
|
if arg_name not in py_parameters:
|
|
class_name = ''
|
|
if hasattr(xml_item, 'className') and xml_item.className is not None:
|
|
class_name = wx2Sphinx(xml_item.className)[1] + '.'
|
|
|
|
print((message % (class_name + name, arg, signature, py_parameters)))
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
docstrings = ''
|
|
|
|
for name, parameter in list(self.py_parameters.items()):
|
|
|
|
pdef = parameter.pdef
|
|
|
|
if pdef.out or pdef.ignored or pdef.docsIgnored:
|
|
continue
|
|
|
|
## print name
|
|
## print parameter.Join()
|
|
## print
|
|
if parameter.type.strip():
|
|
docstrings += ':param `%s`: %s\n'%(name, parameter.Join().lstrip('\n'))
|
|
docstrings += ':type `%s`: %s\n'%(name, parameter.type)
|
|
else:
|
|
docstrings += ':param `%s`: %s\n'%(name, parameter.Join().lstrip('\n'))
|
|
|
|
|
|
if docstrings:
|
|
docstrings = '\n\n\n%s\n\n'%docstrings
|
|
|
|
for child in self.children:
|
|
if not isinstance(child, Parameter):
|
|
docstrings += child.Join() + '\n\n'
|
|
|
|
return docstrings
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Parameter(Node):
|
|
"""
|
|
This class holds information about XML elements with ``<parametername>``
|
|
``<parameterdescription>`` tags.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, parent, pdef):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param Node `parent`: the parent node, must not be ``None``
|
|
:param `pdef`: a `ParamDef` class, as described in `etgtools/extractors.py`.
|
|
"""
|
|
|
|
Node.__init__(self, '', parent)
|
|
|
|
self.pdef = pdef
|
|
self.name = pdef.name
|
|
|
|
self.type = pythonizeType(pdef.type, is_param=True)
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Paragraph(Node):
|
|
"""
|
|
This class holds information about XML elements with a ``<para>`` tag.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent, kind):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the ``<para>`` tag
|
|
:param Node `parent`: the parent node, must not be ``None``
|
|
:param string `kind`: one of `class`, `method`, `function`.
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
self.kind = kind
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
text = Node.Join(self, with_tail)
|
|
|
|
if 'Availability:' not in text:
|
|
return text
|
|
|
|
newtext = ''
|
|
|
|
for line in text.splitlines():
|
|
|
|
if 'Availability:' in line:
|
|
|
|
first = line.index('Availability:')
|
|
|
|
element = ET.Element('available', kind='available')
|
|
element.text = line[first+13:]
|
|
|
|
section = Section(element, None, self.kind)
|
|
|
|
root = self.GetTopLevelParent()
|
|
# TODO: Why is there sometimes not a top-level parent node?
|
|
if root is not None:
|
|
root.AddSection(section)
|
|
|
|
else:
|
|
|
|
newtext += line + '\n'
|
|
|
|
return newtext
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class ReturnType(Node):
|
|
"""
|
|
A special admonition section to customize the `:rtype:` ReST role from
|
|
the XML / Python description.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element
|
|
:param Node `parent`: the parent node, must not be ``None``
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
docstrings = '\n\n:rtype: %s\n\n' % self.element
|
|
|
|
return docstrings
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class List(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<itemizedlist>``
|
|
and ``<orderedlist>`` tags.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the ``<itemizedlist>`` and ``<orderedlist>`` tags
|
|
:param Node `parent`: the parent node, must not be ``None``
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
docstrings = Node.Join(self, with_tail=False)
|
|
docstrings = '\n\n%s\n'%docstrings
|
|
|
|
if self.element.tail:
|
|
spacer = ('ParameterList' in self.GetHierarchy() and [' '] or [''])[0]
|
|
text = '%s%s\n'%(spacer, convertToPython(self.element.tail.strip()))
|
|
docstrings += text
|
|
|
|
return docstrings
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class ListItem(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<listitem>`` tag.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the ``<listitem>`` tag
|
|
:param Node `parent`: the parent node, must not be ``None``
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
self.level = parent.GetHierarchy().count('List') - 1
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def GetSpacing(self):
|
|
|
|
hierarchy = self.GetHierarchy()
|
|
|
|
if 'ParameterList' in hierarchy:
|
|
return ' '
|
|
|
|
elif 'Section' in hierarchy:
|
|
return ' '*3
|
|
|
|
return ' ' * self.level
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
spacer = self.GetSpacing()
|
|
|
|
to_remove = ['(id, event, func)', '(id1, id2, event, func)', '(id1, id2, func)',
|
|
'(id, func)', '(func)',
|
|
'(id, event, func)', '(id1, id2, event, func)', '(id1, id2, func)',
|
|
'(id, func)']
|
|
|
|
docstrings = ''
|
|
|
|
for child in self.children:
|
|
child_text = child.Join(with_tail)
|
|
for rem in to_remove:
|
|
child_text = child_text.replace(rem, '')
|
|
|
|
if '_:' in child_text:
|
|
child_text = child_text.replace('_:', '_*:')
|
|
|
|
docstrings += child_text
|
|
|
|
docstrings = '%s- %s\n'%(spacer, docstrings)
|
|
return docstrings
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Section(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<xrefsect>`` and
|
|
``<simplesect>`` tags.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent, kind, is_overload=False, share_docstrings=False):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the ``<xrefsect>`` and ``<simplesect>`` tags
|
|
:param Node `parent`: the parent node, can be ``None``
|
|
:param string `kind`: one of `class`, `method`, `function`
|
|
:param bool `is_overload`: ``True`` if the root node describes an overloaded
|
|
method/function, ``False`` otherwise.
|
|
:param bool `share_docstrings`: ``True`` if all the overloaded methods/functions
|
|
share the same docstrings.
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
self.kind = kind
|
|
self.is_overload = is_overload
|
|
self.share_docstrings = share_docstrings
|
|
|
|
dummy, section_type = list(self.element.items())[0]
|
|
self.section_type = section_type.split("_")[0]
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
section_type = self.section_type
|
|
|
|
text = Node.Join(self, with_tail=False)
|
|
|
|
if not text.strip() or len(text.strip()) < 3:
|
|
# Empty text or just trailing commas
|
|
return ''
|
|
|
|
if self.is_overload and self.share_docstrings:
|
|
return ''
|
|
|
|
sub_spacer = ' '*3
|
|
|
|
if section_type == 'since':
|
|
# Special treatment for the versionadded
|
|
if len(text) > 6:
|
|
version, remainder = text[0:6], text[6:]
|
|
if '.' in version:
|
|
text = '%s\n%s%s'%(version, sub_spacer, remainder)
|
|
else:
|
|
target = (' 2.' in text and [' 2.'] or [' 3.'])[0]
|
|
vindex1 = text.index(target)
|
|
vindex2 = text[vindex1+2:].index(' ') + vindex1 + 2
|
|
version = text[vindex1:vindex2].strip()
|
|
if version.endswith('.'):
|
|
version = version[0:-1]
|
|
text = '%s\n%s%s'%(version, sub_spacer, text)
|
|
|
|
# Show both the wxPython and the wxWidgets version numbers for
|
|
# versions >= 3. That's not entirely accurate, but close enough.
|
|
if text.startswith('3.'):
|
|
wx_ver = text[:5]
|
|
text = '4.{}/wxWidgets-{} {}'.format(wx_ver[2], wx_ver, text[5:])
|
|
|
|
elif section_type == 'deprecated':
|
|
# Special treatment for deprecated, wxWidgets devs do not put the version number
|
|
text = '\n%s%s'%(sub_spacer, text.lstrip('Deprecated'))
|
|
|
|
elif section_type == 'par':
|
|
# Horrible hack... Why is there a </para> end tag inside the @par tag???
|
|
text = Node.Join(self, with_tail=True)
|
|
split = text.split('\n')
|
|
current = 0
|
|
for index, line in enumerate(split):
|
|
if '---' in line:
|
|
current = index-1
|
|
break
|
|
|
|
return '\n\n' + '\n'.join(split[current:]) + '\n\n'
|
|
|
|
if section_type in ['note', 'remark', 'remarks', 'return']:
|
|
text = '\n\n' + sub_spacer + text
|
|
|
|
for section, replacement in SECTIONS:
|
|
if section == section_type:
|
|
break
|
|
|
|
docstrings = ''
|
|
section_spacer = ''
|
|
|
|
if section_type != 'return':
|
|
section_spacer = self.GetSpacing()
|
|
|
|
docstrings = '\n%s%s %s\n\n'%(section_spacer, replacement, text)
|
|
|
|
return '\n' + docstrings
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Image(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<image>`` tag.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the ``<image>`` tag
|
|
:param Node `parent`: the parent node, must not be ``None``
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
for key, value in list(self.element.items()):
|
|
if key == 'name':
|
|
break
|
|
|
|
if 'appear-' in value:
|
|
return ''
|
|
|
|
image_path = os.path.normpath(os.path.join(DOXYROOT, 'images', value))
|
|
static_path = os.path.join(OVERVIEW_IMAGES_ROOT, os.path.split(image_path)[1])
|
|
|
|
if not os.path.exists(image_path):
|
|
return ''
|
|
|
|
if not os.path.isfile(static_path):
|
|
shutil.copyfile(image_path, static_path)
|
|
|
|
rel_path_index = static_path.rfind('_static')
|
|
rel_path = os.path.normpath(static_path[rel_path_index:])
|
|
|
|
docstrings = '\n\n'
|
|
# Sphinx (on windows) can't parse windows style paths when reading
|
|
# .rst files. Therefore paths are written unix style.
|
|
docstrings += '.. figure:: %s\n' % rel_path.replace('\\', '/')
|
|
docstrings += ' :align: center\n\n\n'
|
|
docstrings += '|\n\n'
|
|
|
|
if self.element.tail and self.element.tail.strip():
|
|
docstrings += convertToPython(self.element.tail.rstrip())
|
|
|
|
return docstrings
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Table(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<table>`` tag.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent, xml_item_name):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the ``<table>`` tag
|
|
:param Node `parent`: the parent node, must not be ``None``
|
|
:param string `xml_item_name`: if a custom version of a table has been created
|
|
inside the ``TABLEROOT`` folder, the `xml_item_name` string will match the
|
|
``*.rst`` file name for the custom table.
|
|
|
|
.. note::
|
|
There are 4 customized versions of 4 XML tables up to now, for various
|
|
reasons:
|
|
|
|
1. The `wx.Sizer` flags table is a flexible grid table, very difficult
|
|
to ReSTify automatically due to embedded newlines messing up the col
|
|
width calculations.
|
|
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`
|
|
4. The `wx.ArtProvider` IDs table
|
|
|
|
The customized versions of those tables are located in
|
|
``docs/sphinx/rest_substitutions/tables``
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
self.xml_item_name = xml_item_name
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
needs_space = 'ParameterList' in self.GetHierarchy()
|
|
spacer = (needs_space and [' '] or [''])[0]
|
|
|
|
rows, cols = int(self.element.get('rows')), int(self.element.get('cols'))
|
|
|
|
longest = [0]*cols
|
|
has_title = False
|
|
|
|
count = 0
|
|
for row in range(rows):
|
|
for col in range(cols):
|
|
child = self.children[count]
|
|
|
|
text = child.Join(with_tail)
|
|
longest[col] = max(len(text), longest[col])
|
|
|
|
if (row == 0 and col == 0 and '**' in text) or child.GetTag('thead') == 'yes':
|
|
has_title = True
|
|
|
|
count += 1
|
|
|
|
table = '\n\n'
|
|
table += spacer
|
|
formats = []
|
|
|
|
for lng in longest:
|
|
table += '='* lng + ' '
|
|
formats.append('%-' + '%ds'%(lng+1))
|
|
|
|
table += '\n'
|
|
|
|
count = 0
|
|
|
|
for row in range(rows):
|
|
|
|
table += spacer
|
|
|
|
for col in range(cols):
|
|
table += formats[col] % (self.children[count].Join(with_tail).strip())
|
|
count += 1
|
|
|
|
table += '\n'
|
|
|
|
if row == 0 and has_title:
|
|
|
|
table += spacer
|
|
|
|
for lng in longest:
|
|
table += '='* lng + ' '
|
|
|
|
table += '\n'
|
|
|
|
table += spacer
|
|
|
|
for lng in longest:
|
|
table += '='* lng + ' '
|
|
|
|
table += '\n\n%s|\n\n'%spacer
|
|
|
|
possible_rest_input = os.path.join(TABLEROOT, self.xml_item_name)
|
|
|
|
if os.path.isfile(possible_rest_input):
|
|
# Work around for the buildbot sphinx generator which seems unable
|
|
# to find the tables...
|
|
rst_file = os.path.split(possible_rest_input)[1]
|
|
rst_folder = os.path.normpath(os.path.relpath(TABLEROOT, SPHINXROOT))
|
|
table = '\n\n' + spacer + '.. include:: %s\n\n'%os.path.join(rst_folder, rst_file)
|
|
|
|
if self.element.tail and self.element.tail.strip():
|
|
rest = convertToPython(self.element.tail.rstrip())
|
|
split = rest.splitlines()
|
|
for index, r in enumerate(split):
|
|
table += spacer + r
|
|
if index < len(split)-1:
|
|
table += '\n'
|
|
|
|
return table
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class TableEntry(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<entry>`` tag.
|
|
"""
|
|
pass
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Snippet(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<programlisting>``,
|
|
``<sp>``, ``<codeline>``, ``<highlight>`` and ``<ref>`` (but only when the
|
|
``<ref>`` tags appears as a child of ``<programlisting>``) tags.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent, cpp_file, python_file, converted_py):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the aforementioned tags
|
|
:param Node `parent`: the parent node, must not be ``None``
|
|
:param string `cpp_file`: the path to the C++ snippet of code found in the XML
|
|
wxWidgets docstring, saved into the ``SNIPPETROOT/cpp`` folder
|
|
:param string `python_file`: the path to the roughly-converted to Python
|
|
snippet of code found in the XML wxWidgets docstring, saved into the
|
|
``SNIPPETROOT/python`` folder
|
|
:param string `converted_py`: the path to the fully-converted to Python
|
|
snippet of code found in the XML wxWidgets docstring, saved into the
|
|
``SNIPPETROOT/python/converted`` folder.
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
self.cpp_file = cpp_file
|
|
self.python_file = python_file
|
|
self.converted_py = converted_py
|
|
|
|
self.snippet = ''
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def AddCode(self, element):
|
|
"""
|
|
Adds a C++ part of the code snippet.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the aforementioned tags.
|
|
"""
|
|
|
|
tag = element.tag
|
|
|
|
if tag == 'codeline':
|
|
self.snippet += '\n'
|
|
|
|
elif tag in ['highlight', 'ref', 'sp']:
|
|
|
|
if tag == 'sp':
|
|
self.snippet += ' '
|
|
|
|
if isinstance(element, str):
|
|
self.snippet += element
|
|
else:
|
|
if element.text:
|
|
self.snippet += element.text.strip(' ')
|
|
if element.tail:
|
|
self.snippet += element.tail.strip(' ')
|
|
|
|
else:
|
|
raise Exception('Unhandled tag in class Snippet: %s'%tag)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
docstrings = ''
|
|
|
|
if not os.path.exists(os.path.dirname(self.cpp_file)):
|
|
os.makedirs(os.path.dirname(self.cpp_file))
|
|
|
|
with open(self.cpp_file, 'wt') as fid:
|
|
fid.write(self.snippet)
|
|
|
|
if not os.path.isfile(self.converted_py):
|
|
|
|
message = '\nWARNING: Missing C++ => Python conversion of the snippet of code for %s'%(os.path.split(self.cpp_file)[1])
|
|
message += '\n\nA slightly Pythonized version of this snippet has been saved into:\n\n ==> %s\n\n'%self.python_file
|
|
|
|
print(message)
|
|
|
|
py_code = self.snippet.replace(';', '')
|
|
py_code = py_code.replace('{', '').replace('}', '')
|
|
py_code = py_code.replace('->', '.').replace('//', '#')
|
|
py_code = py_code.replace('m_', 'self.')
|
|
py_code = py_code.replace('::', '.')
|
|
py_code = py_code.replace('wx', 'wx.')
|
|
py_code = py_code.replace('new ', '').replace('this', 'self')
|
|
py_code = py_code.replace('( ', '(').replace(' )', ')')
|
|
py_code = py_code.replace('||', 'or').replace('&&', 'and')
|
|
|
|
spacer = ' '*4
|
|
new_py_code = ''
|
|
|
|
for code in py_code.splitlines():
|
|
new_py_code += spacer + code + '\n'
|
|
|
|
with open(self.python_file, 'wt') as fid:
|
|
fid.write(new_py_code)
|
|
else:
|
|
|
|
highlight = None
|
|
|
|
with open(self.converted_py, 'rt') as fid:
|
|
while True:
|
|
tline = fid.readline()
|
|
|
|
if not tline: # end of file
|
|
code = ""
|
|
break
|
|
|
|
if 'code-block::' in tline:
|
|
highlight = tline.replace('#', '').strip()
|
|
continue
|
|
|
|
if not tline.strip():
|
|
continue
|
|
|
|
code = tline + fid.read()
|
|
break
|
|
|
|
if highlight:
|
|
docstrings += '\n\n%s\n\n'%highlight
|
|
else:
|
|
docstrings += '::\n\n'
|
|
|
|
docstrings += code.rstrip() + '\n\n'
|
|
|
|
if self.element.tail and len(self.element.tail.strip()) > 1:
|
|
hierarchy = self.GetHierarchy()
|
|
spacer = ''
|
|
if 'Section' in hierarchy:
|
|
spacer = ' '*3
|
|
elif 'Parameter' in hierarchy:
|
|
spacer = ' '
|
|
elif 'List' in hierarchy:
|
|
spacer = ' '
|
|
|
|
tail = convertToPython(self.element.tail.lstrip())
|
|
tail = tail.replace('\n', ' ')
|
|
docstrings += spacer + tail.replace(' ', ' ')
|
|
|
|
return docstrings
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class XRef(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<ref>`` tag, excluding
|
|
when these elements are children of a ``<programlisting>`` element.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the aforementioned tags
|
|
:param Node `parent`: the parent node, must not be ``None``.
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
imm = ItemModuleMap()
|
|
|
|
element = self.element
|
|
text = element.text
|
|
|
|
tail = element.tail
|
|
tail = (tail is not None and [tail] or [''])[0]
|
|
|
|
hascomma = '::' in text
|
|
|
|
original = text
|
|
text = removeWxPrefix(text)
|
|
text = text.replace("::", ".")
|
|
|
|
if "(" in text:
|
|
text = text[0:text.index("(")]
|
|
|
|
refid = element.get('refid', '')
|
|
remainder = refid.split('_')[-1]
|
|
values = [v for k,v in element.items()]
|
|
|
|
space_before, space_after = countSpaces(text)
|
|
stripped = text.strip()
|
|
|
|
if stripped in IGNORE:
|
|
return space_before + text + space_after + tail
|
|
|
|
if ' ' in stripped or 'overview' in values:
|
|
if 'classwx_' in refid:
|
|
ref = 1000
|
|
if '_1' in refid:
|
|
ref = refid.index('_1')
|
|
|
|
ref = underscore2Capitals(refid[6:ref])
|
|
ref = imm.get_fullname(ref)
|
|
text = ':ref:`%s <%s>`'%(stripped, ref)
|
|
|
|
elif 'funcmacro' in values or 'samples' in values or 'debugging' in text.lower() or \
|
|
'unix' in text.lower() or 'page_libs' in values:
|
|
text = space_before + space_after
|
|
elif 'library list' in stripped.lower():
|
|
text = space_before + text + space_after
|
|
else:
|
|
backlink = stripped.lower()
|
|
if 'device context' in backlink:
|
|
backlink = 'device contexts'
|
|
elif 'reference count' in backlink or 'refcount' in values:
|
|
backlink = 'reference counting'
|
|
elif 'this list' in backlink:
|
|
backlink = 'stock items'
|
|
elif 'window deletion' in backlink:
|
|
backlink = 'window deletion'
|
|
elif 'programming with wxboxsizer' in backlink:
|
|
stripped = 'Programming with BoxSizer'
|
|
backlink = 'programming with boxsizer'
|
|
|
|
text = ':ref:`%s <%s>`'%(stripped, backlink)
|
|
|
|
elif (text.upper() == text and len(stripped) > 4):
|
|
|
|
if not original.strip().startswith('wx') or ' ' in stripped:
|
|
text = ''
|
|
|
|
elif not isNumeric(text):
|
|
text = '``%s``' % text.strip()
|
|
|
|
elif 'funcmacro' in values:
|
|
if '(' in stripped:
|
|
stripped = stripped[0:stripped.index('(')].strip()
|
|
|
|
text = ':func:`%s`'%stripped
|
|
|
|
elif hascomma or len(remainder) > 30:
|
|
if '.m_' in text:
|
|
text = '``%s``'%stripped
|
|
else:
|
|
# it was :meth:
|
|
if '.wx' in text:
|
|
prev = text.split('.')
|
|
text = '.'.join(prev[:-1]) + '.__init__'
|
|
text = ':meth:`%s` '%text.strip()
|
|
|
|
else:
|
|
stripped = text.strip()
|
|
|
|
if '(' in stripped:
|
|
stripped = stripped[0:stripped.index('(')].strip()
|
|
|
|
if stripped in imm:
|
|
text = ':ref:`%s`' % (imm.get_fullname(stripped))
|
|
else:
|
|
if '.' not in stripped:
|
|
klass = self.IsClassDescription()
|
|
if klass:
|
|
text = ':meth:`~%s.%s`' % (klass, stripped)
|
|
else:
|
|
text = ':meth:`%s` ' % stripped
|
|
else:
|
|
scope, item_name = stripped.split('.', 1)
|
|
scope = wx2Sphinx(scope)[1]
|
|
text = ':meth:`%s.%s` ' % (scope, item_name)
|
|
|
|
else:
|
|
text = ':ref:`%s`' % wx2Sphinx(stripped)[1]
|
|
|
|
return space_before + text + space_after + convertToPython(tail)
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class ComputerOutput(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<computeroutput>`` tag.
|
|
"""
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the aforementioned tags
|
|
:param Node `parent`: the parent node, must not be ``None``.
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
text = self.element.text
|
|
if not text and not self.children:
|
|
return ''
|
|
|
|
if text is not None:
|
|
stripped = text.strip()
|
|
space_before, space_after = countSpaces(text)
|
|
|
|
text = removeWxPrefix(text.strip())
|
|
|
|
else:
|
|
text = ''
|
|
|
|
for child in self.children:
|
|
text += child.Join(with_tail=False)
|
|
|
|
if '`' not in text:
|
|
text = "``%s`` "%text
|
|
|
|
if self.element.tail:
|
|
text += convertToPython(self.element.tail)
|
|
|
|
space_before, space_after = countSpaces(text)
|
|
if space_before == '':
|
|
space_before = ' '
|
|
|
|
return space_before + text + space_after
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Emphasis(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<emphasis>`` and
|
|
``<bold>`` tags.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the aforementioned tags
|
|
:param Node `parent`: the parent node, must not be ``None``.
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
|
|
text = Node.Join(self, with_tail=False)
|
|
|
|
if '``' in text:
|
|
format = '%s'
|
|
emphasis = ''
|
|
elif self.element.tag == 'emphasis':
|
|
format = '`%s`'
|
|
emphasys = '`'
|
|
elif self.element.tag == 'bold':
|
|
format = '**%s**'
|
|
emphasys = '**'
|
|
|
|
spacing = ('ParameterList' in self.GetHierarchy() and [' '] or [''])[0]
|
|
|
|
if self.children:
|
|
|
|
startPos = 0
|
|
newText = spacing
|
|
|
|
for child in self.children:
|
|
childText = child.Join()
|
|
|
|
tail = child.element.tail
|
|
tail = (tail is not None and [tail] or [''])[0]
|
|
|
|
if tail.strip() != ':':
|
|
childText = childText.replace(convertToPython(tail), '')
|
|
|
|
fullChildText = child.Join()
|
|
endPos = text.index(childText)
|
|
|
|
if text[startPos:endPos].strip():
|
|
newText += ' ' + emphasys + text[startPos:endPos].strip() + emphasys + ' '
|
|
else:
|
|
newText += ' ' + emphasys + ' '
|
|
|
|
newText += childText + ' '
|
|
remaining = fullChildText.replace(childText, '')
|
|
if remaining.strip():
|
|
newText += emphasys + remaining.strip() + emphasys + ' '
|
|
else:
|
|
newText += emphasys + ' '
|
|
|
|
startPos = endPos
|
|
|
|
text = newText
|
|
|
|
else:
|
|
|
|
if text.strip():
|
|
text = spacing + format % text.strip()
|
|
|
|
if self.element.tail:
|
|
text += convertToPython(self.element.tail)
|
|
|
|
return text
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class Title(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<title>`` tag.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the aforementioned tags
|
|
:param Node `parent`: the parent node, must not be ``None``.
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
if isinstance(self.parent, Section) and self.parent.section_type == 'par':
|
|
# Sub-title in a @par doxygen tag
|
|
text = convertToPython(self.element.text)
|
|
underline = '-'
|
|
else:
|
|
# Normal big title
|
|
text = '|phoenix_title| ' + convertToPython(self.element.text)
|
|
underline = '='
|
|
|
|
lentext = len(text)
|
|
text = '\n\n%s\n%s\n\n'%(text.rstrip('.'), underline*lentext)
|
|
|
|
return text
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class ULink(Node):
|
|
"""
|
|
This class holds information about XML elements with the ``<ulink>`` tag.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, element, parent):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the aforementioned tags
|
|
:param Node `parent`: the parent node, must not be ``None``.
|
|
"""
|
|
|
|
Node.__init__(self, element, parent)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Join(self, with_tail=True):
|
|
"""
|
|
Join this node `element` attribute text and tail, adding all its children's
|
|
node text and tail in the meanwhile.
|
|
|
|
:param `with_tail`: ``True`` if the element tail should be included in the
|
|
text, ``False`` otherwise.
|
|
|
|
:rtype: `string`
|
|
|
|
:returns: A string containing the ReSTified version of this node `element` text and
|
|
tail plus all its children's element text and tail.
|
|
|
|
.. note:: For some of the classes in this script (for example the :class:`Emphasis`,
|
|
:class:`ComputerOutput`) the `with_tail` parameter should be set to ``False`` in order
|
|
to avoid wrong ReST output.
|
|
"""
|
|
|
|
dummy, link = list(self.element.items())[0]
|
|
text = self.element.text
|
|
|
|
text = '`%s <%s>`_'%(text, link)
|
|
|
|
if self.element.tail:
|
|
text += convertToPython(self.element.tail)
|
|
|
|
return text
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class DashBase(Node):
|
|
dash_text = '-'
|
|
|
|
def Join(self, with_tail=True):
|
|
text = self.dash_text
|
|
if self.element.text:
|
|
text += self.element.text
|
|
if with_tail and self.element.tail:
|
|
text += convertToPython(self.element.tail)
|
|
return text
|
|
|
|
|
|
class EnDash(DashBase):
|
|
dash_text = u'\u2013'
|
|
|
|
class EmDash(DashBase):
|
|
dash_text = u'\u2014'
|
|
|
|
|
|
# ----------------------------------------------------------------------- #
|
|
|
|
class XMLDocString(object):
|
|
"""
|
|
This is the main class of this script, and it uses heavily the :class:`Node`
|
|
subclasses documented above.
|
|
|
|
The :class:`XMLDocString` is responsible for building function/method signatures,
|
|
class descriptions, window styles and events and so on.
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def __init__(self, xml_item, is_overload=False, share_docstrings=False):
|
|
"""
|
|
Class constructor.
|
|
|
|
:param `xml_item`: one of the classes available in `etgtools/extractors.py`, such as
|
|
`PyMethodDef`, `PyFunctionDef` and so on
|
|
:param bool `is_overload`: ``True`` if this class describes an overloaded
|
|
method/function, ``False`` otherwise.
|
|
:param bool `share_docstrings`: ``True`` if all the overloaded methods/functions
|
|
share the same docstrings.
|
|
"""
|
|
|
|
self.xml_item = xml_item
|
|
self.is_overload = is_overload
|
|
self.share_docstrings = share_docstrings
|
|
|
|
self.docstrings = ''
|
|
|
|
self.class_name = ''
|
|
|
|
self.snippet_count = 0
|
|
self.contrib_snippets = []
|
|
|
|
self.table_count = 0
|
|
|
|
self.list_level = 1
|
|
self.list_order = {}
|
|
|
|
self.parameter_list = None
|
|
|
|
self.root = Root(self, is_overload, share_docstrings)
|
|
|
|
self.appearance = []
|
|
self.overloads = []
|
|
|
|
if isinstance(xml_item, extractors.MethodDef):
|
|
self.kind = 'method'
|
|
elif isinstance(xml_item, (extractors.FunctionDef, extractors.PyFunctionDef)):
|
|
self.kind = 'function'
|
|
elif isinstance(xml_item, (extractors.ClassDef, extractors.PyClassDef, extractors.TypedefDef)):
|
|
self.kind = 'class'
|
|
self.appearance = findControlImages(xml_item)
|
|
self.class_name = xml_item.pyName if xml_item.pyName else removeWxPrefix(xml_item.name)
|
|
self.isInner = getattr(xml_item, 'isInner', False)
|
|
elif isinstance(xml_item, extractors.EnumDef):
|
|
self.kind = 'enum'
|
|
elif isinstance(xml_item, extractors.MemberVarDef):
|
|
self.kind = 'memberVar'
|
|
else:
|
|
raise Exception('Unhandled docstring kind for %s'%xml_item.__class__.__name__)
|
|
|
|
# Some of the Extractors (xml item) will set deprecated themselves, in which case it is set as a
|
|
# non-empty string. In such cases, this branch will insert a deprecated section into the xml tree
|
|
# so that the Node Tree (see classes above) will generate the deprecated tag on their own in self.RecurseXML
|
|
if hasattr(xml_item, 'deprecated') and xml_item.deprecated and isinstance(xml_item.deprecated, str):
|
|
element = ET.Element('deprecated', kind='deprecated')
|
|
element.text = xml_item.deprecated
|
|
|
|
deprecated_section = Section(element, None, self.kind, self.is_overload, self.share_docstrings)
|
|
self.root.AddSection(deprecated_section)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def ToReST(self):
|
|
""" Auxiliary method. """
|
|
|
|
brief, detailed = self.xml_item.briefDoc, self.xml_item.detailedDoc
|
|
|
|
self.RecurseXML(brief, self.root)
|
|
|
|
for detail in detailed:
|
|
blank_element = Node('\n\n\n', self.root)
|
|
self.RecurseXML(detail, self.root)
|
|
|
|
self.InsertParameterList()
|
|
self.BuildSignature()
|
|
self.docstrings = self.root.Join()
|
|
|
|
self.LoadOverLoads()
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def GetBrief(self):
|
|
"""
|
|
Returns a ReSTified version of the `briefDoc` attribute for the XML docstrings.
|
|
|
|
:rtype: `string`
|
|
"""
|
|
|
|
brief = self.xml_item.briefDoc
|
|
|
|
dummy_root = Root(self, False, False)
|
|
rest_class = self.RecurseXML(brief, dummy_root)
|
|
return rest_class.Join()
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def LoadOverLoads(self):
|
|
"""
|
|
Extracts the overloaded implementations of a method/function, unless this
|
|
class is itself an overload or the current method/function has no overloads.
|
|
"""
|
|
|
|
if self.is_overload:
|
|
return
|
|
|
|
if self.kind not in ['method', 'function'] or not self.xml_item.overloads:
|
|
return
|
|
|
|
share_docstrings = True
|
|
all_docs = []
|
|
|
|
for sub_item in [self.xml_item] + self.xml_item.overloads:
|
|
|
|
if sub_item.ignored or sub_item.docsIgnored:
|
|
continue
|
|
|
|
dummy_root = Root(self, False, False)
|
|
self.RecurseXML(sub_item.briefDoc, dummy_root)
|
|
|
|
for det in sub_item.detailedDoc:
|
|
self.RecurseXML(det, dummy_root)
|
|
|
|
all_docs.append(dummy_root.Join())
|
|
|
|
if len(all_docs) == 1:
|
|
# Only one overload, don't act like there were more
|
|
self.xml_item.overloads = []
|
|
return
|
|
|
|
zero = all_docs[0]
|
|
for docs in all_docs[1:]:
|
|
if docs != zero:
|
|
share_docstrings = False
|
|
break
|
|
|
|
self.share_docstrings = share_docstrings
|
|
|
|
snippet_count = 0
|
|
for sub_item in [self.xml_item] + self.xml_item.overloads:
|
|
|
|
if sub_item.ignored or sub_item.docsIgnored:
|
|
continue
|
|
|
|
sub_item.name = self.xml_item.pyName or removeWxPrefix(self.xml_item.name)
|
|
docstring = XMLDocString(sub_item, is_overload=True, share_docstrings=share_docstrings)
|
|
docstring.class_name = self.class_name
|
|
docstring.current_module = self.current_module
|
|
docstring.snippet_count = snippet_count
|
|
|
|
docs = docstring.Dump()
|
|
self.overloads.append(docs)
|
|
snippet_count = docstring.snippet_count
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def RecurseXML(self, element, parent):
|
|
"""
|
|
Scan recursively all the XML elements which make up the whole documentation for
|
|
a particular class/method/function.
|
|
|
|
:param xml.etree.ElementTree.Element `element`: a XML element containing the
|
|
information coming from Doxygen about the aforementioned tags
|
|
:param Node `parent`: the parent node, a subclass of the :class:`Node` class.
|
|
|
|
:rtype: a subclass of :class:`Node`
|
|
|
|
.. note: This is a recursive method.
|
|
"""
|
|
|
|
if element is None:
|
|
return Node('', parent)
|
|
|
|
if isinstance(element, str):
|
|
rest_class = Paragraph(element, parent, self.kind)
|
|
return rest_class
|
|
|
|
tag, text, tail = element.tag, element.text, element.tail
|
|
|
|
text = (text is not None and [text] or [''])[0]
|
|
tail = (tail is not None and [tail] or [''])[0]
|
|
|
|
if tag == 'parameterlist':
|
|
rest_class = ParameterList(element, parent, self.xml_item, self.kind)
|
|
self.parameter_list = rest_class
|
|
|
|
elif tag == 'parametername':
|
|
self.parameter_name = text
|
|
rest_class = self.parameter_list
|
|
|
|
elif tag == 'parameterdescription':
|
|
parameter_class = self.parameter_list.Get(self.parameter_name)
|
|
if parameter_class:
|
|
rest_class = parameter_class
|
|
parameter_class.element = element
|
|
else:
|
|
rest_class = self.parameter_list
|
|
|
|
elif tag in ['itemizedlist', 'orderedlist']:
|
|
rest_class = List(element, parent)
|
|
|
|
elif tag == 'listitem':
|
|
rest_class = ListItem(element, parent)
|
|
|
|
elif tag in ['simplesect', 'xrefsect']:
|
|
if 'ListItem' in parent.GetHierarchy():
|
|
rest_class = Section(element, parent, self.kind, self.is_overload, self.share_docstrings)
|
|
else:
|
|
dummy, section_type = list(element.items())[0]
|
|
section_type = section_type.split("_")[0]
|
|
|
|
if element.tail and section_type != 'par':
|
|
Node(element.tail, parent)
|
|
|
|
if section_type == 'par':
|
|
# doxygen @par stuff
|
|
rest_class = Section(element, parent, self.kind, self.is_overload, self.share_docstrings)
|
|
if element.tail:
|
|
Node(element.tail, rest_class)
|
|
else:
|
|
rest_class = Section(element, None, self.kind, self.is_overload, self.share_docstrings)
|
|
self.root.AddSection(rest_class)
|
|
|
|
elif tag == 'image':
|
|
rest_class = Image(element, parent)
|
|
|
|
elif tag == 'table':
|
|
fullname = self.GetFullName()
|
|
self.table_count += 1
|
|
fullname = '%s.%d.rst'%(fullname, self.table_count)
|
|
rest_class = Table(element, parent, fullname)
|
|
self.table = rest_class
|
|
|
|
elif tag == 'entry':
|
|
rest_class = TableEntry(element, self.table)
|
|
|
|
elif tag == 'row':
|
|
rest_class = self.table
|
|
|
|
elif tag == 'programlisting':
|
|
cpp_file, python_file, converted_py = self.SnippetName()
|
|
rest_class = Snippet(element, parent, cpp_file, python_file, converted_py)
|
|
self.code = rest_class
|
|
|
|
elif tag in ['codeline', 'highlight', 'sp']:
|
|
self.code.AddCode(element)
|
|
rest_class = self.code
|
|
|
|
elif tag == 'ref':
|
|
if 'Snippet' in parent.GetHierarchy():
|
|
self.code.AddCode(element)
|
|
rest_class = self.code
|
|
else:
|
|
rest_class = XRef(element, parent)
|
|
|
|
elif tag == 'computeroutput':
|
|
rest_class = ComputerOutput(element, parent)
|
|
|
|
elif tag in ['emphasis', 'bold']:
|
|
rest_class = Emphasis(element, parent)
|
|
|
|
elif tag == 'title':
|
|
text = convertToPython(element.text)
|
|
rest_class = Title(element, parent)
|
|
|
|
elif tag == 'para':
|
|
rest_class = Paragraph(element, parent, self.kind)
|
|
|
|
elif tag == 'linebreak':
|
|
spacer = ('ParameterList' in parent.GetHierarchy() and [' '] or [''])[0]
|
|
dummy = Node('\n\n%s%s'%(spacer, tail.strip()), parent)
|
|
rest_class = parent
|
|
|
|
elif tag == 'ulink':
|
|
rest_class = ULink(element, parent)
|
|
|
|
elif tag == 'onlyfor':
|
|
onlyfor = ET.Element('available', kind='available')
|
|
onlyfor.text = text
|
|
onlyfor.tail = tail
|
|
|
|
section = Section(onlyfor, None, self.kind)
|
|
|
|
self.root.AddSection(section)
|
|
rest_class = parent
|
|
|
|
elif tag == 'ndash':
|
|
rest_class = EnDash(element, parent)
|
|
elif tag == 'mdash':
|
|
rest_class = EmDash(element, parent)
|
|
|
|
else:
|
|
rest_class = Node('', parent)
|
|
|
|
for child_element in element:
|
|
self.RecurseXML(child_element, rest_class)
|
|
|
|
return rest_class
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def GetFullName(self):
|
|
"""
|
|
Returns the complete name for a class/method/function, including
|
|
its module/package.
|
|
|
|
:rtype: `string`
|
|
"""
|
|
|
|
imm = ItemModuleMap()
|
|
|
|
if self.kind == 'class':
|
|
klass = self.xml_item
|
|
name = klass.pyName if klass.pyName else removeWxPrefix(klass.name)
|
|
fullname = imm.get_fullname(name)
|
|
|
|
elif self.kind == 'method':
|
|
method = self.xml_item
|
|
if hasattr(method, 'isCtor') and method.isCtor:
|
|
method_name = '__init__'
|
|
else:
|
|
method_name = method.pyName if method.pyName else method.name
|
|
|
|
if hasattr(method, 'className') and method.className is not None:
|
|
klass = removeWxPrefix(method.className)
|
|
else:
|
|
klass = method.klass.pyName if method.klass.pyName else removeWxPrefix(method.klass.name)
|
|
|
|
fullname = '%s.%s' % (imm.get_fullname(klass), method_name)
|
|
|
|
elif self.kind == 'function':
|
|
function = self.xml_item
|
|
name = function.pyName if function.pyName else function.name
|
|
fullname = self.current_module + 'functions.%s'%name
|
|
|
|
if not fullname.strip():
|
|
dummy = xml_item.name or xml_item.pyName
|
|
raise Exception('Invalid item name for %s (kind=%s)'%(dummy, self.kind))
|
|
|
|
return fullname
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def SnippetName(self):
|
|
"""
|
|
Returns a tuple of 3 elements (3 file paths), representing the following:
|
|
|
|
1. `cpp_file`: the path to the C++ snippet of code found in the XML
|
|
wxWidgets docstring, saved into the ``SNIPPETROOT/cpp`` folder
|
|
2. `python_file`: the path to the roughly-converted to Python
|
|
snippet of code found in the XML wxWidgets docstring, saved into the
|
|
``SNIPPETROOT/python`` folder
|
|
3. `converted_py`: the path to the fully-converted to Python
|
|
snippet of code found in the XML wxWidgets docstring, saved into the
|
|
``SNIPPETROOT/python/converted`` folder.
|
|
"""
|
|
|
|
fullname = self.GetFullName()
|
|
|
|
self.snippet_count += 1
|
|
|
|
cpp_file = os.path.join(SNIPPETROOT, 'cpp', fullname + '.%d.cpp'%self.snippet_count)
|
|
python_file = os.path.join(SNIPPETROOT, 'python', fullname + '.%d.py'%self.snippet_count)
|
|
converted_py = os.path.join(SNIPPETROOT, 'python', 'converted', fullname + '.%d.py'%self.snippet_count)
|
|
|
|
return cpp_file, python_file, converted_py
|
|
|
|
|
|
def HuntContributedSnippets(self):
|
|
|
|
fullname = self.GetFullName()
|
|
contrib_folder = os.path.join(SNIPPETROOT, 'python', 'contrib')
|
|
|
|
possible_py = []
|
|
|
|
for suffix in range(1, 101):
|
|
|
|
sample = os.path.join(contrib_folder, '%s.%d.py'%(fullname, suffix))
|
|
|
|
if not os.path.isfile(sample):
|
|
break
|
|
|
|
possible_py.append(sample)
|
|
|
|
return possible_py
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Dump(self, write=True):
|
|
"""
|
|
Dumps the whole ReSTified docstrings and returns its correct ReST representation.
|
|
|
|
:param bool `write`: ``True`` to write the resulting docstrings to a file, ``False``
|
|
otherwise.
|
|
|
|
:rtype: `string`
|
|
"""
|
|
|
|
self.ToReST()
|
|
|
|
methodMap = {
|
|
'class' : self.DumpClass,
|
|
'method' : self.DumpMethod,
|
|
'function' : self.DumpFunction,
|
|
'enum' : self.DumpEnum,
|
|
}
|
|
|
|
function = methodMap[self.kind]
|
|
return function(write)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def DumpClass(self, write):
|
|
"""
|
|
Dumps a ReSTified class description and returns its correct ReST representation.
|
|
|
|
:param bool `write`: ``True`` to write the resulting docstrings to a file, ``False``
|
|
otherwise.
|
|
|
|
:rtype: `string`
|
|
"""
|
|
|
|
stream = StringIO()
|
|
|
|
# class declaration
|
|
klass = self.xml_item
|
|
name = self.class_name
|
|
dummy, fullname = wx2Sphinx(name)
|
|
|
|
# if '.' in fullname:
|
|
# module = self.current_module[:-1]
|
|
# stream.write('\n\n.. currentmodule:: %s\n\n' % module)
|
|
|
|
stream.write(templates.TEMPLATE_DESCRIPTION % (fullname, fullname))
|
|
|
|
self.Reformat(stream)
|
|
|
|
inheritance_diagram = InheritanceDiagram(klass.nodeBases)
|
|
png, map = inheritance_diagram.makeInheritanceDiagram()
|
|
|
|
image_desc = templates.TEMPLATE_INHERITANCE % ('class', name, png, name, map)
|
|
stream.write(image_desc)
|
|
|
|
if self.appearance:
|
|
appearance_desc = templates.TEMPLATE_APPEARANCE % tuple(self.appearance)
|
|
stream.write(appearance_desc)
|
|
|
|
if klass.subClasses:
|
|
subs = [':ref:`%s`' % wx2Sphinx(cls)[1] for cls in klass.subClasses]
|
|
subs = ', '.join(subs)
|
|
subs_desc = templates.TEMPLATE_SUBCLASSES % subs
|
|
stream.write(subs_desc)
|
|
|
|
possible_py = self.HuntContributedSnippets()
|
|
|
|
if possible_py:
|
|
possible_py.sort()
|
|
snippets = formatContributedSnippets(self.kind, possible_py)
|
|
stream.write(snippets)
|
|
|
|
if klass.method_list:
|
|
summary = makeSummary(name, klass.method_list, templates.TEMPLATE_METHOD_SUMMARY, 'meth')
|
|
stream.write(summary)
|
|
|
|
if klass.property_list or klass.memberVar_list:
|
|
allAttrs = klass.property_list + klass.memberVar_list
|
|
summary = makeSummary(name, allAttrs, templates.TEMPLATE_PROPERTY_SUMMARY, 'attr')
|
|
stream.write(summary)
|
|
|
|
stream.write(templates.TEMPLATE_API)
|
|
stream.write("\n.. class:: %s" % fullname)
|
|
|
|
bases = klass.bases or ['object']
|
|
|
|
if bases:
|
|
stream.write('(')
|
|
bases = [removeWxPrefix(b) for b in bases] # ***
|
|
stream.write(', '.join(bases))
|
|
stream.write(')')
|
|
|
|
stream.write('\n\n')
|
|
|
|
py_docs = klass.pyDocstring
|
|
|
|
if isinstance(self.xml_item, (extractors.PyClassDef, extractors.TypedefDef)):
|
|
newlines = self.xml_item.briefDoc.splitlines()
|
|
else:
|
|
newlines = []
|
|
|
|
found = False
|
|
for line in py_docs.splitlines():
|
|
if line.startswith(name):
|
|
if not found:
|
|
newlines.append("**Possible constructors**::\n")
|
|
found = True
|
|
else:
|
|
found = False
|
|
newlines.append(convertToPython(line))
|
|
|
|
if found:
|
|
line = line.replace('wx.EmptyString', '""')
|
|
line = line.replace('wx.', '') # ***
|
|
newlines = self.CodeIndent(line, newlines)
|
|
|
|
newdocs = ''
|
|
for line in newlines:
|
|
newdocs += ' '*3 + line + "\n"
|
|
|
|
stream.write(newdocs + "\n\n")
|
|
|
|
if write:
|
|
writeSphinxOutput(stream, self.output_file)
|
|
else:
|
|
return stream.getvalue()
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def BuildSignature(self):
|
|
""" Builds a function/method signature. """
|
|
|
|
if self.kind not in ['method', 'function']:
|
|
return
|
|
|
|
if self.kind == 'method':
|
|
|
|
method = self.xml_item
|
|
name = method.name or method.pyName
|
|
name = removeWxPrefix(name)
|
|
|
|
if method.hasOverloads() and not self.is_overload:
|
|
if not method.isStatic:
|
|
arguments = '(self, *args, **kw)'
|
|
else:
|
|
arguments = '(*args, **kw)'
|
|
else:
|
|
arguments = method.pyArgsString
|
|
if not arguments:
|
|
arguments = '()'
|
|
if not method.isStatic:
|
|
if arguments[:2] == '()':
|
|
arguments = '(self)' + arguments[2:]
|
|
else:
|
|
arguments = '(self, ' + arguments[1:]
|
|
|
|
if '->' in arguments:
|
|
arguments, after = arguments.split("->")
|
|
self.AddReturnType(after, name)
|
|
|
|
arguments = arguments.rstrip()
|
|
if arguments.endswith(','):
|
|
arguments = arguments[0:-1]
|
|
|
|
if not arguments.endswith(')'):
|
|
arguments += ')'
|
|
|
|
if self.is_overload:
|
|
arguments = '`%s`'%arguments.strip()
|
|
|
|
elif self.kind == 'function':
|
|
|
|
function = self.xml_item
|
|
name = function.pyName if function.pyName else function.name
|
|
name = removeWxPrefix(name)
|
|
|
|
if function.hasOverloads() and not self.is_overload:
|
|
arguments = '(*args, **kw)'
|
|
else:
|
|
if "->" in function.pyArgsString:
|
|
arguments, after = function.pyArgsString.split("->")
|
|
self.AddReturnType(after, name)
|
|
else:
|
|
arguments = function.pyArgsString
|
|
|
|
if self.is_overload:
|
|
arguments = '`%s`'%arguments.strip()
|
|
|
|
arguments = arguments.replace('wx.', '')
|
|
self.arguments = arguments
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def InsertParameterList(self):
|
|
"""
|
|
Inserts a :class:`ParameterList` item in the correct position into the
|
|
:class:`Root` hierarchy, and checks the signature validity against the
|
|
parameter list itself.
|
|
"""
|
|
|
|
if self.kind not in ['method', 'function']:
|
|
return
|
|
|
|
if self.parameter_list is not None:
|
|
self.parameter_list.CheckSignature()
|
|
return
|
|
|
|
if not self.xml_item.hasOverloads() or self.is_overload:
|
|
self.parameter_list = ParameterList('', None, self.xml_item, self.kind)
|
|
self.root.Insert(self.parameter_list, before=Section)
|
|
self.parameter_list.CheckSignature()
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def AddReturnType(self, after, name):
|
|
|
|
after = after.strip()
|
|
|
|
if not after:
|
|
return
|
|
|
|
if '(' in after:
|
|
|
|
rtype = ReturnType('`tuple`', None)
|
|
|
|
return_section = after.lstrip('(').rstrip(')')
|
|
return_section = return_section.split(',')
|
|
new_section = []
|
|
|
|
for ret in return_section:
|
|
stripped = ret.strip()
|
|
imm = ItemModuleMap()
|
|
if stripped in imm:
|
|
ret = imm[stripped] + stripped
|
|
new_section.append(':ref:`%s`' % ret)
|
|
else:
|
|
if ret[0].isupper():
|
|
new_section.append(':ref:`%s`' % stripped)
|
|
else:
|
|
new_section.append('`%s`' % stripped)
|
|
|
|
element = ET.Element('return', kind='return')
|
|
element.text = '( %s )'%(', '.join(new_section))
|
|
|
|
return_section = Section(element, None, self.kind, self.is_overload, self.share_docstrings)
|
|
self.root.AddSection(return_section)
|
|
|
|
else:
|
|
|
|
rtype = pythonizeType(after, is_param=False)
|
|
|
|
if not rtype:
|
|
return
|
|
|
|
if rtype[0].isupper() or '.' in rtype:
|
|
rtype = ':ref:`%s`'%rtype
|
|
else:
|
|
rtype = '`%s`'%rtype
|
|
|
|
rtype = ReturnType(rtype, None)
|
|
|
|
if self.parameter_list:
|
|
self.parameter_list.Add(rtype)
|
|
else:
|
|
self.root.Insert(rtype, before=Section)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def DumpMethod(self, write):
|
|
"""
|
|
Dumps a ReSTified method description and returns its correct ReST representation.
|
|
|
|
:param bool `write`: ``True`` to write the resulting docstrings to a file, ``False``
|
|
otherwise.
|
|
|
|
:rtype: `string`
|
|
"""
|
|
|
|
stream = StringIO()
|
|
|
|
method = self.xml_item
|
|
name = method.pyName if method.pyName else method.name
|
|
name = removeWxPrefix(name)
|
|
|
|
if self.is_overload:
|
|
definition = '**%s** '%name
|
|
else:
|
|
if method.isStatic:
|
|
definition = ' .. staticmethod:: ' + name
|
|
else:
|
|
definition = ' .. method:: ' + name
|
|
|
|
# write the method declaration
|
|
stream.write('\n%s'%definition)
|
|
|
|
stream.write(self.arguments)
|
|
stream.write('\n\n')
|
|
|
|
self.Reformat(stream)
|
|
|
|
possible_py = self.HuntContributedSnippets()
|
|
|
|
if possible_py:
|
|
possible_py.sort()
|
|
snippets = formatContributedSnippets(self.kind, possible_py)
|
|
stream.write(snippets)
|
|
|
|
stream.write("\n\n")
|
|
|
|
if not self.is_overload and write:
|
|
writeSphinxOutput(stream, self.output_file, append=True)
|
|
|
|
return stream.getvalue()
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def DumpFunction(self, write):
|
|
"""
|
|
Dumps a ReSTified function description and returns its correct ReST representation.
|
|
|
|
:param bool `write`: ``True`` to write the resulting docstrings to a file, ``False``
|
|
otherwise.
|
|
|
|
:rtype: `string`
|
|
"""
|
|
|
|
stream = StringIO()
|
|
|
|
function = self.xml_item
|
|
name = function.pyName or function.name
|
|
fullname = ItemModuleMap().get_fullname(name)
|
|
|
|
if self.is_overload:
|
|
definition = '**%s** ' % name
|
|
else:
|
|
definition = '.. function:: ' + fullname
|
|
|
|
stream.write('\n%s' % definition)
|
|
|
|
stream.write(self.arguments.strip())
|
|
stream.write('\n\n')
|
|
|
|
self.Reformat(stream)
|
|
|
|
possible_py = self.HuntContributedSnippets()
|
|
|
|
if possible_py:
|
|
possible_py.sort()
|
|
snippets = formatContributedSnippets(self.kind, possible_py)
|
|
stream.write(snippets)
|
|
|
|
if not self.is_overload and write:
|
|
pickleItem(stream.getvalue(), self.current_module, name, 'function')
|
|
|
|
return stream.getvalue()
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def DumpEnum(self, write):
|
|
"""
|
|
Dumps a ReSTified enumeration description and returns its correct ReST representation.
|
|
|
|
:param bool `write`: ``True`` to write the resulting docstrings to a file, ``False``
|
|
otherwise.
|
|
|
|
:rtype: `string`
|
|
"""
|
|
|
|
enum_name, fullname = wx2Sphinx(self.xml_item.name)
|
|
|
|
if '@' in enum_name:
|
|
return
|
|
|
|
if self.current_class:
|
|
self.current_class.enum_list.append(fullname)
|
|
|
|
stream = StringIO()
|
|
self.output_file = "%s.enumeration.txt" % fullname
|
|
|
|
# if self.current_module.strip():
|
|
# module = self.current_module.strip()[:-1]
|
|
# stream.write('\n\n.. currentmodule:: %s\n\n' % module)
|
|
|
|
stream.write(templates.TEMPLATE_DESCRIPTION % (fullname, fullname))
|
|
stream.write('\n\nThe `%s` enumeration provides the following values:\n\n' % enum_name)
|
|
|
|
stream.write('\n\n' + '='*80 + ' ' + '='*80 + '\n')
|
|
stream.write('%-80s **Value**\n'%'**Description**')
|
|
stream.write('='*80 + ' ' + '='*80 + '\n')
|
|
|
|
count = 0
|
|
|
|
for v in self.xml_item.items:
|
|
if v.ignored or v.docsIgnored:
|
|
continue
|
|
|
|
docstrings = v.briefDoc
|
|
name = v.pyName if v.pyName else removeWxPrefix(v.name)
|
|
name = convertToPython(name)
|
|
stream.write('%-80s' % name)
|
|
|
|
if not isinstance(docstrings, str):
|
|
rest_class = self.RecurseXML(docstrings, self.root)
|
|
docstrings = rest_class.Join()
|
|
|
|
stream.write(' %s\n'%docstrings)
|
|
count += 1
|
|
|
|
stream.write('='*80 + ' ' + '='*80 + '\n\n|\n\n')
|
|
|
|
text_file = os.path.join(SPHINXROOT, self.output_file)
|
|
|
|
if count > 0 and write:
|
|
writeSphinxOutput(stream, self.output_file)
|
|
|
|
return stream.getvalue()
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def EventsInStyle(self, line, class_name, added):
|
|
|
|
docstrings = ''
|
|
newline = line
|
|
|
|
if 'supports the following styles:' in line:
|
|
if class_name is not None:
|
|
# Crappy wxWidgets docs!!! They put the Window Styles inside the
|
|
# constructor!!!
|
|
docstrings += templates.TEMPLATE_WINDOW_STYLES % class_name
|
|
|
|
elif 'The following event handler macros' in line and not added:
|
|
index = line.index('The following event handler macros')
|
|
newline1 = line[0:index] + '\n\n'
|
|
macro_line = line[index:]
|
|
last = macro_line.index(':')
|
|
line = macro_line[last+1:].strip()
|
|
|
|
if line.count(':') > 2:
|
|
newline = 'Handlers bound for the following event types will receive one of the %s parameters.'%line
|
|
else:
|
|
newline = 'Handlers bound for the following event types will receive a %s parameter.'%line
|
|
|
|
docstrings += newline1 + templates.TEMPLATE_EVENTS % class_name
|
|
docstrings = docstrings.replace('Event macros for events emitted by this class: ', '')
|
|
newline = newline.replace('Event macros for events emitted by this class: ', '')
|
|
added = True
|
|
|
|
elif 'Event macros for events' in line:
|
|
if added:
|
|
newline = ''
|
|
else:
|
|
docstrings += templates.TEMPLATE_EVENTS % class_name
|
|
|
|
added = True
|
|
|
|
elif 'following extra styles:' in line:
|
|
docstrings += templates.TEMPLATE_WINDOW_EXTRASTYLES % class_name
|
|
|
|
return docstrings, newline, added
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def CodeIndent(self, code, newlines):
|
|
|
|
if len(code) < 72:
|
|
newlines.append(' %s'%code)
|
|
newlines.append(' ')
|
|
return newlines
|
|
|
|
start = code.index('(')
|
|
wrapped = textwrap.wrap(code, width=72)
|
|
|
|
newcode = ''
|
|
for indx, line in enumerate(wrapped):
|
|
if indx == 0:
|
|
newlines.append(' %s'%line)
|
|
else:
|
|
newlines.append(' '*(start+5) + line)
|
|
|
|
newlines.append(' ')
|
|
|
|
return newlines
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Indent(self, class_name, item, spacer, docstrings):
|
|
|
|
added = False
|
|
for line in item.splitlines():
|
|
if line.strip():
|
|
newdocs, newline, added = self.EventsInStyle(line, class_name, added)
|
|
if newline.strip():
|
|
docstrings += newdocs
|
|
docstrings += spacer + newline + '\n'
|
|
else:
|
|
docstrings += line + '\n'
|
|
|
|
return docstrings
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def Reformat(self, stream):
|
|
|
|
spacer = ''
|
|
|
|
if not self.is_overload:
|
|
if self.kind == 'function':
|
|
spacer = 3*' '
|
|
elif self.kind == 'method':
|
|
spacer = 6*' '
|
|
|
|
if self.overloads and not self.share_docstrings:
|
|
|
|
docstrings = ''
|
|
|
|
elif self.is_overload and self.share_docstrings:
|
|
|
|
docstrings = self.Indent(None, self.docstrings, spacer, '')
|
|
|
|
else:
|
|
|
|
class_name = None
|
|
|
|
if self.kind == 'class':
|
|
class_name = self.class_name
|
|
|
|
if isinstance(self.xml_item, (extractors.PyFunctionDef, extractors.PyClassDef)):
|
|
docstrings = self.xml_item.briefDoc
|
|
if docstrings:
|
|
docstrings = self.Indent(class_name, docstrings, spacer, '')
|
|
else:
|
|
docstrings = ''
|
|
else:
|
|
docstrings = self.Indent(class_name, self.docstrings, spacer, '')
|
|
|
|
if self.kind == 'class':
|
|
desc = chopDescription(docstrings)
|
|
self.short_description = desc
|
|
pickleItem(desc, self.current_module, self.class_name, 'class')
|
|
|
|
if self.overloads:
|
|
docstrings += '\n\n%s|overload| **Overloaded Implementations:**\n\n'%spacer
|
|
docstrings += '%s:html:`<hr class="overloadsep" /><br />`\n\n'%spacer
|
|
|
|
for index, over in enumerate(self.overloads):
|
|
for line in over.splitlines():
|
|
docstrings += spacer + line + '\n'
|
|
docstrings += '%s:html:`<hr class="overloadsep" /><br />`\n\n'%spacer
|
|
|
|
if '**Perl Note:**' in docstrings:
|
|
index = docstrings.index('**Perl Note:**')
|
|
docstrings = docstrings[0:index]
|
|
|
|
stream.write(docstrings + "\n\n")
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class SphinxGenerator(generators.DocsGeneratorBase):
|
|
|
|
def generate(self, module):
|
|
self.current_module = MODULENAME_REPLACE[module.module]
|
|
self.module_name = module.name
|
|
self.current_class = None
|
|
|
|
self.generateModule(module)
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def removeDuplicated(self, class_name, class_items):
|
|
|
|
duplicated_indexes = []
|
|
done = []
|
|
|
|
properties = (extractors.PropertyDef, extractors.PyPropertyDef)
|
|
methods = (extractors.MethodDef, extractors.CppMethodDef, extractors.CppMethodDef_sip,
|
|
extractors.PyMethodDef, extractors.PyFunctionDef)
|
|
|
|
message = '\nWARNING: Duplicated instance of %s `%s` encountered in class `%s`.\n' \
|
|
'The last occurrence of `%s` (an instance of `%s`) will be discarded.\n\n'
|
|
|
|
for index, item in enumerate(class_items):
|
|
if isinstance(item, methods):
|
|
name, dummy = self.getName(item)
|
|
kind = 'method'
|
|
elif isinstance(item, properties):
|
|
name = item.name
|
|
kind = 'property'
|
|
else:
|
|
continue
|
|
|
|
if name in done:
|
|
print((message % (kind, name, class_name, name, item.__class__.__name__)))
|
|
duplicated_indexes.append(index)
|
|
continue
|
|
|
|
done.append(name)
|
|
|
|
duplicated_indexes.reverse()
|
|
for index in duplicated_indexes:
|
|
class_items.pop(index)
|
|
|
|
return class_items
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def generateModule(self, module):
|
|
"""
|
|
Generate code for each of the top-level items in the module.
|
|
"""
|
|
assert isinstance(module, extractors.ModuleDef)
|
|
|
|
methodMap = {
|
|
extractors.ClassDef : self.generateClass,
|
|
extractors.DefineDef : self.generateDefine,
|
|
extractors.FunctionDef : self.generateFunction,
|
|
extractors.EnumDef : self.generateEnum,
|
|
extractors.GlobalVarDef : self.generateGlobalVar,
|
|
extractors.TypedefDef : self.generateTypedef,
|
|
extractors.WigCode : self.generateWigCode,
|
|
extractors.PyCodeDef : self.generatePyCode,
|
|
extractors.CppMethodDef : self.generateFunction,
|
|
extractors.CppMethodDef_sip : self.generateFunction,
|
|
extractors.PyFunctionDef : self.generatePyFunction,
|
|
extractors.PyClassDef : self.generatePyClass,
|
|
}
|
|
|
|
if module.isARealModule:
|
|
filename = os.path.join(SPHINXROOT, self.current_module+'1moduleindex.pkl')
|
|
with PickleFile(filename) as pf:
|
|
pf.items[DOCSTRING_KEY] = module.docstring
|
|
|
|
for item in module:
|
|
if item.ignored or item.docsIgnored:
|
|
continue
|
|
|
|
function = methodMap[item.__class__]
|
|
function(item)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def generatePyFunction(self, function):
|
|
name = function.pyName if function.pyName else removeWxPrefix(function.name)
|
|
imm = ItemModuleMap()
|
|
fullname = imm.get_fullname(name)
|
|
|
|
function.overloads = []
|
|
function.pyArgsString = function.argsString
|
|
|
|
self.unIndent(function)
|
|
|
|
# docstring
|
|
docstring = XMLDocString(function)
|
|
docstring.kind = 'function'
|
|
docstring.current_module = self.current_module
|
|
docstring.Dump()
|
|
|
|
desc = chopDescription(docstring.docstrings)
|
|
pickleFunctionInfo(fullname, desc)
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def generateFunction(self, function):
|
|
name = function.pyName if function.pyName else removeWxPrefix(function.name)
|
|
if name.startswith('operator'):
|
|
return
|
|
|
|
imm = ItemModuleMap()
|
|
fullname = imm.get_fullname(name)
|
|
|
|
# docstring
|
|
docstring = XMLDocString(function)
|
|
docstring.kind = 'function'
|
|
docstring.current_module = self.current_module
|
|
docstring.Dump()
|
|
|
|
desc = chopDescription(docstring.docstrings)
|
|
pickleFunctionInfo(fullname, desc)
|
|
|
|
|
|
def unIndent(self, item):
|
|
|
|
if not item.briefDoc:
|
|
return
|
|
|
|
newdocs = ''
|
|
for line in item.briefDoc.splitlines():
|
|
if line.strip():
|
|
stripped = len(line) - len(line.lstrip())
|
|
break
|
|
|
|
newdocs = ''
|
|
for line in item.briefDoc.splitlines():
|
|
if line.strip():
|
|
line = line[stripped:]
|
|
|
|
newdocs += line + '\n'
|
|
|
|
item.briefDoc = newdocs
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def generatePyClass(self, klass):
|
|
|
|
self.fixNodeBaseNames(klass, ItemModuleMap())
|
|
|
|
klass.module = self.current_module
|
|
self.current_class = klass
|
|
|
|
class_name = klass.name
|
|
class_fullname = ItemModuleMap().get_fullname(class_name)
|
|
self.current_class.method_list = []
|
|
self.current_class.property_list = []
|
|
self.current_class.memberVar_list = []
|
|
|
|
class_items = [i for i in klass if not (i.ignored or i.docsIgnored)]
|
|
class_items = sorted(class_items, key=operator.attrgetter('name'))
|
|
|
|
class_items = self.removeDuplicated(class_fullname, class_items)
|
|
|
|
init_position = -1
|
|
|
|
for index, item in enumerate(class_items):
|
|
if isinstance(item, extractors.PyFunctionDef):
|
|
method_name, simple_docs = self.getName(item)
|
|
if method_name == '__init__':
|
|
init_position = index
|
|
self.current_class.method_list.insert(0, ('%s.%s'%(class_fullname, method_name), simple_docs))
|
|
else:
|
|
self.current_class.method_list.append(('%s.%s'%(class_fullname, method_name), simple_docs))
|
|
elif isinstance(item, extractors.PyPropertyDef):
|
|
simple_docs = self.createPropertyLinks(class_fullname, item)
|
|
self.current_class.property_list.append(('%s.%s'%(class_fullname, item.name), simple_docs))
|
|
|
|
if init_position >= 0:
|
|
init_method = class_items.pop(init_position)
|
|
class_items.insert(0, init_method)
|
|
|
|
self.unIndent(klass)
|
|
|
|
docstring = XMLDocString(klass)
|
|
docstring.kind = 'class'
|
|
|
|
filename = "%s.txt" % class_fullname
|
|
docstring.output_file = filename
|
|
docstring.current_module = self.current_module
|
|
|
|
docstring.Dump()
|
|
|
|
pickleClassInfo(class_fullname, self.current_class, docstring.short_description)
|
|
|
|
# these are the only kinds of items allowed to be items in a PyClass
|
|
dispatch = [(extractors.PyFunctionDef, self.generateMethod),
|
|
(extractors.PyPropertyDef, self.generatePyProperty),
|
|
(extractors.PyCodeDef, self.generatePyCode),
|
|
(extractors.PyClassDef, self.generatePyClass)]
|
|
|
|
for kind, function in dispatch:
|
|
for item in class_items:
|
|
if kind == item.__class__:
|
|
item.klass = klass
|
|
function(item)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def generatePyProperty(self, prop):
|
|
|
|
c = self.current_class
|
|
name = c.pyName if c.pyName else removeWxPrefix(c.name)
|
|
fullname = ItemModuleMap().get_fullname(name)
|
|
getter_setter = self.createPropertyLinks(fullname, prop)
|
|
|
|
stream = StringIO()
|
|
stream.write('\n .. attribute:: %s\n\n' % prop.name)
|
|
stream.write(' %s\n\n'%getter_setter)
|
|
|
|
filename = "%s.txt" % fullname
|
|
|
|
writeSphinxOutput(stream, filename, append=True)
|
|
|
|
# -----------------------------------------------------------------------
|
|
def generateClass(self, klass):
|
|
|
|
assert isinstance(klass, extractors.ClassDef)
|
|
|
|
if klass.ignored or klass.docsIgnored:
|
|
return
|
|
|
|
imm = ItemModuleMap()
|
|
|
|
self.fixNodeBaseNames(klass, imm)
|
|
|
|
# generate nested classes
|
|
for item in klass.innerclasses:
|
|
self.generateClass(item)
|
|
|
|
name = klass.pyName if klass.pyName else removeWxPrefix(klass.name)
|
|
fullname = imm.get_fullname(name)
|
|
|
|
klass.module = self.current_module
|
|
self.current_class = klass
|
|
|
|
klass.method_list = []
|
|
klass.property_list = []
|
|
klass.memberVar_list = []
|
|
klass.enum_list = []
|
|
|
|
# Inspected class Method to call Sort order
|
|
dispatch = {
|
|
extractors.MethodDef : (self.generateMethod, 1),
|
|
extractors.CppMethodDef : (self.generateMethod, 1),
|
|
extractors.CppMethodDef_sip : (self.generateMethod, 1),
|
|
extractors.PyMethodDef : (self.generatePyMethod, 1),
|
|
extractors.MemberVarDef : (self.generateMemberVar, 2),
|
|
extractors.PropertyDef : (self.generateProperty, 2),
|
|
extractors.PyPropertyDef : (self.generateProperty, 2),
|
|
extractors.EnumDef : (self.generateEnum, 0),
|
|
extractors.PyCodeDef : (self.generatePyCode, 3),
|
|
extractors.WigCode : (self.generateWigCode, 4),
|
|
extractors.TypedefDef : (lambda a: None, 5),
|
|
}
|
|
|
|
# Build a list to check if there are any properties
|
|
properties = (extractors.PropertyDef, extractors.PyPropertyDef)
|
|
methods = (extractors.MethodDef, extractors.CppMethodDef, extractors.CppMethodDef_sip, extractors.PyMethodDef)
|
|
|
|
# Split the items documenting the __init__ methods first
|
|
ctors = [i for i in klass if
|
|
isinstance(i, extractors.MethodDef) and
|
|
i.protection == 'public' and (i.isCtor or i.isDtor)]
|
|
|
|
class_items = [i for i in klass if i not in ctors and not (i.ignored or i.docsIgnored)]
|
|
|
|
for item in class_items:
|
|
item.sort_order = dispatch[item.__class__][1]
|
|
|
|
class_items = sorted(class_items, key=operator.attrgetter('sort_order', 'name'))
|
|
class_items = self.removeDuplicated(fullname, class_items)
|
|
|
|
for item in class_items:
|
|
if isinstance(item, methods) and not self.IsFullyDeprecated(item):
|
|
method_name, simple_docs = self.getName(item)
|
|
klass.method_list.append(('%s.%s' % (fullname, method_name), simple_docs))
|
|
elif isinstance(item, properties):
|
|
simple_docs = self.createPropertyLinks(fullname, item)
|
|
klass.property_list.append(('%s.%s' % (fullname, item.name), simple_docs))
|
|
elif isinstance(item, extractors.MemberVarDef):
|
|
description = self.createMemberVarDescription(item)
|
|
klass.memberVar_list.append(('%s.%s' % (fullname, item.name), description))
|
|
|
|
for item in ctors:
|
|
if item.isCtor:
|
|
method_name, simple_docs = self.getName(item)
|
|
klass.method_list.insert(0, ('%s.__init__'%fullname, simple_docs))
|
|
|
|
docstring = XMLDocString(klass)
|
|
|
|
filename = "%s.txt" % fullname
|
|
docstring.output_file = filename
|
|
docstring.current_module = self.current_module
|
|
|
|
docstring.Dump()
|
|
|
|
pickleClassInfo(fullname, self.current_class, docstring.short_description)
|
|
|
|
for item in ctors:
|
|
if item.isCtor:
|
|
self.generateMethod(item, name='__init__', docstring=klass.pyDocstring)
|
|
|
|
for item in class_items:
|
|
f = dispatch[item.__class__][0]
|
|
f(item)
|
|
|
|
if klass.postProcessReST is not None:
|
|
full_name = os.path.join(SPHINXROOT, filename)
|
|
with textfile_open(full_name) as f:
|
|
text = f.read()
|
|
text = klass.postProcessReST(text)
|
|
with textfile_open(full_name, 'wt') as f:
|
|
f.write(text)
|
|
|
|
if klass.enum_list:
|
|
stream = StringIO()
|
|
stream.write("\n.. toctree::\n :maxdepth: 1\n :hidden:\n\n")
|
|
for enum_name in klass.enum_list:
|
|
stream.write(" {}.enumeration\n".format(enum_name))
|
|
writeSphinxOutput(stream, filename, True)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def fixNodeBaseNames(self, klass, imm):
|
|
# convert the names in nodeBases to fullnames
|
|
def _fix(name):
|
|
return imm.get_fullname(removeWxPrefix(name))
|
|
|
|
if not klass.nodeBases:
|
|
name = klass.pyName if klass.pyName else klass.name
|
|
name = _fix(name)
|
|
klass.nodeBases = ([(name, [])], [name])
|
|
return
|
|
|
|
bases, specials = klass.nodeBases
|
|
bases = list(bases.values())
|
|
specials = [_fix(s) for s in specials]
|
|
for idx, (name, baselist) in enumerate(bases):
|
|
name = _fix(name)
|
|
baselist = [_fix(b) for b in baselist]
|
|
bases[idx] = (name, baselist)
|
|
klass.nodeBases = (bases, specials)
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def generateMethod(self, method, name=None, docstring=None):
|
|
|
|
if method.ignored or method.docsIgnored:
|
|
return
|
|
|
|
name = name or self.getName(method)[0]
|
|
## if name.startswith("__") and "__init__" not in name:
|
|
## return
|
|
|
|
if isinstance(method, extractors.PyFunctionDef):
|
|
self.unIndent(method)
|
|
|
|
imm = ItemModuleMap()
|
|
c = self.current_class
|
|
class_name = c.pyName if c.pyName else removeWxPrefix(c.name)
|
|
fullname = imm.get_fullname(class_name)
|
|
|
|
# docstring
|
|
method.name = name
|
|
method.pyArgsString = method.pyArgsString.replace('(self)', ' ').replace('(self, ', ' ')
|
|
docstring = XMLDocString(method)
|
|
docstring.kind = 'method'
|
|
|
|
filename = "%s.txt" % fullname
|
|
|
|
docstring.output_file = filename
|
|
docstring.class_name = class_name
|
|
docstring.current_module = self.current_module
|
|
|
|
docstring.Dump()
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def IsFullyDeprecated(self, pyMethod):
|
|
|
|
if not isinstance(pyMethod, extractors.PyMethodDef):
|
|
return False
|
|
|
|
if pyMethod.deprecated:
|
|
brief, detailed = pyMethod.briefDoc, pyMethod.detailedDoc
|
|
if not brief and not detailed:
|
|
# Skip simple wrappers unless they have a brief or a detailed doc
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def generatePyMethod(self, pm):
|
|
|
|
assert isinstance(pm, extractors.PyMethodDef)
|
|
|
|
if pm.ignored or pm.docsIgnored:
|
|
return
|
|
|
|
if self.IsFullyDeprecated(pm):
|
|
return
|
|
|
|
stream = StringIO()
|
|
stream.write('\n .. method:: %s%s\n\n' % (pm.name, pm.argsString))
|
|
|
|
docstrings = return_type = ''
|
|
|
|
for line in pm.pyDocstring.splitlines():
|
|
if '->' in line:
|
|
arguments, after = line.strip().split("->")
|
|
return_type = self.returnSection(after)
|
|
else:
|
|
docstrings += line + '\n'
|
|
|
|
docstrings = convertToPython(docstrings)
|
|
|
|
newdocs = ''
|
|
spacer = ' '*6
|
|
|
|
for line in docstrings.splitlines():
|
|
if not line.startswith(spacer):
|
|
newdocs += spacer + line + "\n"
|
|
else:
|
|
newdocs += line + "\n"
|
|
|
|
stream.write(newdocs + '\n\n')
|
|
|
|
c = self.current_class
|
|
name = c.pyName if c.pyName else removeWxPrefix(c.name)
|
|
imm = ItemModuleMap()
|
|
filename = "%s.txt" % imm.get_fullname(name)
|
|
|
|
writeSphinxOutput(stream, filename, append=True)
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def generateMemberVar(self, memberVar):
|
|
assert isinstance(memberVar, extractors.MemberVarDef)
|
|
if memberVar.ignored or memberVar.docsIgnored or memberVar.protection != 'public':
|
|
return
|
|
|
|
c = self.current_class
|
|
name = c.pyName if c.pyName else removeWxPrefix(c.name)
|
|
fullname = ItemModuleMap().get_fullname(name)
|
|
|
|
description = self.createMemberVarDescription(memberVar)
|
|
|
|
stream = StringIO()
|
|
stream.write('\n .. attribute:: %s\n\n' % memberVar.name)
|
|
stream.write(' %s\n\n' % description)
|
|
|
|
filename = "%s.txt" % fullname
|
|
writeSphinxOutput(stream, filename, append=True)
|
|
|
|
|
|
def createMemberVarDescription(self, memberVar):
|
|
varType = pythonizeType(memberVar.type, False)
|
|
if varType.startswith('wx.'):
|
|
varType = ':ref:`~%s`' % varType
|
|
else:
|
|
varType = '``%s``' % varType
|
|
|
|
description = 'A public C++ attribute of type %s.' % varType
|
|
|
|
brief = memberVar.briefDoc
|
|
briefDoc = None
|
|
if not isinstance(brief, str):
|
|
docstring = XMLDocString(memberVar)
|
|
#docstring.current_module = self.current_module
|
|
briefDoc = docstring.GetBrief()
|
|
elif brief is not None:
|
|
briefDoc = convertToPython(brief)
|
|
|
|
if briefDoc:
|
|
description += ' ' + briefDoc
|
|
|
|
return description
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def generateProperty(self, prop):
|
|
|
|
if prop.ignored or prop.docsIgnored:
|
|
return
|
|
|
|
c = self.current_class
|
|
name = c.pyName if c.pyName else removeWxPrefix(c.name)
|
|
fullname = ItemModuleMap().get_fullname(name)
|
|
|
|
getter_setter = self.createPropertyLinks(fullname, prop)
|
|
|
|
stream = StringIO()
|
|
stream.write('\n .. attribute:: %s\n\n' % prop.name)
|
|
stream.write(' %s\n\n' % getter_setter)
|
|
|
|
filename = "%s.txt" % fullname
|
|
writeSphinxOutput(stream, filename, append=True)
|
|
|
|
|
|
def createPropertyLinks(self, name, prop):
|
|
|
|
if prop.getter and prop.setter:
|
|
return 'See :meth:`~%s.%s` and :meth:`~%s.%s`' % (name, prop.getter, name, prop.setter)
|
|
else:
|
|
method = (prop.getter and [prop.getter] or [prop.setter])[0]
|
|
return 'See :meth:`~%s.%s`' % (name, method)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
def generateEnum(self, enum):
|
|
|
|
assert isinstance(enum, extractors.EnumDef)
|
|
if enum.ignored or enum.docsIgnored:
|
|
return
|
|
|
|
docstring = XMLDocString(enum)
|
|
docstring.current_module = self.current_module
|
|
docstring.current_class = self.current_class if hasattr(self, 'current_class') else None
|
|
|
|
docstring.Dump()
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
def generateGlobalVar(self, globalVar):
|
|
assert isinstance(globalVar, extractors.GlobalVarDef)
|
|
if globalVar.ignored or globalVar.docsIgnored:
|
|
return
|
|
name = globalVar.pyName or globalVar.name
|
|
if guessTypeInt(globalVar):
|
|
valTyp = '0'
|
|
elif guessTypeFloat(globalVar):
|
|
valTyp = '0.0'
|
|
elif guessTypeStr(globalVar):
|
|
valTyp = '""'
|
|
else:
|
|
valTyp = removeWxPrefix(globalVar.type) + '()'
|
|
|
|
# -----------------------------------------------------------------------
|
|
def generateDefine(self, define):
|
|
assert isinstance(define, extractors.DefineDef)
|
|
# write nothing for this one
|
|
|
|
# -----------------------------------------------------------------------
|
|
def generateTypedef(self, typedef):
|
|
assert isinstance(typedef, extractors.TypedefDef)
|
|
|
|
if typedef.ignored or typedef.docsIgnored or not typedef.docAsClass:
|
|
return
|
|
|
|
name = typedef.pyName if typedef.pyName else removeWxPrefix(typedef.name)
|
|
typedef.module = self.current_module
|
|
|
|
all_classes = {}
|
|
fullname = name
|
|
specials = [fullname]
|
|
|
|
baselist = [base for base in typedef.bases if base != 'object']
|
|
all_classes[fullname] = (fullname, baselist)
|
|
|
|
for base in baselist:
|
|
all_classes[base] = (base, [])
|
|
|
|
self.unIndent(typedef)
|
|
|
|
typedef.nodeBases = all_classes, specials
|
|
self.fixNodeBaseNames(typedef, ItemModuleMap())
|
|
typedef.subClasses = []
|
|
typedef.method_list = []
|
|
typedef.property_list = []
|
|
typedef.memberVar_list = []
|
|
typedef.pyDocstring = typedef.briefDoc
|
|
|
|
self.current_class = typedef
|
|
|
|
docstring = XMLDocString(typedef)
|
|
docstring.kind = 'class'
|
|
|
|
filename = self.current_module + "%s.txt"%name
|
|
docstring.output_file = filename
|
|
docstring.current_module = self.current_module
|
|
|
|
docstring.Dump()
|
|
|
|
pickleClassInfo(self.current_module + name, self.current_class, docstring.short_description)
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
def generateWigCode(self, wig):
|
|
assert isinstance(wig, extractors.WigCode)
|
|
# write nothing for this one
|
|
|
|
# -----------------------------------------------------------------------
|
|
def generatePyCode(self, pc):
|
|
|
|
assert isinstance(pc, extractors.PyCodeDef)
|
|
|
|
# -----------------------------------------------------------------------
|
|
def getName(self, method):
|
|
|
|
if hasattr(method, 'isCtor') and method.isCtor:
|
|
method_name = '__init__'
|
|
else:
|
|
method_name = method.pyName or method.name
|
|
if method_name in MAGIC_METHODS:
|
|
method_name = MAGIC_METHODS[method_name]
|
|
|
|
simple_docs = ''
|
|
|
|
if isinstance(method, extractors.PyMethodDef):
|
|
simple_docs = convertToPython(method.pyDocstring)
|
|
else:
|
|
brief = method.briefDoc
|
|
if not isinstance(brief, str):
|
|
docstring = XMLDocString(method)
|
|
docstring.kind = 'method'
|
|
docstring.current_module = self.current_module
|
|
simple_docs = docstring.GetBrief()
|
|
elif brief is not None:
|
|
simple_docs = convertToPython(brief)
|
|
|
|
simple_docs = chopDescription(simple_docs)
|
|
|
|
return method_name, simple_docs
|
|
|
|
# ---------------------------------------------------------------------------
|
|
def returnSection(self, after):
|
|
|
|
if '(' in after:
|
|
|
|
rtype1 = ReturnType('`tuple`', None)
|
|
|
|
return_section = after.strip().lstrip('(').rstrip(')')
|
|
return_section = return_section.split(',')
|
|
new_section = []
|
|
|
|
for ret in return_section:
|
|
stripped = ret.strip()
|
|
imm = ItemModuleMap()
|
|
if stripped in imm:
|
|
ret = imm[stripped] + stripped
|
|
new_section.append(':ref:`%s`'%ret)
|
|
else:
|
|
if ret[0].isupper():
|
|
new_section.append(':ref:`%s`'%stripped)
|
|
else:
|
|
new_section.append('`%s`'%stripped)
|
|
|
|
element = ET.Element('return', kind='return')
|
|
element.text = '( %s )'%(', '.join(new_section))
|
|
|
|
rtype2 = Section(element, None, 'method')
|
|
rtype = rtype1.Join() + rtype2.Join()
|
|
|
|
else:
|
|
|
|
rtype = pythonizeType(after, is_param=False)
|
|
|
|
if not rtype:
|
|
return ''
|
|
|
|
if rtype[0].isupper() or '.' in rtype:
|
|
rtype = ':ref:`%s`'%rtype
|
|
else:
|
|
rtype = '`%s`'%rtype
|
|
|
|
rtype = ReturnType(rtype, None)
|
|
rtype = rtype.Join()
|
|
|
|
out = ''
|
|
for r in rtype.splitlines():
|
|
out += 6*' ' + r + '\n'
|
|
|
|
return out
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# helpers
|
|
|
|
def guessTypeInt(v):
|
|
if isinstance(v, extractors.EnumValueDef):
|
|
return True
|
|
if isinstance(v, extractors.DefineDef) and '"' not in v.value:
|
|
return True
|
|
type = v.type.replace('const', '')
|
|
type = type.replace(' ', '')
|
|
if type in ['int', 'long', 'byte', 'size_t']:
|
|
return True
|
|
if 'unsigned' in type:
|
|
return True
|
|
return False
|
|
|
|
|
|
def guessTypeFloat(v):
|
|
type = v.type.replace('const', '')
|
|
type = type.replace(' ', '')
|
|
if type in ['float', 'double', 'wxDouble']:
|
|
return True
|
|
return False
|
|
|
|
def guessTypeStr(v):
|
|
if hasattr(v, 'value') and '"' in v.value:
|
|
return True
|
|
if 'wxString' in v.type:
|
|
return True
|
|
return False
|
|
|
|
# ---------------------------------------------------------------------------
|