Merge remote-tracking branch 'origin/go-parsed-example'

pull/6/head
Eric Holscher 9 years ago
commit b56f2dc1d8

@ -1,5 +1,6 @@
import os
import yaml
import json
import fnmatch
from sphinx.util.console import darkgreen
@ -63,7 +64,7 @@ class AutoAPIDomain(object):
def __init__(self, app):
self.app = app
def read_file(self, path):
def read_file(self, path, format='yaml'):
'''Read file input into memory, returning deserialized objects
:param path: Path of file to read
@ -72,16 +73,26 @@ class AutoAPIDomain(object):
# TODO sphinx way of reporting errors in logs?
try:
with open(path, 'r') as handle:
obj = yaml.safe_load(handle)
if format == 'yaml':
obj = yaml.safe_load(handle)
elif format == 'json':
obj = json.load(handle)
except IOError:
raise Warning('Error reading file: {0}'.format(path))
except yaml.YAMLError:
raise Warning('Error parsing file: {0}'.format(path))
except ValueError:
raise Warning('Error parsing file: {0} at {1}'.format(path, json.last_error_position))
return obj
def create_class(self, obj):
'''Create class object from obj'''
raise NotImplementedError
def add_object(self, obj):
'''
Add object to local and app environment storage
:param obj: Instance of a AutoAPI object
'''
self.app.env.autoapi_data.append(obj)
self.objects[obj.name] = obj
def get_config(self, key):
if self.app.config is not None:
@ -112,3 +123,19 @@ class AutoAPIDomain(object):
darkgreen,
len(files_to_read)):
yield _path
def get_objects(self, pattern, format='yaml'):
'''Trigger find of serialized sources and build objects'''
for path in self.find_files(pattern):
data = self.read_file(path, format=format)
if data:
for obj in self.create_class(data):
self.add_object(obj)
def create_class(self, obj):
'''
Create class object.
:param obj: Instance of a AutoAPI object
'''
raise NotImplementedError

@ -1,2 +1,3 @@
from .dotnet import DotNetDomain
from .python import PythonDomain
from .go import GoDomain

@ -37,35 +37,34 @@ class DotNetDomain(AutoAPIDomain):
:param data: dictionary data from Roslyn output artifact
'''
# TODO replace this with a global mapping
self.list_classes = [
DotNetNamespace, DotNetClass, DotNetEnum,
DotNetStruct, DotNetInterface, DotNetDelegate
]
self.detail_classes = [
DotNetProperty, DotNetMethod, DotNetConstructor,
DotNetField, DotNetEvent
]
classes = self.detail_classes + self.list_classes
obj = None
for cls in classes:
if data.get('type', '').lower() == cls.type.lower():
obj = cls(data)
break
if data.get('id', None) in MADE:
print "DOING IT AGAIN: %s" % data.get('id')
MADE.add(data['id'])
# Append child objects
# TODO this should recurse in the case we're getting back more complex
# argument listings
# if 'children' in data:
# for item in data['children']:
# child_obj = self.create_class(item)
# obj.children.append(child_obj)
return obj
obj_map = dict(
(cls.type, cls) for cls
in [
DotNetNamespace, DotNetClass, DotNetEnum, DotNetStruct,
DotNetInterface, DotNetDelegate, DotNetProperty, DotNetMethod,
DotNetConstructor, DotNetField, DotNetEvent
])
try:
cls = obj_map[data['type'].lower()]
except KeyError:
self.app.warn('Unknown type: %s' % data)
else:
obj = cls(data)
# TODO what is MADE?
if data.get('id', None) in MADE:
self.app.warn("Object already added: %s" % data.get('id'))
MADE.add(data['id'])
# Append child objects
# TODO this should recurse in the case we're getting back more
# complex argument listings
# if 'children' in data:
# for item in data['children']:
# child_obj = self.create_class(item)
# obj.children.append(child_obj)
yield obj
def get_objects(self, pattern):
'''Trigger find of serialized sources and build objects'''
@ -75,8 +74,8 @@ class DotNetDomain(AutoAPIDomain):
data_objects = data_objects['items']
try:
for data in data_objects:
obj = self.create_class(data)
self.add_object(obj)
for obj in self.create_class(data):
self.add_object(obj)
except:
import traceback
traceback.print_exc()
@ -86,7 +85,7 @@ class DotNetDomain(AutoAPIDomain):
:param obj: Instance of a .NET object
'''
if type(obj) in self.list_classes:
if obj.top_level_object:
self.top_level_objects[obj.name] = obj
if type(obj) == DotNetNamespace:
self.namespaces[obj.name] = obj
@ -112,9 +111,9 @@ class DotNetDomain(AutoAPIDomain):
ns_obj = self.top_namespaces.get(namespace)
if ns_obj is None or type(ns_obj) != DotNetNamespace:
print "Adding Namespace %s" % namespace
ns_obj = self.create_class({'id': namespace,
'type': 'namespace'})
self.top_namespaces[ns_obj.id] = ns_obj
for ns_obj in self.create_class({'id': namespace,
'type': 'namespace'}):
self.top_namespaces[ns_obj.id] = ns_obj
if obj not in ns_obj.children and namespace != obj.id:
ns_obj.children.append(obj)
@ -184,6 +183,7 @@ class DotNetBase(AutoAPIBase):
'''Base .NET object representation'''
language = 'dotnet'
top_level_object = False
def __init__(self, obj):
super(DotNetBase, self).__init__(obj)
@ -316,6 +316,7 @@ class DotNetNamespace(DotNetBase):
type = 'namespace'
ref_directive = 'ns'
plural = 'namespaces'
top_level_object = True
class DotNetMethod(DotNetBase):
@ -335,6 +336,7 @@ class DotNetEnum(DotNetBase):
ref_type = 'enumeration'
ref_directive = 'enum'
plural = 'enumerations'
top_level_object = True
class DotNetStruct(DotNetBase):
@ -342,6 +344,7 @@ class DotNetStruct(DotNetBase):
ref_type = 'structure'
ref_directive = 'struct'
plural = 'structures'
top_level_object = True
class DotNetConstructor(DotNetBase):
@ -354,18 +357,21 @@ class DotNetInterface(DotNetBase):
type = 'interface'
ref_directive = 'iface'
plural = 'interfaces'
top_level_object = True
class DotNetDelegate(DotNetBase):
type = 'delegate'
ref_directive = 'del'
plural = 'delegates'
top_level_object = True
class DotNetClass(DotNetBase):
type = 'class'
ref_directive = 'cls'
plural = 'classes'
top_level_object = True
class DotNetField(DotNetBase):

@ -0,0 +1,199 @@
import os
import json
from collections import defaultdict
from sphinx.util.osutil import ensuredir
from ..base import AutoAPIBase, AutoAPIDomain
from ..settings import env
class GoDomain(AutoAPIDomain):
'''Auto API domain handler for Go
Parses directly from Go files.
:param app: Sphinx application passed in as part of the extension
'''
# def read_file(self, path, format=None):
# '''Read file input into memory, returning deserialized objects
# :param path: Path of file to read
# '''
# TODO support JSON here
# TODO sphinx way of reporting errors in logs?
# try:
# raw_json = os.system('godocjson %s' % path)
# parsed_data = json.loads(raw_json)
# return parsed_data
# except IOError:
# print Warning('Error reading file: {0}'.format(path))
# except TypeError:
# print Warning('Error reading file: {0}'.format(path))
# return None
def create_class(self, data):
'''Return instance of class based on Go data
Data keys handled here:
type
Set the object class
consts, types, vars, funcs
Recurse into :py:meth:`create_class` to create child object
instances
:param data: dictionary data from godocjson output
'''
obj_map = dict(
(cls.type, cls) for cls
in [GoConstant, GoFunction, GoPackage, GoVariable, GoType, GoMethod]
)
try:
cls = obj_map[data['type']]
except KeyError as e:
self.app.warn('Unknown Type: %s' % data)
else:
if cls.inverted_names and 'names' in data:
# Handle types that have reversed names parameter
for name in data['names']:
data_inv = {}
data_inv.update(data)
data_inv['name'] = name
if 'names' in data_inv:
del data_inv['names']
for obj in self.create_class(data_inv):
yield obj
else:
# Recurse for children
obj = cls(data)
for child_type in ['consts', 'types', 'vars', 'funcs']:
for child_data in data.get(child_type, []):
obj.children += list(self.create_class(child_data))
yield obj
def full(self):
self.get_objects(self.get_config('autoapi_file_pattern'), format='json')
self.generate_output()
self.write_indexes()
def generate_output(self):
for obj in self.app.env.autoapi_data:
if not obj:
continue
rst = obj.render()
# Detail
try:
filename = obj.name.split('(')[0]
except IndexError:
filename = obj.name
detail_dir = os.path.join(self.get_config('autoapi_root'),
*filename.split('.'))
ensuredir(detail_dir)
# TODO: Better way to determine suffix?
path = os.path.join(detail_dir, '%s%s' % ('index', self.get_config('source_suffix')[0]))
if rst:
with open(path, 'w+') as detail_file:
detail_file.write(rst.encode('utf-8'))
def write_indexes(self):
# Write Index
top_level_index = os.path.join(self.get_config('autoapi_root'),
'index.rst')
with open(top_level_index, 'w+') as top_level_file:
content = env.get_template('index.rst')
top_level_file.write(content.render())
class GoBase(AutoAPIBase):
language = 'go'
inverted_names = False
def __init__(self, obj):
super(GoBase, self).__init__(obj)
self.name = obj.get('name') or obj.get('packageName')
self.id = self.name
# Second level
self.imports = obj.get('imports', [])
self.children = []
self.parameters = map(
lambda n: {'name': n['name'],
'type': n['type'].lstrip('*')},
obj.get('parameters', [])
)
self.docstring = obj.get('doc', '')
# Go Specific
self.notes = obj.get('notes', {})
self.filenames = obj.get('filenames', [])
self.bugs = obj.get('bugs', [])
def __str__(self):
return '<{cls} {id}>'.format(cls=self.__class__.__name__,
id=self.id)
@property
def short_name(self):
'''Shorten name property'''
return self.name.split('.')[-1]
@property
def namespace(self):
pieces = self.id.split('.')[:-1]
if pieces:
return '.'.join(pieces)
@property
def ref_type(self):
return self.type
@property
def ref_directive(self):
return self.type
@property
def methods(self):
return self.obj.get('methods', [])
def __lt__(self, other):
'''Sort object by name'''
if isinstance(other, GoBase):
return self.name.lower() < other.name.lower()
return self.name < other
class GoVariable(GoBase):
type = 'var'
inverted_names = True
class GoMethod(GoBase):
type = 'method'
ref_directive = 'meth'
class GoConstant(GoBase):
type = 'const'
inverted_names = True
class GoFunction(GoBase):
type = 'func'
ref_type = 'function'
class GoPackage(GoBase):
type = 'package'
ref_directive = 'pkg'
class GoType(GoBase):
type = 'type'

@ -30,25 +30,21 @@ class PythonDomain(AutoAPIDomain):
Recurse into :py:meth:`create_class` to create child object
instances
:param data: dictionary data from Roslyn output artifact
:param data: dictionary data of epydoc output
'''
# TODO replace this with a global mapping
classes = [PythonClass, PythonFunction, PythonModule]
obj = None
for cls in classes:
if data['type'].lower() == cls.type.lower():
obj = cls(data)
if not obj:
print "Unknown Type: %s" % data['type']
# Append child objects
# TODO this should recurse in the case we're getting back more complex
# argument listings
if 'children' in data:
for item in data['children']:
child_obj = self.create_class(item)
obj.children.append(child_obj)
return obj
obj_map = dict((cls.type, cls) for cls
in [PythonClass, PythonFunction, PythonModule])
try:
cls = obj_map[data['type']]
except KeyError:
self.app.warn("Unknown Type: %s" % data['type'])
else:
obj = cls(data)
if 'children' in data:
for child_data in data['children']:
child_obj = self.create_class(child_data)
obj.children.append(child_obj)
yield obj
def read_file(self, path):
'''Read file input into memory, returning deserialized objects

@ -6,7 +6,7 @@ Sphinx Auto-API
import fnmatch
import shutil
from .domains import DotNetDomain, PythonDomain
from .domains import DotNetDomain, PythonDomain, GoDomain
def ignore_file(app, filename):
@ -30,6 +30,8 @@ def load_yaml(app):
domain = DotNetDomain(app)
elif app.config.autoapi_type == 'python':
domain = PythonDomain(app)
elif app.config.autoapi_type == 'go':
domain = GoDomain(app)
domain.full()

@ -0,0 +1,26 @@
.. go:{{ obj.ref_type }}:: {{ obj.name }}
{%- if obj.type == 'func' -%}
{%- set argjoin = joiner(', ') -%}
({%- for param in obj.parameters -%}
{{ argjoin() }}{{ param.name }} {{ param.type }}
{%- endfor -%})
{%- endif %}
{% macro render() %}{{ obj.docstring }}{% endmacro %}
{{ render()|indent(4) }}
{# Don't define parameter description here, that can be done in the block
above #}
{% for param in obj.parameters %}
:type {{ param.name }}: {{ param.type }}
{%- endfor %}
{%- if obj.returns %}
:rtype: {{ obj.returns.type }}
{%- endif %}
{% if obj.children -%}
{%- for child in obj.children|sort %}
{% macro render_child() %}{{ child.render() }}{% endmacro %}
{{ render_child()|indent(4) }}
{%- endfor %}
{%- endif %}

@ -0,0 +1 @@
{% extends "go/base_member.rst" %}

@ -0,0 +1 @@
{% extends "go/base_member.rst" %}

@ -0,0 +1 @@
{% extends "go/base_member.rst" %}

@ -0,0 +1,32 @@
.. go:package:: {{ obj.name }}
{{ obj.name }}
{{ "=" * obj.name|length }}
{% block toc %}
{%- if obj.children %}
{# TODO Make this work
.. toctree::
:maxdepth: 4
{% for item in obj.children|sort %}
/autoapi/{{ item.id.split('.')|join('/') }}/index
{%- endfor %}
#}
{%- endif %}
{% endblock %}
{% if obj.docstring %}
{{ obj.docstring }}
{% endif %}
{% block content %}
{%- for obj_item in obj.children|sort %}
{% macro render() %}{{ obj_item.render() }}{% endmacro %}
{{ render()|indent(0) }}
{%- endfor %}
{% endblock %}

@ -0,0 +1 @@
{% extends "go/base_member.rst" %}

@ -0,0 +1 @@
{% extends "go/base_member.rst" %}

@ -9,6 +9,7 @@ Below is a list of all items that are documented here.
:glob:
:maxdepth: 1
*/*
{% for page in pages|sort %}
/autoapi/{{ page.id.split('.')|join('/') }}/index
{% endfor %}

@ -29,11 +29,11 @@ import shlex
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['autoapi.extension']
extensions = ['autoapi.extension', 'sphinxcontrib.golangdomain']
autoapi_type = 'python'
autoapi_dir = '../autoapi'
autoapi_file_pattern = '*.py'
autoapi_type = 'go'
autoapi_dir = '/Users/eric/projects/godocjson'
autoapi_file_pattern = '*.json'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

@ -31,7 +31,7 @@ Context
Every template will be given a set context. It will contain:
* `obj`: A Python object derived from :any:`AutoAPIBase`
* `obj`: A Python object derived from
This object has a number of standard attributes you can reliably access:

@ -0,0 +1,127 @@
{
"type": "package",
"doc": "This is the golang UUID module",
"name": "go.uuid",
"import_path": "uuid.go",
"imports": [
"something",
"foo",
"bar"
],
"filenames": [
"something.go",
"foo.go",
"bar.go"
],
"notes": {
"TODO": [
{
"pos": 27,
"end": 42,
"uid": "someone",
"body": "This is a code TODO note, we probably shouldn't show it."
}
],
"BUG": [
{
"pos": 43,
"end": 44,
"uid": "someone",
"body": "This is a bug admonition"
}
]
},
"bugs": [
"Bug notes here"
],
"consts": [
{
"name": "awesome",
"type": "const",
"doc": "This is the documentation for the const",
"names": [
"not",
"sure",
"what",
"goes",
"here"
]
}
],
"types": [
{
"type": "type",
"doc": "This is the documentation for the type",
"name": "uuid.foobar",
"consts": [
{
"type": "const",
"doc": "This is the documentation for a const nested in a type",
"names": [
"shit"
]
}
],
"vars": [
{
"type": "variable",
"doc": "This is the documentation for a variable nested in a type",
"names": [
"more"
]
}
],
"funcs": [
{
"type": "func",
"doc": "This is documentation for a function",
"name": "uuid.some_func",
"recv": "",
"orig": "",
"level": 0
}
],
"methods": [
{
"type": "method",
"doc": "This is documentation for a method",
"name": "uuid.some_method",
"recv": "T",
"orig": "T",
"level": 0
}
]
}
],
"vars": [
{
"type": "var",
"doc": "This is the documentation for the variable",
"names": [
"not",
"sure",
"what",
"goes",
"here"
]
}
],
"funcs": [
{
"type": "func",
"doc": "This is documentation for a function",
"name": "uuid.some_func",
"recv": "",
"orig": "",
"level": 0
},
{
"type": "method",
"doc": "This is documentation for a method",
"name": "uuid.some_method",
"recv": "T",
"orig": "T",
"level": 0
}
]
}
Loading…
Cancel
Save