mirror of
https://github.com/readthedocs/sphinx-autoapi
synced 2024-11-17 21:25:35 +00:00
Fix Python support
This commit is contained in:
parent
56ceab81e5
commit
09097cf364
@ -22,6 +22,7 @@ class AutoAPIBase(object):
|
|||||||
'{language}/{type}.rst'.format(language=self.language, type=self.type)
|
'{language}/{type}.rst'.format(language=self.language, type=self.type)
|
||||||
)
|
)
|
||||||
ctx.update(**self.get_context_data())
|
ctx.update(**self.get_context_data())
|
||||||
|
print ctx['item_map'].keys()
|
||||||
return template.render(**ctx)
|
return template.render(**ctx)
|
||||||
|
|
||||||
def get_absolute_path(self):
|
def get_absolute_path(self):
|
||||||
@ -88,13 +89,13 @@ class AutoAPIDomain(object):
|
|||||||
if self.app.config is not None:
|
if self.app.config is not None:
|
||||||
return getattr(self.app.config, key, None)
|
return getattr(self.app.config, key, None)
|
||||||
|
|
||||||
def find_files(self):
|
def find_files(self, pattern='*.yaml'):
|
||||||
'''Find YAML/JSON files to parse for namespace information'''
|
'''Find YAML/JSON files to parse for namespace information'''
|
||||||
# TODO do an intelligent glob here, we're picking up too much
|
# TODO do an intelligent glob here, we're picking up too much
|
||||||
files_to_read = []
|
files_to_read = []
|
||||||
absolute_dir = os.path.normpath(self.get_config('autoapi_dir'))
|
absolute_dir = os.path.normpath(self.get_config('autoapi_dir'))
|
||||||
for root, dirnames, filenames in os.walk(absolute_dir):
|
for root, dirnames, filenames in os.walk(absolute_dir):
|
||||||
for filename in fnmatch.filter(filenames, '*.yaml'):
|
for filename in fnmatch.filter(filenames, pattern):
|
||||||
if os.path.isabs(filename):
|
if os.path.isabs(filename):
|
||||||
files_to_read.append(os.path.join(filename))
|
files_to_read.append(os.path.join(filename))
|
||||||
else:
|
else:
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
from .dotnet import DotNetDomain
|
from .dotnet import DotNetDomain
|
||||||
# from .python import PythonDomain
|
from .python import PythonDomain
|
||||||
|
@ -39,8 +39,6 @@ class DotNetDomain(AutoAPIDomain):
|
|||||||
for cls in classes:
|
for cls in classes:
|
||||||
if data.get('type', '').lower() == cls.type.lower():
|
if data.get('type', '').lower() == cls.type.lower():
|
||||||
obj = cls(data)
|
obj = cls(data)
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Append child objects
|
# Append child objects
|
||||||
# TODO this should recurse in the case we're getting back more complex
|
# TODO this should recurse in the case we're getting back more complex
|
||||||
@ -74,6 +72,8 @@ class DotNetDomain(AutoAPIDomain):
|
|||||||
'''Organize objects and namespaces'''
|
'''Organize objects and namespaces'''
|
||||||
|
|
||||||
def _recurse_ns(obj):
|
def _recurse_ns(obj):
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
namespace = obj.namespace
|
namespace = obj.namespace
|
||||||
if namespace is not None:
|
if namespace is not None:
|
||||||
ns_obj = None
|
ns_obj = None
|
||||||
|
@ -1,108 +1,188 @@
|
|||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from sphinx.util.osutil import ensuredir
|
||||||
from epyparse import parsed
|
from epyparse import parsed
|
||||||
|
|
||||||
from ..base import AutoAPIBase
|
from ..base import AutoAPIBase, AutoAPIDomain
|
||||||
from ..settings import env
|
from ..settings import env
|
||||||
|
|
||||||
# for root, dirnames, filenames in os.walk(app.config.autoapi_dir):
|
|
||||||
# for filename in fnmatch.filter(filenames, u'*.py'):
|
class PythonDomain(AutoAPIDomain):
|
||||||
# to_open = os.path.join(root, filename)
|
|
||||||
# if ignore_file(app, to_open):
|
'''Auto API domain handler for Python
|
||||||
# continue
|
|
||||||
# # print "Parsing Python File from %s" % to_open
|
Parses directly from Python files.
|
||||||
# try:
|
|
||||||
# parsed_data = parsed(to_open)
|
:param app: Sphinx application passed in as part of the extension
|
||||||
# app.env.autoapi_data.append(parsed_data)
|
'''
|
||||||
# except Exception:
|
|
||||||
# print "Exception, Keeping going: %s" % to_open
|
def create_class(self, data):
|
||||||
# import traceback
|
'''Return instance of class based on Roslyn type property
|
||||||
# traceback.print_exc()
|
|
||||||
# app.env.autoapi_enabled = True
|
Data keys handled here:
|
||||||
#
|
|
||||||
# # Generate RST
|
type
|
||||||
# for obj in app.env.autoapi_data:
|
Set the object class
|
||||||
# # print "Parsing %s" % obj['fullname']
|
|
||||||
# rst = classify(obj, 'python').render()
|
items
|
||||||
# if rst:
|
Recurse into :py:meth:`create_class` to create child object
|
||||||
# path = os.path.join(app.config.autoapi_root, '%s%s' % (obj['fullname'], app.config.source_suffix[0]))
|
instances
|
||||||
# ensuredir(app.config.autoapi_root)
|
|
||||||
# with open(path, 'w+') as fp:
|
:param data: dictionary data from Roslyn output artifact
|
||||||
# fp.write(rst.encode('utf8'))
|
'''
|
||||||
|
# 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
|
||||||
|
|
||||||
|
def read_file(self, path):
|
||||||
|
'''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:
|
||||||
|
parsed_data = parsed(path)
|
||||||
|
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 get_objects(self):
|
||||||
|
'''Trigger find of serialized sources and build objects'''
|
||||||
|
for path in self.find_files(pattern='*.py'):
|
||||||
|
data = self.read_file(os.path.join(self.get_config('autoapi_dir'), path))
|
||||||
|
if data:
|
||||||
|
obj = self.create_class(data)
|
||||||
|
self.add_object(obj)
|
||||||
|
|
||||||
|
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.append(obj)
|
||||||
|
|
||||||
|
def organize_objects(self):
|
||||||
|
'''Organize objects and namespaces'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
def full(self):
|
||||||
|
print "Reading"
|
||||||
|
self.get_objects()
|
||||||
|
self.organize_objects()
|
||||||
|
print "Writing"
|
||||||
|
self.generate_output()
|
||||||
|
self.write_indexes()
|
||||||
|
|
||||||
|
def generate_output(self):
|
||||||
|
for obj in self.app.env.autoapi_data:
|
||||||
|
|
||||||
|
# TODO not here!
|
||||||
|
for child in obj.children:
|
||||||
|
obj.item_map[child.type].append(child)
|
||||||
|
for key in obj.item_map.keys():
|
||||||
|
obj.item_map[key].sort()
|
||||||
|
|
||||||
|
rst = obj.render()
|
||||||
|
# Detail
|
||||||
|
detail_dir = os.path.join(self.get_config('autoapi_root'),
|
||||||
|
*obj.name.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)
|
||||||
|
|
||||||
|
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 PythonBase(AutoAPIBase):
|
class PythonBase(AutoAPIBase):
|
||||||
|
|
||||||
language = 'python'
|
language = 'python'
|
||||||
|
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
obj = super(PythonBase, self).__init__(obj)
|
super(PythonBase, self).__init__(obj)
|
||||||
obj.name = obj['fullname']
|
# Always exist
|
||||||
|
self.id = obj['fullname']
|
||||||
|
|
||||||
def render(self, ctx):
|
# Optional
|
||||||
added_ctx = {
|
self.imports = obj.get('imports', [])
|
||||||
'underline': len(self.name) * self.header
|
self.children = []
|
||||||
}
|
self.parameters = obj.get('params', [])
|
||||||
added_ctx.update(**ctx)
|
self.docstring = obj.get('docstring', '')
|
||||||
super(PythonBase, self).render(ctx=added_ctx)
|
|
||||||
|
# For later
|
||||||
|
self.item_map = defaultdict(list)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '<{cls} {id}>'.format(cls=self.__class__.__name__,
|
||||||
|
id=self.id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
'''Return short name for member id
|
||||||
|
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
return self.obj['fullname']
|
||||||
|
except KeyError:
|
||||||
|
return 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
|
||||||
|
|
||||||
|
|
||||||
class PythonFunction(PythonBase):
|
class PythonFunction(PythonBase):
|
||||||
type = 'function'
|
type = 'function'
|
||||||
|
|
||||||
|
|
||||||
class PythonModule(object):
|
class PythonModule(PythonBase):
|
||||||
|
type = 'module'
|
||||||
def __init__(self, obj):
|
|
||||||
self.obj = obj
|
|
||||||
self.item_map = defaultdict(list)
|
|
||||||
self.sort()
|
|
||||||
|
|
||||||
def sort(self):
|
|
||||||
from .utils import classify
|
|
||||||
for item in self.obj.get('children', []):
|
|
||||||
if 'type' not in item:
|
|
||||||
print "Missing Type: %s" % item
|
|
||||||
continue
|
|
||||||
self.item_map[item['type']].append(classify(item, 'python'))
|
|
||||||
|
|
||||||
def render(self):
|
|
||||||
# print "Rendering module %s" % self.obj['fullname']
|
|
||||||
self.obj['underline'] = len(self.obj['fullname']) * "#"
|
|
||||||
template = env.get_template('python/module.rst')
|
|
||||||
|
|
||||||
ctx = self.obj
|
|
||||||
ctx.update(dict(
|
|
||||||
methods=self.item_map['function'],
|
|
||||||
classes=self.item_map['class'],
|
|
||||||
imports=self.obj['imports'],
|
|
||||||
))
|
|
||||||
return template.render(**ctx)
|
|
||||||
|
|
||||||
|
|
||||||
class PythonClass(object):
|
class PythonClass(PythonBase):
|
||||||
|
type = 'class'
|
||||||
def __init__(self, obj):
|
|
||||||
self.obj = obj
|
|
||||||
self.item_map = defaultdict(list)
|
|
||||||
self.sort()
|
|
||||||
|
|
||||||
def sort(self):
|
|
||||||
from .utils import classify
|
|
||||||
for item in self.obj.get('children', []):
|
|
||||||
if 'type' not in item:
|
|
||||||
print "Missing Type: %s" % item
|
|
||||||
continue
|
|
||||||
self.item_map[item['type']].append(classify(item, 'python'))
|
|
||||||
|
|
||||||
def render(self, indent=4):
|
|
||||||
# print "Rendering class %s" % self.obj['fullname']
|
|
||||||
template = env.get_template('python/class.rst')
|
|
||||||
ctx = self.obj
|
|
||||||
ctx.update(dict(
|
|
||||||
underline=len(self.obj['fullname']) * "-",
|
|
||||||
methods=self.item_map['function'],
|
|
||||||
classes=self.item_map['class'],
|
|
||||||
indent=indent,
|
|
||||||
))
|
|
||||||
return template.render(**ctx)
|
|
||||||
|
@ -6,7 +6,7 @@ Sphinx Auto-API
|
|||||||
import fnmatch
|
import fnmatch
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from .domains import DotNetDomain
|
from .domains import DotNetDomain, PythonDomain
|
||||||
|
|
||||||
|
|
||||||
def ignore_file(app, filename):
|
def ignore_file(app, filename):
|
||||||
@ -24,8 +24,8 @@ def load_yaml(app):
|
|||||||
|
|
||||||
if app.config.autoapi_type == 'dotnet':
|
if app.config.autoapi_type == 'dotnet':
|
||||||
domain = DotNetDomain(app)
|
domain = DotNetDomain(app)
|
||||||
#elif app.config.autoapi_type == 'python':
|
elif app.config.autoapi_type == 'python':
|
||||||
# domain = PythonDomain
|
domain = PythonDomain(app)
|
||||||
domain.full()
|
domain.full()
|
||||||
|
|
||||||
|
|
||||||
@ -44,3 +44,4 @@ def setup(app):
|
|||||||
app.add_config_value('autoapi_ignore', ['*migrations*'], 'html')
|
app.add_config_value('autoapi_ignore', ['*migrations*'], 'html')
|
||||||
app.add_config_value('autoapi_dir', '', 'html')
|
app.add_config_value('autoapi_dir', '', 'html')
|
||||||
app.add_config_value('autoapi_keep_files', True, 'html')
|
app.add_config_value('autoapi_keep_files', True, 'html')
|
||||||
|
app.add_stylesheet('autoapi.css')
|
||||||
|
@ -1,24 +1,15 @@
|
|||||||
.. class:: {{ fullname }}({{ args|join(',') }})
|
.. class:: {{ object.name }}({{ object.args|join(',') }})
|
||||||
|
|
||||||
{% if docstring %}
|
{% if object.docstring %}
|
||||||
|
|
||||||
.. rubric:: Summary
|
.. rubric:: Summary
|
||||||
|
|
||||||
{{ docstring|indent(3) }}
|
{{ object.docstring|indent(3) }}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if methods %}
|
{% if methods %}
|
||||||
|
|
||||||
{% for class in classes %}
|
|
||||||
{% macro render() %}{{ class.render() }}{% endmacro %}
|
|
||||||
{{ render()|indent(3) }}
|
|
||||||
{%- endfor %}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if methods %}
|
|
||||||
|
|
||||||
{% for method in methods %}
|
{% for method in methods %}
|
||||||
|
|
||||||
{% macro render() %}{{ method.render() }}{% endmacro %}
|
{% macro render() %}{{ method.render() }}{% endmacro %}
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
{% if is_method %}
|
{% if is_method %}
|
||||||
{# Slice self off #}
|
{# Slice self off #}
|
||||||
.. method:: {{ fullname.split('.')[-1] }}({{ args[1:]|join(',') }})
|
.. method:: {{ object.name.split('.')[-1] }}({{ args[1:]|join(',') }})
|
||||||
{% else %}
|
{% else %}
|
||||||
.. function:: {{ fullname.split('.')[-1] }}({{ args|join(',') }})
|
.. function:: {{ object.name.split('.')[-1] }}({{ args|join(',') }})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if docstring %}
|
{% if object.docstring %}
|
||||||
{{ docstring|indent(3) }}
|
{{ object.docstring|indent(3) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{# Identention in this file is important #}
|
{# Identention in this file is important #}
|
||||||
|
|
||||||
.. {{ type }}:: {{ fullname }}
|
.. {{ obj.type }}:: {{ object.name }}
|
||||||
|
|
||||||
{{ docstring|indent(3) }}
|
{{ object.docstring|indent(3) }}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,38 +1,35 @@
|
|||||||
{{ fullname }}
|
{{ object.name }}
|
||||||
{{ underline }}
|
{{ "-" * object.name|length }}
|
||||||
|
|
||||||
{% if docstring %}
|
{% if object.docstring %}
|
||||||
|
|
||||||
.. rubric:: Summary
|
.. rubric:: Summary
|
||||||
|
|
||||||
{{ docstring }}
|
{{ object.docstring }}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
.. module:: {{ fullname }}
|
.. module:: {{ object.name }}
|
||||||
|
|
||||||
|
|
||||||
{% if classes %}
|
|
||||||
|
|
||||||
.. rubric:: Classes
|
{% block content %}
|
||||||
|
|
||||||
{% for class in classes %}
|
{%- macro display_type(item_type) %}
|
||||||
|
{%- if item_type in item_map %}
|
||||||
|
|
||||||
{{ class.render() }}
|
{{ item_type.title() }}
|
||||||
|
{{ "*" * item_type|length }}
|
||||||
|
|
||||||
{% endfor %}
|
{%- for obj_item in item_map.get(item_type, []) %}
|
||||||
|
{% macro render() %}{{ obj_item.render() }}{% endmacro %}
|
||||||
|
{{ render()|indent(4) }}
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
{% endif %}
|
{%- for item_type in ['function', 'class'] %}
|
||||||
|
{{ display_type(item_type) }}
|
||||||
|
{%- endfor %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
{% if methods %}
|
|
||||||
|
|
||||||
.. rubric:: Functions
|
|
||||||
|
|
||||||
{% for method in methods %}
|
|
||||||
|
|
||||||
{{ method.render() }}
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user