Retire stackforge/sphinxcontrib-docbookrestapi
This commit is contained in:
parent
c2470abd86
commit
895836149f
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=stackforge/sphinxcontrib-docbookrestapi.git
|
@ -1,4 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_command=${PYTHON:-python} -m subunit.run discover sphinxcontrib/docbookrestapi/tests $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
204
LICENSE
204
LICENSE
@ -1,204 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
--- License for python-keystoneclient versions prior to 2.1 ---
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of this project nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,4 +1,7 @@
|
||||
sphinxcontrib-docbookrestapi
|
||||
=======================
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git source code
|
||||
management system. To see the contents of this repository before it reached
|
||||
its end of life, please check out the previous commit with
|
||||
"git checkout HEAD^1".
|
||||
|
||||
Extension to Sphinx for generating REST API documentation for http://api.openstack.org/
|
||||
|
@ -1,3 +0,0 @@
|
||||
docutils==0.9.1
|
||||
pytidylib6
|
||||
sphinx>=1.1.2,<1.2
|
31
setup.cfg
31
setup.cfg
@ -1,31 +0,0 @@
|
||||
[metadata]
|
||||
name = sphinxcontrib-docbookrestapi
|
||||
author = Cyril Roelandt
|
||||
author-email = cyril.roelandt@enovance.com
|
||||
summary = Extension to Sphinx for generating REST API documentation for http://api.openstack.org/
|
||||
description-file = README.rst
|
||||
license = Apache-2
|
||||
classifier =
|
||||
Development Status :: 3 - Alpha
|
||||
Environment :: Console
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: Information Technology
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.3
|
||||
Topic :: Software Development :: Documentation
|
||||
keywords =
|
||||
documentation
|
||||
docbook
|
||||
sphinx
|
||||
version=0.2.1
|
||||
|
||||
[files]
|
||||
packages =
|
||||
sphinxcontrib
|
||||
sphinxcontrib.docbookrestapi
|
||||
namespace_packages =
|
||||
sphinxcontrib
|
8
setup.py
8
setup.py
@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
setup_requires=['pbr'],
|
||||
pbr=True,
|
||||
)
|
@ -1,17 +0,0 @@
|
||||
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
|
||||
#
|
||||
# Author: Cyril Roelandt <cyril.roelandt@enovance.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
@ -1,400 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
|
||||
#
|
||||
# Author: Cyril Roelandt <cyril.roelandt@enovance.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from datetime import date
|
||||
from docutils.nodes import SparseNodeVisitor, StopTraversal
|
||||
import json
|
||||
import os
|
||||
from sphinx.builders import Builder
|
||||
import tidylib
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
output_file = None
|
||||
|
||||
|
||||
def generate_id(path, method):
|
||||
path = path.replace('(', '')
|
||||
path = path.replace(')', '')
|
||||
elems = path.split('/')
|
||||
elems = list(filter(lambda x: x, elems)) # Remove empty strings
|
||||
elems = elems[1:] # Remove "vx" (v1, v2...)
|
||||
|
||||
n_elems = len(elems)
|
||||
name = ""
|
||||
|
||||
if method == 'delete':
|
||||
if elems[-1].endswith('_id'):
|
||||
name += "delete" + elems[-1][0:-3].capitalize()
|
||||
elif method == 'get':
|
||||
if elems[-1].endswith('_id'):
|
||||
name += "show" + elems[-1][0:-3].capitalize()
|
||||
elif elems[-1].endswith('_name'):
|
||||
name += "show" + elems[-1][0:-5].capitalize()
|
||||
elif n_elems > 2:
|
||||
if elems[-3][:-1] + '_id' == elems[-2]:
|
||||
name += 'show' + elems[-3][:-1].capitalize()
|
||||
name += elems[-1].capitalize()
|
||||
elif elems[-3][:-1] + '_name' == elems[-2]:
|
||||
name += 'show' + elems[-3][:-1].capitalize()
|
||||
name += elems[-1].capitalize()
|
||||
else:
|
||||
name += "list" + elems[-1].capitalize()
|
||||
elif method == 'post':
|
||||
if elems[-1].endswith('_id'):
|
||||
name += "create" + elems[-1][0:-3].capitalize()
|
||||
elif elems[-1].endswith('_name'):
|
||||
name += "create" + elems[-1][0:-5].capitalize()
|
||||
else:
|
||||
name += "create" + elems[-1][:-1].capitalize()
|
||||
elif method == 'put':
|
||||
if elems[-1].endswith('_id'):
|
||||
name += "update" + elems[-1][0:-3].capitalize()
|
||||
elif elems[-2].endswith('_id'):
|
||||
# The name is probably something like ".../foos/foo_id/bar". This
|
||||
# is the case in Ceilometer for "/v2/alarms/alarm_id/state"
|
||||
name += "update" + elems[-2][0:-3].capitalize() + \
|
||||
elems[-1].capitalize()
|
||||
|
||||
if not name:
|
||||
name = raw_input('id for %s (%s)' % (path, method))
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def generate_title_from_id(id_):
|
||||
words = []
|
||||
current_start = 0
|
||||
for i, char in enumerate(id_):
|
||||
if not char.islower():
|
||||
words.append(id_[current_start:i].lower())
|
||||
current_start = i
|
||||
|
||||
if i > current_start:
|
||||
words.append(id_[current_start:])
|
||||
|
||||
return ' '.join(words).capitalize()
|
||||
|
||||
|
||||
def clean_up_xml(xml_str):
|
||||
# When using UTF-8, tidy does not add an encoding attribute. Do it
|
||||
# ourselves. See
|
||||
# http://tidy.cvs.sourceforge.net/viewvc/tidy/tidy/src/lexer.c?
|
||||
# revision=1.194&view=markup
|
||||
xml_str = xml_str.replace('?>', ' encoding="UTF-8"?>', 1)
|
||||
|
||||
# tidy automatically inserts a whitespace at the end of a self-closing tag.
|
||||
# See line 1347 at:
|
||||
# http://tidy.cvs.sourceforge.net/viewvc/tidy/tidy/src/pprint.c?
|
||||
# revision=1.119&view=markup
|
||||
|
||||
xml_str = xml_str.replace(' />', '/>')
|
||||
|
||||
# Add this comment right after the <?...?> line. Not sure how to do this
|
||||
# using ElementTree.Comment(), since there is no parent element here.
|
||||
# XXX(cyril): The starting year may not be the right one for every project.
|
||||
xml_str = xml_str.replace('?>\n', '''?>
|
||||
<!-- (C) 2012-%d OpenStack Foundation, All Rights Reserved -->
|
||||
<!--*******************************************************-->
|
||||
<!-- Import Common XML Entities -->
|
||||
<!-- -->
|
||||
<!-- You can resolve the entites with xmllint -->
|
||||
<!-- -->
|
||||
<!-- xmllint -noent os-compute-2.wadl -->
|
||||
<!--*******************************************************-->
|
||||
''' % date.today().year, 1)
|
||||
|
||||
return xml_str
|
||||
|
||||
|
||||
class MyNodeVisitor(SparseNodeVisitor):
|
||||
def __init__(self, document):
|
||||
SparseNodeVisitor.__init__(self, document)
|
||||
|
||||
self.must_parse = False
|
||||
self.methods = []
|
||||
self.paths = {}
|
||||
self.current_method = None
|
||||
self.root = None
|
||||
self.in_method_definition = False
|
||||
self.needs_method_description = False
|
||||
self.in_bullet_list = False
|
||||
self.in_request = False
|
||||
self.in_response = False
|
||||
self.current_request_example = []
|
||||
self.current_response_example = []
|
||||
|
||||
def depart_document(self, node):
|
||||
if not self.must_parse:
|
||||
# We've probably raised StopTraversal, but walkabout() will keep
|
||||
# running the depart_* functions. Here, this is only
|
||||
# depart_document(), so we have to leave early.
|
||||
return
|
||||
|
||||
# We are done parsing this document, let's print everything we need.
|
||||
resources = ET.SubElement(self.root, 'resources', {
|
||||
'base': 'http://www.example.com'
|
||||
})
|
||||
|
||||
# Create 'd', a dict of the form:
|
||||
# {
|
||||
# 'foo': {
|
||||
# 'bar': {},
|
||||
# 'baz': {}
|
||||
# }
|
||||
# }
|
||||
# If the paths are 'foo/bar' and 'foo/baz'.
|
||||
d = {}
|
||||
for path, methods in self.paths.iteritems():
|
||||
cd = d
|
||||
l = path[1:-1].replace('(', '{').replace(')', '}').split('/')
|
||||
for e in l:
|
||||
if e not in cd:
|
||||
cd[e] = {}
|
||||
cd = cd[e]
|
||||
|
||||
def build_resources(root, d, path=''):
|
||||
for k, v in d.iteritems():
|
||||
tmp = ET.SubElement(root, 'resource', {
|
||||
# NOTE(cyril): sometimes, id and path might differ. This
|
||||
# should be good enough, though.
|
||||
'id': k.replace('{', '').replace('}', ''),
|
||||
'path': k,
|
||||
})
|
||||
if path + '/' + k + '/' in self.paths:
|
||||
for method in self.paths[path + '/' + k + '/']:
|
||||
ET.SubElement(tmp, 'method', {'href': '#' + method})
|
||||
build_resources(tmp, v, path + '/' + k)
|
||||
|
||||
build_resources(resources, d)
|
||||
|
||||
# Now, add all the methods we've gathered.
|
||||
for method in self.methods:
|
||||
self.root.append(method)
|
||||
|
||||
# Finally, write the output.
|
||||
with open(output_file, 'w+') as f:
|
||||
options = {
|
||||
'add-xml-decl': True,
|
||||
'indent': True,
|
||||
'indent-spaces': 4,
|
||||
'input-xml': True,
|
||||
'output-encoding': 'utf8',
|
||||
'output-xml': True,
|
||||
'wrap': 70
|
||||
}
|
||||
xml_str = tidylib.tidy_document(ET.tostring(self.root),
|
||||
options=options)[0]
|
||||
f.write(clean_up_xml(xml_str))
|
||||
|
||||
# If we're inside a bullet list, all the "paragraph" elements will be
|
||||
# parameters description, so we need to know whether we currently are in a
|
||||
# bullet list.
|
||||
def depart_bullet_list(self, node):
|
||||
self.in_bullet_list = False
|
||||
|
||||
def visit_bullet_list(self, node):
|
||||
self.in_bullet_list = True
|
||||
|
||||
def visit_comment(self, node):
|
||||
# If this .rst file is meant to be parsed by
|
||||
# sphinxcontrib-docbookrestapi, then it must have a comment, before the
|
||||
# first section, that just reads 'docbookrestapi'.
|
||||
if node.astext() == 'docbookrestapi':
|
||||
self.must_parse = True
|
||||
|
||||
def visit_document(self, node):
|
||||
# This is where we should start. Let's build the root.
|
||||
attrs = {
|
||||
'xmlns': 'http://wadl.dev.java.net/2009/02',
|
||||
'xmlns:xsdxt': 'http://docs.rackspacecloud.com/xsd-ext/v1.0',
|
||||
'xmlns:wadl': 'http://wadl.dev.java.net/2009/02',
|
||||
'xmlns:xsd': 'http://docs.rackspacecloud.com/xsd/v1.0'
|
||||
}
|
||||
self.root = ET.Element('application', attrs)
|
||||
|
||||
# If we are in a 'desc' node with the 'domain' attribute equal to 'http',
|
||||
# we're in a method definition. The next 'paragraph' element will be the
|
||||
# description of this method.
|
||||
def depart_desc(self, node):
|
||||
self.in_method_definition = False
|
||||
|
||||
def visit_desc(self, node):
|
||||
attrs = dict(node.attlist())
|
||||
if attrs['domain'] == 'http':
|
||||
self.in_method_definition = True
|
||||
|
||||
def visit_desc_signature(self, node):
|
||||
attrs = dict(node.attlist())
|
||||
if 'method' in attrs and 'path' in attrs:
|
||||
method_id = generate_id(attrs['path'], attrs['method'])
|
||||
self.current_method = ET.Element('method', {
|
||||
'id': method_id,
|
||||
'name': attrs['method'].upper()
|
||||
})
|
||||
self.methods.append(self.current_method)
|
||||
path = attrs['path'].replace('(', '{').replace(')', '}')
|
||||
self.paths.setdefault(path, []).append(method_id)
|
||||
self.current_wadl_doc = ET.SubElement(
|
||||
self.current_method, 'wadl:doc', {
|
||||
'xmlns': 'http://docbook.org/ns/docbook',
|
||||
'xml:lang': 'EN',
|
||||
'title': generate_title_from_id(method_id)
|
||||
}
|
||||
)
|
||||
self.current_request = ET.SubElement(self.current_method,
|
||||
'request')
|
||||
self.current_response = ET.SubElement(self.current_method,
|
||||
'response',
|
||||
{'status': '200'})
|
||||
self.needs_method_description = True
|
||||
|
||||
def visit_paragraph(self, node):
|
||||
if self.in_method_definition and self.needs_method_description:
|
||||
text = node.astext()
|
||||
|
||||
# Remove ":type data: foo" and ":return type: bar".
|
||||
type_index = text.find(':type data:')
|
||||
return_index = text.find(':return type:')
|
||||
min_index = min(type_index, return_index)
|
||||
if min_index > -1:
|
||||
text = text[0:min_index]
|
||||
|
||||
# Create the doc node in the method.
|
||||
ET.SubElement(self.current_wadl_doc, 'para', {
|
||||
'role': 'shortdesc'
|
||||
}).text = text
|
||||
self.needs_method_description = False
|
||||
elif self.in_bullet_list:
|
||||
# We're describing a parameter here.
|
||||
# They look like "param_name (type) -- description in one or more
|
||||
# words".
|
||||
text = node.astext()
|
||||
dashes_index = text.find('--')
|
||||
param_name = text[0:text.find(' ')]
|
||||
param_type = text[text.find(' ') + 1: dashes_index - 2]
|
||||
param_descr = text[dashes_index + 3:]
|
||||
param_type = param_type[1:] # Remove '('
|
||||
# Sometimes (especially when using enumerations), only some values
|
||||
# may be allowed. If so, they should be listed in the
|
||||
# documentation; store them here, and insert them in the code when
|
||||
# creating the required elements.
|
||||
valid_values = None
|
||||
|
||||
# There are probably more types.
|
||||
if param_type.startswith("Enum"):
|
||||
valid_values = param_type[5:-1].split(', ')
|
||||
param_type = 'xsd:dict'
|
||||
elif param_type.startswith("int"):
|
||||
param_type = 'xsd:int'
|
||||
elif param_type.startswith("list"):
|
||||
param_type = 'xsd:list'
|
||||
elif param_type .startswith("unicode"):
|
||||
param_type = 'xsd:string'
|
||||
else:
|
||||
param_type = param_type[:-1] # Remove ')'
|
||||
|
||||
tmp = ET.SubElement(self.current_request, 'param', {
|
||||
'name': param_name,
|
||||
'type': param_type,
|
||||
'required': 'false', # XXX Can we get the right value ?
|
||||
'style': 'query' # XXX Can we get the right value ?
|
||||
})
|
||||
tmp = ET.SubElement(tmp, 'wadl:doc', {
|
||||
'xml:lang': 'EN',
|
||||
'xmlns': 'http://docbook.org/ns/docbook'
|
||||
})
|
||||
tmp = ET.SubElement(tmp, 'para')
|
||||
tmp.text = param_descr
|
||||
if valid_values:
|
||||
tmp.text += ' Valid values are '
|
||||
for i, value in enumerate(valid_values):
|
||||
code = ET.SubElement(tmp, 'code')
|
||||
code.text = value
|
||||
if i + 1 != len(valid_values):
|
||||
code.tail = ', '
|
||||
if i + 2 == len(valid_values):
|
||||
code.tail += 'or '
|
||||
else:
|
||||
code.tail = '.'
|
||||
elif self.in_request or self.in_response:
|
||||
self.visit_term(node)
|
||||
|
||||
def visit_section(self, node):
|
||||
# If, by the time we visit the first section, we have not determined
|
||||
# that this .rst file defines a REST API, then we probably should not
|
||||
# be parsing it.
|
||||
if not self.must_parse:
|
||||
raise StopTraversal
|
||||
|
||||
def visit_term(self, node):
|
||||
if self.in_request:
|
||||
self.current_request_example.append(node.astext())
|
||||
elif self.in_response:
|
||||
self.current_response_example.append(node.astext())
|
||||
|
||||
def _finalize_json_example(self, parent, body):
|
||||
tmp = ET.SubElement(parent, 'representation', {
|
||||
'mediaType': 'application/json'
|
||||
})
|
||||
tmp = ET.SubElement(tmp, 'wadl:doc', {'xml:lang': 'EN'})
|
||||
json_text = json.loads(''.join(body))
|
||||
json_text = json.dumps(json_text, indent=4, sort_keys=True)
|
||||
ET.SubElement(tmp, 'xsdxt:code').text = json_text
|
||||
|
||||
def visit_field_name(self, node):
|
||||
text = node.astext()
|
||||
if text == "Request json":
|
||||
self.in_request = True
|
||||
self.in_responses = False
|
||||
elif text == "Response json":
|
||||
self.in_request = False
|
||||
self.in_response = True
|
||||
else:
|
||||
self.in_request = False
|
||||
if self.current_request_example:
|
||||
self._finalize_json_example(self.current_request,
|
||||
self.current_request_example)
|
||||
self.current_request_example = []
|
||||
self.in_response = False
|
||||
if self.current_response_example:
|
||||
self._finalize_json_example(self.current_response,
|
||||
self.current_response_example)
|
||||
self.current_response_example = []
|
||||
|
||||
|
||||
class DocBookBuilder(Builder):
|
||||
name = 'docbook'
|
||||
format = 'docbook'
|
||||
out_suffix = '.wadl'
|
||||
|
||||
def get_outdated_docs(self):
|
||||
return 'all documents' # XXX for now
|
||||
|
||||
def prepare_writing(self, docnames):
|
||||
pass
|
||||
|
||||
def write_doc(self, docname, doctree):
|
||||
global output_file
|
||||
output_file = os.path.join(self.outdir, os.path.basename(docname) +
|
||||
self.out_suffix)
|
||||
|
||||
visitor = MyNodeVisitor(doctree)
|
||||
doctree.walkabout(visitor)
|
||||
|
||||
def get_target_uri(self, docname, typ=None):
|
||||
return ''
|
@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
|
||||
#
|
||||
# Author: Cyril Roelandt <cyril.roelandt@enovance.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from sphinxcontrib.docbookrestapi.docbook import DocBookBuilder
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_builder(DocBookBuilder)
|
@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
|
||||
#
|
||||
# Author: Cyril Roelandt <cyril.roelandt@enovance.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
from sphinxcontrib.docbookrestapi.docbook import clean_up_xml, generate_id
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
def test_generate_id(self):
|
||||
test_cases = [
|
||||
# (path, method, expected_result)
|
||||
('/v2/foos', 'get', 'listFoos'),
|
||||
|
||||
('/v2/foos/foo_id', 'delete', 'deleteFoo'),
|
||||
('/v2/foos/foo_id', 'get', 'showFoo'),
|
||||
('/v2/foos/foo_id', 'post', 'createFoo'),
|
||||
('/v2/foos/foo_id', 'put', 'updateFoo'),
|
||||
|
||||
('/v2/foos/foo_id/bar', 'get', 'showFooBar'),
|
||||
('/v2/foos/foo_id/bar', 'put', 'updateFooBar'),
|
||||
|
||||
('/v2/foos/foo_name', 'get', 'showFoo'),
|
||||
('/v2/foos/foo_name', 'post', 'createFoo'),
|
||||
|
||||
('/v2/foos/foo_name/bar', 'get', 'showFooBar'),
|
||||
]
|
||||
|
||||
for (path, method, result) in test_cases:
|
||||
self.assertEqual(generate_id(path, method), result)
|
||||
|
||||
def test_clean_up_xml_encoding(self):
|
||||
# Make sure the right encoding is added.
|
||||
self.assertEqual(
|
||||
clean_up_xml('<?xml version="1.0"?>'),
|
||||
'<?xml version="1.0" encoding="UTF-8"?>'
|
||||
)
|
||||
|
||||
def test_clean_up_xml(self):
|
||||
# Make sure the whitespace at the end of a self-closing tag is removed.
|
||||
bad_xml = '''
|
||||
<root>
|
||||
<selfclosingtag />
|
||||
</root>'''
|
||||
good_xml = '''
|
||||
<root>
|
||||
<selfclosingtag/>
|
||||
</root>'''
|
||||
self.assertEqual(clean_up_xml(bad_xml), good_xml)
|
||||
self.assertEqual(clean_up_xml(good_xml), good_xml)
|
@ -1,2 +0,0 @@
|
||||
discover
|
||||
testrepository>=0.0.17
|
29
tox.ini
29
tox.ini
@ -1,29 +0,0 @@
|
||||
[tox]
|
||||
minversion = 1.6
|
||||
skipsdist = True
|
||||
envlist = pep8,py26,py27,py33
|
||||
|
||||
[testenv]
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
install_command = pip install -U {opts} {packages}
|
||||
usedevelop = True
|
||||
commands =
|
||||
python setup.py testr --slowest --testr-args='{posargs}'
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
# Install bounded pep8/pyflakes first, then let flake8 install
|
||||
deps = pep8==1.4.5
|
||||
pyflakes==0.7.2
|
||||
flake8==2.0
|
||||
hacking>=0.5.6,<0.8
|
||||
commands =
|
||||
flake8
|
||||
|
||||
|
||||
[flake8]
|
||||
exclude=.venv,.git,.tox,*egg,build
|
||||
show-source = True
|
Loading…
x
Reference in New Issue
Block a user