pax_global_header00006660000000000000000000000064132130224240014503gustar00rootroot0000000000000052 comment=4760b9a278d2b9487d96dad7b18b1e1140b6f1a2 doxylink-1.5/000077500000000000000000000000001321302242400132115ustar00rootroot00000000000000doxylink-1.5/.gitignore000066400000000000000000000001121321302242400151730ustar00rootroot00000000000000.tox/ parsing_profile *.pyc sphinxcontrib_doxylink.egg-info/ dist/ build/ doxylink-1.5/.travis.yml000066400000000000000000000017401321302242400153240ustar00rootroot00000000000000--- language: python python: - '3.4' - '3.5' - '3.6' before_install: - sudo apt-get -qq update - sudo apt-get install -y doxygen install: - pip install tox script: - tox deploy: provider: pypi user: milliams password: secure: P98BvIsOAHKwxlGbUY/mj3ooTX+agaaOVMZvwXbPzlZYJWMYr1eKrIr1urearpCdHSmd/w1VF39bMliYUhCckZeEuNQsvpTzoesjiGcH8AF9TBumiNrpry9Z9H1PXxodlhaS3cBbdyfm+xGU88pvqqaHU7sMULJs6PXmg4YU+d4qc5EzHdUmo9ZEBbHE2e3PniJ8h5HN13fx/nXQ5gtcUvLG34zTz9fpoHUGp4+kEhjlEDwUuD5sSbiIdoADhWExAf+67WpGCjZozwkiQqnyhsylbmtkGe7XlU6K55Bj/why4eD0Sq4+eKDyPsze/1gP5oyzjhPcwFoaNSxeR1POIKkMxD2d4Z8Cd6P8nymonoOdm4rwH4yDBZc8jkEadRhUNtUIus6q9oym+oJkiunZUNd85192S9WbCQ7D1TMqTrboDJ34F49C3RPf6MuvmlG79vP5Z+KdymsjiJAY9FND7ZvWfWY1+6Ggi/VN+K23bvBh+pE2rr2Cm/LH3Y7h4OekLT2ulAgGrFwq+sD4LCRqwTWfPQwyGq7wON490BczGt4SHHTV/re4RAIgz7vR52xdQRzGAgl20joZulhoTjMqKXoGQZ3sdecfe7cVvIzhWY1ficEHV0YTl76FIY25ieLn66/3nnDuPoAnhRSjGWdBybJ0nXysW/SeGtIghBdDL7g= distributions: sdist bdist_wheel on: tags: true doxylink-1.5/CHANGES.rst000066400000000000000000000035161321302242400150200ustar00rootroot000000000000001.5 (Dec 9, 2017) ==================== - Fix #6: convert dict_values to list before indexing [Stein Heselmans] - fix parsing for C++11 functions with specifiers () final, () override or () = default [Elco Jacobs] 1.4 (Dec 4, 2017) ==================== - Add bug fix from Stein Heselmans to force the qualifier to be a single string - Remove Python 2 compatibility 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 doxylink-1.5/LICENSE000066400000000000000000000024461321302242400142240ustar00rootroot00000000000000BSD 2-Clause License Copyright (c) 2017, Matt Williams All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. doxylink-1.5/MANIFEST.in000066400000000000000000000000671321302242400147520ustar00rootroot00000000000000include README.rst include LICENSE include CHANGES.rst doxylink-1.5/README.rst000066400000000000000000000007341321302242400147040ustar00rootroot00000000000000###################### 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 .. _`Sphinx`: http://www.sphinx-doc.org .. _`documentation`: http://sphinxcontrib-doxylink.readthedocs.io/en/stable/ doxylink-1.5/doc/000077500000000000000000000000001321302242400137565ustar00rootroot00000000000000doxylink-1.5/doc/api.rst000066400000000000000000000002071321302242400152600ustar00rootroot00000000000000Function reference ================== .. automodule:: sphinxcontrib.doxylink.doxylink .. automodule:: sphinxcontrib.doxylink.parsing doxylink-1.5/doc/conf.py000066400000000000000000000163411321302242400152620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # sphinxcontrib-doxylink documentation build configuration file, created by # sphinx-quickstart on Thu Aug 12 16:41:53 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.todo', 'sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. #templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'sphinxcontrib-doxylink' copyright = u'2017, Matt Williams' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.5' # The full version, including alpha/beta/rc tags. release = '1.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinxdoc' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'sphinxcontrib-doxylinkdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'sphinxcontrib-doxylink.tex', u'sphinxcontrib-doxylink Documentation', u'Matt Williams', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'sphinxcontrib-doxylink', u'sphinxcontrib-doxylink Documentation', [u'Matt Williams'], 1) ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} autodoc_default_flags = ['members'] def setup(app): app.add_description_unit('confval', 'confval', 'pair: %s; configuration value') doxylink-1.5/doc/index.rst000066400000000000000000000140501321302242400156170ustar00rootroot00000000000000Welcome to sphinxcontrib-doxylink's documentation ================================================= Doxylink is a Sphinx extension to link to external Doxygen API documentation. It allows you to specify C++ symbols and it will convert them into links to the HTML page of their Doxygen documentation. .. toctree:: :hidden: api Usage ----- You use Doxylink like: .. code-block:: rst :polyvox:`PolyVox::Volume` You use :qtogre:`QtOgre::Log` to log events for the user. :polyvox:`PolyVox::Array::operator[]` Where :rst:role:`polyvox` and :rst:role:`qtogre` roles are defined by the :confval:`doxylink` configuration value. Like any interpreted text role in Sphinx, if you want to display different text to what you searched for, you can include some angle brackets ``<...>``. In this case, the text inside the angle brackets will be used to match up with Doxygen and the part in front will be displayed to the user: .. code-block:: rst :polyvox:`Array `. :polyvox:`tidyUpMemory ` will reduce memory usage. .. note:: In C++, it is common that classes and functions will be templated and so will have angle brackets themselves. For example, the C++ class: .. code-block:: c++ PolyVox::Array<0,ElementType> would be naively linked to with Doxylink with: .. code-block:: rst :polyvox:`PolyVox::Array<0,ElementType>` but that would result in Sphinx parsing it as you wanting to search for ``0,ElementType`` and display ``PolyVox::Array`` as the text to the user. To avoid this misparsing you must escape the opening ``<`` by prepending it with a ``\``: .. code-block:: rst :polyvox:`PolyVox::Array\<0,ElementType>` If you want to use templated symbols inside the angle brackets like: .. code-block:: rst :polyvox:`Array >` then that will work without having to escape anything. Namespaces, classes etc. ^^^^^^^^^^^^^^^^^^^^^^^^ For non-functions (i.e. namespaces, classes, enums, variables) you simply pass in the name of the symbol. If you pass in a partial symbol, e.g. ```Volume``` when you have a symbol in C++ called ``PolyVox::Utils::Volume`` then it would be able to match it as long as there is no ambiguity (e.g. with another symbol called ``PolyVox::Old::Volume``). If there is ambiguity then simply enter the fully qualified name like: .. code-block:: rst :polyvox:`PolyVox::Utils::Volume` or :polyvox:`PolyVox::Utils::Volume ` Functions ^^^^^^^^^ For functions there is more to be considered due to C++'s ability to overload a function with multiple signatures. If you want to link to a function and either that function is not overloaded or you don't care which version of it you link to, you can simply give the name of the function with no parentheses: .. code-block:: rst :polyvox:`PolyVox::Volume::getVoxelAt` Depending on whether you have set the :confval:`add_function_parentheses` configuration value, Doxylink will automatically add on parentheses to that it will be printed as ``PolyVox::Volume::getVoxelAt()``. If you want to link to a specific version of the function, you must provide the correct signature. For a requested signature to match on in the tag file, it must exactly match a number of features: - The types must be correct, including all qualifiers, e.g. ``unsigned const int`` - You must include any pointer or reference labeling, e.g. ``char*``, ``const QString &`` or ``int **`` - You must include whether the function is const, e.g. ``getx() const`` The argument list is not whitespace sensitive (any more than C++ is anyway) and the names of the arguments and their default values are ignored so the following are all considered equivalent: .. code-block:: rst :myapi:`foo( const QString & text, bool recalc, bool redraw = true )` :myapi:`foo(const QString &foo, bool recalc, bool redraw = true )` :myapi:`foo( const QString& text, bool recalc, bool redraw )` :myapi:`foo(const QString&,bool,bool)` When making a match, Doxylink splits up the requested string into the function symbol and the argument list. If it finds a match for the function symbol part but not for the argument list then it will return a link to any one of the function versions. Files ^^^^^ You can also link directly to a header or source file by giving the name of the file: .. code-block:: rst :myapi:`main.cpp` :myapi:`MainWindow.h` Setup ----- When generating your Doxygen documentation, you need to instruct it to create a 'tag' file. This is an XML file which contains the mapping between symbols and HTML files. To make Doxygen create this file ensure that you have a line like: .. code-block:: ini GENERATE_TAGFILE = PolyVox.tag in your ``Doxyfile``. Configuration values -------------------- .. confval:: doxylink The environment is set up with a dictionary mapping the interpereted text role to a tuple of tag file and prefix: .. code-block:: python doxylink = { 'polyvox' : ('/home/matt/PolyVox.tag', '/home/matt/PolyVox/html/'), 'qtogre' : ('/home/matt/QtOgre.tag', '/home/matt/QtOgre/html/'), } .. confval:: add_function_parentheses A boolean that decides whether parentheses are appended to function and method role text. Default is ``True``. Bug reports ----------- If you find any errors, bugs, crashes etc. then please raise an issue `on GitHub `_. If there is a crash please include the backtrace and log returned by Sphinx. If you have a bug, particularly with Doxylink not being able to parse a function, please send the tag file so tat I can reproduce and fix it. :requires: Python 3.4 .. todo:: Add unit tests for things in doxylink.py Parallelise the calls to normalise() in parse_tag_file() using multiprocessing. Set up a pool of processes and pass in a queue of strings. Non-function calls will be done in the same way as present. For function calls, build up the information into a list of tuples, convert it into an appropriate Queue format and run it. Maybe even a multiprocessing.Pool.map could do the job. :copyright: Copyright 2017 by Matt Williams :license: BSD, see LICENSE for details. doxylink-1.5/examples/000077500000000000000000000000001321302242400150275ustar00rootroot00000000000000doxylink-1.5/examples/Doxyfile000066400000000000000000000002261321302242400165350ustar00rootroot00000000000000FILE_PATTERNS = *.h EXTRACT_ALL = YES GENERATE_TAGFILE = my_lib.tag GENERATE_LATEX = NO GENERATE_HTML = NO doxylink-1.5/examples/conf.py000066400000000000000000000004101321302242400163210ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import sys sys.path.insert(0, os.path.abspath('..')) extensions = ['sphinxcontrib.doxylink'] doxylink = { 'my_lib': (os.path.abspath('./my_lib.tag'), 'https://examples.com/'), } master_doc = 'index' doxylink-1.5/examples/index.rst000066400000000000000000000000631321302242400166670ustar00rootroot00000000000000:my_lib:`my_func` :my_lib:`my_namespace::MyClass` doxylink-1.5/examples/my_lib.h000066400000000000000000000005501321302242400164530ustar00rootroot00000000000000#include /** * Example documented function */ int my_func(); int my_func(int foo); int my_func(float); int my_func(std::string a, int b); int my_func(int b, std::string a); namespace my_namespace { class MyClass { MyClass(); }; } /// This class has the same name but is a different class class MyClass { MyClass(); }; doxylink-1.5/release_checklist.rst000066400000000000000000000005511321302242400174150ustar00rootroot00000000000000Release checklist ================= - Update version number in `doc/conf.py` and `setup.py`. - Update date in CHANGES.rst and make sure new features and bug fixes are mentioned. - Commit sources with updated version numbers `git commit`. - Tag new version `git tag 1.1` (or whatever the version is). - Upload new version to PyPI `python setup.py sdist upload` doxylink-1.5/requirements.txt000066400000000000000000000000251321302242400164720ustar00rootroot00000000000000Sphinx>=0.6 pyparsingdoxylink-1.5/setup.cfg000066400000000000000000000001231321302242400150260ustar00rootroot00000000000000[egg_info] #tag_build = dev #tag_date = true [aliases] release = egg_info -RDb '' doxylink-1.5/setup.py000066400000000000000000000022731321302242400147270ustar00rootroot00000000000000# -*- 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.5', url='http://sphinxcontrib-doxylink.readthedocs.io/en/stable/', download_url='http://pypi.python.org/pypi/sphinxcontrib-doxylink', license='BSD', author='Matt Williams', author_email='matt@milliams.com', description='Sphinx extension for linking to Doxygen documentation.', long_description=long_desc, keywords=['sphinx', 'doxygen', 'documentation', 'c++'], 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 :: 3', 'Topic :: Documentation', 'Topic :: Utilities', ], packages=find_packages(exclude=['doc', 'examples', 'html', 'tests']), install_requires=requires, python_requires='~=3.4', namespace_packages=['sphinxcontrib'], ) doxylink-1.5/sphinxcontrib/000077500000000000000000000000001321302242400161035ustar00rootroot00000000000000doxylink-1.5/sphinxcontrib/__init__.py000066400000000000000000000005551321302242400202210ustar00rootroot00000000000000# -*- 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__) doxylink-1.5/sphinxcontrib/doxylink/000077500000000000000000000000001321302242400177445ustar00rootroot00000000000000doxylink-1.5/sphinxcontrib/doxylink/__init__.py000066400000000000000000000000341321302242400220520ustar00rootroot00000000000000from .doxylink import setup doxylink-1.5/sphinxcontrib/doxylink/doxylink.py000066400000000000000000000465031321302242400221670ustar00rootroot00000000000000# -*- coding: utf-8 -*- import os import xml.etree.ElementTree as ET import urllib 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 not in {'namespace', 'class', 'struct', '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 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 = 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 element, use the parent compounds 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 = compound_name + '::' + member.findtext('name') member_kind = member.get('kind') arglist_text = member.findtext('./arglist') # If it has an then we assume it's a function. Empty returns '', not None. Things like typedefs and enums can have empty arglists if arglist_text and member_kind not in {'variable', 'typedef', '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 f in function_list: member_symbol = f[0] kind = f[2] anchor_link = f[3] try: normalised_tuple = normalise(f[0] + f[1]) except ParseException as e: print('Skipping %s %s%s. Error reported from parser was: %s' % (f[2], f[0], f[1], e)) else: normalised_arglist = normalised_tuple[1] 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}} return mapping def find_url2(mapping, symbol: str) -> dict: """ 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) == 1: return return_from_mapping(list(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) == 1: return list(classes_list.values())[0] #print("Still", len(classes_list), 'possible matches') # If we exhausted the list by requiring classes, use the list from before the filter. if not classes_list: classes_list = piecewise_list no_templates_list = find_url_remove_templates(classes_list, symbol) if len(no_templates_list) == 1: return return_from_mapping(list(no_templates_list.values())[0], normalised_arglist) #print("Still", len(no_templates_list), 'possible matches') # If not found by now, return the shortest match, assuming that's the most specific if no_templates_list: # TODO return a warning here? shortest_match = min(no_templates_list.keys(), key=len) return return_from_mapping(no_templates_list[shortest_match], normalised_arglist) else: LookupError('Could not find a match') def return_from_mapping(mapping_entry: dict, normalised_arglist: str='') -> dict: """ 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 ``mapping_entry['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 ``mapping_entry['arglist']``. This parameter is ignored if ``mapping_entry['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: try: filename = mapping_entry['arglist'][normalised_arglist] except KeyError: raise LookupError('Argument list match not found') else: # Otherwise just return the first entry (if they don't care they get whatever comes first) filename = list(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: dict, symbol: str) -> dict: """ 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 FileNotFoundError: tag_file = None app.warn(standout('Could not find 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 ` 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 urllib.parse.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.items(): 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) doxylink-1.5/sphinxcontrib/doxylink/parsing.py000066400000000000000000000152641321302242400217710ustar00rootroot00000000000000from typing import Tuple from pyparsing import Word, Literal, nums, alphanums, OneOrMore, Optional,\ SkipTo, ParseException, Group, Combine, delimitedList, quotedString,\ nestedExpr, ParseResults, oneOf, ungroup # 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')) qualifier = ungroup(qualifier.addParseAction(' '.join)) def turn_parseresults_to_list(s, loc, toks): return ParseResults(normalise_templates(toks[0].asList())) def normalise_templates(toks): s_list = ['<'] s_list_append = s_list.append # lookup append func once, instead of many times for tok in toks: if isinstance(tok, str): # 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: str) -> Tuple[str, str]: """ Takes a c++ symbol or function 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('()'): arglist_input_string_no_spaces = arglist_input_string.replace(' override', '').replace(' final', '').replace(' ', '') if arglist_input_string_no_spaces in ('()', '()=0', '()=default'): return function_name, '()' elif arglist_input_string_no_spaces in ('()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: raise 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 doxylink-1.5/test-requirements.txt000066400000000000000000000000061321302242400174460ustar00rootroot00000000000000pytestdoxylink-1.5/tests/000077500000000000000000000000001321302242400143535ustar00rootroot00000000000000doxylink-1.5/tests/__init__.py000066400000000000000000000000001321302242400164520ustar00rootroot00000000000000doxylink-1.5/tests/test_doxylink.py000066400000000000000000000062131321302242400176270ustar00rootroot00000000000000import datetime import glob import os import os.path import subprocess import xml.etree.ElementTree as ET import pytest from sphinxcontrib.doxylink import doxylink @pytest.fixture def examples_tag_file(): basedir = os.path.join(os.path.dirname(__file__), '../examples') extensions = None tagfile = None with open(os.path.join(basedir, 'Doxyfile')) as doxyfile: for line in doxyfile: if line.startswith('FILE_PATTERNS'): extensions = line.split('=')[1].strip().split(',') elif line.startswith('GENERATE_TAGFILE'): tagfile_name = line.split('=')[1].strip() tagfile = os.path.join(basedir, tagfile_name) if None in [extensions, tagfile]: raise RuntimeError('Could not find FILE_PATTERNS or GENERATE_TAGFILE in Doxyfile') matches = [] for extension in extensions: m = glob.glob(os.path.join(basedir, extension)) matches.extend(m) if not os.path.isfile(tagfile): recreate = True else: latest_file_changed = max([datetime.datetime.fromtimestamp(os.stat(f).st_mtime) for f in matches]) tagfile_changed = datetime.datetime.fromtimestamp(os.stat(tagfile).st_mtime) if latest_file_changed > tagfile_changed: recreate = True else: recreate = False if recreate: subprocess.call('doxygen', cwd=basedir) return tagfile def test_parse_tag_file(examples_tag_file): tag_file = ET.parse(examples_tag_file) mapping = doxylink.parse_tag_file(tag_file) from pprint import pprint pprint(mapping) assert 'my_lib.h' in mapping assert 'my_lib.h::my_func' in mapping assert '()' in mapping['my_lib.h::my_func']['arglist'] assert len(mapping['my_lib.h::my_func']['arglist']) == 5 assert 'my_namespace' in mapping assert 'my_namespace::MyClass' in mapping def test_find_url_piecewise(examples_tag_file): tag_file = ET.parse(examples_tag_file) mapping = doxylink.parse_tag_file(tag_file) assert 'my_namespace' in doxylink.find_url_piecewise(mapping, 'my_namespace') assert 'my_namespace::MyClass' in doxylink.find_url_piecewise(mapping, 'my_namespace::MyClass') assert 'my_namespace::MyClass' in doxylink.find_url_piecewise(mapping, 'MyClass') assert 'MyClass' in doxylink.find_url_piecewise(mapping, 'MyClass') assert len(doxylink.find_url_piecewise(mapping, 'MyClass')) == 2 def test_return_from_mapping(examples_tag_file): tag_file = ET.parse(examples_tag_file) mapping = doxylink.parse_tag_file(tag_file) mapping_entry = mapping['my_lib.h::my_func'] assert doxylink.return_from_mapping(mapping_entry) assert doxylink.return_from_mapping(mapping_entry, '()') assert doxylink.return_from_mapping(mapping_entry, '(int)') assert doxylink.return_from_mapping(mapping_entry, '(float)') with pytest.raises(LookupError): print(doxylink.return_from_mapping(mapping_entry, '(double)')) def test_find_url2(examples_tag_file): tag_file = ET.parse(examples_tag_file) mapping = doxylink.parse_tag_file(tag_file) assert doxylink.find_url2(mapping, 'MyClass')['file'] == 'classMyClass.html' doxylink-1.5/tests/test_parser.py000066400000000000000000000143301321302242400172610ustar00rootroot00000000000000import pytest from sphinxcontrib.doxylink import parsing import pstats # List of tuples of: (input, correct output) # Input is a string, output is a tuple. arglists = [ ('( QUrl source )', ('', '(QUrl)')), ('( QUrl * source )', ('', '(QUrl*)')), ('( QUrl ** source )', ('', '(QUrl**)')), ('( const QUrl ** source )', ('', '(const QUrl**)')), ('( const QUrl source )', ('', '(const QUrl)')), ('( QUrl & source )', ('', '(QUrl&)')), ('( const QUrl & source )', ('', '(const QUrl&)')), ('( const QUrl * source )', ('', '(const QUrl*)')), ('( const QUrl const * source )', ('', '(const QUrl const *)')), ('( const QByteArray & data, const QUrl & documentUri = QUrl() )', ('', '(const QByteArray&, const QUrl&)')), ('(void)', ('', '(void)')), ('(uint32_t uNoOfBlocksToProcess=(std::numeric_limits< uint32_t >::max)())', ('', '(uint32_t)')), ('( QWidget * parent = 0, const char * name = 0, Qt::WindowFlags f = 0 )', ('', '(QWidget*, const char*, Qt::WindowFlags)')), ('()', ('', '()')), ('( int index = 0 )', ('', '(int)')), ('( bool ascending = true )', ('', '(bool)')), ('( const QIcon & icon, const QString & label, int width = -1 )', ('', '(const QIcon&, const QString&, int)')), ('( QWidget * parent = 0, const char * name = 0, Qt::WindowFlags f = Qt::WType_TopLevel )', ('', '(QWidget*, const char*, Qt::WindowFlags)')), ('( QMutex * mutex, unsigned long time = ULONG_MAX )', ('', '(QMutex*, unsigned long)')), ('(const VolumeSampler< VoxelType > &volIter)', ('', '(const VolumeSampler< VoxelType >&)')), ('(VolumeSampler< VoxelType > &volIter)', ('', '(VolumeSampler< VoxelType >&)')), ('(const VolumeSampler< VoxelType > &volIter)', ('', '(const VolumeSampler< VoxelType >&)')), ('(const uint32_t(&pDimensions)[noOfDims])', ('', '(const uint32_t)')), ('(Array< noOfDims, ElementType > &rhs)', ('', '(Array< noOfDims, ElementType >&)')), ('( const QString & path, const QString & nameFilter, SortFlags sort = SortFlags( Name | IgnoreCase ))', ('', '(const QString&, const QString&, SortFlags)')), ('( GLuint texture_id )', ('', '(GLuint)')), ('( Q3ValueList::size_type i )', ('', '(Q3ValueList< T >::size_type)')), ('(STLAllocator< T, P > const *, STLAllocator< T2, P > const &)', ('', '(STLAllocator< T, P > const *, STLAllocator< T2, P > const &)')), ('(STLAllocator< T, P > const &, STLAllocator< T2, P > const &)', ('', '(STLAllocator< T, P > const &, STLAllocator< T2, P > const &)')), ('(const String &errorMessage, String logName="")', ('', '(const String&, String)')), ('(PixelFormat = PF_BYTE)', ('', '(PixelFormat)')), ('(const SharedPtr< ControllerValue< T > > &src)', ('', '(const SharedPtr< ControllerValue < T > >&)')), ('(typename T::iterator start, typename T::iterator last)', ('', '(typename T::iterator, typename T::iterator)')), ('(const Matrix4 *const *blendMatrices)', ('', '(const Matrix4* const *)')), ('(const Matrix4 *const *blendMatrices) const =0', ('', '(const Matrix4* const *) const')), ] varargs = [ ('(int nb=0,...)', ('', '(int, ...)')), ] multiple_qualifiers = [ ('( QReadWriteLock * readWriteLock, unsigned long time = ULONG_MAX )', ('', '(QReadWriteLock*, unsigned long)')), ] numbers_for_defaults = [ ('( const QPixmap & pixmap, const QString & text, int index = -1 )', ('', '(const QPixmap&, const QString&, int)')), ('( const char ** strings, int numStrings = -1, int index = -1 )', ('', '(const char**, int, int)')), ('( const QStringList & list, int index = -1 )', ('', '(const QStringList&, int)')), ] flags_in_defaults = [ ('( const QString & text, int column, ComparisonFlags compare = ExactMatch | Qt::CaseSensitive )', ('', '(const QString&, int, ComparisonFlags)')), ] functions = [ ('PolyVox::Volume::getDepth', ('PolyVox::Volume::getDepth', '')), ('PolyVox::Volume::getDepth()', ('PolyVox::Volume::getDepth', '()')), ('Volume::getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tDefault=VoxelType()) const', ('Volume::getVoxelAt', '(uint16_t, uint16_t, uint16_t, VoxelType) const')), ('PolyVox::Array::operator[]', ('PolyVox::Array::operator[]', '')), ('operator[]', ('operator[]', '')), ] multiple_namespaces = [ ('PolyVox::Test::TestFunction(int foo)', ('PolyVox::Test::TestFunction', '(int)')), ] @pytest.mark.parametrize('test_input, expected', functions) def test_split_function(test_input, expected): assert parsing.normalise(test_input) == expected @pytest.mark.parametrize('test_input, expected', arglists) def test_normalise_arglist(test_input, expected): assert parsing.normalise(test_input) == expected @pytest.mark.parametrize('test_input, expected', varargs) def test_varargs(test_input, expected): assert parsing.normalise(test_input) == expected @pytest.mark.parametrize('test_input, expected', multiple_qualifiers) def test_multiple_qualifiers(test_input, expected): assert parsing.normalise(test_input) == expected @pytest.mark.parametrize('test_input, expected', numbers_for_defaults) def test_numbers_for_defaults(test_input, expected): assert parsing.normalise(test_input) == expected @pytest.mark.parametrize('test_input, expected', flags_in_defaults) def test_flags_in_defaults(test_input, expected): assert parsing.normalise(test_input) == expected @pytest.mark.parametrize('test_input, expected', multiple_namespaces) def test_multiple_namespaces(test_input, expected): assert parsing.normalise(test_input) == expected def test_false_signatures(): # This is an invalid function definition. Caused by a bug in Doxygen. See openbabel/src/ops.cpp : theOpCenter("center") from pyparsing import ParseException with pytest.raises(ParseException): parsing.normalise('("center")') if __name__ == "__main__": try: import cProfile as profile except ImportError: import profile all_tests = arglists + varargs + multiple_qualifiers + functions + numbers_for_defaults + flags_in_defaults all_tests += all_tests + all_tests + all_tests + all_tests profile.runctx("for arglist in all_tests: parsing.normalise(arglist[0])", globals(), locals(), filename='parsing_profile') p = pstats.Stats('parsing_profile') p.strip_dirs().sort_stats('time', 'cumtime').print_stats(40) doxylink-1.5/tox.ini000066400000000000000000000010721321302242400145240ustar00rootroot00000000000000[tox] envlist=benchmark,test,examples,doc [testenv:benchmark] deps= py pytest commands= python tests/test_parser.py [testenv:examples] deps= sphinx changedir=examples whitelist_externals=doxygen commands= doxygen Doxyfile sphinx-build -W -b html . {envtmpdir}/examples/_build [testenv:test] deps= -r{toxinidir}/test-requirements.txt commands= pytest [testenv:doc] deps= sphinx commands= sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees doc {envtmpdir}/linkcheck sphinx-build -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html