Skip to main content

Generate API info for Python Packages via Epydoc

Project description

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')

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

epyparse-0.2.5.zip (13.9 kB view hashes)

Uploaded Source

epyparse-0.2.5.tar.gz (11.0 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page