diff --git a/docs/sphinx/rest_substitutions/overviews/filesystem_overview.rst b/docs/sphinx/rest_substitutions/overviews/filesystem_overview.rst index fb5d3e76..1b895017 100644 --- a/docs/sphinx/rest_substitutions/overviews/filesystem_overview.rst +++ b/docs/sphinx/rest_substitutions/overviews/filesystem_overview.rst @@ -46,9 +46,9 @@ The left location precedes the protocol in the URL string. It is not used by global protocols like HTTP but it becomes handy when nesting protocols - for example you may want to access files in a ZIP archive: -file:archives/cpp_doc.zip#zip:reference/fopen.htm#syntax +``file:archives/cpp_doc.zip#zip:reference/fopen.htm#syntax`` -In this example, the protocol is "zip", right location is "reference/fopen.htm", anchor is "syntax" and left location is "file:archives/cpp_doc.zip". +In this example, the protocol is "zip", right location is "reference/fopen.htm", anchor is "syntax" and left location is ``file:archives/cpp_doc.zip``. There are two protocols used in this example: "zip" and "file". diff --git a/docs/sphinx/rest_substitutions/overviews/writing_non_english_applications.rst b/docs/sphinx/rest_substitutions/overviews/writing_non_english_applications.rst index 7e65af12..79878b9b 100644 --- a/docs/sphinx/rest_substitutions/overviews/writing_non_english_applications.rst +++ b/docs/sphinx/rest_substitutions/overviews/writing_non_english_applications.rst @@ -4,7 +4,7 @@ .. _writing non-english applications: ===================================================== -|phoenix_title| **writing Non-English Applications** +|phoenix_title| **Writing Non-English Applications** ===================================================== This article describes how to write applications that communicate with the diff --git a/etgtools/sphinx_generator.py b/etgtools/sphinx_generator.py index deb00a3d..fd28e916 100644 --- a/etgtools/sphinx_generator.py +++ b/etgtools/sphinx_generator.py @@ -2237,7 +2237,7 @@ class XMLDocString(object): inheritance_diagram = InheritanceDiagram(klass.nodeBases) png, map = inheritance_diagram.MakeInheritanceDiagram() - image_desc = templates.TEMPLATE_INHERITANCE % (name, png, name, map) + image_desc = templates.TEMPLATE_INHERITANCE % ('class', name, png, name, map) stream.write(image_desc) if self.appearance: diff --git a/sphinxtools/constants.py b/sphinxtools/constants.py index 1bf0baf5..33afcb1b 100644 --- a/sphinxtools/constants.py +++ b/sphinxtools/constants.py @@ -145,6 +145,19 @@ HTML_REPLACE = ['module', 'function', 'method', 'class', 'classmethod', 'staticm # Today's date representation for the Sphinx HTML docs TODAY = datetime.date.today().strftime('%d %B %Y') +# Inheritance diagram external hyperlinks + +PYTHON_DOCS = 'http://docs.python.org/library/' +NUMPY_DOCS = 'http://docs.scipy.org/doc/numpy/reference/generated/' + +EXTERN_INHERITANCE = {'UserDict.' : PYTHON_DOCS, + 'ctypes.' : PYTHON_DOCS, + 'code.' : PYTHON_DOCS, + 'exceptions.': PYTHON_DOCS, + 'threading.' : PYTHON_DOCS, + 'numpy.' : NUMPY_DOCS + } + # wx.lib and other pure-Python stuff class Enumeration(object): diff --git a/sphinxtools/inheritance.py b/sphinxtools/inheritance.py index 12a8d472..ff1ea6dc 100644 --- a/sphinxtools/inheritance.py +++ b/sphinxtools/inheritance.py @@ -18,7 +18,7 @@ from subprocess import Popen, PIPE # Phoenix-specific imports -from utilities import Wx2Sphinx +from utilities import Wx2Sphinx, FormatExternalLink from constants import INHERITANCEROOT ENOENT = getattr(errno, 'ENOENT', 0) @@ -91,8 +91,10 @@ class InheritanceDiagram(object): nodename = fullname = name_parts[-1] else: # Just the last 2 parts - nodename = '.'.join(name_parts[-2:]) - + nodename = '.'.join(name_parts[-2:]) + if fullname.startswith('wx.'): + fullname = fullname[3:] + return nodename, fullname @@ -120,14 +122,12 @@ class InheritanceDiagram(object): 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={}): + def generate_dot(self, class_summary, name="dummy", 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. """ @@ -165,8 +165,21 @@ class InheritanceDiagram(object): newname, fullname = Wx2Sphinx(name) else: newname = name - - this_node_attrs['URL'] = '"%s.html"'%fullname + + if class_summary is None: + # Phoenix base classes, assume there is always a link + this_node_attrs['URL'] = '"%s.html"'%fullname + else: + if 'wx.' in fullname: + fullname = fullname[3:] + + if fullname in class_summary: + this_node_attrs['URL'] = '"%s.html"'%fullname + else: + full_page = FormatExternalLink(fullname, inheritance=True) + if full_page: + this_node_attrs['URL'] = full_page + res.append(' "%s" [%s];\n' % (newname, self._format_node_attrs(this_node_attrs))) @@ -189,13 +202,16 @@ class InheritanceDiagram(object): # ----------------------------------------------------------------------- # - def MakeInheritanceDiagram(self): + def MakeInheritanceDiagram(self, class_summary): """ 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). + for more information). + + :param `class_summary`: if not ``None``, used to identify if a class is actually been + wrapped or not (to avoid links pointing to non-existent pages). :rtype: `tuple` @@ -221,7 +237,7 @@ class InheritanceDiagram(object): fid.close() return os.path.split(outfn)[1], map.replace('\n', ' ') - code = self.generate_dot() + code = self.generate_dot(class_summary) # graphviz expects UTF-8 by default if isinstance(code, unicode): diff --git a/sphinxtools/librarydescription.py b/sphinxtools/librarydescription.py index 59c1837f..0fca66ce 100644 --- a/sphinxtools/librarydescription.py +++ b/sphinxtools/librarydescription.py @@ -1,22 +1,18 @@ import sys import os import operator -import errno import re +import cPickle from StringIO import StringIO -from subprocess import Popen, PIPE from inspect import getmro, getclasstree, getdoc, getcomments from utilities import MakeSummary, ChopDescription, WriteSphinxOutput -from utilities import FindControlImages, PickleClassInfo -from constants import object_types, MODULE_TO_ICON, DOXY_2_REST +from utilities import FindControlImages, FormatExternalLink +from constants import object_types, MODULE_TO_ICON, DOXY_2_REST, SPHINXROOT import templates -ENOENT = getattr(errno, 'ENOENT', 0) -EPIPE = getattr(errno, 'EPIPE', 0) - EPYDOC_PATTERN = re.compile(r'\S+{\S+}', re.DOTALL) reload(sys) @@ -75,7 +71,7 @@ def generic_summary(libraryItem, stream): table = [] for item in sub_list: - if item.is_redundant or item.GetShortName().startswith('__test') or '.extern.' in item.name: + if item.is_redundant: continue item_docs = ReplaceWxDot(item.docs) @@ -140,7 +136,13 @@ def ReplaceWxDot(text): newtext += header + '\n' newtext += '-'*len(header) + '\n\n' - + + # Try and replace True with ``True`` and False with ``False`` + # ``None`` gives trouble sometimes... + + for keyword in ['True', 'False']: + newtext = re.sub(r'\s%s\s'%keyword, ' ``%s`` '%keyword, newtext) + return newtext @@ -183,8 +185,10 @@ def FindBestLink(klass, newlink): return ':mod:`~%s`'%child.name elif child.kind in range(object_types.FUNCTION, object_types.INSTANCE_METHOD): return ':meth:`~%s`'%child.name - else: + elif child.kind == object_types.KLASS: return ':class:`~%s`'%child.name + else: + return ':attr:`~%s`'%child.name full_loop = FindInHierarchy(klass, newlink) @@ -290,6 +294,9 @@ class ParentBase(object): def Save(self): + if self.GetShortName().startswith('__test') or '.extern.' in self.name: + self.is_redundant = True + self.children = sorted(self.children, key=lambda k: (getattr(k, "order"), getattr(k, "name").lower())) if self.docs is None: @@ -377,7 +384,7 @@ class ParentBase(object): return items - def ToRest(self): + def ToRest(self, class_summary): pass @@ -403,10 +410,10 @@ class Library(ParentBase): return self.name - def Walk(self, obj): + def Walk(self, obj, class_summary): if obj == self: - obj.ToRest() + obj.ToRest(class_summary) # must have at least root folder children = obj.GetChildren() @@ -420,10 +427,10 @@ class Library(ParentBase): if child.is_redundant: continue - child.ToRest() + child.ToRest(class_summary) # recursively scan other folders, appending results - self.Walk(child) + self.Walk(child, class_summary) def FindItem(self, newlink, obj=None): @@ -451,8 +458,10 @@ class Library(ParentBase): return ':mod:`~%s`'%child.name elif child.kind in range(object_types.FUNCTION, object_types.INSTANCE_METHOD+1): return ':meth:`~%s`'%child.name - else: + elif child.kind == object_types.KLASS: return ':class:`~%s`'%child.name + else: + return ':attr:`~%s`'%child.name bestlink = self.FindItem(newlink, child) @@ -467,7 +476,7 @@ class Library(ParentBase): return self.python_version - def ToRest(self): + def ToRest(self, class_summary): print '\n\nReST-ifying %s...\n\n'%self.base_name stream = StringIO() @@ -483,7 +492,51 @@ class Library(ParentBase): generic_summary(self, stream) WriteSphinxOutput(stream, self.sphinx_file) + + def ClassesToPickle(self, obj, class_dict): + + # must have at least root folder + children = obj.GetChildren() + + if not children: + return class_dict + + # check each name + for child in children: + if child.kind == object_types.KLASS: + if child.is_redundant: + continue + class_dict[child.name] = (child.method_list, child.bases) + + # recursively scan other folders, appending results + class_dict = self.ClassesToPickle(child, class_dict) + + return class_dict + + + def Save(self): + + ParentBase.Save(self) + + class_dict = {} + class_dict = self.ClassesToPickle(self, class_dict) + + pickle_file = os.path.join(SPHINXROOT, 'class_summary.lst') + + if os.path.isfile(pickle_file): + fid = open(pickle_file, 'rb') + items = cPickle.load(fid) + fid.close() + else: + items = {} + + items.update(class_dict) + fid = open(pickle_file, 'wb') + cPickle.dump(items, fid) + fid.close() + + class Module(ParentBase): def __init__(self, name, kind): @@ -508,9 +561,9 @@ class Module(ParentBase): self.inheritance_diagram = None - def ToRest(self): + def ToRest(self, class_summary): - if self.is_redundant or self.GetShortName().startswith('__test') or '.extern.' in self.name: + if self.is_redundant: return stream = StringIO() @@ -537,9 +590,9 @@ class Module(ParentBase): if self.kind != object_types.PACKAGE: print '%s - %s (module)'%(spacer, self.name) if self.inheritance_diagram: - png, map = self.inheritance_diagram.MakeInheritanceDiagram() + png, map = self.inheritance_diagram.MakeInheritanceDiagram(class_summary) short_name = self.GetShortName() - image_desc = templates.TEMPLATE_INHERITANCE % (short_name, png, short_name, map) + image_desc = templates.TEMPLATE_INHERITANCE % ('module', short_name, png, short_name, map) stream.write(image_desc) else: print '%s - %s (package)'%(spacer, self.name) @@ -564,7 +617,14 @@ class Module(ParentBase): WriteSphinxOutput(stream, self.sphinx_file) - + + def Save(self): + + ParentBase.Save(self) + + if self.GetShortName().startswith('__test') or '.extern.' in self.name: + self.is_redundant = True + class Class(ParentBase): @@ -635,9 +695,9 @@ class Class(ParentBase): self.sphinx_file = MakeSphinxFile(name) - def ToRest(self): + def ToRest(self, class_summary): - if self.is_redundant or self.GetShortName().startswith('__test') or '.extern.' in self.name: + if self.is_redundant: return stream = StringIO() @@ -656,9 +716,9 @@ class Class(ParentBase): stream.write(class_docs + '\n\n') if self.inheritance_diagram: - png, map = self.inheritance_diagram.MakeInheritanceDiagram() + png, map = self.inheritance_diagram.MakeInheritanceDiagram(class_summary) short_name = self.GetShortName() - image_desc = templates.TEMPLATE_INHERITANCE % (short_name, png, short_name, map) + image_desc = templates.TEMPLATE_INHERITANCE % ('class', short_name, png, short_name, map) stream.write(image_desc) appearance = FindControlImages(self.name.lower()) @@ -667,13 +727,13 @@ class Class(ParentBase): stream.write(appearance_desc + '\n\n') if self.subClasses: - subs = [':ref:`%s`'%cls for cls in self.subClasses] + subs = [FormatExternalLink(cls) for cls in self.subClasses] subs = ', '.join(subs) subs_desc = templates.TEMPLATE_SUBCLASSES % subs stream.write(subs_desc) if self.superClasses: - sups = [':ref:`%s`'%cls for cls in self.superClasses] + sups = [FormatExternalLink(cls) for cls in self.superClasses] sups = ', '.join(sups) sups_desc = templates.TEMPLATE_SUPERCLASSES % sups stream.write(sups_desc) @@ -691,23 +751,16 @@ class Class(ParentBase): 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): @@ -736,6 +789,22 @@ class Class(ParentBase): if len(self.signature) < 2: self.is_redundant = True + if self.GetShortName().startswith('__test') or '.extern.' in self.name: + self.is_redundant = True + + if self.is_redundant: + return + + methods = self.GetItemByKind(object_types.METHOD, object_types.INSTANCE_METHOD) + method_list = [] + + for meth in methods: + if not meth.is_redundant: + method_list.append(meth.GetShortName()) + + self.method_list = method_list + self.bases = self.superClasses + class ChildrenBase(object): @@ -793,7 +862,7 @@ class ChildrenBase(object): self.comments = u'' - def ToRest(self): + def ToRest(self, class_summary): pass @@ -972,7 +1041,7 @@ class Attribute(ChildrenBase): self.order = 7 - def ToRest(self): + def ToRest(self, class_summary): pass diff --git a/sphinxtools/modulehunter.py b/sphinxtools/modulehunter.py index 198a6ae1..71d5a12e 100644 --- a/sphinxtools/modulehunter.py +++ b/sphinxtools/modulehunter.py @@ -15,6 +15,8 @@ import pkgutil import __builtin__ +from buildtools.config import phoenixDir + from inspect import getargspec, ismodule, getdoc, getmodule, getcomments, isfunction from inspect import ismethoddescriptor, getsource, ismemberdescriptor, isgetsetdescriptor from inspect import isbuiltin, isclass, getfile, ismethod @@ -491,7 +493,7 @@ def Import(init_name, import_name, full_process=True): def PrintProgress(name, looped_names): looped_names.append(name) - if len(looped_names) == 5: + if len(looped_names) == 4: message = ", ".join(looped_names) looped_names = [] print message @@ -552,15 +554,29 @@ def SubImport(import_string, module, parent_class, ispkg): return module_class, count +def ToRest(import_name): + + sphinxDir = os.path.join(phoenixDir(), 'docs', 'sphinx') + pickle_file = os.path.join(sphinxDir, 'wx%s.pkl'%import_name) + + fid = open(pickle_file, 'rb') + library_class = cPickle.load(fid) + fid.close() + + fid = open(os.path.join(sphinxDir, 'class_summary.lst'), 'rb') + class_summary = cPickle.load(fid) + fid.close() + + library_class.Walk(library_class, class_summary) + + def ModuleHunter(init_name, import_name, version): - pickle_file = os.path.join(os.getcwd(), 'docs', 'sphinx', 'wx%s.pkl'%import_name) + sphinxDir = os.path.join(phoenixDir(), 'docs', 'sphinx') + pickle_file = os.path.join(sphinxDir, 'wx%s.pkl'%import_name) if os.path.isfile(pickle_file): - fid = open(pickle_file, 'rb') - library_class = cPickle.load(fid) - fid.close() - library_class.Walk(library_class) + ToRest(import_name) return path = list(sys.path) @@ -627,7 +643,7 @@ def ModuleHunter(init_name, import_name, version): cPickle.dump(library_class, fid) fid.close() - library_class.Walk(library_class) + ToRest(import_name) if __name__ == "__main__": diff --git a/sphinxtools/templates.py b/sphinxtools/templates.py index c5c11aa7..dd1b8b71 100644 --- a/sphinxtools/templates.py +++ b/sphinxtools/templates.py @@ -33,7 +33,7 @@ TEMPLATE_INHERITANCE = ''' |class_hierarchy| Inheritance Diagram ===================================== -Inheritance diagram for: **%s** +Inheritance diagram for %s **%s** .. raw:: html diff --git a/sphinxtools/utilities.py b/sphinxtools/utilities.py index 0eb3758e..64ce1403 100644 --- a/sphinxtools/utilities.py +++ b/sphinxtools/utilities.py @@ -23,7 +23,7 @@ from templates import TEMPLATE_CONTRIB from constants import IGNORE, PUNCTUATION from constants import CPP_ITEMS, VERSION, VALUE_MAP -from constants import RE_KEEP_SPACES +from constants import RE_KEEP_SPACES, EXTERN_INHERITANCE from constants import DOXYROOT, SPHINXROOT, WIDGETS_IMAGES_ROOT @@ -472,9 +472,11 @@ def MakeSummary(item_list, template, kind, add_tilde=True): :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:" or ":ref:". + :param string `kind`: can be ``:meth:`` or ``:attr:`` or ``:ref:`` or ``:mod:``; + :param bool `add_tilde`: ``True`` to add the ``~`` character in front of the first + summary table column, ``False`` otherwise. - :rtype: `string` + :rtype: `string` """ maxlen = 0 @@ -507,7 +509,9 @@ def WriteSphinxOutput(stream, filename, append=False): Writes the text contained in the `stream` to the `filename` output file. :param StringIO.StringIO `stream`: the stream where the text lives. - :param string `filename`: the output file we want to write the text in. + :param string `filename`: the output file we want to write the text in; + :param bool `append`: ``True`` to append to the file, ``False`` to simply + write to it. """ text_file = os.path.join(SPHINXROOT, filename) @@ -587,6 +591,13 @@ def PickleItem(description, current_module, name, kind): # ----------------------------------------------------------------------- # def PickleClassInfo(class_name, element): + """ + Saves some information about a class in a cPickle-compatible file., i.e. the + list of methods in that class and its super-classes. + + :param string `class_name`: the name of the class we want to pickle; + :param xml.etree.ElementTree.Element `element`: the XML element we want to examine. + """ pickle_file = os.path.join(SPHINXROOT, 'class_summary.lst') @@ -649,6 +660,19 @@ RAW_2 = """ """ def FormatContributedSnippets(kind, contrib_snippets): + """ + This method will include and properly ReST-ify contributed snippets + of wxPython code (at the moment only 2 snippets are available), by + including the Python code into the ReST files and allowing the user to + show/hide the snippets using a JavaScript "Accordion" script thanks to + the ``.. raw::`` directive (default for snippets is to be hidden). + + :param string `kind`: can be "method", "function" or "class" depending on the + current item being scanned by the `sphinxgenerator.py` tool; + :param list `contrib_snippets`: a list of file names (with the ``*.py`` extension) + containing the contributed snippets of code. Normally these snippets live + in the ``SPHINXROOT/rest_substitutions/snippets/python/contrib`` folder. + """ spacer = '' if kind == 'function': @@ -678,3 +702,55 @@ def FormatContributedSnippets(kind, contrib_snippets): text += RAW_2%(spacer, spacer) return text + + +def FormatExternalLink(fullname, inheritance=False): + """ + Analyzes the input `fullname` string to check whether a class description + is actually coming from an external documentation tool + (like http://docs.python.org/library/ or http://docs.scipy.org/doc/numpy/reference/generated/). + + If the method finds such an external link, the associated inheritance + diagram (if `inheritance` is ``True``) or the ``:ref:`` directive are + modified accordingly to link it to the correct external documentation. + + :param string `fullname`: the fully qualified name for a class, method or function, + i.e. `exceptions.Exception` or `threading.Thread`; + :param bool `inheritance`: ``True`` if the call is coming from :mod:`inheritance`, + ``False`` otherwise. + """ + + if fullname.count('.') == 0: + if not inheritance: + return ':class:`%s`'%fullname + return '' + + parts = fullname.split('.') + possible_external = parts[-2] + '.' + real_name = '.'.join(parts[-2:]) + + if possible_external.startswith('_'): + # funny ctypes... + possible_external = possible_external[1:] + real_name = real_name[1:] + + if possible_external not in EXTERN_INHERITANCE: + if not inheritance: + return ':class:`%s`'%fullname + + return '' + + base_address = EXTERN_INHERITANCE[possible_external] + + if 'numpy' in real_name: + htmlpage = '%s.html#%s'%(real_name.lower(), real_name) + else: + htmlpage = '%shtml#%s'%(possible_external.lower(), real_name) + + if inheritance: + full_page = '"%s"'%(base_address + htmlpage) + else: + full_page = '`%s <%s>`_'%(fullname, base_address + htmlpage) + + return full_page +