From 6323a74071f2f50c7fac247a9d5101f0ed14c0d9 Mon Sep 17 00:00:00 2001 From: Steven Robertson Date: Tue, 29 Nov 2016 01:18:29 +0000 Subject: [PATCH 1/2] added support for template yml files in docstring, needs testing --- examples/example.py | 25 +++++++++- examples/user_put.yml | 7 ++- flask_swagger.py | 104 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 114 insertions(+), 22 deletions(-) diff --git a/examples/example.py b/examples/example.py index 4506572..95c2f9b 100644 --- a/examples/example.py +++ b/examples/example.py @@ -49,9 +49,32 @@ def post(self, team_id): def put(self, user_id): """ - Update a user + Update a user, this summary stays swagger_from_file: user_put.yml + + --- + tags: + - user_update + parameters: + - name: filename + in: formData + description: filename. this overrides what's in the .yml file + required: false + type: string + - name: username + in: formData + description: username + required: false + type: file + - name: password + in: formData + description: password + required: false + type: file + responses: + 200: + description: this description stays """ return {} diff --git a/examples/user_put.yml b/examples/user_put.yml index a11d5f6..eea2696 100644 --- a/examples/user_put.yml +++ b/examples/user_put.yml @@ -1,4 +1,4 @@ -Update a user +Update a user in yml --- tags: - users @@ -17,6 +17,11 @@ parameters: name: type: string description: name for user + - name: filename + in: formData + description: filename of the yaml or python script used for training. This is not used as there's a filename already. + required: true + type: string responses: 204: description: User updated diff --git a/flask_swagger.py b/flask_swagger.py index 352e201..93ece54 100644 --- a/flask_swagger.py +++ b/flask_swagger.py @@ -17,24 +17,27 @@ def _sanitize(comment): return comment.replace('\n', '
') if comment else comment -def _find_from_file(full_doc, from_file_keyword): +def _find_and_remove_from_file(full_doc, from_file_keyword): """ Finds a line in like - and return path + and return path. Also remove the line from the docstring. """ path = None - for line in full_doc.splitlines(): + full_doc_lines = full_doc.splitlines() + for line_count in xrange(len(full_doc_lines) - 1, -1, -1): + line = full_doc_lines[line_count] if from_file_keyword in line: parts = line.strip().split(':') if len(parts) == 2 and parts[0].strip() == from_file_keyword: path = parts[1].strip() + del full_doc_lines[line_count] break - return path + return path, '\n'.join(full_doc_lines) def _doc_from_file(path): @@ -44,27 +47,88 @@ def _doc_from_file(path): return doc +def _merge_swag(swag, doc_swag): + # recursively update any dicts that are the same + if isinstance(swag, dict) and isinstance(doc_swag, dict): + for key, val in doc_swag.items(): + if key not in swag: + swag[key] = val + else: + swag[key] = _merge_swag(swag[key], val) + # if there are multiple params for each type, then + # we'll check the `name` to see what to override, if anything + elif isinstance(swag, list) and isinstance(doc_swag, list): + # AFAIK both lists should contain the same datatype + if not swag: + swag = doc_swag + # for strings and lists, we'll go with the default swag + # for dicts, we'll add doc_swag to our swag if there's a `name` val + # that's not in any of the swag dicts + elif swag and isinstance(swag[0], dict): + # grab all the names of the dict objs + swag_names = [] + for item in swag: + name = [val for key, val in item.items() if key == 'name'] + if name: + swag_names.append(name[0]) + for doc_item in doc_swag: + name = doc_item.get('name') + if name is not None: + if name not in swag_names: + swag.append(doc_item) + else: + # if there's no name, then there's no way to check for + # uniqueness. Assume unique. + swag.append(doc_item) + return swag + + +def _merge_template(doc_template, process_doc, summary, desc, swag): + doc_summary, doc_desc, doc_swag = _get_docstring_parts( + doc_template, process_doc) + # favor everything from the original docstring + if not summary: + summary = doc_summary + if not desc: + desc = doc_desc + if doc_swag: + if not swag: + swag = doc_swag + else: + _merge_swag(swag, doc_swag) + return summary, desc, swag + + +def _get_docstring_parts(docstring, process_doc): + other_lines, swag = None, None + line_feed = docstring.find('\n') + if line_feed != -1: + first_line = process_doc(docstring[:line_feed]) + yaml_sep = docstring[line_feed+1:].find('---') + if yaml_sep != -1: + other_lines = process_doc(docstring[line_feed+1:line_feed+yaml_sep]) + swag = yaml.load(docstring[line_feed+yaml_sep:]) + else: + other_lines = process_doc(docstring[line_feed+1:]) + else: + first_line = docstring + return first_line, other_lines, swag + + def _parse_docstring(obj, process_doc, from_file_keyword): - first_line, other_lines, swag = None, None, None + first_line, other_lines, swag, doc_template = None, None, None, None full_doc = inspect.getdoc(obj) if full_doc: if from_file_keyword is not None: - from_file = _find_from_file(full_doc, from_file_keyword) + from_file, full_doc = _find_and_remove_from_file( + full_doc, from_file_keyword) if from_file: - full_doc_from_file = _doc_from_file(from_file) - if full_doc_from_file: - full_doc = full_doc_from_file - line_feed = full_doc.find('\n') - if line_feed != -1: - first_line = process_doc(full_doc[:line_feed]) - yaml_sep = full_doc[line_feed+1:].find('---') - if yaml_sep != -1: - other_lines = process_doc(full_doc[line_feed+1:line_feed+yaml_sep]) - swag = yaml.load(full_doc[line_feed+yaml_sep:]) - else: - other_lines = process_doc(full_doc[line_feed+1:]) - else: - first_line = full_doc + doc_template = _doc_from_file(from_file) + first_line, other_lines, swag = _get_docstring_parts( + full_doc, process_doc) + if doc_template: + first_line, other_lines, swag = _merge_template( + doc_template, process_doc, first_line, other_lines, swag) return first_line, other_lines, swag From f1783919e817f3945cf0a208c036be1c8ab0fe51 Mon Sep 17 00:00:00 2001 From: Steven Robertson Date: Tue, 29 Nov 2016 17:31:27 +0000 Subject: [PATCH 2/2] minor comment changes --- examples/user_put.yml | 2 +- flask_swagger.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/user_put.yml b/examples/user_put.yml index eea2696..b508cf9 100644 --- a/examples/user_put.yml +++ b/examples/user_put.yml @@ -1,4 +1,4 @@ -Update a user in yml +Update a user from yml. This is not used. --- tags: - users diff --git a/flask_swagger.py b/flask_swagger.py index 93ece54..5f7c950 100644 --- a/flask_swagger.py +++ b/flask_swagger.py @@ -65,7 +65,6 @@ def _merge_swag(swag, doc_swag): # for dicts, we'll add doc_swag to our swag if there's a `name` val # that's not in any of the swag dicts elif swag and isinstance(swag[0], dict): - # grab all the names of the dict objs swag_names = [] for item in swag: name = [val for key, val in item.items() if key == 'name']