Skip to content

Commit

Permalink
Add optional JSON doc formatting options and adapt unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ValentinFrancois authored and jwg4 committed May 21, 2023
1 parent 91f6e20 commit 59c54fd
Show file tree
Hide file tree
Showing 10 changed files with 517 additions and 187 deletions.
10 changes: 5 additions & 5 deletions examples/custom/blog.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from os import path
from json import dumps

from flask import Flask, redirect, request, jsonify
from flask import Flask, redirect, request
from flask_selfdoc.autodoc import custom_jsonify
from flask_selfdoc import Autodoc


Expand Down Expand Up @@ -58,7 +58,7 @@ def get_post(id):

@app.route('/post', methods=["POST"])
@auto.doc(groups=['posts', 'private'],
form_data=['title', 'content', 'authorid'])
form_data=['title', 'content', 'authorid'])
def post_post():
"""Create a new post."""
authorid = request.form.get('authorid', None)
Expand All @@ -84,7 +84,7 @@ def get_user(id):

@app.route('/users', methods=['POST'])
@auto.doc(groups=['users', 'private'],
form_data=['username'])
form_data=['username'])
def post_user(id):
"""Creates a new user."""
User(request.form['username'])
Expand All @@ -111,7 +111,7 @@ def private_doc():

@app.route('/doc/json')
def public_doc_json():
return jsonify(auto.generate())
return custom_jsonify(auto.generate(), indent=4, separators=(',', ': '))


if __name__ == '__main__':
Expand Down
7 changes: 4 additions & 3 deletions examples/simple/blog.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from json import dumps

from flask import Flask, redirect, request, jsonify
from flask import Flask, redirect, request
from flask_selfdoc.autodoc import custom_jsonify
from flask_selfdoc import Autodoc


Expand Down Expand Up @@ -128,12 +129,12 @@ def private_doc():

@app.route('/doc/json')
def public_doc_json():
return jsonify(auto.generate())
return custom_jsonify(auto.generate(), indent=4, separators=(',', ': '))


@app.route('/doc/builtin_json')
def public_doc_builtin_json():
return auto.json()
return auto.json(indent=2, separators=(',', ': '))


if __name__ == '__main__':
Expand Down
24 changes: 21 additions & 3 deletions flask_selfdoc/autodoc.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import json
from operator import attrgetter, itemgetter
import os
import re
from collections import defaultdict
import sys
import inspect
from typing import Optional, Tuple

from flask import current_app, render_template, render_template_string, jsonify
from jinja2.exceptions import TemplateAssertionError
Expand Down Expand Up @@ -40,6 +42,19 @@
get_function_code = attrgetter('__code__')


def custom_jsonify(*args,
indent: Optional[int] = None,
separators: Optional[Tuple] = (',', ':'),
**kwargs):
response = jsonify(*args, **kwargs)
json_data = json.loads(response.data.decode('utf-8'))
json_string = json.dumps(json_data,
indent=indent,
separators=separators)
response.data = json_string.encode('utf-8')
return response


def get_decorator_frame_info(frame) -> dict:
"""
The way that the line number of a decorator is detected changed across
Expand Down Expand Up @@ -239,7 +254,7 @@ def generate(self, groups='all', sort=None):
methods=sorted(list(rule.methods)),
rule="%s" % rule,
endpoint=rule.endpoint,
docstring=func.__doc__,
docstring=func.__doc__.strip(' ') if func.__doc__ else None,
args=arguments,
defaults=rule.defaults or dict(),
location=location,
Expand Down Expand Up @@ -287,7 +302,10 @@ def html(self, groups='all', template=None, **context):
raise RuntimeError(
"Autodoc was not initialized with the Flask app.")

def json(self, groups='all'):
def json(self,
groups='all',
indent: Optional[int] = None,
separators: Optional[Tuple] = (',', ':')):
"""Return a json object with documentation for all the routes specified
by the doc() method.
Expand All @@ -310,7 +328,7 @@ def endpoint_info(doc):
'endpoints':
[endpoint_info(doc) for doc in autodoc]
}
return jsonify(data)
return custom_jsonify(data, indent=indent, separators=separators)


def sort_lexically(links):
Expand Down
1 change: 0 additions & 1 deletion run_tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import doctest
import logging
import os
import subprocess
import unittest

Expand Down
127 changes: 126 additions & 1 deletion tests/files/builtin.json
Original file line number Diff line number Diff line change
@@ -1 +1,126 @@
{"endpoints":[{"args":[],"docstring":"Return all posts.","methods":["GET","HEAD","OPTIONS"],"rule":"/"},{"args":[],"docstring":"Admin interface.","methods":["GET","HEAD","OPTIONS"],"rule":"/admin"},{"args":[["greeting","Hello"],["id",null]],"docstring":"Return the user for the given id.","methods":["GET","HEAD","OPTIONS"],"rule":"/greet/<greeting>/user/<int:id>"},{"args":[["id",-1]],"docstring":"Return the user for the given id.","methods":["GET","HEAD","OPTIONS"],"rule":"/hello/user/<int:id>"},{"args":[],"docstring":"Create a new post.\n Form Data: title, content, authorid.\n ","methods":["OPTIONS","POST"],"rule":"/post"},{"args":[["id",null]],"docstring":"Return the post for the given id.","methods":["GET","HEAD","OPTIONS"],"rule":"/post/<int:id>"},{"args":[],"docstring":"Return all posts.","methods":["GET","HEAD","OPTIONS"],"rule":"/posts"},{"args":[["id",null]],"docstring":"Return the user for the given id.","methods":["GET","HEAD","OPTIONS"],"rule":"/user/<int:id>"},{"args":[],"docstring":"Return all users.","methods":["GET","HEAD","OPTIONS"],"rule":"/users"},{"args":[],"docstring":"Creates a new user.\n Form Data: username.\n ","methods":["OPTIONS","POST"],"rule":"/users"}]}
{
"endpoints": [
{
"args": [],
"docstring": "Return all posts.",
"methods": [
"GET",
"HEAD",
"OPTIONS"
],
"rule": "/"
},
{
"args": [],
"docstring": "Admin interface.",
"methods": [
"GET",
"HEAD",
"OPTIONS"
],
"rule": "/admin"
},
{
"args": [
[
"greeting",
"Hello"
],
[
"id",
null
]
],
"docstring": "Return the user for the given id.",
"methods": [
"GET",
"HEAD",
"OPTIONS"
],
"rule": "/greet/<greeting>/user/<int:id>"
},
{
"args": [
[
"id",
-1
]
],
"docstring": "Return the user for the given id.",
"methods": [
"GET",
"HEAD",
"OPTIONS"
],
"rule": "/hello/user/<int:id>"
},
{
"args": [],
"docstring": "Create a new post.\n Form Data: title, content, authorid.\n",
"methods": [
"OPTIONS",
"POST"
],
"rule": "/post"
},
{
"args": [
[
"id",
null
]
],
"docstring": "Return the post for the given id.",
"methods": [
"GET",
"HEAD",
"OPTIONS"
],
"rule": "/post/<int:id>"
},
{
"args": [],
"docstring": "Return all posts.",
"methods": [
"GET",
"HEAD",
"OPTIONS"
],
"rule": "/posts"
},
{
"args": [
[
"id",
null
]
],
"docstring": "Return the user for the given id.",
"methods": [
"GET",
"HEAD",
"OPTIONS"
],
"rule": "/user/<int:id>"
},
{
"args": [],
"docstring": "Return all users.",
"methods": [
"GET",
"HEAD",
"OPTIONS"
],
"rule": "/users"
},
{
"args": [],
"docstring": "Creates a new user.\n Form Data: username.\n",
"methods": [
"OPTIONS",
"POST"
],
"rule": "/users"
}
]
}
Loading

0 comments on commit 59c54fd

Please sign in to comment.