- First stub at integrating `wx.lib` (116 warnings);
- Added `internationalization.rst` file from Werner (with a cool sample app as well);
- Added new samples for AffineMatrix2D.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@71099 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Andrea Gavana
2012-04-05 18:28:47 +00:00
parent ba91908eec
commit 77338050a0
27 changed files with 2477 additions and 222 deletions

View File

@@ -68,6 +68,8 @@ Usage: ./build.py [command(s)] [options]
SIP files
sphinx Run the documentation building process using Sphinx (this
needs to be done after dox and etg)
wxlib Run the documentation building process using Sphinx for
wx.lib (and possibly other pure-Python modules/packages)
sip Run sip
test Run the unit test suite
test_* Run just one test module
@@ -123,7 +125,7 @@ def main(args):
elif cmd in ['dox', 'doxhtml', 'etg', 'sip', 'touch', 'test',
'build_wx', 'build_py', 'setup_py', 'waf_py', 'build', 'bdist',
'clean', 'clean_wx', 'clean_py', 'cleanall', 'clean_sphinx',
'sphinx']:
'sphinx', 'wxlib']:
function = globals()[cmd]
function(options, args)
else:
@@ -592,6 +594,23 @@ def sphinx(options, args):
msg('Postprocesing sphinx output...')
PostProcess(htmlDir)
def wxlib(options, args):
from sphinxtools.modulehunter import ModuleHunter
cmdTimer = CommandTimer('wxlib')
pwd = pushDir(phoenixDir())
libDir = os.path.join(phoenixDir(), 'wx', 'lib')
if not os.path.isdir(libDir):
raise Exception('Missing wx.lib folder in the distribution')
init_name = os.path.join(libDir, '__init__.py')
import_name = 'lib'
version = version3
ModuleHunter(init_name, import_name, version)
def sip(options, args):

View File

@@ -0,0 +1,107 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-#
#
# Author: Werner F. Bruhin
# Purpose: how to I18N enable an application
#
# Inspired by the I18N wxPython demo and the Internationalization page on
# the wxPython wiki.
#
import sys
import os
import wx
# Install a custom displayhook to keep Python from setting the global
# _ (underscore) to the value of the last evaluated expression. If
# we don't do this, our mapping of _ to gettext can get overwritten.
# This is useful/needed in interactive debugging with PyShell.
def _displayHook(obj):
if obj is not None:
print repr(obj)
# add translation macro to builtin similar to what gettext does
import __builtin__
__builtin__.__dict__['_'] = wx.GetTranslation
from wx.lib.mixins.inspection import InspectionMixin
class BaseApp(wx.App, InspectionMixin):
def OnInit(self):
self.Init() # InspectionMixin
# work around for Python stealing "_"
sys.displayhook = _displayHook
self.appName = "I18N sample application"
# define the translation domain, the name has to match your .mo files
self.langDomain = "I18Nwxapp"
# languages you want to support
self.supLang = {u"en": wx.LANGUAGE_ENGLISH,
u"fr": wx.LANGUAGE_FRENCH,
u"de": wx.LANGUAGE_GERMAN,
}
self.doConfig()
self.locale = None
wx.Locale.AddCatalogLookupPathPrefix('locale')
self.updateLanguage(self.appConfig.Read(u"Language"))
return True
def doConfig(self):
"""Setup an application configuration file"""
# configuration folder
sp = wx.StandardPaths.Get()
self.configLoc = sp.GetUserConfigDir()
self.configLoc = os.path.join(self.configLoc, self.appName)
# win: C:\Users\userid\AppData\Roaming\appName
# nix: \home\userid\appName
if not os.path.exists(self.configLoc):
os.mkdir(self.configLoc)
# AppConfig stuff is here
self.appConfig = wx.FileConfig(appName=self.appName,
vendorName=u'who you wish',
localFilename=os.path.join(
self.configLoc, "AppConfig"))
if not self.appConfig.HasEntry(u'Language'):
# on first run we default to German
self.appConfig.Write(key=u'Language', value=u'de')
self.appConfig.Flush()
def updateLanguage(self, lang):
"""
Update the language to the requested one.
Make *sure* any existing locale is deleted before the new
one is created. The old C++ object needs to be deleted
before the new one is created, and if we just assign a new
instance to the old Python variable, the old C++ locale will
not be destroyed soon enough, likely causing a crash.
:param string `lang`: one of the supported language codes
"""
# if an unsupported language is requested default to English
if lang in self.supLang:
selLang = self.supLang[lang]
else:
selLang = wx.LANGUAGE_ENGLISH
if self.locale:
assert sys.getrefcount(self.locale) <= 2
del self.locale
# create a locale object for this language
self.locale = wx.Locale(selLang)
if self.locale.IsOk():
self.locale.AddCatalog(self.langDomain)
else:
self.locale = None

View File

@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-#
#!/usr/bin/env python
"""
This will generate the .pot and .mo files for the application domain and languages defined below.
The .po and .mo files are placed as per convention in
"appfolder/locale/lang/LC_MESSAGES"
The .pot file is placed in the current folder.
This script or something similar should be added to your build process.
The actual translation work is normally done using a tool like poEdit or
similar, it allows you to generate a particular language catalog from the .pot
file or to use the .pot to merge new translations into an existing language
catalog.
"""
domainName = 'i18nwxapp'
# define the languages you are supporting and need translation, so if texts
# in your source are English then you don't need 'en' here
supLang = ['fr', 'de']
import os
import sys
import subprocess
appFolder, loc = os.path.split(os.getcwd())
# setup some stuff to get at Python I18N tools/utilities
pyExe = sys.executable
pyFolder = os.path.split(pyExe)[0]
pyToolsFolder = os.path.join(pyFolder, 'Tools')
pyI18nFolder = os.path.join(pyToolsFolder, 'i18n')
pyGettext = os.path.join(pyI18nFolder, 'pygettext.py')
pyMsgfmt = os.path.join(pyI18nFolder, 'msgfmt.py')
outFolder = os.getcwd()
# build command for pygettext
gtOptions = '-a -d %s -o %s.pot -p %s %s'
tCmd = pyExe + ' ' + pyGettext + ' ' + (gtOptions % (domainName,
domainName,
outFolder,
appFolder))
print "Generating the .pot file"
print "cmd: %s" % tCmd
rCode = subprocess.call(tCmd)
print "return code: %s\n\n" % rCode
for tLang in supLang:
# build command for msgfmt
langDir = os.path.join(appFolder, ('locale\%s\LC_MESSAGES' % tLang))
poFile = os.path.join(langDir, domainName + '.po')
tCmd = pyExe + ' ' + pyMsgfmt + ' ' + poFile
print "Generating the .mo file"
print "cmd: %s" % tCmd
rCode = subprocess.call(tCmd)
print "return code: %s\n\n" % rCode

View File

@@ -0,0 +1,73 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2012-04-05 12:12+Paris, Madrid (heure d<><64>t<EFBFBD>)\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: ENCODING\n"
"Generated-By: pygettext.py 1.5\n"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:17
msgid "The I18N sample application"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:28
msgid "Close"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:29
msgid "Close the application"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:31
msgid "&File"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:36
msgid "Edit something"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:37
msgid "Edit an entry of something"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:40
msgid "&Edit"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:45
msgid "&About"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:46
msgid "About the program"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:48
msgid "&Help"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:58
msgid "A nice label for the TextCtrl"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:62
msgid "a search control"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:68
msgid "Open a file dialog"
msgstr ""
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:80
msgid "Choose a file"
msgstr ""

Binary file not shown.

View File

@@ -0,0 +1,74 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: I18N wxPyhton sample\n"
"POT-Creation-Date: 2012-04-05 12:12+Paris, Madrid (heure d’été)\n"
"PO-Revision-Date: 2012-04-05 12:13+0100\n"
"Last-Translator: Werner F. Bruhin <werner@thewinecellarbook.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Poedit-Language: English\n"
"X-Poedit-SourceCharset: utf-8\n"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:17
msgid "The I18N sample application"
msgstr "Die I18N-Beispielanwendung"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:28
msgid "Close"
msgstr "Schliessen"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:29
msgid "Close the application"
msgstr "Die Anwendung schliessen"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:31
msgid "&File"
msgstr "&Datei"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:36
msgid "Edit something"
msgstr "Etwas bearbeiten"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:37
msgid "Edit an entry of something"
msgstr "Einenen Eintrag bearbeiten von etwas"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:40
msgid "&Edit"
msgstr "&Bearbeiten"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:45
msgid "&About"
msgstr "&Über"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:46
msgid "About the program"
msgstr "Über die Anwendung"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:48
msgid "&Help"
msgstr "&Hilfe"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:58
msgid "A nice label for the TextCtrl"
msgstr "Ein schönes Etikett für die TextCtrl"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:62
msgid "a search control"
msgstr "Eine Such Kontrolle"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:68
msgid "Open a file dialog"
msgstr "Einen Dateidialog öffnen"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:80
msgid "Choose a file"
msgstr "Eine Datei auswählen"

View File

@@ -0,0 +1,74 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: I18N wxPyhton sample\n"
"POT-Creation-Date: 2012-04-05 12:12+Paris, Madrid (heure d’été)\n"
"PO-Revision-Date: 2012-04-05 12:14+0100\n"
"Last-Translator: Werner F. Bruhin <werner@thewinecellarbook.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Poedit-Language: French\n"
"X-Poedit-SourceCharset: utf-8\n"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:17
msgid "The I18N sample application"
msgstr "L'exemple d'application I18N"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:28
msgid "Close"
msgstr "Terminer"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:29
msgid "Close the application"
msgstr "Terminer l'application"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:31
msgid "&File"
msgstr "&Fichier"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:36
msgid "Edit something"
msgstr "Modifier quelque chose"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:37
msgid "Edit an entry of something"
msgstr "Modifier une entrée de quelque chose"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:40
msgid "&Edit"
msgstr "&Modifier"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:45
msgid "&About"
msgstr "&Au sujet de"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:46
msgid "About the program"
msgstr "Au sujet du programm"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:48
msgid "&Help"
msgstr "&Aide"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:58
msgid "A nice label for the TextCtrl"
msgstr "Une belle étiquette pour le TextCtrl"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:62
msgid "a search control"
msgstr "Une 'control' de recherche"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:68
msgid "Open a file dialog"
msgstr "Ouvrir un dialogue de fichier"
#: h:\devProjectsT\PhoenixI18N\i18nwxapp\sampleapp.py:80
msgid "Choose a file"
msgstr "Choisir un fichier"

View File

@@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-#
#!/usr/bin/env python
"""The sample I18N application"""
import os
import wx
import wx.lib.sized_controls as sc
class AppI18N(sc.SizedFrame):
def __init__(self, parent, **kwds):
"""
A sample application to demonstrate how to enable I18N support
"""
super(AppI18N, self).__init__(parent, **kwds)
self.SetTitle(_(u"The I18N sample application"))
self.createMenu()
self.createOtherCtrls()
def createMenu(self):
menubar = wx.MenuBar()
# file menu
fileMenu = wx.Menu()
closeMenuItem = fileMenu.Append(wx.NewId(),
_(u"Close"),
_(u"Close the application"))
self.Bind(wx.EVT_MENU, self.onClose, closeMenuItem)
menubar.Append(fileMenu, _(u"&File"))
# edit menu
manageMenu = wx.Menu()
manageSomethingMenuItem = manageMenu.Append(wx.NewId(),
_(u"Edit something"),
_(u"Edit an entry of something"))
self.Bind(wx.EVT_MENU, self.doEditSomething, manageSomethingMenuItem)
menubar.Append(manageMenu, _(u"&Edit"))
# help menu
helpMenu = wx.Menu()
aboutMenuItem = helpMenu.Append(wx.NewId(),
_(u"&About"),
_(u"About the program"))
self.Bind(wx.EVT_MENU, self.doAboutBox, aboutMenuItem)
menubar.Append(helpMenu, _(u"&Help"))
self.SetMenuBar(menubar)
def createOtherCtrls(self):
pane = self.GetContentsPane()
cPane = sc.SizedPanel(pane)
cPane.SetSizerType("grid", options={"cols": 2})
st = wx.StaticText(cPane, wx.ID_ANY,
_(u"A nice label for the TextCtrl"))
st.SetSizerProps(valign='center')
tc = wx.TextCtrl(cPane, wx.ID_ANY)
searchSt = wx.StaticText(cPane, wx.ID_ANY,
_(u"a search control"))
searchSt.SetSizerProps(valign='center')
searchC = wx.SearchCtrl(cPane, wx.ID_ANY)
sline = wx.StaticLine(pane, wx.ID_ANY)
sline.SetSizerProps(expand=True)
bPane = sc.SizedPanel(pane)
fB = wx.Button(bPane, wx.ID_ANY, _(u"Open a file dialog"))
fB.SetSizerProps(align="center")
fB.Bind(wx.EVT_BUTTON, self.onFbButton)
def onFbButton(self, event):
wildcard = "Python source (*.py)|*.py|" \
"Compiled Python (*.pyc)|*.pyc|" \
"SPAM files (*.spam)|*.spam|" \
"Egg file (*.egg)|*.egg|" \
"All files (*.*)|*.*"
with wx.FileDialog(
self, message=_(u"Choose a file"),
defaultDir=os.getcwd(),
defaultFile="",
wildcard=wildcard,
style=wx.OPEN | wx.MULTIPLE | wx.CHANGE_DIR
) as dlg:
# Show the dialog and retrieve the user response. If it is the
# OK response,
# process the data.
if dlg.ShowModal() == wx.ID_OK:
# This returns a Python list of files that were selected.
paths = dlg.GetPaths()
def onClose(self, event):
event.Skip()
def doEditSomething(self, event):
event.Skip()
def doAboutBox(self, event):
event.Skip()
if __name__ == '__main__':
import app_base as ab
app = ab.BaseApp(redirect=False)
frame = AppI18N(None)
frame.Show()
app.MainLoop()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -134,7 +134,43 @@ The new wxPython API documentation is available `in this page <main.html>`_.
:hidden:
:glob:
*
MigrationGuide
TODO
DocstringsGuidelines
functions
1classindex
app_overview
bitmap_overview
bookctrl_overview
common_dialogs_overview
config_overview
datetime_overview
dc_overview
dialog_overview
dnd_overview
events_overview
filesystem_overview
font_encodings
font_overview
internationalization
listctrl_overview
log_classes_overview
printing_framework_overview
refcount_overview
scrolling_overview
sizers_overview
splitterwindow_overview
standard_event_identifiers
stock_items
toolbar_overview
treectrl_overview
validator_overview
window_deletion_overview
window_ids_overview
window_sizing_overview
window_styles_overview
dataview.1classindex
lib
Indices and tables

View File

@@ -2,13 +2,105 @@
.. _internationalization:
.. _I18N:
.. _localization:
.. _L10N:
==================================================
|phoenix_title| **Internationalization Overview**
==================================================
.. todo:: Write this section.
"Internationalization" (often referred to as I18N) is the process to change an
application so that all user visible texts are translated to the user selected
language and that things like dates, money amounts and numbers in general are
shown in a format the user is familiar with/or used to.
The easiest way to show what is needed is by using a little code sample.
Text translation
================
Prepare the source code
-----------------------
Text translation in Python is done using gettext [1]_ , to ensure that all wxPython
labels are also translated we will use :class:`Locale` and :func:`GetTranslation` .
How to prepare your source code to enable translation of texts::
aString = _(u"This is a string which will be translated")
As you can see it is very simple, you just enclose the text with the translation function "_()",
obviously there is a bit more to it, see below.
Enabling I18N for a whole application you would do some setup in the application file along the following lines:
.. literalinclude:: _downloads/i18nwxapp/app_base.py
:lines: 25-27
Here we setup the "_" translation function and making it available application by adding it to builtin.
The code required to change to a different language is as follows:
.. literalinclude:: _downloads/i18nwxapp/app_base.py
:pyobject: BaseApp.updateLanguage
Do the actual translation work
------------------------------
You need to extract all the text strings marked by the "_" function, a little script `geni18n.py` is in the
:download:`downloadable zip file <_downloads/i18nwxapp/i18nwxapp.zip>`, it will extract all the strings
and generate a ``.pot`` file. The `geni18n.py` script will also generate the ``.mo`` files for defined languages.
The ``.pot`` file is then provided to the translators and they use it to generate a ``.po`` file for
the language they translate too or they can also use the ``.pot`` file to merge new/changed text strings
to an existing ``.po`` file.
To do the actual translation we recomment `poEdit` [2]_ , it allows you to create or update a translation
catalog (``.po`` file) from the ``.pot`` file.
Sample application
------------------
In the :download:`downloadable zip file <_downloads/i18nwxapp/i18nwxapp.zip>` we included a small
sample application showing the above in action.
- `app_base.py` contains the initialization code
- `sampleapp.py` is the main frame/application, just run this to see things in action
- `i18ntools/geni18n.py` is the script to generate the ``.pot`` file and it also generates the ``.mo`` files.
.. note::
The application has a button which displays a file dialog, as wxPython uses a native widget for this the
text are shown in the operating system language and not the language which is selected in `app_base.py`.
Localization overview
=====================
"Localization", often referred to as "L10N", is the process to adapt the display of dates and numbers to local custom.
E.g. "4/5/2012" would for an American mean April 5. 2012, but for most Europeans it would be 4. May 2012.
Localize dates
==============
.. todo:: to be written
Localize numbers
================
.. todo:: to be written
.. rubric:: Footnotes
.. [1] gettext - http://docs.python.org/library/gettext.html
.. [2] poEdit - http://www.poedit.net/

View File

@@ -0,0 +1,4 @@
# | t.self.11 t.self.12 0 | | self.11 self.12 0 |
# matrix' = | t.self.21 t.self.22 0 | x | self.21 self.22 0 |
# | t.self.tx t.self.ty 1 | | self.tx self.ty 1 |

View File

@@ -0,0 +1,4 @@
# | self.11 self.12 0 |
# Invert | self.21 self.22 0 |
# | self.tx self.ty 1 |

View File

@@ -0,0 +1,4 @@
# | t.self.11 t.self.12 0 | | self.11 self.12 0 |
# matrix' = | t.self.21 t.self.22 0 | x | self.21 self.22 0 |
# | t.self.tx t.self.ty 1 | | self.tx self.ty 1 |

View File

@@ -0,0 +1,4 @@
# | self.11 self.12 0 |
# Invert | self.21 self.22 0 |
# | self.tx self.ty 1 |

View File

@@ -2218,6 +2218,11 @@ class XMLDocString(object):
klass = self.xml_item
name = self.class_name
dummy, fullname = Wx2Sphinx(name)
if '.' in fullname:
parts = fullname.split('.')
module = '.'.join(parts[0:-1])
stream.write('\n\n.. currentmodule:: %s\n\n'%module)
stream.write(templates.TEMPLATE_DESCRIPTION % (fullname, name))
@@ -2250,11 +2255,11 @@ class XMLDocString(object):
stream.write(snippets)
if klass.method_list:
summary = MakeSummary(name, klass.method_list, templates.TEMPLATE_METHOD_SUMMARY, 'meth')
summary = MakeSummary(klass.method_list, templates.TEMPLATE_METHOD_SUMMARY, 'meth')
stream.write(summary)
if klass.property_list:
summary = MakeSummary(name, klass.property_list, templates.TEMPLATE_PROPERTY_SUMMARY, 'attr')
summary = MakeSummary(klass.property_list, templates.TEMPLATE_PROPERTY_SUMMARY, 'attr')
stream.write(summary)
stream.write(templates.TEMPLATE_API)
@@ -2548,6 +2553,9 @@ class XMLDocString(object):
stream = StringIO()
self.output_file = self.current_module + "%s.enumeration.txt"%enum_name
if self.current_module.strip():
stream.write('\n\n.. currentmodule:: %s\n\n'%self.current_module[0:-1])
stream.write(templates.TEMPLATE_DESCRIPTION % (fullname, enum_name))
stream.write('\n\nThe `%s` enumeration provides the following values:\n\n'%enum_name)
@@ -2856,14 +2864,14 @@ class SphinxGenerator(generators.DocsGeneratorBase):
klass.module = self.current_module
self.current_class = klass
name = klass.name
class_name = klass.name
self.current_class.method_list = []
self.current_class.property_list = []
class_items = [i for i in klass if not i.ignored]
class_items = sorted(class_items, key=operator.attrgetter('name'))
class_items = self.RemoveDuplicated(name, class_items)
class_items = self.RemoveDuplicated(class_name, class_items)
init_position = -1
@@ -2872,14 +2880,14 @@ class SphinxGenerator(generators.DocsGeneratorBase):
method_name, simple_docs = self.getName(item)
if method_name == '__init__':
init_position = index
self.current_class.method_list.insert(0, (method_name, simple_docs))
self.current_class.method_list.insert(0, ('%s.%s'%(class_name, method_name), simple_docs))
else:
self.current_class.method_list.append((method_name, simple_docs))
self.current_class.method_list.append(('%s.%s'%(class_name, method_name), simple_docs))
elif isinstance(item, extractors.PyPropertyDef):
simple_docs = self.createPropertyLinks(name, item)
self.current_class.property_list.append((item.name, simple_docs))
simple_docs = self.createPropertyLinks(class_name, item)
self.current_class.property_list.append(('%s.%s'%(class_name, item.name), simple_docs))
PickleClassInfo(self.current_module + name, self.current_class)
PickleClassInfo(self.current_module + class_name, self.current_class)
if init_position >= 0:
init_method = class_items.pop(init_position)
@@ -2890,7 +2898,7 @@ class SphinxGenerator(generators.DocsGeneratorBase):
docstring = XMLDocString(klass)
docstring.kind = 'class'
filename = self.current_module + "%s.txt"%name
filename = self.current_module + "%s.txt"%class_name
docstring.output_file = filename
docstring.current_module = self.current_module
@@ -2983,15 +2991,15 @@ class SphinxGenerator(generators.DocsGeneratorBase):
for item in class_items:
if isinstance(item, methods) and not self.IsFullyDeprecated(item):
method_name, simple_docs = self.getName(item)
self.current_class.method_list.append((method_name, simple_docs))
self.current_class.method_list.append(('%s.%s'%(name, method_name), simple_docs))
elif isinstance(item, properties):
simple_docs = self.createPropertyLinks(name, item)
self.current_class.property_list.append((item.name, simple_docs))
self.current_class.property_list.append(('%s.%s'%(name, item.name), simple_docs))
for item in ctors:
if item.isCtor:
method_name, simple_docs = self.getName(item)
self.current_class.method_list.insert(0, ('__init__', simple_docs))
self.current_class.method_list.insert(0, ('%s.__init__'%name, simple_docs))
PickleClassInfo(self.current_module + name, self.current_class)

View File

@@ -144,8 +144,68 @@ HTML_REPLACE = ['module', 'function', 'method', 'class', 'classmethod', 'staticm
# The SVN revision of wxWidgets/Phoenix used to build the Sphinx docs.
# There must be a more intelligent way to get this information automatically.
SVN_REVISION = '71086'
SVN_REVISION = '71066'
# Today's date representation for the Sphinx HTML docs
TODAY = datetime.date.today().strftime('%d %B %Y')
# wx.lib and other pure-Python stuff
class Enumeration(object):
def __init__(self, name, enumList):
self.__doc__ = name
lookup = { }
reverseLookup = { }
i = 0
uniqueNames = [ ]
uniqueValues = [ ]
for item in enumList:
x = item.upper()
uniqueNames.append(x)
uniqueValues.append(i)
lookup[x] = i
reverseLookup[i] = x
i = i + 1
self.lookup = lookup
self.reverseLookup = reverseLookup
def __getattr__(self, attr):
if not self.lookup.has_key(attr):
raise AttributeError
return self.lookup[attr]
def whatis(self, value):
return self.reverseLookup[value]
CONSTANT_RE = re.compile('^([\w\s,]+)=', re.M)
EXCLUDED_ATTRS = ['__builtins__', '__doc__', '__name__', '__file__', '__path__',
'__module__', '__all__']
TYPE_DESCRIPTION = ['library',
'package',
'py_module', 'pyd_module', 'pyc_module', 'pyw_module',
'klass',
'function',
'method', 'static_method', 'class_method', 'instance_method',
'method_descriptor', 'builtin_method', 'builtin_function',
'property',
'booltype', 'classtype', 'complextype', 'dictproxytype', 'dicttype', 'filetype',
'floattype', 'instancetype', 'inttype', 'lambdatype', 'listtype', 'longtype',
'nonetype', 'objecttype', 'slicetype', 'strtype', 'tracebacktype', 'tupletype',
'typetype', 'unicodetype', 'unknowntype', 'xrangetype']
object_types = Enumeration('Object_Types', TYPE_DESCRIPTION)
MODULE_TO_ICON = [(".py", object_types.PY_MODULE, "Py_Module"), (".pyd", object_types.PYD_MODULE, "Pyd_Module"),
(".pyc", object_types.PYC_MODULE, "Pyc_Module"), (".pyw", object_types.PYW_MODULE, "Pyw_Module"),
(".so", object_types.PYD_MODULE, "Pyd_Module")]

View File

@@ -25,192 +25,246 @@ ENOENT = getattr(errno, 'ENOENT', 0)
EPIPE = getattr(errno, 'EPIPE', 0)
class InheritanceDiagram(object):
"""
Given a list of classes, determines the set of classes that they inherit
from all the way to the root "object", and then is able to generate a
graphviz dot graph from them.
"""
# ----------------------------------------------------------------------- #
def __init__(self, class_info):
#print class_info
#print
#print
self.class_info, self.specials = class_info
# These are the default attrs for graphviz
default_graph_attrs = {
'rankdir': 'LR',
'size': '"8.0, 12.0"',
}
default_node_attrs = {
'shape': 'box',
'fontsize': 10,
'height': 0.3,
'fontname': 'Vera Sans, DejaVu Sans, Liberation Sans, '
'Arial, Helvetica, sans',
'style': '"setlinewidth(0.5)"',
}
default_edge_attrs = {
'arrowsize': 0.5,
'style': '"setlinewidth(0.5)"',
}
# ----------------------------------------------------------------------- #
def FormatNodeAttrs(self, attrs):
return ','.join(['%s=%s' % x for x in attrs.items()])
# ----------------------------------------------------------------------- #
def FormatGraphAttrs(self, attrs):
return ''.join(['%s=%s;\n' % x for x in attrs.items()])
# ----------------------------------------------------------------------- #
def GenerateDot(self, name="dummy"):
"""
Generate a graphviz dot graph from the classes that were passed in to `__init__`.
:param string `name`: the name of the graph.
:rtype: `string`
:returns: A string representing the Graphviz dot diagram.
"""
inheritance_graph_attrs = dict(fontsize=9, ratio='auto', size='""', rankdir="LR")
inheritance_node_attrs = {"align": "center", 'shape': 'box',
'fontsize': 10, 'height': 0.3,
'fontname': 'Vera Sans, DejaVu Sans, Liberation Sans, '
'Arial, Helvetica, sans', 'style': '"setlinewidth(0.5)"',
'labelloc': 'c', 'fontcolor': 'grey45'}
inheritance_edge_attrs = {'arrowsize': 0.5, 'style': '"setlinewidth(0.5)"', "color": "black"}
g_attrs = self.default_graph_attrs.copy()
n_attrs = self.default_node_attrs.copy()
e_attrs = self.default_edge_attrs.copy()
g_attrs.update(inheritance_graph_attrs)
n_attrs.update(inheritance_node_attrs)
e_attrs.update(inheritance_edge_attrs)
res = []
res.append('digraph %s {\n' % name)
res.append(self.FormatGraphAttrs(g_attrs))
for name, fullname, bases in self.class_info.values():
# Write the node
this_node_attrs = n_attrs.copy()
if name in self.specials:
this_node_attrs['fontcolor'] = 'black'
this_node_attrs['color'] = 'blue'
this_node_attrs['style'] = 'bold'
newname, fullname = Wx2Sphinx(name)
this_node_attrs['URL'] = '"%s.html"'%fullname
res.append(' "%s" [%s];\n' %
(newname, self.FormatNodeAttrs(this_node_attrs)))
# Write the edges
for base_name in bases:
this_edge_attrs = e_attrs.copy()
if name in self.specials:
this_edge_attrs['color'] = 'red'
base_name, dummy = Wx2Sphinx(base_name)
res.append(' "%s" -> "%s" [%s];\n' %
(base_name, newname,
self.FormatNodeAttrs(this_edge_attrs)))
res.append('}\n')
return ''.join(res)
# ----------------------------------------------------------------------- #
def MakeInheritanceDiagram(self):
"""
Actually generates the inheritance diagram as a PNG file plus the corresponding
MAP file for mouse navigation over the inheritance boxes.
These two files are saved into the ``INHERITANCEROOT`` folder (see `sphinxtools/constants.py`
for more information).
:rtype: `tuple`
:returns: a tuple containing the PNG file name and a string representing the content
of the MAP file (with newlines stripped away).
.. note:: The MAP file is deleted as soon as its content has been read.
"""
code = self.GenerateDot()
# graphviz expects UTF-8 by default
if isinstance(code, unicode):
code = code.encode('utf-8')
static_root = INHERITANCEROOT
dot_args = ['dot']
dummy, filename = Wx2Sphinx(self.specials[0])
outfn = os.path.join(static_root, filename + '_inheritance.png')
if os.path.isfile(outfn):
os.remove(outfn)
if os.path.isfile(outfn + '.map'):
os.remove(outfn + '.map')
dot_args.extend(['-Tpng', '-o' + outfn])
dot_args.extend(['-Tcmapx', '-o%s.map' % outfn])
try:
p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE)
except OSError, err:
if err.errno != ENOENT: # No such file or directory
raise
print '\nERROR: Graphviz command `dot` cannot be run (needed for Graphviz output), check your ``PATH`` setting'
try:
# Graphviz may close standard input when an error occurs,
# resulting in a broken pipe on communicate()
stdout, stderr = p.communicate(code)
except OSError, err:
# in this case, read the standard output and standard error streams
# directly, to get the error message(s)
stdout, stderr = p.stdout.read(), p.stderr.read()
p.wait()
if p.returncode != 0:
print '\nERROR: Graphviz `dot` command exited with error:\n[stderr]\n%s\n[stdout]\n%s\n\n' % (stderr, stdout)
mapfile = outfn + '.map'
fid = open(mapfile, 'rt')
map = fid.read()
fid.close()
os.remove(mapfile)
return os.path.split(outfn)[1], map.replace('\n', ' ')
class InheritanceDiagram(object):
"""
Given a list of classes, determines the set of classes that they inherit
from all the way to the root "object", and then is able to generate a
graphviz dot graph from them.
"""
def __init__(self, classes, main_class=None):
if main_class is None:
self.class_info, self.specials = classes
self.class_info = self.class_info.values()
else:
self.class_info, self.specials = self._class_info(classes)
self.main_class = main_class
def _class_info(self, classes):
"""Return name and bases for all classes that are ancestors of
*classes*.
*parts* gives the number of dotted name parts that is removed from the
displayed node names.
"""
all_classes = {}
specials = []
def recurse(cls):
nodename, fullname = self.class_name(cls)
baselist = []
all_classes[cls] = (nodename, fullname, baselist)
for base in cls.__bases__:
baselist.append(self.class_name(base)[0])
if base not in all_classes:
recurse(base)
for cls in classes:
recurse(cls)
specials.append(self.class_name(cls)[1])
return all_classes.values(), specials
def class_name(self, cls):
"""Given a class object, return a fully-qualified name.
This works for things I've tested in matplotlib so far, but may not be
completely general.
"""
module = cls.__module__
if module == '__builtin__':
fullname = cls.__name__
else:
fullname = '%s.%s' % (module, cls.__name__)
name_parts = fullname.split('.')
if 'wx._' in fullname:
nodename = fullname = name_parts[-1]
else:
# Just the last 2 parts
nodename = '.'.join(name_parts[-2:])
return nodename, fullname
# These are the default attrs for graphviz
default_graph_attrs = {
'rankdir': 'LR',
'size': '"8.0, 12.0"',
}
default_node_attrs = {
'shape': 'box',
'fontsize': 10,
'height': 0.3,
'fontname': 'Vera Sans, DejaVu Sans, Liberation Sans, '
'Arial, Helvetica, sans',
'style': '"setlinewidth(0.5)"',
}
default_edge_attrs = {
'arrowsize': 0.5,
'style': '"setlinewidth(0.5)"',
}
def _format_node_attrs(self, attrs):
return ','.join(['%s=%s' % x for x in attrs.items()])
def _format_graph_attrs(self, attrs):
return ''.join(['%s=%s;\n' % x for x in attrs.items()])
def generate_dot(self, name="dummy", urls={}, graph_attrs={}, node_attrs={}, edge_attrs={}):
"""Generate a graphviz dot graph from the classes that were passed in
to __init__.
*name* is the name of the graph.
*urls* is a dictionary mapping class names to HTTP URLs.
*graph_attrs*, *node_attrs*, *edge_attrs* are dictionaries containing
key/value pairs to pass on as graphviz properties.
"""
inheritance_graph_attrs = dict(fontsize=9, ratio='auto', size='""', rankdir="LR")
inheritance_node_attrs = {"align": "center", 'shape': 'box',
'fontsize': 10, 'height': 0.3,
'fontname': 'Vera Sans, DejaVu Sans, Liberation Sans, '
'Arial, Helvetica, sans', 'style': '"setlinewidth(0.5)"',
'labelloc': 'c', 'fontcolor': 'grey45'}
inheritance_edge_attrs = {'arrowsize': 0.5, 'style': '"setlinewidth(0.5)"', "color": "black"}
g_attrs = self.default_graph_attrs.copy()
n_attrs = self.default_node_attrs.copy()
e_attrs = self.default_edge_attrs.copy()
g_attrs.update(inheritance_graph_attrs)
n_attrs.update(inheritance_node_attrs)
e_attrs.update(inheritance_edge_attrs)
res = []
res.append('digraph %s {\n' % name)
res.append(self._format_graph_attrs(g_attrs))
for name, fullname, bases in self.class_info:
# Write the node
this_node_attrs = n_attrs.copy()
if fullname in self.specials:
this_node_attrs['fontcolor'] = 'black'
this_node_attrs['color'] = 'blue'
this_node_attrs['style'] = 'bold'
if self.main_class is None:
newname, fullname = Wx2Sphinx(name)
else:
newname = name
this_node_attrs['URL'] = '"%s.html"'%fullname
res.append(' "%s" [%s];\n' %
(newname, self._format_node_attrs(this_node_attrs)))
# Write the edges
for base_name in bases:
this_edge_attrs = e_attrs.copy()
if fullname in self.specials:
this_edge_attrs['color'] = 'red'
if self.main_class is None:
base_name, dummy = Wx2Sphinx(base_name)
res.append(' "%s" -> "%s" [%s];\n' %
(base_name, newname,
self._format_node_attrs(this_edge_attrs)))
res.append('}\n')
return ''.join(res)
# ----------------------------------------------------------------------- #
def MakeInheritanceDiagram(self):
"""
Actually generates the inheritance diagram as a PNG file plus the corresponding
MAP file for mouse navigation over the inheritance boxes.
These two files are saved into the ``INHERITANCEROOT`` folder (see `sphinxtools/constants.py`
for more information).
:rtype: `tuple`
:returns: a tuple containing the PNG file name and a string representing the content
of the MAP file (with newlines stripped away).
.. note:: The MAP file is deleted as soon as its content has been read.
"""
static_root = INHERITANCEROOT
if self.main_class is not None:
filename = self.main_class.name
else:
dummy, filename = Wx2Sphinx(self.specials[0])
outfn = os.path.join(static_root, filename + '_inheritance.png')
mapfile = outfn + '.map'
if os.path.isfile(outfn) and os.path.isfile(mapfile):
fid = open(mapfile, 'rt')
map = fid.read()
fid.close()
return os.path.split(outfn)[1], map.replace('\n', ' ')
code = self.generate_dot()
# graphviz expects UTF-8 by default
if isinstance(code, unicode):
code = code.encode('utf-8')
dot_args = ['dot']
if os.path.isfile(outfn):
os.remove(outfn)
if os.path.isfile(mapfile):
os.remove(mapfile)
dot_args.extend(['-Tpng', '-o' + outfn])
dot_args.extend(['-Tcmapx', '-o' + mapfile])
try:
p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE)
except OSError, err:
if err.errno != ENOENT: # No such file or directory
raise
print '\nERROR: Graphviz command `dot` cannot be run (needed for Graphviz output), check your ``PATH`` setting'
try:
# Graphviz may close standard input when an error occurs,
# resulting in a broken pipe on communicate()
stdout, stderr = p.communicate(code)
except OSError, err:
# in this case, read the standard output and standard error streams
# directly, to get the error message(s)
stdout, stderr = p.stdout.read(), p.stderr.read()
p.wait()
if p.returncode != 0:
print '\nERROR: Graphviz `dot` command exited with error:\n[stderr]\n%s\n[stdout]\n%s\n\n' % (stderr, stdout)
fid = open(mapfile, 'rt')
map = fid.read()
fid.close()
return os.path.split(outfn)[1], map.replace('\n', ' ')

View File

@@ -0,0 +1,735 @@
import sys
import os
import operator
import errno
import re
from StringIO import StringIO
from subprocess import Popen, PIPE
from inspect import getmro, getclasstree, getdoc, getcomments
from utilities import MakeSummary, ChopDescription, WriteSphinxOutput, PickleClassInfo
from constants import object_types, MODULE_TO_ICON
import templates
ENOENT = getattr(errno, 'ENOENT', 0)
EPIPE = getattr(errno, 'EPIPE', 0)
reload(sys)
sys.setdefaultencoding("utf-8")
def make_class_tree(tree):
class_tree = []
if isinstance(tree, list):
for node in tree:
class_tree.append(make_class_tree(node))
else:
name = tree[0].__name__
class_tree.append(name)
return class_tree
def generic_summary(libraryItem, stream):
write_toc = True
add_tilde = [True, True]
if libraryItem.kind in [object_types.LIBRARY, object_types.PACKAGE]:
list1 = libraryItem.GetItemByKind(object_types.PACKAGE)
list2 = libraryItem.GetItemByKind(object_types.PY_MODULE, object_types.PYW_MODULE)
templ = [templates.TEMPLATE_PACKAGE_SUMMARY, templates.TEMPLATE_MODULE_SUMMARY]
refs = ['mod', 'mod']
elif libraryItem.kind in range(object_types.PY_MODULE, object_types.PYW_MODULE+1):
list1 = libraryItem.GetItemByKind(object_types.FUNCTION)
list2 = libraryItem.GetItemByKind(object_types.KLASS, recurse=True)
templ = [templates.TEMPLATE_STD_FUNCTION_SUMMARY, templates.TEMPLATE_STD_CLASS_SUMMARY]
refs = ['func', 'ref']
add_tilde = [True, False]
elif libraryItem.kind == object_types.KLASS:
write_toc = False
list1 = libraryItem.GetItemByKind(object_types.METHOD, object_types.INSTANCE_METHOD)
list2 = libraryItem.GetItemByKind(object_types.PROPERTY)
templ = [templates.TEMPLATE_METHOD_SUMMARY, templates.TEMPLATE_PROPERTY_SUMMARY]
refs = ['meth', 'attr']
add_tilde = [True, True]
else:
raise Exception('Invalid library item: %s'%libraryItem.GetShortName())
toctree = ''
for index, sub_list in enumerate([list1, list2]):
table = []
for item in sub_list:
if item.is_redundant:
continue
docs = ChopDescription(ReplaceWxDot(item.docs))
table.append((item.name, docs))
if item.kind != object_types.FUNCTION:
toctree += ' %s\n'%item.name
if table:
summary = MakeSummary(table, templ[index], refs[index], add_tilde[index])
stream.write(summary)
if toctree and write_toc:
stream.write(templates.TEMPLATE_TOCTREE%toctree)
stream.write('\n\n')
def MakeSphinxFile(name):
return os.path.join(os.getcwd(), 'docs', 'sphinx', '%s.txt'%name)
def ReplaceWxDot(text):
# Double ticks with 'wx.' in them
text = re.sub(r'``wx\.(.*?)``', r'``\1`` ', text)
# Signle ticks with 'wx.' in them... try and referencing them
text = re.sub(r'`wx\.(.*?)`', r'`\1` ', text)
return text
class ParentBase(object):
def __init__(self, name, kind):
self.name = name
self.kind = kind
self.docs = u''
self.comments = u''
self.is_redundant = False
self.children = []
def Add(self, klass):
if u'lambda' in klass.name:
return
for child in self.children:
if child.name == klass.name:
return
klass.parent = self
self.children.append(klass)
def Save(self):
self.children = sorted(self.children, key=lambda k: (getattr(k, "order"), getattr(k, "name").lower()))
if self.docs is None:
self.docs = u''
if self.comments is None or not self.comments.strip():
self.comments = u''
for child in self.children:
child.Save()
def GetImage(self):
return self.kind
def GetName(self):
return self.name
def GetShortName(self):
return self.name.split(".")[-1]
def GetObject(self):
return self.obj_type
def GetChildren(self):
return self.children
def GetChildrenCount(self, recursively=True):
"""
Gets the number of children of this item.
:param bool `recursively`: if ``True``, returns the total number of descendants,
otherwise only one level of children is counted.
"""
count = len(self.children)
if not recursively:
return count
total = count
for n in xrange(count):
total += self.children[n].GetChildrenCount()
return total
def GetKindCount(self, minObj, maxObj=None):
if maxObj is None:
maxObj = minObj
count = 0
for child in self.children:
if minObj <= child.kind <= maxObj:
count += 1
return count
def GetItemByKind(self, minObj, maxObj=None, recurse=False):
if maxObj is None:
maxObj = minObj
items = []
for child in self.children:
if minObj <= child.kind <= maxObj:
items.append(child)
if recurse:
items = items + child.GetItemByKind(minObj, maxObj, recurse)
return items
def ToRest(self):
pass
class Library(ParentBase):
def __init__(self, name):
ParentBase.__init__(self, name, object_types.LIBRARY)
self.parent = None
self.filename = u''
self.order = 0
self.obj_type = u"Library"
self.python_version = u''
self.sphinx_file = MakeSphinxFile(name)
self.base_name = name
def GetShortName(self):
return self.name
def Walk(self, obj):
if obj == self:
obj.ToRest()
# must have at least root folder
children = obj.GetChildren()
if not children:
return
# check each name
for child in children:
if child.is_redundant:
continue
child.ToRest()
# recursively scan other folders, appending results
self.Walk(child)
def GetPythonVersion(self):
return self.python_version
def ToRest(self):
print '\n\nReST-ifying %s...\n\n'%self.base_name
stream = StringIO()
header = templates.TEMPLATE_DESCRIPTION%(self.base_name, self.base_name + ' Library')
stream.write(header)
stream.write(ReplaceWxDot(self.docs) + '\n\n')
generic_summary(self, stream)
WriteSphinxOutput(stream, self.sphinx_file)
class Module(ParentBase):
def __init__(self, name, kind):
ParentBase.__init__(self, name, kind)
self.filename = u''
self.sphinx_file = MakeSphinxFile(name)
if kind == object_types.PACKAGE:
self.obj_type = u"Package"
self.order = kind
return
self.order = object_types.PY_MODULE
for dummy, icon, description in MODULE_TO_ICON:
if icon == kind:
self.obj_type = description
break
self.inheritance_diagram = None
def ToRest(self):
stream = StringIO()
label = 'Module'
if self.kind == object_types.PACKAGE:
label = 'Package'
stream.write('.. module:: %s\n\n'%self.name)
stream.write('.. currentmodule:: %s\n\n'%self.name)
header = templates.TEMPLATE_DESCRIPTION%(self.name, '%s %s'%(self.name, label))
stream.write(header)
stream.write(ReplaceWxDot(self.docs) + '\n\n')
spacer = ' '*self.name.count('.')
if self.kind != object_types.PACKAGE:
print '%s - %s (module)'%(spacer, self.name)
if self.inheritance_diagram:
png, map = self.inheritance_diagram.MakeInheritanceDiagram()
short_name = self.GetShortName()
image_desc = templates.TEMPLATE_INHERITANCE % (short_name, png, short_name, map)
stream.write(image_desc)
else:
print '%s - %s (package)'%(spacer, self.name)
generic_summary(self, stream)
if self.kind != object_types.PACKAGE:
functions = self.GetItemByKind(object_types.FUNCTION)
count = 0
for fun in functions:
if not fun.is_redundant:
count = 1
break
if count > 0:
stream.write('\n\nFunctions\n===========\n\n')
for fun in functions:
if fun.is_redundant:
continue
fun.Write(stream)
WriteSphinxOutput(stream, self.sphinx_file)
class Class(ParentBase):
def __init__(self, name, obj):
ParentBase.__init__(self, name, object_types.KLASS)
try:
subs = obj.__subclasses__()
except (AttributeError, TypeError):
subs = []
sups = list(obj.__bases__)
sortedSubClasses = []
sortedSupClasses = []
for item in sups:
item = repr(item)
sup = item.replace("<class ", "").replace(">", "").replace("<type ", "")
sup = sup.strip().replace('"', "").replace("'", "")
if " at " in sup:
sup = sup[0:sup.index(" at ")].strip()
if 'wx._' in sup:
name_parts = sup.split('.')
sup = name_parts[-1]
sortedSupClasses.append(sup)
sortedSupClasses.sort()
for s in subs:
s = repr(s)
if "'" in s:
start = s.index("'")
end = s.rindex("'")
cls = s[start+1:end]
else:
cls = s
if 'wx._' in cls:
name_parts = cls.split('.')
cls = name_parts[-1]
sortedSubClasses.append(cls)
sortedSubClasses.sort()
self.class_tree = make_class_tree(getclasstree(getmro(obj)))
self.subClasses = sortedSubClasses
self.superClasses = sortedSupClasses
self.signature = ""
self.inheritance_diagram = None
self.order = 3
self.obj_type = u"Class"
self.sphinx_file = MakeSphinxFile(name)
def ToRest(self):
if self.is_redundant:
return
stream = StringIO()
parts = self.name.split('.')
current_module = '.'.join(parts[0:-1])
stream.write('.. currentmodule:: %s\n\n'%current_module)
header = templates.TEMPLATE_DESCRIPTION%(self.name, self.GetShortName())
stream.write(header)
stream.write(ReplaceWxDot(self.docs) + '\n\n')
if self.inheritance_diagram:
png, map = self.inheritance_diagram.MakeInheritanceDiagram()
short_name = self.GetShortName()
image_desc = templates.TEMPLATE_INHERITANCE % (short_name, png, short_name, map)
stream.write(image_desc)
if self.subClasses:
subs = [':ref:`%s`'%cls for cls in self.superClasses]
subs = ', '.join(subs)
subs_desc = templates.TEMPLATE_SUBCLASSES % subs
stream.write(subs_desc)
if self.superClasses:
subs = [':ref:`%s`'%cls for cls in self.superClasses]
subs = ', '.join(subs)
subs_desc = templates.TEMPLATE_SUPERCLASSES % subs
stream.write(subs_desc)
generic_summary(self, stream)
stream.write(templates.TEMPLATE_API)
stream.write("\n.. class:: %s\n\n\n"%self.signature)
methods = self.GetItemByKind(object_types.METHOD, object_types.INSTANCE_METHOD)
properties = self.GetItemByKind(object_types.PROPERTY)
method_list = []
for meth in methods:
meth.Write(stream)
if not meth.is_redundant:
method_list.append((meth.GetShortName(), ''))
for prop in properties:
prop.Write(stream, short_name)
WriteSphinxOutput(stream, self.sphinx_file)
self.bases = self.superClasses
self.method_list = method_list
PickleClassInfo(self.name, self)
def Save(self):
ParentBase.Save(self)
pop = -1
for index, child in enumerate(self.children):
name = child.GetShortName()
if name == '__init__':
pop = index
break
if pop >= 0:
init = self.children.pop(pop)
self.children.insert(0, init)
self.signature = self.signature.replace('wx.', '')
self.signature = self.signature.rstrip(':').lstrip('class ')
if ' def __init__' in self.signature:
index = self.signature.index(' def __init__')
self.signature = self.signature[0:index]
self.signature.strip()
class ChildrenBase(object):
def __init__(self, name, kind):
self.name = name
self.kind = kind
self.order = 4
self.docs = u''
self.comments = u''
self.is_redundant = False
## self.id = NewId()
def GetImage(self):
return self.kind
def GetName(self):
return self.name
def GetShortName(self):
return self.name.split(".")[-1]
def GetChildren(self):
return []
def GetChildrenCount(self, recursively=True):
return 0
def GetObject(self):
return self.obj_type
def Save(self):
if self.docs is None:
self.docs = u''
if self.comments is None or not self.comments.strip():
self.comments = u''
def ToRest(self):
pass
class Method(ChildrenBase):
def __init__(self, name, kind):
ChildrenBase.__init__(self, name, kind)
self.order = 5
self.arguments = []
self.signature = u''
self.obj_type = u"Method/Function"
def Save(self):
ChildrenBase.Save(self)
newargs = []
if self.arguments and any(self.arguments[0]):
for name, repr_val, eval_val in self.arguments:
repr_val = (repr_val is not None and [repr_val] or [""])[0]
eval_val = (eval_val is not None and [eval_val] or [""])[0]
newargs.append((name, repr_val, eval_val))
self.arguments = newargs
self.signature = self.signature.replace('wx.', '')
self.signature = self.signature.rstrip(':').lstrip()
if self.signature.startswith('def '):
self.signature = self.signature[4:]
if not self.signature.strip():
self.is_redundant = True
def Write(self, stream):
if self.is_redundant:
return
if self.kind == object_types.FUNCTION:
stream.write('.. function:: %s\n\n'%self.signature)
indent = 3*' '
else:
stream.write(' .. method:: %s\n\n'%self.signature)
indent = 6*' '
if not self.docs.strip():
stream.write('\n')
return
text = ''
for line in self.docs.splitlines():
text += indent + ReplaceWxDot(line) + '\n'
text += '\n\n'
stream.write(text)
class Property(ChildrenBase):
def __init__(self, name, item):
ChildrenBase.__init__(self, name, object_types.PROPERTY)
self.getter = self.setter = self.deleter = ""
try:
if item.fget:
self.getter = item.fget.__name__
if item.fset:
self.setter = item.fset.__name__
if item.fdel:
self.deleter = item.fdel.__name__
except AttributeError:
# Thank you for screwing it up, Cython...
if item.fget:
self.getter = item.fget.__class__.__name__
if item.fset:
self.setter = item.fset.__class__.__name__
if item.fdel:
self.deleter = item.fdel.__class__.__name__
self.docs = getdoc(item)
self.comments = getcomments(item)
self.obj_type = u"Property"
self.order = 6
def Write(self, stream, class_name):
if self.is_redundant:
return
docs = ''
for item in [self.setter, self.getter, self.deleter]:
if item and 'lambda' not in item and not item.startswith('_'):
if docs:
docs += ', :meth:`~%s.%s` '%(class_name, item)
else:
docs += ':meth:`~%s.%s` '%(class_name, item)
if docs:
docs = 'See %s'%docs
stream.write(' .. attribute:: %s\n\n'%self.GetShortName())
stream.write(' %s\n\n\n'%docs)
class Attribute(ChildrenBase):
def __init__(self, name, specs, value):
specs = unicode(specs)
start, end = specs.find("'"), specs.rfind("'")
specs = specs[start+1:end]
strValue = repr(value)
uspecs = specs.upper()
try:
kind = getattr(object_types, uspecs)
except AttributeError:
try:
uspecs = uspecs + u"TYPE"
kind = getattr(object_types, uspecs)
except AttributeError:
kind = object_types.UNKNOWNTYPE
try:
reprValue = repr(value.__class__)
except (NameError, AttributeError):
reprValue = ""
if u"class" in strValue or u"class" in reprValue:
kind = object_types.INSTANCETYPE
ChildrenBase.__init__(self, name, kind)
self.value = strValue
self.specs = specs
try:
self.docs = getdoc(value)
except (NameError, AttributeError):
self.docs = u''
self.obj_type = u"Attribute"
self.order = 7
def ToRest(self):
pass

632
sphinxtools/modulehunter.py Normal file
View File

@@ -0,0 +1,632 @@
# -*- coding: utf-8 -*-
# Describe classes, methods and functions in a module.
# Works with user-defined modules, all Python library
# modules, including built-in modules.
import os
import sys
import glob
import types
import cPickle
import imp
import traceback
import pkgutil
import __builtin__
from inspect import getargspec, ismodule, getdoc, getmodule, getcomments, isfunction
from inspect import ismethoddescriptor, getsource, ismemberdescriptor, isgetsetdescriptor
from inspect import isbuiltin, isclass, getfile, ismethod
from librarydescription import Library, Module, Class
from librarydescription import Method, Property, Attribute
import inheritance
from constants import object_types, EXCLUDED_ATTRS, MODULE_TO_ICON
from constants import CONSTANT_RE
reload(sys)
sys.setdefaultencoding("utf-8")
try:
import wx
except ImportError:
wxPath = None
for path in sys.path:
if 'wx-' in path:
wxPath = path
break
if wxPath is None:
raise Exception('Unable to find the wx package')
sys.path.insert(0, wxPath)
import wx
if hasattr(os.path, "relpath"):
relpath = os.path.relpath # since Python 2.6
else:
def relpath(path, start=os.path.curdir):
"""Return a relative version of a path"""
if not path:
raise ValueError("no path specified")
start_list = os.path.abspath(start).split(os.path.sep)
path_list = os.path.abspath(path).split(os.path.sep)
# Work out how much of the filepath is shared by start and path.
i = len(os.path.commonprefix([start_list, path_list]))
rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
if not rel_list:
return os.path.curdir
return os.path.join(*rel_list)
def format_traceback():
t, v = sys.exc_info()[:2]
message = "".join(traceback.format_exception_only(t, v)).replace("\n", " ")
return message.strip()
def format_method(method):
method = method.strip()
if "def " not in method:
return None, None
indx1, indx2, indx3 = method.index("def "), method.index("("), method.rindex(")")
name = method[indx1+4:indx2]
signature = method[indx2+1:indx3]
if "\n" in signature:
sig = signature.split("\n")
params = ""
for s in sig:
params += s.strip() + " "
params = params.rstrip()
else:
params = signature
return name, params
def analyze_params(obj, signature):
params = signature.split(",")
if "self" in params:
params.remove("self")
signature = ",".join(params)
param_tuple = []
if not params:
return signature, param_tuple
try:
arginfo = getargspec(obj)
except TypeError:
arginfo = None
pevals = {}
if arginfo:
args = arginfo[0]
argsvar = arginfo[1]
if arginfo[3]:
dl = len(arginfo[3])
al = len(args)
defargs = args[al-dl:al]
info = arginfo[3]
for d, i in zip(defargs, info):
pevals[d] = i
for par in params:
p = par.strip()
pvalue = peval = None
if "=" in p:
all_values = p.split("=")
pname, pvalue = all_values[0].strip(), "=".join(all_values[1:]).strip()
pvalue = pvalue.strip()
if pname in pevals:
try:
peval = unicode(pevals[pname])
except UnicodeDecodeError:
peval = repr(pevals[pname])
except TypeError:
peval = u''
else:
pname = p
param_tuple.append((pname, pvalue, peval))
return signature, param_tuple
def get_constructor(source):
description = ""
hasComma = False
for line in source.split("\n"):
if '#' in line:
line = line[0:line.index('#')].strip()
if ":" in line:
hasComma = True
commaPos = line.index(":")
if ('"""' in line or "'''" in line) and hasComma:
break
if hasComma and ' def ' in line:
defPos = line.index(' def ')
if defPos > commaPos:
break
description += " " + line.strip()
if "):" in line or ") :" in line or ") :" in line:
break
return description
def inspect_source(method_class, obj, source):
description = get_constructor(source)
name, params = format_method(description)
if name is None:
return
signature, param_tuple = analyze_params(obj, params)
method_class.arguments = param_tuple
method_class.signature = description.strip()
if "classmethod " in description or is_classmethod(obj):
method_class.kind = object_types.CLASS_METHOD
elif "staticmethod " in description:
method_class.kind = object_types.STATIC_METHOD
def is_classmethod(instancemethod):
" Determine if an instancemethod is a classmethod. "
if hasattr(instancemethod, 'im_self'):
return instancemethod.im_self is not None
return False
def describe_func(obj, parent_class, module_name):
"""
Describe the function object passed as argument.
If this is a method object, the second argument will
be passed as True.
"""
try:
name = obj.__name__
except AttributeError:
# Funny comtypes...
return
if name.startswith("_") and "__init__" not in name:
return
name = parent_class.name + "." + name
docs = getdoc(obj)
comments = getcomments(obj)
if isfunction(obj):
method = object_types.FUNCTION
elif ismethod(obj):
method = object_types.METHOD
elif ismethoddescriptor(obj):
method = object_types.METHOD_DESCRIPTOR
if isinstance(obj, types.MethodType):
method = object_types.INSTANCE_METHOD
try:
source_code = getsource(obj)
except (IOError, TypeError):
source_code = ""
klass = Method(name, method)
klass.docs = docs
klass_module = getmodule(obj)
if klass_module and klass_module.__name__ != module_name:
klass.is_redundant = True
if source_code:
inspect_source(klass, obj, source_code)
klass.number_lines = "%d"%len(source_code.split("\n"))
if isinstance(obj, staticmethod):
klass.method = method = object_types.STATIC_METHOD
try:
if method in [object_types.METHOD, object_types.METHOD_DESCRIPTOR, object_types.INSTANCE_METHOD]:
code = obj.im_func.func_code
elif method == object_types.STATIC_METHOD:
code = obj.im_func.func_code
else:
code = obj.func_code
except AttributeError:
code = None
if code is not None:
klass.firstlineno = "%d"%code.co_firstlineno
parent_class.Add(klass)
def describe_class(obj, module_class, module_name, constants):
"""
Describe the class object passed as argument,
including its methods.
"""
class_name = obj.__name__
if class_name == "object":
return
class_name = module_class.name + "." + class_name
docs = getdoc(obj)
comments = getcomments(obj)
obj_dict = obj.__dict__
klass = Class(class_name, obj)
count = 0
for name in obj_dict:
if name.startswith("_") and "__init__" not in name:
continue
if name in EXCLUDED_ATTRS:
continue
try:
item = getattr(obj, name)
except AttributeError:
# Thanks to ReportLab for this funny exception...
continue
if ismodule(item):
continue
if ismemberdescriptor(item) or isgetsetdescriptor(item):
continue
if isbuiltin(item):
count += 1
elif ismethod(item) or isfunction(item) or ismethoddescriptor(item) or \
isinstance(item, types.MethodType):
count += 1
describe_func(item, klass, module_name)
elif isclass(item):
count += 1
describe_class(item, klass, module_name, constants)
else:
name = class_name + "." + name
if isinstance(item, property):
item_class = Property(name, item)
klass.Add(item_class)
item_module = getmodule(obj)
if item_module and item_module.__name__ != module_name:
item_class.is_redundant = True
else:
item_class = Attribute(name, type(item), item)
klass.Add(item_class)
if constants:
item_class.is_redundant = name not in constants
count += 1
klass.docs = docs
klass.comments = comments
klass_module = getmodule(obj)
if klass_module and klass_module.__name__ != module_name:
klass.is_redundant = True
else:
klass.inheritance_diagram = inheritance.InheritanceDiagram([obj], klass)
module_class.Add(klass)
try:
source_code = getsource(obj)
except (IOError, TypeError):
source_code = ""
if source_code:
description = get_constructor(source_code)
klass.signature = description.strip()
klass.number_lines = '%d'%len(source_code.split("\n"))
def describe_module(module, kind, constants=[]):
"""
Describe the module object passed as argument
including its classes and functions.
"""
module_name = module.__name__
if kind == object_types.LIBRARY:
klass = Library(module_name)
else:
klass = Module(module_name, kind)
klass.docs = getdoc(module)
klass.comments = getcomments(module)
klass.filename = module.__file__
inheritance_diagram = []
count = 0
for name in dir(module):
if name in EXCLUDED_ATTRS:
continue
obj = getattr(module, name)
if ismodule(obj):
continue
if ismemberdescriptor(obj) or isgetsetdescriptor(obj):
continue
if isclass(obj):
count += 1
describe_class(obj, klass, module_name, constants)
if obj.__module__ == module.__name__:
inheritance_diagram.append(obj)
elif isbuiltin(obj):
count += 1
elif ismethod(obj) or isfunction(obj) or ismethoddescriptor(obj) or \
isinstance(obj, types.MethodType):
count +=1
describe_func(obj, klass, module_name)
else:
attribute = Attribute(module_name + "." + name, type(obj), obj)
klass.Add(attribute)
if constants:
attribute.is_redundant = name not in constants
if kind not in [object_types.PACKAGE, object_types.LIBRARY]:
if inheritance_diagram and len(inheritance_diagram) < 20:
klass.inheritance_diagram = inheritance.InheritanceDiagram(inheritance_diagram, klass)
return klass, count
def Import(init_name, import_name, full_process=True):
directory, module_name = os.path.split(init_name)
dirname = os.path.dirname(directory)
if not full_process:
path = list(sys.path)
sys.path.insert(0, dirname)
f = None
try:
f, filename, description = imp.find_module(import_name, [dirname])
mainmod = imp.load_module(import_name, f, filename, description)
except (ImportError, NameError):
message = format_traceback()
message += ' Please check that the "Import command" text box is correctly filled'
print "Error: %s"%message
if not full_process:
sys.path = path[:]
return
if f:
f.close()
if not full_process:
sys.path = path[:]
try:
version = mainmod.__version__
except AttributeError:
try:
version = mainmod.__VERSION__
except AttributeError:
print "Warning: Library '%s' has no __version__ or __VERSION__ attribute. Please specify it in the 'Import command' textbox"%import_name
return
print version
return mainmod
def PrintProgress(name, looped_names):
looped_names.append(name)
if len(looped_names) == 5:
message = ", ".join(looped_names)
looped_names = []
print message
return looped_names
def FindModuleType(filename):
splitext = os.path.splitext(filename)[0]
for extension, icon, description in MODULE_TO_ICON:
if os.path.isfile(splitext + extension):
return icon
def SubImport(import_string, module, parent_class, ispkg):
try:
submod = __import__(import_string, fromlist=[module])
except:
# pubsub and Editra can be funny sometimes...
message = "Unable to import module/package '%s'.\n Exception was: %s"%(module, format_traceback())
print "\nWARNING: %s\n"%message
return None, 0
if not ismodule(submod):
return None, 0
filename = getfile(submod)
if ispkg:
kind = object_types.PACKAGE
else:
kind = FindModuleType(filename)
constants = []
if kind in [object_types.PY_MODULE, object_types.PACKAGE]:
contents = open(filename, "rt").read()
consts = CONSTANT_RE.findall(contents)
for c in consts:
if "," in c:
c = c.split(",")
constants.extend([v.strip() for v in c])
else:
constants.append(c.strip())
module_class, count = describe_module(submod, kind=kind, constants=constants)
parent_class.Add(module_class)
return module_class, count
def ModuleHunter(init_name, import_name, version):
pickle_file = os.path.join(os.getcwd(), 'docs', 'sphinx', 'wxlib.pkl')
if os.path.isfile(pickle_file):
fid = open(pickle_file, 'rb')
library_class = cPickle.load(fid)
fid.close()
library_class.Walk(library_class)
return
path = list(sys.path)
directory, module_name = os.path.split(init_name)
path = list(sys.path)
sys.path.insert(0, os.path.dirname(directory))
mainmod = Import(init_name, import_name)
if mainmod is None:
return
message = "Importing main library '%s'..."%import_name
print "Message: %s"%message
module_name = os.path.splitext(getfile(mainmod))[0] + ".py"
contents = open(module_name, "rt").read()
constants = CONSTANT_RE.findall(contents)
library_class, count = describe_module(mainmod, kind=object_types.LIBRARY, constants=constants)
library_class.name = "%s-%s"%(import_name, version)
message = "Main library '%s' imported..."%library_class.name
print "Message: %s"%message
message = "Importing sub-modules and sub-packages...\n"
print "Message: %s"%message
looped_names = []
ancestors_dict = {import_name: library_class}
for importer, module_name, ispkg in pkgutil.walk_packages(path=[directory],
prefix=import_name+".",
onerror=lambda x: None):
import_string = module_name
splitted = module_name.split(".")
fromlist = splitted[-1]
parent_name = ".".join(splitted[0:-1])
parent_class = ancestors_dict[parent_name]
module_class, count = SubImport(import_string, fromlist, parent_class, ispkg)
if module_class is None:
continue
looped_names = PrintProgress(module_name, looped_names)
if module_name not in ancestors_dict:
ancestors_dict[module_name] = module_class
major, minor, micro, release = sys.version_info[0:-1]
pythonVersion = u"%d.%d.%d-%s"%(major, minor, micro, release)
library_class.python_version = pythonVersion
library_class.Save()
sys.path[:] = path # restore
fid = open(pickle_file, 'wb')
cPickle.dump(library_class, fid)
fid.close()
library_class.Walk(library_class)
if __name__ == "__main__":
argv = sys.argv[1:]
if len(argv) == 2:
init_name, import_name = argv
Import(init_name, import_name, full_process=False)
else:
init_name, import_name, version, save_dir = argv
ModuleHunter(init_name, import_name, version, save_dir)

View File

@@ -70,7 +70,7 @@ def SphinxIndexes(sphinxDir):
if file.endswith('functions.pkl'):
ReformatFunctions(file)
elif 'classindex' in file:
MakeClassIndex(file)
MakeClassIndex(sphinxDir, file)
BuildEnumsAndMethods(sphinxDir)
@@ -122,9 +122,10 @@ def BuildEnumsAndMethods(sphinxDir):
if widget_name in SECTIONS_EXCLUDE:
start, end = SECTIONS_EXCLUDE[widget_name]
lindex = text.index(start)
rindex = text.index(end)
text = text[0:lindex] + text[rindex:]
if start in text and end in text:
lindex = text.index(start)
rindex = text.index(end)
text = text[0:lindex] + text[rindex:]
# Replace the "Perl Note" stuff, we don't need it
newtext = ''
@@ -361,7 +362,7 @@ def ReformatFunctions(file):
# ----------------------------------------------------------------------- #
def MakeClassIndex(file):
def MakeClassIndex(sphinxDir, file):
text_file = os.path.splitext(file)[0] + '.txt'
local_file = os.path.split(file)[1]
@@ -376,13 +377,24 @@ def MakeClassIndex(file):
if local_file.count('.') == 1:
# Core functions
label = 'Core'
module = ''
enumDots = 1
else:
label = local_file.split('.')[0:-2][0]
module = label
enumDots = 2
enum_files = glob.glob(sphinxDir + '/%s*.enumeration.txt'%module)
enum_base = [os.path.split(os.path.splitext(enum)[0])[1] for enum in enum_files]
names = classes.keys()
names.sort()
text = templates.TEMPLATE_CLASS_INDEX % (label, label)
text = ''
if module:
text += '\n\n.. module:: %s\n\n'%module
text += templates.TEMPLATE_CLASS_INDEX % (label, label)
text += 80*'=' + ' ' + 80*'=' + '\n'
text += '%-80s **Short Description**\n'%'**Class**'
@@ -391,8 +403,24 @@ def MakeClassIndex(file):
for cls in names:
text += '%-80s %s\n'%(':ref:`%s`'%Wx2Sphinx(cls)[1], classes[cls])
text += 80*'=' + ' ' + 80*'=' + '\n'
text += 80*'=' + ' ' + 80*'=' + '\n\n'
contents = []
for cls in names:
contents.append(Wx2Sphinx(cls)[1])
for enum in enum_base:
if enum.count('.') == enumDots:
contents.append(enum)
contents.sort()
toctree = ''
for item in contents:
toctree += ' %s\n'%item
text += templates.TEMPLATE_TOCTREE%toctree
writeIfChanged(text_file, text)

View File

@@ -16,9 +16,9 @@ TEMPLATE_DESCRIPTION = '''
.. _%s:
=====================================================================================
==========================================================================================================================================
|phoenix_title| **%s**
=====================================================================================
==========================================================================================================================================
'''
@@ -93,6 +93,18 @@ TEMPLATE_SUBCLASSES = '''
'''
# Template for the superclasses of a class, with a string containing a list
# of comma separated class names with their ReST role as :ref: prepended
TEMPLATE_SUPERCLASSES = '''
|super_classes| Known Superclasses
==================================
%s
|
'''
# Template for the method summary of a class, containing a table made of
# ``method_name`` ``method description``
@@ -247,3 +259,53 @@ TEMPLATE_HEADINGS = '''
'''
# Templates for the summary of modules/packages, containing a table made of
# ``module name`` ``short description``
TEMPLATE_MODULE_SUMMARY = '''
|module_summary| Modules Summary
================================
%s
|
'''
TEMPLATE_PACKAGE_SUMMARY = '''
|package_summary| Packages Summary
==================================
%s
|
'''
TEMPLATE_STD_FUNCTION_SUMMARY = '''
|function_summary| Functions Summary
====================================
%s
|
'''
TEMPLATE_STD_CLASS_SUMMARY = '''
|class_summary| Classes Summary
===============================
%s
|
'''
TEMPLATE_TOCTREE = '''
.. toctree::
:maxdepth: 1
:hidden:
%s
'''

View File

@@ -458,26 +458,38 @@ def FindControlImages(element):
# ----------------------------------------------------------------------- #
def MakeSummary(name, item_list, template, kind):
def MakeSummary(item_list, template, kind, add_tilde=True):
"""
This function generates a table containing a method/property name
and a shortened version of its docstrings.
:param string `name`: the method/property class name.
:param list `item_list`: a list of tuples like `(method/property name, short docstrings)`.
:param string `template`: the template to use (from `sphinxtools/templates.py`, can
be the ``TEMPLATE_METHOD_SUMMARY`` or the ``TEMPLATE_PROPERTY_SUMMARY``.
:param string `kind`: can be ":meth:" or ":attr:".
:param string `kind`: can be ":meth:" or ":attr:" or ":ref:".
:rtype: `string`
"""
maxlen = 0
for method, simple_docs in item_list:
substr = ':%s:`~%s`'%(kind, method)
maxlen = max(maxlen, len(substr))
maxlen = max(80, maxlen)
summary = '='*80 + ' ' + '='*80 + "\n"
summary = '='*maxlen + ' ' + '='*80 + "\n"
format = '%-' + str(maxlen) + 's %s'
for method, simple_docs in item_list:
summary += '%-80s %s'%(':%s:`~%s.%s`'%(kind, name, method), simple_docs) + '\n'
if add_tilde:
substr = ':%s:`~%s`'%(kind, method)
else:
substr = ':%s:`%s`'%(kind, method)
summary += format%(substr, simple_docs) + '\n'
summary += '='*80 + ' ' + '='*80 + "\n"
summary += '='*maxlen + ' ' + '='*80 + "\n"
return template % summary
@@ -585,7 +597,7 @@ def PickleClassInfo(class_name, element):
for base in element.bases:
bases.append(Wx2Sphinx(base)[1])
items[class_name] = (method_list, bases)
fid = open(pickle_file, 'wb')
cPickle.dump(items, fid)