Home > python-epyparse

python-epyparse

Python-epyparse is a project mainly written in Python, it's free.

epydoc to json-serialized objects

epyparse.py

Formatting and serialization of epydoc-parsed Python source code. Source code is only ever parsed textually, never imported.

Command line Usage

prettyprint


Pretty print a representation of the API of a named object or file::

    $ epyparse print -path <name_or_path> [options]

which can be shortened to::

    $ epyparse <name_or_path> [options]

By default, output is colored for the terminal, you can turn this off with
the `-nocolor` option.

serialize
~~~~~~~~~

Flatten the recursive API structure of a named object or file to a sequence
of individual dictionaries. Each dictionary is JSON-serialized and saved to
its own file::

    $ epyparse serialize -path <name_or_path> [options]

By default, files are written to the current working directory, you can
specify another directory with the `-out` option::

    $ epyparse serialize -path <name_or_path> -out docs/api

Programmatic Usage
------------------

Top-level functions are `parsed`, `flattened`, `pprint` and `objectify`::

    >>> from epyparse import parsed, flattened, pprint, objectify

Input can be a file path::

    >>> pprint('setup.py')
    ################################################################################
    #    setup
    ################################################################################
    """
    """
    <BLANKLINE>
    import distutils.core.setup
    import sys
    import os
    import epyparse
    <BLANKLINE>

which must exist::

    >>> parsed('does/not/exist')
    Traceback (most recent call last):
        ...
    IOError: No such file does/not/exist

Or a named object::

    >>> pprint('os.getenv')
    def getenv(key, default=None):
        """
        Get an environment variable, return None if it doesn't exist.
        The optional second argument can specify an alternate default.
        """
    <BLANKLINE>

which must be accessible::

    >>> pprint('does.not.exist')
    Traceback (most recent call last):
        ...
    ImportError: No Python source file found.

and not a builtin or c-based object::

    >>> pprint('cStringIO.StringIO')
    Traceback (most recent call last):
        ...
    ImportError: No Python source file for builtin modules.

Modules, functions, classes or methods can be accessed::

    >>> pprint('StringIO.StringIO.write')
    def write(self, s):
        """
        Write a string to the file.
    <BLANKLINE>
        There is no return value.
        """
    <BLANKLINE>


Parse to a nested dictionary

Parse module::

>>> d = parsed('epydoc.apidoc')
>>> sorted(d.keys())
['children', 'docstring', 'fullname', 'imports', 'type']
>>> d['type']
'module'
>>> for item in sorted(d['children'], key=lambda d: (d['type'], d['fullname'])):
...     print item['type'], ' -> ', item['fullname']
class  ->  epydoc.apidoc.APIDoc
class  ->  epydoc.apidoc.ClassDoc
class  ->  epydoc.apidoc.ClassMethodDoc
class  ->  epydoc.apidoc.DocIndex
class  ->  epydoc.apidoc.DottedName
class  ->  epydoc.apidoc.GenericValueDoc
class  ->  epydoc.apidoc.ModuleDoc
class  ->  epydoc.apidoc.NamespaceDoc
class  ->  epydoc.apidoc.PropertyDoc
class  ->  epydoc.apidoc.RoutineDoc
class  ->  epydoc.apidoc.StaticMethodDoc
class  ->  epydoc.apidoc.ValueDoc
class  ->  epydoc.apidoc.VariableDoc
class  ->  epydoc.apidoc._Sentinel
function  ->  epydoc.apidoc._flatten
function  ->  epydoc.apidoc._pp_apidoc
function  ->  epydoc.apidoc._pp_dict
function  ->  epydoc.apidoc._pp_list
function  ->  epydoc.apidoc._pp_val
function  ->  epydoc.apidoc.pp_apidoc
function  ->  epydoc.apidoc.reachable_valdocs

Flatten and JSON-serialize



For the `Object.get_parent` and `Object.get_children` deserialization
methods to work, we rely on the convention that api objects have been
saved to the same directory with the canonical name as file name and
with no file extension::

    >>> import os, json
    >>> assert not os.path.exists('TESTOUT')
    >>> os.mkdir('TESTOUT')
    >>> for item in flattened('epydoc.apidoc'):
    ...     print item['fullname']
    ...     with open('TESTOUT/' + item['fullname'], 'w+b') as fp:
    ...         json.dump(item, fp)
    ...
    epydoc.apidoc._flatten
    epydoc.apidoc._pp_apidoc
    epydoc.apidoc._pp_dict
    epydoc.apidoc._pp_list
    epydoc.apidoc._pp_val
    epydoc.apidoc.pp_apidoc
    epydoc.apidoc.reachable_valdocs
    epydoc.apidoc.APIDoc.__cmp__
    epydoc.apidoc.APIDoc.__hash__
    epydoc.apidoc.APIDoc.__init__
    epydoc.apidoc.APIDoc.__repr__
    epydoc.apidoc.APIDoc._debug_setattr
    epydoc.apidoc.APIDoc._debug_setattr
    epydoc.apidoc.APIDoc.apidoc_links
    epydoc.apidoc.APIDoc.is_detailed
    epydoc.apidoc.APIDoc.merge_and_overwrite
    epydoc.apidoc.APIDoc.pp
    epydoc.apidoc.APIDoc.pp
    epydoc.apidoc.APIDoc.specialize_to
    epydoc.apidoc.APIDoc
    epydoc.apidoc.ClassDoc._c3_merge
    epydoc.apidoc.ClassDoc._c3_mro
    epydoc.apidoc.ClassDoc._dfs_bases
    epydoc.apidoc.ClassDoc._report_bad_base
    epydoc.apidoc.ClassDoc.apidoc_links
    epydoc.apidoc.ClassDoc.is_exception
    epydoc.apidoc.ClassDoc.is_newstyle_class
    epydoc.apidoc.ClassDoc.is_type
    epydoc.apidoc.ClassDoc.mro
    epydoc.apidoc.ClassDoc.select_variables
    epydoc.apidoc.ClassDoc
    epydoc.apidoc.ClassMethodDoc
    epydoc.apidoc.DocIndex.__init__
    epydoc.apidoc.DocIndex._get
    epydoc.apidoc.DocIndex._get_from
    epydoc.apidoc.DocIndex._get_module_classes
    epydoc.apidoc.DocIndex._update_funcid_to_doc
    epydoc.apidoc.DocIndex.container
    epydoc.apidoc.DocIndex.find
    epydoc.apidoc.DocIndex.get_valdoc
    epydoc.apidoc.DocIndex.get_vardoc
    epydoc.apidoc.DocIndex.reachable_valdocs
    epydoc.apidoc.DocIndex.read_profiling_info
    epydoc.apidoc.DocIndex
    epydoc.apidoc.DottedName.InvalidDottedName
    epydoc.apidoc.DottedName.__add__
    epydoc.apidoc.DottedName.__cmp__
    epydoc.apidoc.DottedName.__getitem__
    epydoc.apidoc.DottedName.__hash__
    epydoc.apidoc.DottedName.__init__
    epydoc.apidoc.DottedName.__len__
    epydoc.apidoc.DottedName.__radd__
    epydoc.apidoc.DottedName.__repr__
    epydoc.apidoc.DottedName.__str__
    epydoc.apidoc.DottedName.container
    epydoc.apidoc.DottedName.contextualize
    epydoc.apidoc.DottedName.dominates
    epydoc.apidoc.DottedName
    epydoc.apidoc.GenericValueDoc.is_detailed
    epydoc.apidoc.GenericValueDoc
    epydoc.apidoc.ModuleDoc.apidoc_links
    epydoc.apidoc.ModuleDoc.init_submodule_groups
    epydoc.apidoc.ModuleDoc.select_variables
    epydoc.apidoc.ModuleDoc
    epydoc.apidoc.NamespaceDoc.__init__
    epydoc.apidoc.NamespaceDoc._init_grouping
    epydoc.apidoc.NamespaceDoc.apidoc_links
    epydoc.apidoc.NamespaceDoc.group_names
    epydoc.apidoc.NamespaceDoc.init_sorted_variables
    epydoc.apidoc.NamespaceDoc.init_variable_groups
    epydoc.apidoc.NamespaceDoc.is_detailed
    epydoc.apidoc.NamespaceDoc.report_unused_groups
    epydoc.apidoc.NamespaceDoc
    epydoc.apidoc.PropertyDoc.apidoc_links
    epydoc.apidoc.PropertyDoc.is_detailed
    epydoc.apidoc.PropertyDoc
    epydoc.apidoc.RoutineDoc.all_args
    epydoc.apidoc.RoutineDoc.is_detailed
    epydoc.apidoc.RoutineDoc
    epydoc.apidoc.StaticMethodDoc
    epydoc.apidoc.ValueDoc.__getstate__
    epydoc.apidoc.ValueDoc.__repr__
    epydoc.apidoc.ValueDoc.__setstate__
    epydoc.apidoc.ValueDoc.apidoc_links
    epydoc.apidoc.ValueDoc.pyval_repr
    epydoc.apidoc.ValueDoc.summary_pyval_repr
    epydoc.apidoc.ValueDoc
    epydoc.apidoc.VariableDoc.__init__
    epydoc.apidoc.VariableDoc.__repr__
    epydoc.apidoc.VariableDoc._get_defining_module
    epydoc.apidoc.VariableDoc.apidoc_links
    epydoc.apidoc.VariableDoc.is_detailed
    epydoc.apidoc.VariableDoc
    epydoc.apidoc._Sentinel.__init__
    epydoc.apidoc._Sentinel.__nonzero__
    epydoc.apidoc._Sentinel.__repr__
    epydoc.apidoc._Sentinel
    epydoc.apidoc

Deserialize to `Object`
~~~~~~~~~~~~~~~~~~~~~~~

::

    >>> from epyparse import objectify
    >>> obj = objectify('TESTOUT/epydoc.apidoc.APIDoc.merge_and_overwrite')

The returned `Object` type is a dict subclass::

    >>> sorted(obj.keys())
    [u'args', u'attributes', u'fullname', u'is_method', u'lineno', u'params', 'src', u'type']

with attribute-style access::

    >>> obj.args
    [u'self', u'other']
    >>> obj.is_method
    True
    >>> obj.lineno * 0
    0
    >>> obj.params
    [[u'ignore_hash_conflict', False]]
    >>> obj.name
    u'merge_and_overwrite'

`Object.parent` gives the name of the parent object, if any::

    >>> obj.parent
    u'epydoc.apidoc.APIDoc'

And, because we have serialized the parent to the same directory, we can
retrieve it with the `Object.get_parent` method::

    >>> parent = obj.get_parent()
    >>> parent.fullname
    u'epydoc.apidoc.APIDoc'
    >>> parent.type
    u'class'
    >>> parent.name
    u'APIDoc'

Similarly, `Object.members` returns the names of the object's members, if any::

    >>> for name in sorted(parent.members):
    ...     print parent.fullname + '.' + name
    epydoc.apidoc.APIDoc.__cmp__
    epydoc.apidoc.APIDoc.__hash__
    epydoc.apidoc.APIDoc.__init__
    epydoc.apidoc.APIDoc.__repr__
    epydoc.apidoc.APIDoc._debug_setattr
    epydoc.apidoc.APIDoc._debug_setattr
    epydoc.apidoc.APIDoc.apidoc_links
    epydoc.apidoc.APIDoc.is_detailed
    epydoc.apidoc.APIDoc.merge_and_overwrite
    epydoc.apidoc.APIDoc.pp
    epydoc.apidoc.APIDoc.pp
    epydoc.apidoc.APIDoc.specialize_to

and `Object.get_members` deserializes each member::

    >>> for child in sorted(parent.get_members(), key=lambda d: d['fullname']):
    ...     print child.type, ' -> ', child.name
    function  ->  __cmp__
    function  ->  __hash__
    function  ->  __init__
    function  ->  __repr__
    alias  ->  _debug_setattr
    alias  ->  _debug_setattr
    function  ->  apidoc_links
    function  ->  is_detailed
    function  ->  merge_and_overwrite
    function  ->  pp
    function  ->  pp
    function  ->  specialize_to

Introspection - `inspect` vs. `Inspector`
-----------------------------------------

    >>> import inspect
    >>> from epydoc.apidoc import APIDoc
    >>> from epyparse import Inspector
    >>> merge_and_overwrite_obj = obj
    >>> APIDoc_obj = parent

argspec (not identical but good enough for displaying the function signature)::

    >>> inspect.getargspec(APIDoc.merge_and_overwrite)
    ArgSpec(args=['self', 'other', 'ignore_hash_conflict'], varargs=None, keywords=None, defaults=(False,))
    >>> Inspector.getargspec(merge_and_overwrite_obj)
    ArgSpec(args=[u'self', u'other', u'ignore_hash_conflict'], varargs=None, keywords=None, defaults=(False,))

    >>> inspect.getargspec(APIDoc.apidoc_links)
    ArgSpec(args=['self'], varargs=None, keywords='filters', defaults=None)
    >>> Inspector.getargspec(APIDoc_obj.get_member('apidoc_links'))
    ArgSpec(args=[u'self'], varargs=None, keywords=u'filters', defaults=None)

hasattr::

    >>> hasattr(APIDoc, 'merge_and_overwrite')
    True
    >>> Inspector.hasattr(APIDoc_obj, 'merge_and_overwrite')
    True
    >>> Inspector.hasattr(APIDoc_obj, 'somethingsomethingorangessomething')
    False

dir
---

    >>> dir(merge_and_overwrite_obj)
    ['__dict__', u'__doc__', u'__name__']
    >>> sorted(dir(APIDoc_obj))[:7]
    [u'__cmp__', '__dict__', u'__doc__', u'__hash__', u'__init__', u'__name__', u'__repr__']
    >>> sorted(dir(APIDoc_obj))[7:13]
    [u'_debug_setattr', u'_debug_setattr', u'apidoc_links', u'is_detailed', u'merge_and_overwrite', u'pp']
    >>> module_obj = objectify('TESTOUT/epydoc.apidoc')
    >>> sorted(dir(module_obj))[:7]
    [u'APIDoc', u'ClassDoc', u'ClassMethodDoc', u'DocIndex', u'DottedName', u'GenericValueDoc', u'ModuleDoc']

__doc__
-------

    >>> hasattr(APIDoc.merge_and_overwrite, '__doc__')
    True
    >>> hasattr(merge_and_overwrite_obj, '__doc__')
    True
    >>> Inspector.hasattr(merge_and_overwrite_obj, '__doc__')
    True
    >>> actual = ''.join(
    ...   s.strip() for s in APIDoc.merge_and_overwrite.__doc__.splitlines() if s.strip()
    ... )
    ...
    >>> serialized = ''.join(
    ...   s.strip() for s in merge_and_overwrite_obj.__doc__.splitlines() if s.strip()
    ... )
    ...
    >>> assert len(actual) and len(serialized)
    >>> assert actual == serialized

__dict__
--------

    >>> Inspector.hasattr(merge_and_overwrite_obj, '__dict__')
    True
    >>> d = Inspector.getattr(merge_and_overwrite_obj, '__dict__')
    >>> d['__name__']
    u'merge_and_overwrite'
    >>> print d['__doc__'] #doctest: +ELLIPSIS
    Combine C{self} and C{other} into a X{merged object}, such
    that any changes made to one will affect the other.  Any...

Base classes
------------


Tidy up::

    >>> import shutil
    >>> shutil.rmtree('TESTOUT')