diff --git a/.travis.yml b/.travis.yml index eedb46a..519b0f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,17 @@ python: - "2.7" - "3.3" - "3.4" + - "3.5" +env: + - FLASK=0.12 + - FLASK=1.0 + - FLASK=1.0.2 install: - - pip install -r requirements.txt + - pip install Flask==${FLASK} + - pip install flake8 script: - python -m unittest discover + - flake8 --exclude=examples --max-line-length=100 +notifications: + email: false + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1337f53 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "${workspaceFolder}\\venv\\Scripts\\python.exe" +} \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index d03edc7..c2ccd5e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ include README.md -include flask_autodoc/templates/autodoc_default.html +include flask_selfdoc/templates/autodoc_default.html diff --git a/README.md b/README.md index ab8a42e..b43ebb8 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,24 @@ -Flask-Autodoc +Flask-Selfdoc ============= -Flask-Autodoc is a Flask extension that automatically creates documentation for your endpoints based on the routes, function arguments and docstrings. +Flask-Selfdoc is a Flask extension that automatically creates documentation for your endpoints based on the routes, function arguments and docstrings. It was forked from Flask-Autodoc, written by Arnaud Coomans, and is completely compatible as a replacement for that extension. -[![Build](https://api.travis-ci.org/acoomans/flask-autodoc.png)](https://travis-ci.org/acoomans/flask-autodoc) -[![Pypi version](http://img.shields.io/pypi/v/flask-autodoc.svg)](https://pypi.python.org/pypi/Flask-Autodoc) -[![Pypi license](http://img.shields.io/pypi/l/flask-autodoc.svg)](https://pypi.python.org/pypi/Flask-Autodoc) +[![Build](https://api.travis-ci.org/jwg4/flask-selfdoc.png)](https://travis-ci.org/jwg4/flask-selfdoc) +[![Pypi version](http://img.shields.io/pypi/v/flask-selfdoc.svg)](https://pypi.python.org/pypi/Flask-Selfdoc) +[![Pypi license](http://img.shields.io/pypi/l/flask-selfdoc.svg)](https://pypi.python.org/pypi/Flask-Selfdoc) ![Python 2](http://img.shields.io/badge/python-2-blue.svg) ![Python 3](http://img.shields.io/badge/python-3-blue.svg) ## Requirements -Flask-Autodoc is compatible with Python versions 2 and 3; and it depends only on Flask. +Flask-Selfdoc is compatible with Python versions 2 and 3; and it depends only on Flask. ## Install -To install Flask-Autodoc, run pip: +To install Flask-Selfdoc, run pip: - pip install flask-autodoc + pip install flask-selfdoc or clone this directory and run setup: @@ -26,15 +26,15 @@ or clone this directory and run setup: ## Usage -Start using Flask-Autodoc by importing it and initializing it: +Start using Flask-Selfdoc by importing it and initializing it: from flask import Flask - from flask.ext.autodoc import Autodoc + from flask_selfdoc import Autodoc app = Flask(__name__) auto = Autodoc(app) -by default, Flask-Autodoc will only document the routes explicitly decorated with _doc_: +by default, Flask-Selfdoc will only document the routes explicitly decorated with _doc_: @app.route('/user/') @auto.doc() @@ -127,4 +127,4 @@ and connect to [/doc/public](http://127.0.0.1:5000/doc/public) and [/doc/private ![screenshots](screenshots/screenshot00.png) -![screenshots](screenshots/screenshot01.png) \ No newline at end of file +![screenshots](screenshots/screenshot01.png) diff --git a/examples/custom/blog.py b/examples/custom/blog.py index f32436c..5f4c456 100644 --- a/examples/custom/blog.py +++ b/examples/custom/blog.py @@ -2,7 +2,7 @@ from json import dumps from flask import Flask, redirect, request -from flask.ext.autodoc import Autodoc +from flask_selfdoc import Autodoc app = Flask(__name__) diff --git a/examples/factory/blog/doc.py b/examples/factory/blog/doc.py index 17307a6..c245eba 100644 --- a/examples/factory/blog/doc.py +++ b/examples/factory/blog/doc.py @@ -1,5 +1,5 @@ from flask import Blueprint -from flask.ext.autodoc import Autodoc +from flask_selfdoc import Autodoc doc = Blueprint('doc', __name__, url_prefix='/doc') diff --git a/examples/simple/blog.py b/examples/simple/blog.py index 52ca75c..31d2ac8 100644 --- a/examples/simple/blog.py +++ b/examples/simple/blog.py @@ -1,7 +1,7 @@ from json import dumps from flask import Flask, redirect, request -from flask.ext.autodoc import Autodoc +from flask_selfdoc import Autodoc app = Flask(__name__) diff --git a/flask_autodoc/__init__.py b/flask_autodoc/__init__.py deleted file mode 100644 index 89e047e..0000000 --- a/flask_autodoc/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -__author__ = 'arnaud' - -from flask.ext.autodoc.autodoc import Autodoc diff --git a/flask_selfdoc/__init__.py b/flask_selfdoc/__init__.py new file mode 100644 index 0000000..3879f7d --- /dev/null +++ b/flask_selfdoc/__init__.py @@ -0,0 +1,3 @@ +__author__ = 'arnaud' + +from flask_selfdoc.autodoc import Autodoc # noqa: F401 diff --git a/flask_autodoc/autodoc.py b/flask_selfdoc/autodoc.py similarity index 86% rename from flask_autodoc/autodoc.py rename to flask_selfdoc/autodoc.py index bb8f23b..4367d88 100644 --- a/flask_autodoc/autodoc.py +++ b/flask_selfdoc/autodoc.py @@ -5,7 +5,7 @@ import sys import inspect -from flask import current_app, render_template, render_template_string +from flask import current_app, render_template, render_template_string, jsonify from jinja2 import evalcontextfilter @@ -28,7 +28,8 @@ def __init__(self, app=None): self.func_groups = defaultdict(set) self.func_props = defaultdict() self.immutable_props = ['rule', 'endpoint'] - self.default_props = ['methods', 'docstring', + self.default_props = [ + 'methods', 'docstring', 'args', 'defaults', 'location'] + self.immutable_props self.func_locations = defaultdict(dict) if app is not None: @@ -42,7 +43,7 @@ def init_app(self, app): self.add_custom_template_filters(app) def teardown(self, exception): - ctx = stack.top + ctx = stack.top # noqa: F841 def add_custom_template_filters(self, app): """Add custom filters to jinja2 templating engine""" @@ -176,6 +177,8 @@ def html(self, groups='all', template=None, **context): By specifying the group or groups arguments, only routes belonging to those groups will be returned. """ + if not self.app: + raise RuntimeError("Autodoc was not initialized with the Flask app.") context['autodoc'] = context['autodoc'] if 'autodoc' in context \ else self.generate(groups=groups) context['defaults'] = context['defaults'] if 'defaults' in context \ @@ -192,3 +195,28 @@ def html(self, groups='all', template=None, **context): content = file.read() with current_app.app_context(): return render_template_string(content, **context) + + def json(self, groups='all'): + """Return a json object with documentation for all the routes specified + by the doc() method. + + By specifiying the groups argument, only routes belonging to those groups + will be returned. + """ + autodoc = self.generate(groups=groups) + + def endpoint_info(doc): + args = doc['args'] + if args == ['None']: + args = [] + return { + "args": [(arg, doc['defaults'][arg]) for arg in args], + "docstring": doc['docstring'], + "methods": sorted(list(doc['methods'])), + "rule": doc['rule'] + } + data = { + 'endpoints': + [endpoint_info(doc) for doc in autodoc] + } + return jsonify(data) diff --git a/flask_autodoc/templates/autodoc_default.html b/flask_selfdoc/templates/autodoc_default.html similarity index 100% rename from flask_autodoc/templates/autodoc_default.html rename to flask_selfdoc/templates/autodoc_default.html diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e3e9a71..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -Flask diff --git a/setup.py b/setup.py index 066e351..dba1f9a 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,8 @@ """ -Flask-Autodoc +Flask-Selfdoc ------------- -Flask autodoc automatically creates an online documentation for your flask app. +Flask selfdoc automatically creates an online documentation for your flask app. """ from setuptools import setup @@ -13,19 +13,20 @@ def readme(): setup( - name='Flask-Autodoc', - version='0.1.2', - url='http://github.com/acoomans/flask-autodoc', + name='Flask-Selfdoc', + version='1.0.2', + url='http://github.com/jwg4/flask-selfdoc', license='MIT', author='Arnaud Coomans', - author_email='arnaud.coomans@gmail.com', + maintainer='Jack Grahl', + maintainer_email='jack.grahl@gmail.com', description='Documentation generator for flask', long_description=readme(), # py_modules=['flask_autodoc'], # if you would be using a package instead use packages instead # of py_modules: - packages=['flask_autodoc'], - package_data={'flask_autodoc': ['templates/autodoc_default.html']}, + packages=['flask_selfdoc'], + package_data={'flask_selfdoc': ['templates/autodoc_default.html']}, zip_safe=False, include_package_data=True, platforms='any', @@ -41,5 +42,5 @@ def readme(): 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules' ], - test_suite='tests.test_autodoc', + test_suite='tests', ) diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index e839c2f..966c630 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -5,7 +5,7 @@ import os from flask import Flask -from flask.ext.autodoc import Autodoc +from flask_selfdoc import Autodoc class TestAutodoc(unittest.TestCase): @@ -157,13 +157,14 @@ def testCustomParams(self): @self.app.route('/needsargs', methods=['GET']) @self.autodoc.doc('needs_getargs', getargs={ 'a': 'A Value', - 'b': 'B Value' + 'b': 'B Value' }) def getit(): return 'I need specific GET parameters.' @self.app.route('/noargs') - @self.autodoc.doc(groups=['needs_json', 'noargs'], + @self.autodoc.doc( + groups=['needs_json', 'noargs'], expected_type='application/json') def needjson(): return 'I do not need any parameters, but am picky about types.' @@ -259,7 +260,8 @@ def ab(param1, param2): self.assertIn('Returns arguments', doc) def testLocation(self): - line_no = inspect.stack()[0][2] + 2 # the doc() line + line_no = inspect.stack()[0][2] + 3 # the doc() line + @self.app.route('/location') @self.autodoc.doc() def location(): @@ -298,4 +300,3 @@ def redecorate(): self.assertTrue(1 == len(self.autodoc.generate('group1'))) self.assertTrue(1 == len(self.autodoc.generate('group2'))) self.assertFalse(1 == len(self.autodoc.generate('group3'))) - diff --git a/tests/test_error_handling.py b/tests/test_error_handling.py new file mode 100644 index 0000000..cb37698 --- /dev/null +++ b/tests/test_error_handling.py @@ -0,0 +1,13 @@ +import unittest + +from flask import Flask +from flask_selfdoc import Autodoc + + +class TestErrorHandling(unittest.TestCase): + def test_app_not_initialized(self): + app = Flask(__name__) + app.debug = True + autodoc = Autodoc() + with app.app_context(): + self.assertRaises(RuntimeError, lambda: autodoc.html()) diff --git a/tests/test_flask_get.py b/tests/test_flask_get.py new file mode 100644 index 0000000..42c59da --- /dev/null +++ b/tests/test_flask_get.py @@ -0,0 +1,48 @@ +import json +import unittest + +from flask import Flask +from flask_selfdoc import Autodoc + + +class TestAutodocWithFlask(unittest.TestCase): + def setUp(self): + self.app = Flask(__name__) + self.autodoc = Autodoc(self.app) + + @self.app.route('/') + @self.autodoc.doc() + def index(): + """Returns a hello world message""" + return 'Hello World!' + + self.client = self.app.test_client() + + def test_html(self): + @self.app.route('/docs') + def html_docs(): + return self.autodoc.html() + + response = self.client.get('/docs') + self.assertEqual(response.status_code, 200) + + def test_json(self): + @self.app.route('/docs') + def json_docs(): + return self.autodoc.json() + + response = self.client.get('/docs') + self.assertEqual(response.status_code, 200) + + data = json.loads(response.data.decode('utf-8')) + self.assertIn('endpoints', data) + self.assertEqual(len(data['endpoints']), 1) + + endpoint = data['endpoints'][0] + expected = { + "args": [], + "docstring": "Returns a hello world message", + "methods": ["GET", "HEAD", "OPTIONS"], + "rule": "/" + } + self.assertEqual(endpoint, expected)