Source code for ccpn.util.SphinxExtensions

"""Custom extensions to Sphinx documentation generator

"""
#=========================================================================================
# Licence, Reference and Credits
#=========================================================================================
__copyright__ = "Copyright (C) CCPN project (http://www.ccpn.ac.uk) 2014 - 2019"
__credits__ = ("Ed Brooksbank, Luca Mureddu, Timothy J Ragan & Geerten W Vuister")
__licence__ = ("CCPN licence. See http://www.ccpn.ac.uk/v3-software/downloads/license")
__reference__ = ("Skinner, S.P., Fogh, R.H., Boucher, W., Ragan, T.J., Mureddu, L.G., & Vuister, G.W.",
                 "CcpNmr AnalysisAssign: a flexible platform for integrated NMR analysis",
                 "J.Biomol.Nmr (2016), 66, 111-124, http://doi.org/10.1007/s10858-016-0060-y")
#=========================================================================================
# Last code modification
#=========================================================================================
__modifiedBy__ = "$modifiedBy: CCPN $"
__dateModified__ = "$dateModified: 2017-07-07 16:32:59 +0100 (Fri, July 07, 2017) $"
__version__ = "$Revision: 3.0.0 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: CCPN $"
__date__ = "$Date: 2017-04-07 10:28:41 +0000 (Fri, April 07, 2017) $"
#=========================================================================================
# Start of code
#=========================================================================================

import re


replaceInDocStrings = (
    ('typing.', ''),
    ('NoneType', 'None')
    )

# # Format for inserting class documentation in wrapper module
# wrappedClassFormat= """
#
# .. _%(moduleName)s-%(className)s-ref:
#
# %(moduleName)s.%(className)s
# %(underline)s
#
# .. autoclass:: %(moduleName)s.%(className)s
# """

# Pattern for replacing (e.g.) 'ccpn._wrapper._Spectrum.Spectrum' with 'ccpn.Spectrum'
# wrappedClassFullName = re.compile(
#   "(ccpn|application)[.](_wrapper[.]_(?P<classname>[a-zA-Z]+)[.])(?P=classname)"
# )
optionalType = re.compile("Union\[(.+), *NoneType\]")

classRepresentation = re.compile("<class *'(.*?)'>")

forwardReference = re.compile("_ForwardRef *\('(.*?)'\)")

# TODO fix this, probably not OK now!
typeUnion = re.compile("Union\[(.*?), *(.*?)\]")

classesHeader = [
    '',
    'Classes :',
    '---------',
    ''
    ]


[docs]def autodoc_process_docstring(): """Return a listener that will modify doc strings from their Python annotations. If *what* is a sequence of strings, only docstrings of a type in *what* will be processed. In the first version it adds type and modifiability annotation to properties""" def process(app, what_, name, obj, options, lines): """ Emitted when autodoc has read and processed a docstring. lines is a list of strings - the lines of the processed docstring - that the event handler can modify in place to change what Sphinx puts into the output. Parameters: app - the Sphinx application object what - the type of the object which the docstring belongs to (one of "module", "class", "exception", "function", "method", "attribute") name - the fully qualified name of the object obj - the object itself options - the options given to the directive: an object with attributes inherited_members,undoc_members, show_inheritance and noindex that are true if the flag option of same name was given to the auto directive lines - the lines of the docstring, see above """ # # TEMP DEBUG # return if isinstance(obj, property): if not (lines and lines[0].startswith('- ')): if hasattr(obj.fget, '__annotations__'): # Necessary because functools.partial objects do note have __annotations__ attribute typ = repr(obj.fget.__annotations__.get('return')) typ = optionalType.sub(r'\g<1>=None', typ) typ = classRepresentation.sub(r'\g<1>', typ) typ = forwardReference.sub(r'\g<1>', typ) typ = typeUnion.sub(r'\g<1> | \g<2>', typ) for fromText, toText in replaceInDocStrings: typ = typ.replace(fromText, toText) ll = [] if typ: ll.append("*%s*" % typ) if bool(obj.fset): # property is modifiable, add it to doc string ll.append('*mutable*') else: ll.append('*immutable*') if ll: #lines[:0] = [', '.join(ll) + '\n', '\n'] lines[:0] = ['\- %s - ' % ', '.join(ll)] # elif what_ == 'module' and hasattr(obj, '_sphinxWrappedClasses'): # # Probably obsolete, but will not be executed if attribute is missing # lines.extend(classesHeader) # for cls in obj._sphinxWrappedClasses: # tag = cls.__name__ # name = obj.__name__ # text = wrappedClassFormat % {'className':tag, 'moduleName':name, # 'underline':'^'*(len(tag)+len(name)+1)} # lines.extend(text.splitlines()) # # # Change wrapped class names to shorter form, # for ii,line in enumerate(lines): # # removing '_wrapper._ClassName' # lines[ii] = wrappedClassFullName.sub(r'\g<1>.\g<3>',line) # lines[ii] = classRepresentation.sub(r'\g<1>', lines[ii]) # return process
[docs]def autodoc_process_signature(): """Return a listener that will modify doc strings from their Python annotations. If *what* is a sequence of strings, only docstrings of a type in *what* will be processed. In the first version it adds type and modifiability annotation to properties""" def process(app, what, name, obj, options, signature, return_annotation): """Emitted when autodoc has formatted a signature for an object. The event handler can return a new tuple (signature, return_annotation) to change what Sphinx puts into the output. Parameters: -app – the Sphinx application object -what – the type of the object which the docstring belongs to (one of "module", "class", "exception", "function", "method", "attribute") -name – the fully qualified name of the object -obj – the object itself -options – the options given to the directive: an object with attributes inherited_members, undoc_members, show_inheritance and noindex that are true if the flag option of same name was given to the auto directive -signature – function signature, as a string of the form "(parameter_1, parameter_2)", or None if introspection didn’t succeed and signature wasn’t specified in the directive. -return_annotation – function return annotation as a string of the form " -> annotation", or None if there is no return annotation """ if signature: # signature = wrappedClassFullName.sub(r'\g<1>.\g<3>', signature) signature = classRepresentation.sub(r'\g<1>', signature) signature = forwardReference.sub(r'\g<1>', signature) signature = optionalType.sub(r'\g<1>=None', signature) signature = typeUnion.sub(r'\g<1> | \g<2>', signature) # signature = typeUnion.sub(r'\g<1>|\g<2>', signature) for fromText, toText in replaceInDocStrings: signature = signature.replace(fromText, toText) if return_annotation: # return_annotation = wrappedClassFullName.sub(r'\g<1>.\g<3>', return_annotation) return_annotation = classRepresentation.sub(r'\g<1>', return_annotation) for fromText, toText in replaceInDocStrings: return_annotation = return_annotation.replace(fromText, toText) return (signature, return_annotation) # return process