Source code for mmfutils.interface

"""Stand-in for zope.interface if it is not available."""

import logging
import warnings

__all__ = [
    "Interface",
    "Attribute",
    "implementer",
    "verifyObject",
    "verifyClass",
    "describe_interface",
]

try:
    import zope.interface
    from zope.interface import Interface, Attribute, implementer
    from zope.interface.verify import verifyObject, verifyClass
except ImportError:  # pragma: nocover
    zope = None
    warnings.warn("Could not import zope.interface... using dummy stand-ins")

    Interface = object

    class Attribute(object):
        """Dummy"""

        def __init__(self, __name__, __doc__=""):
            pass

    def implementer(*interfaces):
        """Dummy"""
        return lambda cls: cls

    def verifyObject(iface, candidate):
        """Dummy"""

    def verifyClass(iface, candidate):
        """Dummy"""


if zope:
    # Provides a real "asStructuredText" replacement that produces
    # reStructuredText so I can use it in documentation like README.rst etc.

    import zope.interface.document

    if hasattr(zope.interface.document, "asReStructuredText"):
        del zope.interface.document.asReStructuredText

    if not hasattr(zope.interface.document, "asReStructuredText"):
        from zope.interface.document import _justify_and_indent, _trim_doc_string

        def _justify_and_indent_and_trim_doc_string(string, level=0, munge=0):

            # Hack to replace titles used by numpydoc...
            for indent in [" " * 4, " " * 8, " " * 12]:
                for title in ["Parameters", "Arguments"]:
                    src = "\n".join([indent + title, indent + "-" * len(title)])
                    repl = "\n".join([indent[:-2] + title + ":", ""])
                    string = string.replace(src, repl)

            return _justify_and_indent(
                _trim_doc_string(string), level=level, munge=munge
            )

        def asReStructuredText(I, munge=0):
            """Output structured text format.  Note, this will whack any existing
            'structured' format of the text.
            """

            def inline_literal(s):
                return "``%s``" % (s,)

            r = [inline_literal(I.getName())]
            # r[0] = "\n".join([r[0], "="*len(r[-1])])

            outp = r.append
            level = 1

            if I.getDoc():
                outp(_justify_and_indent_and_trim_doc_string(I.getDoc(), level))

            bases = [
                base for base in I.__bases__ if base is not zope.interface.Interface
            ]
            if bases:
                outp(_justify_and_indent("This interface extends:", level, munge))
                level += 1
                for b in bases:
                    item = "o %s" % inline_literal(b.getName())
                    outp(_justify_and_indent_and_trim_doc_string(item, level, munge))
                level -= 1

            namesAndDescriptions = sorted(I.namesAndDescriptions())

            outp(_justify_and_indent("Attributes:", level, munge))
            level += 1
            for name, desc in namesAndDescriptions:
                if not hasattr(desc, "getSignatureString"):  # ugh...
                    item = "%s -- %s" % (
                        inline_literal(desc.getName()),
                        desc.getDoc() or "no documentation",
                    )
                    outp(_justify_and_indent_and_trim_doc_string(item, level, munge))
            level -= 1

            outp(_justify_and_indent("Methods:", level, munge))
            level += 1
            for name, desc in namesAndDescriptions:
                if hasattr(desc, "getSignatureString"):  # ugh...
                    _call = "%s%s" % (desc.getName(), desc.getSignatureString())
                    item = "%s -- %s" % (
                        inline_literal(_call),
                        desc.getDoc() or "no documentation",
                    )
                    outp(_justify_and_indent_and_trim_doc_string(item, level, munge))

            return "\n\n".join(r) + "\n\n"

        logging.info(
            "Patching zope.interface.document.asReStructuredText to format code"
        )
        zope.interface.document.asReStructuredText = asReStructuredText


[docs] def describe_interface(interface, format="ipython"): """Return an HTML object for Jupyter notebooks that describes the interface. Parameters ---------- interface : Interface Interface to extract documentation from. format : 'rst', 'html', 'ipython' Return format. 'rst' is raw RestructuredText, 'html' is packaged as HTML, and 'ipython' is packaged as an IPython.display.HTML() object suitable for Jupyter notebooks. Example ------- >>> class IExample(Interface): ... x = Attribute("Floating point number") ... def two(): ... "Return two" >>> print(describe_interface(IExample, format='rst').strip()) ``IExample`` <BLANKLINE> Attributes: <BLANKLINE> ``x`` -- Floating point number <BLANKLINE> Methods: <BLANKLINE> ``two()`` -- Return two You can also get this wrapped as HTML: >>> print(describe_interface(IExample, format='html').strip()) <!DOCTYPE html ... <p><tt class="docutils literal">IExample</tt></p> <blockquote> <p>Attributes:</p> <blockquote> <tt class="docutils literal">x</tt> -- Floating point number</blockquote> <p>Methods:</p> <blockquote> <tt class="docutils literal">two()</tt> -- Return two</blockquote> </blockquote> </div> In a Jupyter notebook, this will properly display: >>> describe_interface(IExample) <IPython.core.display.HTML object> Other formats are not yet supported: >>> describe_interface(IExample, format='WYSIWYG') Traceback (most recent call last): ... NotImplementedError: format WYSIWYG not supported """ # Chunk of code to display interfaces. # See: http://code.activestate.com/recipes/ # 193890-using-rest-restructuredtext-to-create-html-snippet/ # Local imports since we do not depend on IPython from docutils import core from docutils.writers.html4css1 import Writer, HTMLTranslator import zope.interface.document class NoHeaderHTMLTranslator(HTMLTranslator): def __init__(self, document): HTMLTranslator.__init__(self, document) self.head_prefix = [""] * 5 self.body_prefix = [] self.body_suffix = [] self.stylesheet = [] writer = Writer() writer.translator_class = NoHeaderHTMLTranslator rst = zope.interface.document.asReStructuredText(interface) if format.lower() == "rst": return rst html = core.publish_string(rst, writer=writer).decode() if format.lower() == "html": return html if format.lower() in ["ipython", "jupyter"]: import IPython.display return IPython.display.HTML(html) raise NotImplementedError("format {} not supported".format(format))