[MarryDoc] Usage

Inherit DocString

The inherit() decorator copies a docstring from a specified object and attaches it to the function or method it decorates. In the following example, standard library b2a_hex() function serves as the docstring source:

inherit_example.py.

import binascii
import marrydoc

@marrydoc.inherit(binascii.b2a_hex)
def b2a_hex(data, sep=''):
    mashed = binascii.b2a_hex(data)
    return sep.join(mashed[i:i+2] for i in range(0, len(mashed), 2))

hexlify = b2a_hex

The inherit() decorator guarentees that the docstrings are always synchronized and does not require maintenance of the module when the original docstring changes:

>>> import binascii
>>> import inherit_example
>>>
>>> print binascii.hexlify.__doc__
b2a_hex(data) -> s; Hexadecimal representation of binary data.

This function is also available as "hexlify()".
>>>
>>> print inherit_example.hexlify.__doc__
b2a_hex(data) -> s; Hexadecimal representation of binary data.

This function is also available as "hexlify()".
>>>
>>> binascii.hexlify.__doc__ == inherit_example.hexlify.__doc__
True

Maintain DocString Copies

The shortcoming of the inherit() decorator is that the copied docstring does not appear in the module and makes module maintenance more difficult. The copied_from() decorator defines a one to one relationship between the source of the docstring and the docstring of the function it decorates:

copied_from_example.py.

import binascii
import marrydoc


@marrydoc.copied_from(binascii.b2a_hex)
def b2a_hex(data, sep=''):
    """b2a_hex(data) -> s; Hexadecimal representation of binary data.
    
    This function is also available as "hexlify()"."""
    mashed = binascii.b2a_hex(data)
    return sep.join(mashed[i:i+2] for i in range(0, len(mashed), 2))

hexlify = b2a_hex

The defined relationship allows the Command Line Tool to check the module’s docstring:

$ python -m marrydoc copied_from_example.py
copied_from_example.py ... OK

If the docstring source changed, the command line tool updates the docstring in the module when specifying the --merge option:

$ python -m marrydoc --merge copied_from_example.py
copied_from_example.py ... UPDATED

Note

During normal import of a module, the copied_from() decorator acts as a passthrough and introduces very little overhead.

Maintain DocString With Modifications

The based_on() decorator defines a relationship between the docstring of the function it decorates and the basis from which it was derived. The same as copied_from() and inherit(), the first argument to based_on() is the program construct containing the docstring that is to be tracked. based_on() requires a second argument that is a copy of the source docstring (the source docstring is compared against the copy and if they are unequal, the two values in combination with the actual docstring facilitate a three way merge). For example:

based_on_example.py.

import binascii
import marrydoc


@marrydoc.based_on(
    binascii.b2a_hex,
    """b2a_hex(data) -> s; Hexadecimal representation of binary data.
    
    This function is also available as "hexlify()".""")
def b2a_hex(data, sep=''):
    """b2a_hex(data) -> s; Hexadecimal representation of binary data.
    b2a_hex(data, sep) -> s; Separated hexadecimal representation of binary data.

    This function is also available as "hexlify()"."""
    mashed = binascii.hexlify(data)
    return sep.join(mashed[i:i+2] for i in range(0, len(mashed), 2))

hexlify = b2a_hex

The defined relationship to the source in combination with the docstring copy provided as the second argument allow the Command Line Tool to check if the source docstring has changed:

$ python -m marrydoc based_on_example.py
based_on_example.py ... OK

If the source docstring has changed, the Command Line Tool updates the module’s docstrings by performing a three way merge when specifying the --merge option:

$ python -m marrydoc --merge based_on_example.py
based_on_example.py ... UPDATED

Note

The three way merge requires a merge tool of your choosing. Without configuring, the three way merge attempts usage of kdiff3. See the next section for more information on configuring your favorite merge tool to be used.

During normal import of a module, the based_on() decorator acts as a passthrough and introduces very little overhead.

Configure Your Favorite Merge Tool

For three way merges, the marrydoc command line tool uses kdiff3 when it is installed and in your system path. Otherwise marrydoc generates “base”, “left”, and “right” files on your file system for you to merge manually.

To automatically invoke your favorite three way merge tool instead, set the MARRYDOC_MERGE environment variable and specify the command line invocation using Python’s string format substitution syntax.

For example, on Linux:

export MARRYDOC_MERGE="kdiff3 --merge --auto {base} {left} {right} --output {orig}"

Or on Microsoft Windows:

set MARRYDOC_MERGE="kdiff3 --merge --auto {base} {left} {right} --output {orig}"

Warning

The executable name/path must not contain spaces. The current implementation of marrydoc splits the string on whitespace and passes the result to a subprocess command.

Test DocStrings In A Module Collection

The main() function exposes the command line interface and offers a convenient method to check if the docstrings in a module are up to date. Add a test case within the module’s regression test to call the command line interface and to check the returned exit code for success indication (0).

The --walk option is useful for checking an entire package hierarchy, for example:

import os
import unittest
import marrydoc
import mypackage

class TestDocStrings(unittest.TestCase):

    def test_package(self):
        package_path = os.path.dirname(mypackage.__file__)
        exitcode = marrydoc.main(['--walk', package_path])
        self.assertEqual(exitcode, 0)

Tips and Tricks

  • based_on(), copied_from() may also be used to decorate a class to wed its docstring to another. (inherit() cannot be used to decorate a class because the class docstring is not settable by the time the docorator executes.)

  • The based_on(), copied_from(), and inherit() decorators may also be used in combination with the @classmethod() or staticmethod() decorators. The marrydoc decorator implementations accomodate decorating in either order. For better readability, place the @classmethod() and staticmethod() decorators first (on the outside).

  • When decorating a method using based_on(), copied_from(), or inherit(), a class may be passed as the first argument to specify the source of the docstring. The docstring of the method by the same name in the specified class then acts as the docstring basis.

  • Ensure your operating system path includes the Scripts subdirectory that is part of the normal Python installation. After installation, a marrydoc “shim” executable exists that subdirectory to invoke the command line tool directly:

    $ marrydoc --help
    
  • The marrydoc command line also accepts importable module and package names. Use this form when the module is in the Python system path. This form may be used in combination with the --walk option to check an entire package, for example:

    $ marrydoc baseline --walk
    /usr/local/lib/python3.5/dist-packages/baseline/__about__.py ... OK
    /usr/local/lib/python3.5/dist-packages/baseline/__init__.py ... OK
    /usr/local/lib/python3.5/dist-packages/baseline/__main__.py ... OK
    /usr/local/lib/python3.5/dist-packages/baseline/_baseline.py ... OK
    /usr/local/lib/python3.5/dist-packages/baseline/_script.py ... OK