[Python-modules-commits] [sphinxcontrib-doxylink] 01/03: Imported Upstream version 1.3

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Fri May 6 10:33:11 UTC 2016


This is an automated email from the git hooks/post-receive script.

ghisvail-guest pushed a commit to branch debian/master
in repository sphinxcontrib-doxylink.

commit 0814f01fccecdd9f76bf712d8355cdf1e4ecb612
Author: Ghislain Antony Vaillant <ghisvail at gmail.com>
Date:   Fri May 6 07:56:50 2016 +0100

    Imported Upstream version 1.3
---
 CHANGES.rst                                        |  48 +++
 MANIFEST.in                                        |   3 +
 PKG-INFO                                           |  50 +++
 README.rst                                         |  28 ++
 setup.cfg                                          |   8 +
 setup.py                                           |  41 ++
 sphinxcontrib/__init__.py                          |  14 +
 sphinxcontrib/doxylink/__init__.py                 |   2 +
 sphinxcontrib/doxylink/doxylink.py                 | 452 +++++++++++++++++++++
 sphinxcontrib/doxylink/parsing.py                  | 153 +++++++
 sphinxcontrib_doxylink.egg-info/PKG-INFO           |  50 +++
 sphinxcontrib_doxylink.egg-info/SOURCES.txt        |  18 +
 .../dependency_links.txt                           |   1 +
 .../namespace_packages.txt                         |   1 +
 sphinxcontrib_doxylink.egg-info/not-zip-safe       |   1 +
 sphinxcontrib_doxylink.egg-info/requires.txt       |   2 +
 sphinxcontrib_doxylink.egg-info/top_level.txt      |   2 +
 tests/__init__.py                                  |   0
 tests/test_parser.py                               | 134 ++++++
 19 files changed, 1008 insertions(+)

diff --git a/CHANGES.rst b/CHANGES.rst
new file mode 100644
index 0000000..ab55428
--- /dev/null
+++ b/CHANGES.rst
@@ -0,0 +1,48 @@
+1.3 (Sep 13, 2012)
+====================
+
+- Add fix from Matthias Tuma from Shark3 to allow friend declarations inside classes.
+
+1.2 (Nov 3, 2011)
+====================
+
+- Add Python 3 support
+
+1.1 (Feb 19, 2011)
+====================
+
+- Add support for linking directly to struct definitions.
+- Allow to link to functions etc. which are in a header/source file but not a member of a class.
+
+1.0 (Dec 14, 2010)
+====================
+
+- New Dependency: PyParsing (http://pyparsing.wikispaces.com/)
+- Completely new tag file parsing system. Allows for function overloading.
+  The parsed results are cached to speed things up.
+- Full usage documentation. Build with `sphinx-build -W -b html doc html`.
+- Fix problem with mixed slashes when building on Windows.
+
+0.4 (Aug 15, 2010)
+====================
+
+- Allow URLs as base paths for the HTML links.
+- Don't append parentheses if the user has provided them already in their query.
+
+0.3 (Aug 10, 2010)
+====================
+
+- Only parse the tag file once per run. This should increase the speed.
+- Automatically add parentheses to functions if the add_function_parentheses config variable is set.
+
+0.2 (Jul 31, 2010)
+====================
+
+- When a target cannot be found, make the node an `inline` node so there's no link created.
+- No longer require a trailing slash on the `doxylink` config variable HTML link path.
+- Allow doxylinks to work correctly when created from a documentation subdirectory.
+
+0.1 (Jul 22, 2010)
+==================
+
+- Initial release
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..e7c05de
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include README.rst
+include LICENSE
+include CHANGES.rst
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..f09fc17
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,50 @@
+Metadata-Version: 1.1
+Name: sphinxcontrib-doxylink
+Version: 1.3
+Summary: Sphinx extension for linking to Doxygen documentation.
+Home-page: http://packages.python.org/sphinxcontrib-doxylink
+Author: Matt Williams
+Author-email: matt at milliams.com
+License: BSD
+Download-URL: http://pypi.python.org/pypi/sphinxcontrib-doxylink
+Description: ######################
+        sphinxcontrib-doxylink
+        ######################
+        
+        A Sphinx_ extension to link to external Doxygen API documentation.
+        
+        Usage
+        -----
+        
+        Please refer to the documentation_ for information on using this extension.
+        
+        Installation
+        ------------
+        
+        This extension can be installed from the Python Package Index::
+        
+           pip install sphinxcontrib-doxylink
+        
+        Alternatively, you can clone the sphinx-contrib_ repository from BitBucket,
+        and install the extension directly from the repository::
+        
+           hg clone http://bitbucket.org/birkenfeld/sphinx-contrib
+           cd sphinx-contrib/doxylink
+           python setup.py install
+        
+        .. _`Sphinx`: http://sphinx.pocoo.org/latest
+        .. _`sphinx-contrib`: http://bitbucket.org/birkenfeld/sphinx-contrib
+        .. _`documentation`: http://packages.python.org/sphinxcontrib-doxylink
+        
+Keywords: sphinx,doxygen,documentation,c++
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Documentation
+Classifier: Topic :: Utilities
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..13c04ff
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,28 @@
+######################
+sphinxcontrib-doxylink
+######################
+
+A Sphinx_ extension to link to external Doxygen API documentation.
+
+Usage
+-----
+
+Please refer to the documentation_ for information on using this extension.
+
+Installation
+------------
+
+This extension can be installed from the Python Package Index::
+
+   pip install sphinxcontrib-doxylink
+
+Alternatively, you can clone the sphinx-contrib_ repository from BitBucket,
+and install the extension directly from the repository::
+
+   hg clone http://bitbucket.org/birkenfeld/sphinx-contrib
+   cd sphinx-contrib/doxylink
+   python setup.py install
+
+.. _`Sphinx`: http://sphinx.pocoo.org/latest
+.. _`sphinx-contrib`: http://bitbucket.org/birkenfeld/sphinx-contrib
+.. _`documentation`: http://packages.python.org/sphinxcontrib-doxylink
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..3581741
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,8 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
+[aliases]
+release = egg_info -RDb ''
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..e34795e
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+from setuptools import setup, find_packages
+
+with open('README.rst') as stream:
+    long_desc = stream.read()
+
+requires = ['Sphinx>=0.6', 'pyparsing']
+
+setup(
+    name='sphinxcontrib-doxylink',
+    version='1.3',
+	url='http://packages.python.org/sphinxcontrib-doxylink',
+    download_url='http://pypi.python.org/pypi/sphinxcontrib-doxylink',
+    license='BSD',
+    author='Matt Williams',
+    author_email='matt at milliams.com',
+    description='Sphinx extension for linking to Doxygen documentation.',
+    long_description=long_desc,
+    keywords=['sphinx','doxygen','documentation','c++'],
+    zip_safe=False,
+    classifiers=[
+        'Development Status :: 5 - Production/Stable',
+        'Environment :: Console',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 3',
+        'Topic :: Documentation',
+        'Topic :: Utilities',
+    ],
+    platforms='any',
+    packages=find_packages(),
+    include_package_data=True,
+    install_requires=requires,
+    namespace_packages=['sphinxcontrib'],
+	test_suite="tests",
+	use_2to3=True,
+)
diff --git a/sphinxcontrib/__init__.py b/sphinxcontrib/__init__.py
new file mode 100644
index 0000000..b5a7dc2
--- /dev/null
+++ b/sphinxcontrib/__init__.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+"""
+    sphinxcontrib
+    ~~~~~~~~~~~~~
+
+    This package is a namespace package that contains all extensions
+    distributed in the ``sphinx-contrib`` distribution.
+
+    :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+__import__('pkg_resources').declare_namespace(__name__)
+
diff --git a/sphinxcontrib/doxylink/__init__.py b/sphinxcontrib/doxylink/__init__.py
new file mode 100644
index 0000000..8414033
--- /dev/null
+++ b/sphinxcontrib/doxylink/__init__.py
@@ -0,0 +1,2 @@
+from doxylink import *
+
diff --git a/sphinxcontrib/doxylink/doxylink.py b/sphinxcontrib/doxylink/doxylink.py
new file mode 100644
index 0000000..69f4c16
--- /dev/null
+++ b/sphinxcontrib/doxylink/doxylink.py
@@ -0,0 +1,452 @@
+# -*- coding: utf-8 -*-
+
+import os
+import xml.etree.ElementTree as ET
+import urlparse
+import re
+import itertools
+
+from docutils import nodes, utils
+from sphinx.util.nodes import split_explicit_title
+from sphinx.util.console import bold, standout
+
+from parsing import normalise, ParseException
+
+def find_url(doc, symbol):
+	"""
+	Return the URL for a given symbol.
+	
+	This is where the magic happens.
+	This function could be a lot more clever. At present it required the passed symbol to be almost exactly the same as the entries in the Doxygen tag file.
+	
+	.. todo::
+		
+		Maybe print a list of all possible matches as a warning (but still only return the first)
+	
+	:Parameters:
+		doc : xml.etree.ElementTree
+			The XML DOM object
+		symbol : string
+			The symbol to lookup in the file. E.g. something like 'PolyVox::Array' or 'tidyUpMemory'
+	
+	:return: String representing the filename part of the URL
+	"""
+	
+	#First check for an exact match with a top-level object (namespaces, objects etc.)
+	
+	#env = inliner.document.settings.env
+	
+	matches = []
+	for compound in doc.findall('.//compound'):
+		if compound.find('name').text == symbol:
+			matches += [{'file':compound.find('filename').text, 'kind':compound.get('kind')}]
+	
+	if len(matches) > 1:
+		pass
+		#env.warn(env.docname, 'There were multiple matches for `%s`: %s' % (symbol, matches))
+	if len(matches) == 1:
+		return matches[0]
+	
+	
+	#Strip off first namespace bit of the compound name so that 'ArraySizes' can match 'PolyVox::ArraySizes'
+	for compound in doc.findall('.//compound'):
+		symbol_list = compound.find('name').text.split('::', 1)
+		if len(symbol_list) == 2:
+			reducedsymbol = symbol_list[1]
+			if reducedsymbol == symbol:
+				return {'file':compound.find('filename').text, 'kind':compound.get('kind')}
+	
+	#Now split the symbol by '::'. Find an exact match for the first part and then a member match for the second
+	#So PolyVox::Array::operator[] becomes like {namespace: "PolyVox::Array", endsymbol: "operator[]"}
+	symbol_list = symbol.rsplit('::', 1)
+	if len(symbol_list) == 2:
+		namespace = symbol_list[0]
+		endsymbol = symbol_list[1]
+		for compound in doc.findall('.//compound'):
+			if compound.find('name').text == namespace:
+				for member in compound.findall('member'):
+					#If this compound object contains the matching member then return it
+					if member.find('name').text == endsymbol:
+						return {'file':(member.findtext('anchorfile') or compound.findtext('filename')) + '#' + member.find('anchor').text, 'kind':member.get('kind')}
+	
+	#Then we'll look at unqualified members
+	for member in doc.findall('.//member'):
+		if member.find('name').text == symbol:
+			return {'file':(member.findtext('anchorfile') or compound.findtext('filename')) + '#' + member.find('anchor').text, 'kind':member.get('kind')}
+	
+	return None
+
+def parse_tag_file(doc):
+	"""
+	Takes in an XML tree from a Doxygen tag file and returns a dictionary that looks something like:
+	
+	.. code-block:: python
+	
+		{'PolyVox': {'file': 'namespace_poly_vox.html',
+		             'kind': 'namespace'},
+		 'PolyVox::Array': {'file': 'class_poly_vox_1_1_array.html',
+		                    'kind': 'class'},
+		 'PolyVox::Array1DDouble': {'file': 'namespace_poly_vox.html#a7a1f5fd5c4f7fbb4258a495d707b5c13',
+		                            'kind': 'typedef'},
+		 'PolyVox::Array1DFloat': {'file': 'namespace_poly_vox.html#a879a120e49733eba1905c33f8a7f131b',
+		                           'kind': 'typedef'},
+		 'PolyVox::Array1DInt16': {'file': 'namespace_poly_vox.html#aa1463ece448c6ebed55ab429d6ae3e43',
+		                           'kind': 'typedef'},
+		 'QScriptContext::throwError': {'arglist': {'( Error error, const QString & text )': 'qscriptcontext.html#throwError',
+		                                            '( const QString & text )': 'qscriptcontext.html#throwError-2'},
+		                                'kind': 'function'},
+		 'QScriptContext::toString': {'arglist': {'()': 'qscriptcontext.html#toString'},
+		                              'kind': 'function'}}
+	
+	Note the different form for functions. This is required to allow for 'overloading by argument type'.
+	
+	To access a filename for a symbol you do:
+	
+	.. code-block:: python
+	
+		symbol_mapping = mapping[symbol]
+		if symbol_mapping['kind'] == 'function':
+			url = symbol_mapping['arglist'][argument_string]
+		else:
+			url = symbol_mapping['file']
+	
+	:Parameters:
+		doc : xml.etree.ElementTree
+			The XML DOM object
+	
+	:return: a dictionary mapping fully qualified symbols to files
+	"""
+	
+	mapping = {}
+	function_list = [] #This is a list of function to be parsed and inserted into mapping at the end of the function.
+	for compound in doc.findall("./compound"):
+		compound_kind = compound.get('kind')
+		if compound_kind != 'namespace' and compound_kind != 'class' and compound_kind!= 'struct' and compound_kind != 'file':
+			continue #Skip everything that isn't a namespace, class, struct or file
+		
+		compound_name = compound.findtext('name')
+		compound_filename = compound.findtext('filename')
+		
+		#TODO The following is a hack bug fix I think
+		#Doxygen doesn't seem to include the file extension to <compound kind="file"><filename> entries
+		#If it's a 'file' type, check if it _does_ have an extension, if not append '.html'
+		if compound_kind == 'file' and not os.path.splitext(compound_filename)[1]:
+			compound_filename = join(compound_filename, '.html')
+		
+		#If it's a compound we can simply add it
+		mapping[compound_name] = {'kind' : compound_kind, 'file' : compound_filename}
+		
+		for member in compound.findall('member'):
+			
+			#If the member doesn't have an <anchorfile> element, use the parent compounds <filename> instead
+			#This is the way it is in the qt.tag and is perhaps an artefact of old Doxygen
+			anchorfile = member.findtext('anchorfile') or compound_filename
+			member_symbol = join(compound_name, '::', member.findtext('name'))
+			member_kind = member.get('kind')
+			arglist_text = member.findtext('./arglist') #If it has an <arglist> then we assume it's a function. Empty <arglist> returns '', not None. Things like typedefs and enums can have empty arglists
+			
+			if arglist_text and member_kind != 'variable' and member_kind != 'typedef' and member_kind != 'enumeration':
+				function_list.append((member_symbol, arglist_text, member_kind, join(anchorfile,'#',member.findtext('anchor'))))
+			else:
+				mapping[member_symbol] = {'kind' : member.get('kind'), 'file' : join(anchorfile,'#',member.findtext('anchor'))}
+	
+	for old_tuple, normalised_tuple in zip(function_list, itertools.imap(normalise, (member_tuple[1] for member_tuple in function_list))):
+		member_symbol = old_tuple[0]
+		original_arglist = old_tuple[1]
+		kind = old_tuple[2]
+		anchor_link = old_tuple[3]
+		normalised_arglist = normalised_tuple[1]
+		if normalised_tuple[1] is not None: #This is a 'flag' for a ParseException having happened
+			if mapping.get(member_symbol) and mapping[member_symbol]['kind'] == 'function':
+				mapping[member_symbol]['arglist'][normalised_arglist] = anchor_link
+			else:
+				mapping[member_symbol] = {'kind' : kind, 'arglist' : {normalised_arglist : anchor_link}}
+		else:
+			print('Skipping %s %s%s. Error reported from parser was: %s' % (old_tuple[2], old_tuple[0], old_tuple[1], normalised_tuple[0]))
+	
+	#from pprint import pprint; pprint(mapping)
+	return mapping
+
+def find_url2(mapping, symbol):
+	"""
+	Return the URL for a given symbol.
+	
+	This is where the magic happens.
+	
+	.. todo::
+		
+		Maybe print a list of all possible matches as a warning (but still only return the first)
+	
+	:Parameters:
+		mapping : dictionary
+			A dictionary of the form returned by :py:func:`parse_tag_file`
+		symbol : string
+			The symbol to lookup in the file. E.g. something like 'PolyVox::Array' or 'tidyUpMemory'
+	
+	:return: String representing the filename part of the URL
+	
+	:raises:
+		LookupError
+			Raised if the symbol could not be matched in the file
+	"""
+	#print "\n\nSearching for", symbol
+	try:
+		symbol, normalised_arglist =  normalise(symbol)
+	except ParseException as error:
+		raise LookupError(error)
+	#print symbol, normalised_arglist
+	
+	#If we have an exact match then return it.
+	if mapping.get(symbol):
+		#print ('Exact match')
+		return return_from_mapping(mapping[symbol], normalised_arglist)
+	
+	#If the user didn't pass in any arguments, i.e. `arguments == ''` then they don't care which version of the overloaded funtion they get.
+	
+	#First we check for any mapping entries which even slightly match the requested symbol
+	#endswith_list = {}
+	#for item, data in mapping.items():
+	#	if item.endswith(symbol):
+			#print symbol + ' : ' + item
+	#		endswith_list[item] = data
+	#		mapping[item]['file']
+	
+	#If we only find one then we return it.
+	#if len(endswith_list) is 1:
+	#	return endswith_list.values()[0]['file']
+	
+	#print("Still", len(endswith_list), 'possible matches')
+	
+	piecewise_list = find_url_piecewise(mapping, symbol)
+	
+	#If there is only one match, return it.
+	if len(piecewise_list) is 1:
+		return return_from_mapping(piecewise_list.values()[0], normalised_arglist)
+	
+	#print("Still", len(piecewise_list), 'possible matches')
+	
+	#If there is more than one item in piecewise_list then there is an ambiguity
+	#Often this is due to the symbol matching the name of the constructor as well as the class name itself
+	classes_list = find_url_classes(piecewise_list, symbol)
+	
+	#If there is only one by here we return it.
+	if len(classes_list) is 1:
+		return classes_list.values()[0]
+	
+	#print("Still", len(classes_list), 'possible matches')
+	
+	#If we exhaused the list by requiring classes, use the list from before the filter.
+	if len(classes_list) == 0:
+		classes_list = piecewise_list
+	
+	no_templates_list = find_url_remove_templates(classes_list, symbol)
+	
+	if len(no_templates_list) is 1:
+		return return_from_mapping(no_templates_list.values()[0], normalised_arglist)
+	
+	#print("Still", len(no_templates_list), 'possible matches')
+	
+	#If not found by now, just return the first one in the list
+	if len(no_templates_list) != 0:
+		#TODO return a warning here?
+		return return_from_mapping(no_templates_list.values()[0], normalised_arglist)
+	#Else return None if the list is empty
+	else:
+		LookupError('Could not find a match')
+
+def return_from_mapping(mapping_entry, normalised_arglist=''):
+	"""
+	Return a mapping to a single URL in the form. This is needed since mapping entries for functions are more complicated due to function overriding.
+	
+	If the mapping to be returned is not a function, this will simply return the mapping entry intact. If the entry is a function it will attempt to get the right version based on the function signature.
+	
+	:Parameters:
+		mapping_entry : dict
+			should be a single entry from the large mapping file corresponding to a single symbol. If the symbol is a function, then ``mappingentry['arglist']`` will be a dictionary mapping normalised signatures to URLs
+		normalised_arglist : string
+			the normalised form of the arglist that the user has requested. This can be empty in which case the function will return just the first element of ``mappingentry['arglist']``. This parameter is ignored if ``mappingentry['kind'] != 'function'``
+	
+	:return: dictionary something like:
+	
+		.. code-block:: python
+		
+			{'kind' : 'function', 'file' : 'something.html#foo'}
+	
+	"""
+	#If it's a function we need to grab the right signature from the arglist.
+	if mapping_entry['kind'] == 'function':
+		#If the user has requested a specific function through specifying an arglist then get the right anchor
+		if normalised_arglist:
+			filename = mapping_entry['arglist'].get(normalised_arglist)
+			if not filename: #If we didn't get the filename because it's not in the mapping then we will just return a random one?
+				#TODO return a warning here!
+				filename = mapping_entry['arglist'].values()[0]
+		else:
+			#Otherwise just return the first entry (if they don't care they get whatever comes first)
+			filename = mapping_entry['arglist'].values()[0]
+		
+		return {'kind' : 'function', 'file' : filename}
+	elif mapping_entry.get('arglist'):
+		#This arglist should only be one entry long and that entry should have '' as its key
+		return {'kind' : mapping_entry['kind'], 'file' : mapping_entry['arglist']['']}
+	
+	#If it's not a function, then return it raw
+	return mapping_entry
+
+def find_url_piecewise(mapping, symbol):
+	"""
+	Match the requested symbol reverse piecewise (split on ``::``) against the tag names to ensure they match exactly (modulo ambiguity)
+	So, if in the mapping there is ``PolyVox::Volume::FloatVolume`` and ``PolyVox::Volume`` they would be split into:
+	
+	.. code-block:: python
+	
+		['PolyVox', 'Volume', 'FloatVolume'] and ['PolyVox', 'Volume']
+	
+	and reversed:
+	
+	.. code-block:: python
+	
+		['FloatVolume', 'Volume', 'PolyVox'] and ['Volume', 'PolyVox']
+	
+	and truncated to the shorter of the two:
+	
+	.. code-block:: python
+	
+		['FloatVolume', 'Volume'] and ['Volume', 'PolyVox']
+	
+	If we're searching for the ``PolyVox::Volume`` symbol we would compare:
+	
+	.. code-block:: python
+	
+		['Volume', 'PolyVox'] to ['FloatVolume', 'Volume', 'PolyVox'].
+	
+	That doesn't match so we look at the next in the mapping:
+	
+	.. code-block:: python
+	
+		['Volume', 'PolyVox'] to ['Volume', 'PolyVox'].
+	
+	Good, so we add it to the list
+	
+	"""
+	piecewise_list = {}
+	for item, data in mapping.items():
+		split_symbol = symbol.split('::')
+		split_item = item.split('::')
+		
+		split_symbol.reverse()
+		split_item.reverse()
+		
+		min_length = min(len(split_symbol), len(split_item))
+		
+		split_symbol = split_symbol[:min_length]
+		split_item = split_item[:min_length]
+		
+		#print split_symbol, split_item
+		
+		if split_symbol == split_item:
+			#print symbol + ' : ' + item
+			piecewise_list[item] = data
+	
+	return piecewise_list
+
+def find_url_classes(mapping, symbol):
+	"""Prefer classes over names of constructors"""
+	classes_list = {}
+	for item, data in mapping.items():
+		if data['kind'] == 'class':
+			#print symbol + ' : ' + item
+			classes_list[item] = data
+	
+	return classes_list
+
+def find_url_remove_templates(mapping, symbol):
+	"""Now, to disambiguate between ``PolyVox::Array< 1, ElementType >::operator[]`` and ``PolyVox::Array::operator[]`` matching ``operator[]``, we will ignore templated (as in C++ templates) tag names by removing names containing ``<``"""
+	no_templates_list = {}
+	for item, data in mapping.items():
+		if '<' not in item:
+			#print symbol + ' : ' + item
+			no_templates_list[item] = data
+	
+	return no_templates_list
+
+def join(*args):
+	return ''.join(args)
+
+def create_role(app, tag_filename, rootdir):
+	#Tidy up the root directory path
+	if not rootdir.endswith(('/', '\\')):
+		rootdir = join(rootdir, os.sep)
+	
+	try:
+		tag_file = ET.parse(tag_filename)
+		
+		cache_name = os.path.basename(tag_filename)
+		
+		app.info(bold('Checking tag file cache for %s: ' % cache_name), nonl=True)
+		if not hasattr(app.env, 'doxylink_cache'):
+			# no cache present at all, initialise it
+			app.info('No cache at all, rebuilding...')
+			mapping = parse_tag_file(tag_file)
+			app.env.doxylink_cache = { cache_name : {'mapping' : mapping, 'mtime' : os.path.getmtime(tag_filename)}}
+		elif not app.env.doxylink_cache.get(cache_name):
+			# Main cache is there but the specific sub-cache for this tag file is not
+			app.info('Sub cache is missing, rebuilding...')
+			mapping = parse_tag_file(tag_file)
+			app.env.doxylink_cache[cache_name] = {'mapping' : mapping, 'mtime' : os.path.getmtime(tag_filename)}
+		elif app.env.doxylink_cache[cache_name]['mtime'] < os.path.getmtime(tag_filename):
+			# tag file has been modified since sub-cache creation
+			app.info('Sub-cache is out of date, rebuilding...')
+			mapping = parse_tag_file(tag_file)
+			app.env.doxylink_cache[cache_name] = {'mapping' : mapping, 'mtime' : os.path.getmtime(tag_filename)}
+		else:
+			#The cache is up to date
+			app.info('Sub-cache is up-to-date')
+	except IOError:
+		tag_file = None
+		app.warn(standout('Could not open tag file %s. Make sure your `doxylink` config variable is set correctly.' % tag_filename))
+	
+	def find_doxygen_link(name, rawtext, text, lineno, inliner, options={}, content=[]):
+		text = utils.unescape(text)
+		# from :name:`title <part>`
+		has_explicit_title, title, part = split_explicit_title(text)
+		warning_messages = []
+		if tag_file:
+			url = find_url(tag_file, part)
+			try:
+				url = find_url2(app.env.doxylink_cache[cache_name]['mapping'], part)
+			except LookupError as error:
+				warning_messages.append('Error while parsing `%s`. Is not a well-formed C++ function call or symbol. If this is not the case, it is a doxylink bug so please report it. Error reported was: %s' % (part, error))
+			if url:
+				
+				#If it's an absolute path then the link will work regardless of the document directory
+				#Also check if it is a URL (i.e. it has a 'scheme' like 'http' or 'file')
+				if os.path.isabs(rootdir) or urlparse.urlparse(rootdir).scheme:
+					full_url = join(rootdir, url['file'])
+				#But otherwise we need to add the relative path of the current document to the root source directory to the link
+				else:
+					relative_path_to_docsrc = os.path.relpath(app.env.srcdir, os.path.dirname(inliner.document.current_source))
+					full_url = join(relative_path_to_docsrc, '/', rootdir, url['file']) #We always use the '/' here rather than os.sep since this is a web link avoids problems like documentation/.\../library/doc/ (mixed slashes)
+				
+				if url['kind'] == 'function' and app.config.add_function_parentheses and not normalise(title)[1]:
+					title = join(title, '()')
+				
+				pnode = nodes.reference(title, title, internal=False, refuri=full_url)
+				return [pnode], []
+			#By here, no match was found
+			warning_messages.append('Could not find match for `%s` in `%s` tag file' % (part, tag_filename))
+		else:
+			warning_messages.append('Could not find match for `%s` because tag file not found' % (part))
+		
+		pnode = nodes.inline(rawsource=title, text=title)
+		return [pnode], [inliner.reporter.warning(message, line=lineno) for message in warning_messages]
+	
+	return find_doxygen_link
+
+def setup_doxylink_roles(app):
+	for name, [tag_filename, rootdir] in app.config.doxylink.iteritems():
+		app.add_role(name, create_role(app, tag_filename, rootdir))
+
+def setup(app):
+	app.add_config_value('doxylink', {}, 'env')
+	app.connect('builder-inited', setup_doxylink_roles)
diff --git a/sphinxcontrib/doxylink/parsing.py b/sphinxcontrib/doxylink/parsing.py
new file mode 100644
index 0000000..4e42f44
--- /dev/null
+++ b/sphinxcontrib/doxylink/parsing.py
@@ -0,0 +1,153 @@
+#import multiprocessing
+import itertools
+
+from pyparsing import Word, Literal, alphas, nums, alphanums, OneOrMore, Optional, SkipTo, ParseException, Group, ZeroOrMore, Suppress, Combine, delimitedList, quotedString, nestedExpr, ParseResults, oneOf
+
+# define punctuation - reuse of expressions helps packratting work better
+LPAR,RPAR,LBRACK,RBRACK,COMMA,EQ = map(Literal,"()[],=")
+
+#Qualifier to go in front of type in the argument list (unsigned const int foo)
+qualifier = OneOrMore(oneOf('const unsigned typename struct enum'))
+
+def turn_parseresults_to_list(s, loc, toks):
+	return ParseResults(normalise_templates(toks[0].asList()))
+
+def normalise_templates(toks, isinstance=isinstance, basestring=basestring):
+	s_list = ['<']
+	s_list_append = s_list.append #lookup append func once, instead of many times
+	for tok in toks:
+		if isinstance(tok, basestring): #See if it's a string
+			s_list_append(' ' + tok)
+		else:
+			#If it's not a string
+			s_list_append(normalise_templates(tok))
+	s_list_append(' >')
+	return ''.join(s_list)
+
+#Skip pairs of brackets.
+angle_bracket_pair = nestedExpr(opener='<',closer='>').setParseAction(turn_parseresults_to_list)
+#TODO Fix for nesting brackets
+parentheses_pair = LPAR + SkipTo(RPAR) + RPAR
+square_bracket_pair = LBRACK + SkipTo(RBRACK) + RBRACK
+
+#The raw type of the input, i.e. 'int' in (unsigned const int * foo)
+#TODO I guess this should be a delimited list (by '::') of name and angle brackets
+input_type = Combine(Word(alphanums + ':_') + Optional(angle_bracket_pair + Optional(Word(alphanums + ':_'))))
+
+#A number. e.g. -1, 3.6 or 5
+number = Word('-.' + nums)
+
+#The name of the argument. We will ignore this but it must be matched anyway.
+input_name = OneOrMore(Word(alphanums + '_') | angle_bracket_pair | parentheses_pair | square_bracket_pair)
+
+#Grab the '&', '*' or '**' type bit in (const QString & foo, int ** bar)
+pointer_or_reference = oneOf('* &')
+
+#The '=QString()' or '=false' bit in (int foo = 4, bool bar = false)
+default_value = Literal('=') + OneOrMore(number | quotedString | input_type | parentheses_pair | angle_bracket_pair | square_bracket_pair | Word('|&^'))
+
+#A combination building up the interesting bit -- the argument type, e.g. 'const QString &', 'int' or 'char*'
+argument_type = Optional(qualifier, default='')("qualifier") + \
+                input_type("input_type") + \
+                Optional(pointer_or_reference, default='')("pointer_or_reference1") + \
+                Optional('const')('const_pointer_or_reference') + \
+                Optional(pointer_or_reference, default='')("pointer_or_reference2")
+
+#Argument + variable name + default
+argument = Group(argument_type('argument_type') + Optional(input_name) + Optional(default_value))
+
+#List of arguments in parentheses with an optional 'const' on the end
+arglist = LPAR + delimitedList(argument)('arg_list') + Optional(COMMA + '...')('var_args') + RPAR
+
+def normalise(symbol):
+	"""
+	Takes a c++ symbol or funtion and splits it into symbol and a normalised argument list.
+	
+	:Parameters:
+		symbol : string
+			A C++ symbol or function definition like ``PolyVox::Volume``, ``Volume::printAll() const``
+	
+	:return:
+		a tuple consisting of two strings: ``(qualified function name or symbol, normalised argument list)``
+	"""
+	
+	try:
+		bracket_location = symbol.index('(')
+		#Split the input string into everything before the opening bracket and everything else
+		function_name = symbol[:bracket_location]
+		arglist_input_string = symbol[bracket_location:]
+	except ValueError:
+		#If there's no brackets, then there's no function signature. This means the passed in symbol is just a type name
+		return symbol, ''
+	
+	#This is a very common signature so we'll make a special case for it. It requires no parsing anyway
+	if arglist_input_string.startswith('()'):
+		if arglist_input_string in ('()', '()=0'):
+			return function_name, arglist_input_string
+		elif arglist_input_string in ('() const ', '() const', '() const =0'):
+			return function_name, '() const'
+	
+	#By now we're left with something like "(blah, blah)", "(blah, blah) const" or "(blah, blah) const =0"
+	try:
+		closing_bracket_location = arglist_input_string.rindex(')')
+		arglist_suffix = arglist_input_string[closing_bracket_location+1:]
+		arglist_input_string = arglist_input_string[:closing_bracket_location+1]
+	except ValueError:
+		#This shouldn't happen.
+		print 'Could not find closing bracket in %s' % arglist_input_string
+		raise
+	
+	try:
+		result = arglist.parseString(arglist_input_string)
+	except ParseException as error:
+		#print symbol
+		#print pe
+		return str(error), None
+	else:
+		#Will be a list or normalised string arguments
+		#e.g. ['OBMol&', 'vector< int >&', 'OBBitVec&', 'OBBitVec&', 'int', 'int']
+		normalised_arg_list = []
+		
+		#Cycle through all the matched arguments
+		for arg in result.arg_list:
+			#Here is where we build up our normalised form of the argument
+			argument_string_list = ['']
+			if arg.qualifier:
+				argument_string_list.append(''.join((arg.qualifier,' ')))
+			argument_string_list.append(arg.input_type)
+		
+			#Functions can have a funny combination of *, & and const between the type and the name so build up a list of theose here:
+			const_pointer_ref_list = []
+			const_pointer_ref_list.append(arg.pointer_or_reference1)
+			if arg.const_pointer_or_reference:
+				const_pointer_ref_list.append(''.join((' ', arg.const_pointer_or_reference, ' ')))
+			# same here
+			const_pointer_ref_list.append(arg.pointer_or_reference2)
+			#And combine them into a single normalised string and add them to the argument list
+			argument_string_list.extend(const_pointer_ref_list)
+		
+			#Finally we join our argument string and add it to our list
+			normalised_arg_list.append(''.join(argument_string_list))
+		
+		#If the function contains a variable number of arguments (int foo, ...) then add them on.
+		if result.var_args:
+			normalised_arg_list.append('...')
+		
+		#Combine all the arguments and put parentheses around it
+		normalised_arg_list_string = ''.join(['(', ', '.join(normalised_arg_list), ')'])
+		
+		#Add a const onto the end
+		if 'const' in arglist_suffix:
+			normalised_arg_list_string += ' const'
+		
+		return function_name, normalised_arg_list_string
+	
+	#TODO Maybe this should raise an exception?
+	return None
+
+def normalise_list(list_of_symbols):
+	#normalise_pool = multiprocessing.Pool(multiprocessing.cpu_count() * 2)
+	#results = normalise_pool.map(normalise, list_of_symbols)
+	#normalise_pool.terminate()
+	results = itertools.imap(normalise, list_of_symbols)
+	return results
diff --git a/sphinxcontrib_doxylink.egg-info/PKG-INFO b/sphinxcontrib_doxylink.egg-info/PKG-INFO
new file mode 100644
index 0000000..f09fc17
--- /dev/null
+++ b/sphinxcontrib_doxylink.egg-info/PKG-INFO
@@ -0,0 +1,50 @@
+Metadata-Version: 1.1
+Name: sphinxcontrib-doxylink
+Version: 1.3
+Summary: Sphinx extension for linking to Doxygen documentation.
+Home-page: http://packages.python.org/sphinxcontrib-doxylink
+Author: Matt Williams
+Author-email: matt at milliams.com
+License: BSD
+Download-URL: http://pypi.python.org/pypi/sphinxcontrib-doxylink
+Description: ######################
+        sphinxcontrib-doxylink
+        ######################
+        
+        A Sphinx_ extension to link to external Doxygen API documentation.
+        
+        Usage
+        -----
+        
+        Please refer to the documentation_ for information on using this extension.
+        
+        Installation
+        ------------
+        
+        This extension can be installed from the Python Package Index::
+        
+           pip install sphinxcontrib-doxylink
+        
+        Alternatively, you can clone the sphinx-contrib_ repository from BitBucket,
+        and install the extension directly from the repository::
+        
+           hg clone http://bitbucket.org/birkenfeld/sphinx-contrib
+           cd sphinx-contrib/doxylink
+           python setup.py install
+        
+        .. _`Sphinx`: http://sphinx.pocoo.org/latest
+        .. _`sphinx-contrib`: http://bitbucket.org/birkenfeld/sphinx-contrib
+        .. _`documentation`: http://packages.python.org/sphinxcontrib-doxylink
+        
+Keywords: sphinx,doxygen,documentation,c++
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Documentation
+Classifier: Topic :: Utilities
diff --git a/sphinxcontrib_doxylink.egg-info/SOURCES.txt b/sphinxcontrib_doxylink.egg-info/SOURCES.txt
new file mode 100644
index 0000000..8e87d5d
--- /dev/null
+++ b/sphinxcontrib_doxylink.egg-info/SOURCES.txt
@@ -0,0 +1,18 @@
+CHANGES.rst
+MANIFEST.in
+README.rst
+setup.cfg
+setup.py
+sphinxcontrib/__init__.py
+sphinxcontrib/doxylink/__init__.py
+sphinxcontrib/doxylink/doxylink.py
+sphinxcontrib/doxylink/parsing.py
+sphinxcontrib_doxylink.egg-info/PKG-INFO
+sphinxcontrib_doxylink.egg-info/SOURCES.txt
+sphinxcontrib_doxylink.egg-info/dependency_links.txt
+sphinxcontrib_doxylink.egg-info/namespace_packages.txt
+sphinxcontrib_doxylink.egg-info/not-zip-safe
+sphinxcontrib_doxylink.egg-info/requires.txt
+sphinxcontrib_doxylink.egg-info/top_level.txt
+tests/__init__.py
+tests/test_parser.py
\ No newline at end of file
diff --git a/sphinxcontrib_doxylink.egg-info/dependency_links.txt b/sphinxcontrib_doxylink.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sphinxcontrib_doxylink.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sphinxcontrib_doxylink.egg-info/namespace_packages.txt b/sphinxcontrib_doxylink.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..aa75604
--- /dev/null
+++ b/sphinxcontrib_doxylink.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+sphinxcontrib
diff --git a/sphinxcontrib_doxylink.egg-info/not-zip-safe b/sphinxcontrib_doxylink.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sphinxcontrib_doxylink.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/sphinxcontrib_doxylink.egg-info/requires.txt b/sphinxcontrib_doxylink.egg-info/requires.txt
new file mode 100644
index 0000000..af02715
--- /dev/null
+++ b/sphinxcontrib_doxylink.egg-info/requires.txt
@@ -0,0 +1,2 @@
+Sphinx>=0.6
+pyparsing
\ No newline at end of file
diff --git a/sphinxcontrib_doxylink.egg-info/top_level.txt b/sphinxcontrib_doxylink.egg-info/top_level.txt
new file mode 100644
index 0000000..5318832
... 148 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/sphinxcontrib-doxylink.git



More information about the Python-modules-commits mailing list