From b7fb0ff1bc6ef7760aae2343fa0ffe3299db98a2 Mon Sep 17 00:00:00 2001 From: Vinod Gupta Date: Sun, 5 Jun 2016 14:39:09 -0700 Subject: [PATCH 01/25] Initial setup --- swagger_py_codegen/command.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/swagger_py_codegen/command.py b/swagger_py_codegen/command.py index 31f1767..970c7e0 100644 --- a/swagger_py_codegen/command.py +++ b/swagger_py_codegen/command.py @@ -3,6 +3,7 @@ import simplejson as json except ImportError: import json +from multiprocessing import Pool from os import makedirs from os.path import join, exists, dirname @@ -69,9 +70,11 @@ def _copy_ui_dir(ui_dest, ui_src): @click.option('--ui', default=False, is_flag=True, help='Generate swagger ui.') +@click.option('-j', '--jobs', + default=4, help='Parallel jobs for processing.') def generate(destination, swagger_doc, force=False, package=None, - template_dir=None, specification=False, ui=False): - + template_dir=None, specification=False, ui=False, jobs=4): + pool = Pool(processes=int(jobs)) package = package or destination.replace('-', '_') data = spec_load(swagger_doc) swagger = Swagger(data) From 1425732ac7cc6a906c2b7de878d8dc5b5af11474 Mon Sep 17 00:00:00 2001 From: Vinod Gupta Date: Sun, 5 Jun 2016 15:14:40 -0700 Subject: [PATCH 02/25] Improve parser --- setup.py | 2 +- swagger_py_codegen/command.py | 2 +- swagger_py_codegen/parser.py | 52 +++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 1f33a0b..39e1e6c 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ 'swagger_py_codegen=swagger_py_codegen:generate' ] }, - install_requires=['PyYAML', 'click', 'jinja2', 'dpath'], + install_requires=['PyYAML', 'click', 'jinja2', 'dpath', 'repoze.lru'], tests_require=['pytest'], classifiers=[ 'Development Status :: 3 - Alpha', diff --git a/swagger_py_codegen/command.py b/swagger_py_codegen/command.py index 970c7e0..ecc0bcb 100644 --- a/swagger_py_codegen/command.py +++ b/swagger_py_codegen/command.py @@ -77,7 +77,7 @@ def generate(destination, swagger_doc, force=False, package=None, pool = Pool(processes=int(jobs)) package = package or destination.replace('-', '_') data = spec_load(swagger_doc) - swagger = Swagger(data) + swagger = Swagger(data, pool) generator = FlaskGenerator(swagger) generator.with_spec = specification generator.with_ui = ui diff --git a/swagger_py_codegen/parser.py b/swagger_py_codegen/parser.py index d2d2fb4..a0398dd 100644 --- a/swagger_py_codegen/parser.py +++ b/swagger_py_codegen/parser.py @@ -2,7 +2,8 @@ import string import copy import dpath.util - +from repoze.lru import lru_cache +import sys def schema_var_name(path): return ''.join(map(string.capitalize, path)) @@ -22,12 +23,13 @@ class Swagger(object): separator = '\0' - def __init__(self, data): + def __init__(self, data, pool): self.data = data self.origin_data = copy.deepcopy(data) self._definitions = [] self._references_sort() self._process_ref() + process_references(self, pool) def _process_ref(self): @@ -76,6 +78,12 @@ def search(self, path): for p, d in dpath.util.search(self.data, list(path), True, self.separator): yield tuple(p.split(self.separator)), d + def pickle_search(self, path): + for p, d in dpath.util.search(self.data, list(path), True, + self.separator): + yield (self, tuple(p.split(self.separator)), d) + + @lru_cache(1000) def get(self, path): return dpath.util.get(self.data, list(path)) @@ -99,3 +107,43 @@ def module_name(self): @property def base_path(self): return self.data.get('basePath', '/v1') + + +def process_input_func(data_to_process): + (swagger, path, ref) = data_to_process + sys.stdout.write('.') + sys.stdout.flush() + ref = ref.lstrip('#/').split('/') + ref = tuple(ref) + data = swagger.get(ref) + path = path[:-1] + return (path, RefNode(data, ref)) + + +def process_references(swagger, pool): + """ + Processed references in swagger data + :param swagger: + :return: + """ + data_set = pool.map(process_input_func, + swagger.pickle_search(['**', '$ref'])) + for path, node in data_set: + sys.stdout.write('.') + sys.stdout.flush() + next_ref = swagger.data + for pn in path[:-1]: + if isinstance(next_ref, list): + next_ref = next_ref[int(pn)] + elif isinstance(next_ref, dict): + if pn in next_ref: + next_ref = next_ref[pn] + elif int(pn) in next_ref: + next_ref = next_ref[int(pn)] + if isinstance(next_ref, dict): + idx = path[-1] + next_ref[idx] = node + elif isinstance(next_ref, list): + idx = int(path[-1]) + next_ref[idx] = node + print " " From 1b50f823129460beea258281047fc61f0c5fd5c9 Mon Sep 17 00:00:00 2001 From: Vinod Gupta Date: Sun, 5 Jun 2016 17:17:12 -0700 Subject: [PATCH 03/25] Fix build --- swagger_py_codegen/parser.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/swagger_py_codegen/parser.py b/swagger_py_codegen/parser.py index a0398dd..5494bdb 100644 --- a/swagger_py_codegen/parser.py +++ b/swagger_py_codegen/parser.py @@ -23,13 +23,15 @@ class Swagger(object): separator = '\0' - def __init__(self, data, pool): + def __init__(self, data, pool=None): self.data = data self.origin_data = copy.deepcopy(data) self._definitions = [] self._references_sort() - self._process_ref() - process_references(self, pool) + if pool: + process_references(self, pool) + else: + self._process_ref() def _process_ref(self): From 931313f10f3eca9c37e0381d1e76d26c10bb6e61 Mon Sep 17 00:00:00 2001 From: Vinod Gupta Date: Sun, 5 Jun 2016 17:35:57 -0700 Subject: [PATCH 04/25] Fix lru cache test case --- setup.py | 2 +- swagger_py_codegen/parser.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 39e1e6c..1f33a0b 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ 'swagger_py_codegen=swagger_py_codegen:generate' ] }, - install_requires=['PyYAML', 'click', 'jinja2', 'dpath', 'repoze.lru'], + install_requires=['PyYAML', 'click', 'jinja2', 'dpath'], tests_require=['pytest'], classifiers=[ 'Development Status :: 3 - Alpha', diff --git a/swagger_py_codegen/parser.py b/swagger_py_codegen/parser.py index 5494bdb..0461b2b 100644 --- a/swagger_py_codegen/parser.py +++ b/swagger_py_codegen/parser.py @@ -2,7 +2,6 @@ import string import copy import dpath.util -from repoze.lru import lru_cache import sys def schema_var_name(path): @@ -28,6 +27,7 @@ def __init__(self, data, pool=None): self.origin_data = copy.deepcopy(data) self._definitions = [] self._references_sort() + self._get_cached = {} if pool: process_references(self, pool) else: @@ -85,9 +85,11 @@ def pickle_search(self, path): self.separator): yield (self, tuple(p.split(self.separator)), d) - @lru_cache(1000) def get(self, path): - return dpath.util.get(self.data, list(path)) + key = ''.join(path) + if key not in self._get_cached: + self._get_cached[key] = dpath.util.get(self.data, list(path)) + return self._get_cached[key] def set(self, path, data): dpath.util.set(self.data, list(path), data) From 119cf75955d56006daed79c675f49d970961e6f5 Mon Sep 17 00:00:00 2001 From: zrq495 Date: Tue, 19 Jul 2016 14:27:44 +0800 Subject: [PATCH 05/25] don't modify schema ! --- swagger_py_codegen/jsonschema.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/swagger_py_codegen/jsonschema.py b/swagger_py_codegen/jsonschema.py index 9542151..6c22337 100644 --- a/swagger_py_codegen/jsonschema.py +++ b/swagger_py_codegen/jsonschema.py @@ -158,10 +158,6 @@ def _normalize_dict(schema, data): for key, _schema in schema.get('properties', {}).iteritems(): # set default type_ = _schema.get('type', 'object') - if ('default' not in _schema - and key in schema.get('required', []) - and type_ in required_defaults): - _schema['default'] = required_defaults[type_] # get value value = data.get(key) @@ -169,7 +165,9 @@ def _normalize_dict(schema, data): result[key] = _normalize(_schema, value) elif 'default' in _schema: result[key] = _schema['default'] - elif key in schema.get('required', []): + elif key in schema.get('required', []) and type_ in required_defaults: + result[key] = required_defaults[type_] + else: errors.append(dict(name='property_missing', message='`%s` is required' % key)) From f9cd91b2f4a6b13b873592b3f8f65962f00cea04 Mon Sep 17 00:00:00 2001 From: zrq495 Date: Tue, 19 Jul 2016 18:21:21 +0800 Subject: [PATCH 06/25] fixes default values --- swagger_py_codegen/jsonschema.py | 7 +++++-- swagger_py_codegen/templates/flask/validators.tpl | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/swagger_py_codegen/jsonschema.py b/swagger_py_codegen/jsonschema.py index 99a4bf0..72f963a 100644 --- a/swagger_py_codegen/jsonschema.py +++ b/swagger_py_codegen/jsonschema.py @@ -106,7 +106,7 @@ def _process(self): yield Schema(build_data(self.swagger)) -def merge_default(schema, value): +def merge_default(schema, value, get_first=True): # TODO: more types support type_defaults = { 'integer': 9573, @@ -116,7 +116,10 @@ def merge_default(schema, value): 'boolean': False } - return normalize(schema, value, type_defaults)[0] + results = normalize(schema, value, type_defaults) + if get_first: + return results[0] + return results def build_default(schema): diff --git a/swagger_py_codegen/templates/flask/validators.tpl b/swagger_py_codegen/templates/flask/validators.tpl index 421f4fc..3f9cf11 100644 --- a/swagger_py_codegen/templates/flask/validators.tpl +++ b/swagger_py_codegen/templates/flask/validators.tpl @@ -63,7 +63,7 @@ class FlaskValidatorAdaptor(object): def validate(self, value): value = self.type_convert(value) errors = list(e.message for e in self.validator.iter_errors(value)) - return merge_default(self.validator.schema, value), errors + return normalize(self.validator.schema, value)[0], errors def request_validate(view): @@ -122,10 +122,10 @@ def response_filter(view): # return resp, status, headers abort(500, message='`%d` is not a defined status code.' % status) - resp, errors = normalize(schemas['schema'], resp) + resp, errors = merge_default(schemas['schema'], resp, get_first=False) if schemas['headers']: - headers, header_errors = normalize( - {'properties': schemas['headers']}, headers) + headers, header_errors = merge_default( + {'properties': schemas['headers']}, headers, get_first=False) errors.extend(header_errors) if errors: abort(500, message='Expectation Failed', errors=errors) From 7a77082799909d8f1db069f504ea56daf441abdc Mon Sep 17 00:00:00 2001 From: zrq495 Date: Tue, 19 Jul 2016 19:18:59 +0800 Subject: [PATCH 07/25] modified: swagger_py_codegen/jsonschema.py --- swagger_py_codegen/jsonschema.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/swagger_py_codegen/jsonschema.py b/swagger_py_codegen/jsonschema.py index 72f963a..45511aa 100644 --- a/swagger_py_codegen/jsonschema.py +++ b/swagger_py_codegen/jsonschema.py @@ -182,11 +182,12 @@ def _normalize_dict(schema, data): result[key] = _normalize(_schema, value) elif 'default' in _schema: result[key] = _schema['default'] - elif key in schema.get('required', []) and type_ in required_defaults: - result[key] = required_defaults[type_] - else: - errors.append(dict(name='property_missing', - message='`%s` is required' % key)) + elif key in schema.get('required', []): + if type_ in required_defaults: + result[key] = required_defaults[type_] + else: + errors.append(dict(name='property_missing', + message='`%s` is required' % key)) for _schema in schema.get('allOf', []): rs_component = _normalize(_schema, data) From e1910b7669c9dd4ad69eeefa573dcf77bbcb9725 Mon Sep 17 00:00:00 2001 From: zrq495 Date: Wed, 20 Jul 2016 11:30:31 +0800 Subject: [PATCH 08/25] modified: swagger_py_codegen/templates/flask/validators.tpl --- swagger_py_codegen/templates/flask/validators.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swagger_py_codegen/templates/flask/validators.tpl b/swagger_py_codegen/templates/flask/validators.tpl index 3f9cf11..7a765c3 100644 --- a/swagger_py_codegen/templates/flask/validators.tpl +++ b/swagger_py_codegen/templates/flask/validators.tpl @@ -122,10 +122,10 @@ def response_filter(view): # return resp, status, headers abort(500, message='`%d` is not a defined status code.' % status) - resp, errors = merge_default(schemas['schema'], resp, get_first=False) + resp, errors = normalize(schemas['schema'], resp) if schemas['headers']: - headers, header_errors = merge_default( - {'properties': schemas['headers']}, headers, get_first=False) + headers, header_errors = normalize( + {'properties': schemas['headers']}, headers) errors.extend(header_errors) if errors: abort(500, message='Expectation Failed', errors=errors) From 25969f3d1b72501229191be43a932dbe29bf0e9e Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 20 Jul 2016 16:35:56 +0800 Subject: [PATCH 09/25] bump to 0.1.20 --- swagger_py_codegen/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swagger_py_codegen/__init__.py b/swagger_py_codegen/__init__.py index 532d63d..df61a43 100644 --- a/swagger_py_codegen/__init__.py +++ b/swagger_py_codegen/__init__.py @@ -1,3 +1,3 @@ from .command import generate -__version__ = '0.1.19' +__version__ = '0.1.20' From 76c03c522a6f6aeac944d1e3dd60caf0b42d532e Mon Sep 17 00:00:00 2001 From: Roy Williams Date: Thu, 1 Dec 2016 13:30:16 -0800 Subject: [PATCH 10/25] Support Python 3 and generate Python 3 compatible code --- .travis.yml | 1 + setup.py | 4 +++- swagger_py_codegen/__init__.py | 4 +++- swagger_py_codegen/__main__.py | 2 ++ swagger_py_codegen/base.py | 1 + swagger_py_codegen/command.py | 1 + swagger_py_codegen/flask.py | 14 ++++++++------ swagger_py_codegen/jsonschema.py | 12 +++++++----- swagger_py_codegen/parser.py | 11 +++++++---- swagger_py_codegen/templates/flask/api.tpl | 2 ++ swagger_py_codegen/templates/flask/app.tpl | 4 +++- swagger_py_codegen/templates/flask/blueprint.tpl | 2 ++ .../templates/flask/requirements.tpl | 1 + swagger_py_codegen/templates/flask/routers.tpl | 1 + swagger_py_codegen/templates/flask/validators.tpl | 7 +++++-- swagger_py_codegen/templates/flask/view.tpl | 4 +++- tests/test_flask.py | 1 + tests/test_jsonschema.py | 11 ++++++----- tests/test_parser.py | 1 + 19 files changed, 58 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 58a562a..4b228a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: python python: - "2.7" + - "3.5" install: - python setup.py install before_script: diff --git a/setup.py b/setup.py index 1f33a0b..c800eb1 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import re import ast from setuptools import setup @@ -23,11 +24,12 @@ 'swagger_py_codegen=swagger_py_codegen:generate' ] }, - install_requires=['PyYAML', 'click', 'jinja2', 'dpath'], + install_requires=['PyYAML', 'click', 'jinja2', 'dpath', 'six'], tests_require=['pytest'], classifiers=[ 'Development Status :: 3 - Alpha', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', ], ) diff --git a/swagger_py_codegen/__init__.py b/swagger_py_codegen/__init__.py index df61a43..38e3448 100644 --- a/swagger_py_codegen/__init__.py +++ b/swagger_py_codegen/__init__.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from .command import generate -__version__ = '0.1.20' +__version__ = '0.2.00' diff --git a/swagger_py_codegen/__main__.py b/swagger_py_codegen/__main__.py index e432b33..c7ee60b 100644 --- a/swagger_py_codegen/__main__.py +++ b/swagger_py_codegen/__main__.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from .command import generate generate() diff --git a/swagger_py_codegen/base.py b/swagger_py_codegen/base.py index d2b172d..1d31c94 100644 --- a/swagger_py_codegen/base.py +++ b/swagger_py_codegen/base.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import os from jinja2 import Environment, FileSystemLoader diff --git a/swagger_py_codegen/command.py b/swagger_py_codegen/command.py index 31f1767..1a564cd 100644 --- a/swagger_py_codegen/command.py +++ b/swagger_py_codegen/command.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import codecs try: import simplejson as json diff --git a/swagger_py_codegen/flask.py b/swagger_py_codegen/flask.py index c162cac..b16d905 100644 --- a/swagger_py_codegen/flask.py +++ b/swagger_py_codegen/flask.py @@ -1,8 +1,10 @@ +from __future__ import absolute_import import re from collections import OrderedDict from .base import Code, CodeGenerator from .jsonschema import Schema, SchemaGenerator, build_default +import six SUPPORT_METHODS = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head'] @@ -130,18 +132,18 @@ def _dependence_callback(self, code): # use flask endpoint to replace default validator's key, # example: `('some_path_param', 'method')` validators = OrderedDict() - for k, v in schemas.data['validators'].iteritems(): - locations = {_location(loc): val for loc, val in v.iteritems()} + for k, v in six.iteritems(schemas.data['validators']): + locations = {_location(loc): val for loc, val in six.iteritems(v)} validators[(_path_to_endpoint(k[0]), k[1])] = locations # filters filters = OrderedDict() - for k, v in schemas.data['filters'].iteritems(): + for k, v in six.iteritems(schemas.data['filters']): filters[(_path_to_endpoint(k[0]), k[1])] = v # scopes scopes = OrderedDict() - for k, v in schemas.data['scopes'].iteritems(): + for k, v in six.iteritems(schemas.data['scopes']): scopes[(_path_to_endpoint(k[0]), k[1])] = v schemas.data['validators'] = validators @@ -169,9 +171,9 @@ def _process_data(self): methods[method] = {} validator = self.validators.get((endpoint, method.upper())) if validator: - methods[method]['requests'] = validator.keys() + methods[method]['requests'] = list(validator.keys()) - for status, res_data in data[method].get('responses', {}).iteritems(): + for status, res_data in six.iteritems(data[method].get('responses', {})): if isinstance(status, int) or status.isdigit(): example = res_data.get('schema', {}).get('application/json') if not example: diff --git a/swagger_py_codegen/jsonschema.py b/swagger_py_codegen/jsonschema.py index 45511aa..04f9963 100644 --- a/swagger_py_codegen/jsonschema.py +++ b/swagger_py_codegen/jsonschema.py @@ -1,8 +1,10 @@ +from __future__ import absolute_import from collections import OrderedDict from inspect import getsource from .base import Code, CodeGenerator from .parser import schema_var_name +import six class Schema(Code): @@ -74,7 +76,7 @@ def build_data(swagger): responses = data.get('responses') if responses: filter = {} - for status, res_data in responses.iteritems(): + for status, res_data in six.iteritems(responses): if isinstance(status, int) or status.isdigit(): filter[int(status)] = dict( headers=res_data.get('headers'), @@ -84,7 +86,7 @@ def build_data(swagger): # scopes for security in data.get('security', []): - scopes[(endpoint, method)] = security.values().pop() + scopes[(endpoint, method)] = list(security.values()).pop() break schemas = OrderedDict([(schema_var_name(path), swagger.get(path)) for path in swagger.definitions]) @@ -150,8 +152,8 @@ def has(self, key): def keys(self): if isinstance(self.data, dict): - return self.data.keys() - return vars(self.data).keys() + return list(self.data.keys()) + return list(vars(self.data).keys()) def get_check(self, key, default=None): if isinstance(self.data, dict): @@ -172,7 +174,7 @@ def _normalize_dict(schema, data): if not isinstance(data, DataWrapper): data = DataWrapper(data) - for key, _schema in schema.get('properties', {}).iteritems(): + for key, _schema in six.iteritems(schema.get('properties', {})): # set default type_ = _schema.get('type', 'object') diff --git a/swagger_py_codegen/parser.py b/swagger_py_codegen/parser.py index d2d2fb4..5322dea 100644 --- a/swagger_py_codegen/parser.py +++ b/swagger_py_codegen/parser.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import import string import copy import dpath.util +import six +from six.moves import map def schema_var_name(path): @@ -51,7 +54,7 @@ def get_definition_refs(): ref = ref.lstrip('#/').split('/') ref = tuple(ref) - if schema in definition_refs.keys(): + if schema in list(definition_refs.keys()): definition_refs[schema].add(ref) else: definition_refs[schema] = set([ref]) @@ -61,13 +64,13 @@ def get_definition_refs(): definition_refs = get_definition_refs() while definition_refs: - ready = {definition for definition, refs in definition_refs.iteritems() if not refs} + ready = {definition for definition, refs in six.iteritems(definition_refs) if not refs} if not ready: msg = '$ref circular references found!\n' raise ValueError(msg) for definition in ready: del definition_refs[definition] - for refs in definition_refs.itervalues(): + for refs in six.itervalues(definition_refs): refs.difference_update(ready) self._definitions += ready @@ -89,7 +92,7 @@ def definitions(self): @property def scopes_supported(self): for _, data in self.search(['securityDefinitions', '*', 'scopes']): - return data.keys() + return list(data.keys()) return [] @property diff --git a/swagger_py_codegen/templates/flask/api.tpl b/swagger_py_codegen/templates/flask/api.tpl index c7c3389..9a079b6 100644 --- a/swagger_py_codegen/templates/flask/api.tpl +++ b/swagger_py_codegen/templates/flask/api.tpl @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import + import flask_restful as restful from ..validators import request_validate, response_filter diff --git a/swagger_py_codegen/templates/flask/app.tpl b/swagger_py_codegen/templates/flask/app.tpl index 5cf1788..55436a0 100644 --- a/swagger_py_codegen/templates/flask/app.tpl +++ b/swagger_py_codegen/templates/flask/app.tpl @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import + from flask import Flask -import {{ blueprint }} +from . import {{ blueprint }} def create_app(): diff --git a/swagger_py_codegen/templates/flask/blueprint.tpl b/swagger_py_codegen/templates/flask/blueprint.tpl index 81d31a6..2ccb307 100644 --- a/swagger_py_codegen/templates/flask/blueprint.tpl +++ b/swagger_py_codegen/templates/flask/blueprint.tpl @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import + from flask import Blueprint import flask_restful as restful diff --git a/swagger_py_codegen/templates/flask/requirements.tpl b/swagger_py_codegen/templates/flask/requirements.tpl index 1816eaf..1786ac3 100644 --- a/swagger_py_codegen/templates/flask/requirements.tpl +++ b/swagger_py_codegen/templates/flask/requirements.tpl @@ -2,3 +2,4 @@ Flask Jinja2 Flask-RESTful jsonschema +six diff --git a/swagger_py_codegen/templates/flask/routers.tpl b/swagger_py_codegen/templates/flask/routers.tpl index 5e6a5de..3426541 100644 --- a/swagger_py_codegen/templates/flask/routers.tpl +++ b/swagger_py_codegen/templates/flask/routers.tpl @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- {% include '_do_not_change.tpl' %} +from __future__ import absolute_import {% for view in views -%} from .api.{{ view.endpoint }} import {{ view.name }} diff --git a/swagger_py_codegen/templates/flask/validators.tpl b/swagger_py_codegen/templates/flask/validators.tpl index 7a765c3..ec70c0f 100644 --- a/swagger_py_codegen/templates/flask/validators.tpl +++ b/swagger_py_codegen/templates/flask/validators.tpl @@ -1,10 +1,13 @@ # -*- coding: utf-8 -*- {% include '_do_not_change.tpl' %} +from __future__ import absolute_import from datetime import date from functools import wraps +import six + from werkzeug.datastructures import MultiDict, Headers from flask import request, g, current_app, json from flask_restful import abort @@ -34,7 +37,7 @@ class FlaskValidatorAdaptor(object): if isinstance(obj, (dict, list)) and not isinstance(obj, MultiDict): return obj if isinstance(obj, Headers): - obj = MultiDict(obj.iteritems()) + obj = MultiDict(six.iteritems(obj)) result = dict() convert_funs = { @@ -80,7 +83,7 @@ def request_validate(view): if method == 'HEAD': method = 'GET' locations = validators.get((endpoint, method), {}) - for location, schema in locations.iteritems(): + for location, schema in six.iteritems(locations): value = getattr(request, location, MultiDict()) validator = FlaskValidatorAdaptor(schema) result, errors = validator.validate(value) diff --git a/swagger_py_codegen/templates/flask/view.tpl b/swagger_py_codegen/templates/flask/view.tpl index 81eb7ca..be5384b 100644 --- a/swagger_py_codegen/templates/flask/view.tpl +++ b/swagger_py_codegen/templates/flask/view.tpl @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import, print_function + from flask import request, g from . import Resource @@ -11,7 +13,7 @@ class {{ name }}(Resource): def {{ method.lower() }}(self{{ params.__len__() and ', ' or '' }}{{ params | join(', ') }}): {%- for request in ins.requests %} - print g.{{ request }} + print(g.{{ request }}) {%- endfor %} {% if 'response' in ins -%} diff --git a/tests/test_flask.py b/tests/test_flask.py index a53d7e9..0d5a3ce 100644 --- a/tests/test_flask.py +++ b/tests/test_flask.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from swagger_py_codegen.parser import Swagger from swagger_py_codegen.flask import ( _swagger_to_flask_url, diff --git a/tests/test_jsonschema.py b/tests/test_jsonschema.py index 1c6c05d..42a15fa 100644 --- a/tests/test_jsonschema.py +++ b/tests/test_jsonschema.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from swagger_py_codegen.parser import Swagger from swagger_py_codegen.jsonschema import build_data @@ -66,7 +67,7 @@ def test_schema_ref_01(): swagger = Swagger(data) data = build_data(swagger) assert len(data['schemas']) == 2 - assert data['schemas'].keys()[0] == 'DefinitionsUser' + assert list(data['schemas'].keys())[0] == 'DefinitionsUser' def test_validators(): @@ -290,7 +291,7 @@ def test_merge_default_01(): } } result = merge_default(schema, default) - assert 'roles' not in result.keys() + assert 'roles' not in list(result.keys()) def test_merge_default_02(): @@ -407,7 +408,7 @@ def age(self): del schema['items']['properties']['roles']['default'] users, errors = normalize(schema, [User()]) user = users.pop() - assert 'roles' not in user.keys() + assert 'roles' not in list(user.keys()) assert errors user = User() @@ -469,7 +470,7 @@ def test_normalize_02(): } } result, errors = normalize(schema, default) - assert 'roles' not in result.keys() + assert 'roles' not in list(result.keys()) assert errors @@ -511,7 +512,7 @@ def test_normalize_03(): result, errors = normalize(schema, default) assert errors == [] assert result['name'] == 'bob' - assert 'address' not in result.keys() + assert 'address' not in list(result.keys()) default = { 'id': 123, diff --git a/tests/test_parser.py b/tests/test_parser.py index da9e7e9..31ed0c9 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import pytest from swagger_py_codegen.parser import Swagger From 9c60171312e81c3b2bd01a4f57e5ec4d6092b7f0 Mon Sep 17 00:00:00 2001 From: Roy Williams Date: Thu, 1 Dec 2016 14:31:23 -0800 Subject: [PATCH 11/25] Fix tests --- swagger_py_codegen/flask.py | 12 ++++++++++-- swagger_py_codegen/parser.py | 2 +- tests/test_flask.py | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/swagger_py_codegen/flask.py b/swagger_py_codegen/flask.py index b16d905..96da246 100644 --- a/swagger_py_codegen/flask.py +++ b/swagger_py_codegen/flask.py @@ -96,13 +96,21 @@ def _type(parameters): return url, params +if six.PY3: + def _remove_characters(text, deletechars): + return text.translate({ord(x): None for x in deletechars}) +else: + def _remove_characters(text, deletechars): + return text.translate(None, deletechars) def _path_to_endpoint(swagger_path): - return swagger_path.strip('/').replace('/', '_').replace('-', '_').translate(None, '{}') + return _remove_characters( + swagger_path.strip('/').replace('/', '_').replace('-', '_'), + '{}') def _path_to_resource_name(swagger_path): - return swagger_path.title().translate(None, '{}/_-') + return _remove_characters(swagger_path.title(), '{}/_-') def _location(swagger_location): diff --git a/swagger_py_codegen/parser.py b/swagger_py_codegen/parser.py index 5322dea..4a666e4 100644 --- a/swagger_py_codegen/parser.py +++ b/swagger_py_codegen/parser.py @@ -8,7 +8,7 @@ def schema_var_name(path): - return ''.join(map(string.capitalize, path)) + return ''.join(map(str.capitalize, path)) class RefNode(dict): diff --git a/tests/test_flask.py b/tests/test_flask.py index 0d5a3ce..a694de4 100644 --- a/tests/test_flask.py +++ b/tests/test_flask.py @@ -1,4 +1,5 @@ from __future__ import absolute_import + from swagger_py_codegen.parser import Swagger from swagger_py_codegen.flask import ( _swagger_to_flask_url, @@ -135,6 +136,7 @@ def test_process_data(): swagger = Swagger(data) generator = FlaskGenerator(swagger) schemas, routes, view1, view2 = list(generator.generate())[:4] + view1, view2 = sorted([view1, view2], key=lambda x: x.data['name']) assert ('posts_post_id', 'GET') in schemas.data['validators'] assert schemas.data['validators'][('posts_post_id', 'GET')]['args']['properties']['page']['type'] == 'integer' assert view1.data['url'] == '/posts/' From 1fd9ad2925e348e68006c0046493f658480b9b77 Mon Sep 17 00:00:00 2001 From: foodszhang Date: Tue, 6 Dec 2016 15:10:16 +0800 Subject: [PATCH 12/25] add --- swagger_py_codegen/flask.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swagger_py_codegen/flask.py b/swagger_py_codegen/flask.py index c162cac..3036796 100644 --- a/swagger_py_codegen/flask.py +++ b/swagger_py_codegen/flask.py @@ -173,7 +173,8 @@ def _process_data(self): for status, res_data in data[method].get('responses', {}).iteritems(): if isinstance(status, int) or status.isdigit(): - example = res_data.get('schema', {}).get('application/json') + example = res_data.get('examples', {}).get('application/json') + if not example: example = build_default(res_data.get('schema')) response = example, int(status), build_default(res_data.get('headers')) From 06b334dd7bfb16b31d18d65c0a0c9a1278347449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=8B=BF?= Date: Wed, 7 Dec 2016 14:29:55 +0800 Subject: [PATCH 13/25] fix replace iteritems with items in tpl --- swagger_py_codegen/templates/flask/view.tpl | 2 +- swagger_py_codegen/templates/jsonschema/schemas.tpl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/swagger_py_codegen/templates/flask/view.tpl b/swagger_py_codegen/templates/flask/view.tpl index be5384b..7f48b8d 100644 --- a/swagger_py_codegen/templates/flask/view.tpl +++ b/swagger_py_codegen/templates/flask/view.tpl @@ -9,7 +9,7 @@ from .. import schemas class {{ name }}(Resource): - {%- for method, ins in methods.iteritems() %} + {%- for method, ins in methods.items() %} def {{ method.lower() }}(self{{ params.__len__() and ', ' or '' }}{{ params | join(', ') }}): {%- for request in ins.requests %} diff --git a/swagger_py_codegen/templates/jsonschema/schemas.tpl b/swagger_py_codegen/templates/jsonschema/schemas.tpl index 6f146ff..128fd1c 100644 --- a/swagger_py_codegen/templates/jsonschema/schemas.tpl +++ b/swagger_py_codegen/templates/jsonschema/schemas.tpl @@ -4,24 +4,24 @@ {% include '_do_not_change.tpl' %} -{% for name, value in schemas.iteritems() %} +{% for name, value in schemas.items() %} {{ name }} = {{ value }} {%- endfor %} validators = { -{%- for name, value in validators.iteritems() %} +{%- for name, value in validators.items() %} {{ name }}: {{ value }}, {%- endfor %} } filters = { -{%- for name, value in filters.iteritems() %} +{%- for name, value in filters.items() %} {{ name }}: {{ value }}, {%- endfor %} } scopes = { -{%- for name, value in scopes.iteritems() %} +{%- for name, value in scopes.items() %} {{ name }}: {{ value }}, {%- endfor %} } From 05c39c8784071a874c5aca86dd53fa7559904df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=8B=BF?= Date: Wed, 7 Dec 2016 18:52:37 +0800 Subject: [PATCH 14/25] fixbug six import --- swagger_py_codegen/jsonschema.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/swagger_py_codegen/jsonschema.py b/swagger_py_codegen/jsonschema.py index 04f9963..a72f132 100644 --- a/swagger_py_codegen/jsonschema.py +++ b/swagger_py_codegen/jsonschema.py @@ -130,6 +130,8 @@ def build_default(schema): def normalize(schema, data, required_defaults=None): + import six + if required_defaults is None: required_defaults = {} errors = [] From 4cf98a9b87026b0e14c8e77beb6514971a17b051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=8B=BF?= Date: Thu, 8 Dec 2016 14:32:11 +0800 Subject: [PATCH 15/25] fix compatible bug in validators --- swagger_py_codegen/templates/flask/app.tpl | 2 +- swagger_py_codegen/templates/flask/validators.tpl | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/swagger_py_codegen/templates/flask/app.tpl b/swagger_py_codegen/templates/flask/app.tpl index 55436a0..e96a83c 100644 --- a/swagger_py_codegen/templates/flask/app.tpl +++ b/swagger_py_codegen/templates/flask/app.tpl @@ -3,7 +3,7 @@ from __future__ import absolute_import from flask import Flask -from . import {{ blueprint }} +import {{ blueprint }} def create_app(): diff --git a/swagger_py_codegen/templates/flask/validators.tpl b/swagger_py_codegen/templates/flask/validators.tpl index ec70c0f..49a2a24 100644 --- a/swagger_py_codegen/templates/flask/validators.tpl +++ b/swagger_py_codegen/templates/flask/validators.tpl @@ -52,7 +52,7 @@ class FlaskValidatorAdaptor(object): func = convert_funs.get(type_, lambda v: v[0]) return [func([i]) for i in v] - for k, values in obj.iterlists(): + for k, values in obj.lists(): prop = self.validator.schema['properties'].get(k, {}) type_ = prop.get('type') fun = convert_funs.get(type_, lambda v: v[0]) @@ -118,7 +118,10 @@ def response_filter(view): resp, status, headers = unpack(resp) if len(filter) == 1: - status = filter.keys()[0] + if six.PY3: + status = list(filter.keys())[0] + else: + status = filter.keys()[0] schemas = filter.get(status) if not schemas: From e0f904ea11eccb83a3dcb3902e98948d38875d63 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 8 Dec 2016 14:39:57 +0800 Subject: [PATCH 16/25] bump to 0.2.1 * support python3 compatibility * fix some bugs --- swagger_py_codegen/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swagger_py_codegen/__init__.py b/swagger_py_codegen/__init__.py index 38e3448..8cc0d97 100644 --- a/swagger_py_codegen/__init__.py +++ b/swagger_py_codegen/__init__.py @@ -2,4 +2,4 @@ from .command import generate -__version__ = '0.2.00' +__version__ = '0.2.1' From c0290e2a58078d2c61a2d1f096f44b687fad9bb8 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 8 Dec 2016 15:08:14 +0800 Subject: [PATCH 17/25] Update AUTHORS --- AUTHORS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 05d3c80..f2d2f22 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,6 +7,8 @@ Contributors: * yimiqisan (yimiqisan@gmail.com); * softlns (softliunaisen@gmai.com); +* Justin (justinli.ljt@gmail.com); * teknolog2000 (https://github.com/teknolog2000); * ChaosEternal (https://github.com/ChaosEternal); * SimplicityGuy (https://github.com/SimplicityGuy); +* RoyWilliams (https://github.com/rowillia); From 9c7eda1c995fa1840ca849fdc60356816b6fa103 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 8 Dec 2016 16:44:59 +0800 Subject: [PATCH 18/25] Update AUTHORS --- AUTHORS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index f2d2f22..32a873c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -11,4 +11,5 @@ Contributors: * teknolog2000 (https://github.com/teknolog2000); * ChaosEternal (https://github.com/ChaosEternal); * SimplicityGuy (https://github.com/SimplicityGuy); -* RoyWilliams (https://github.com/rowillia); +* Roy Williams (https://github.com/rowillia); +* Vinod Gupta (https://github.com/codervinod) From 7e16e69b364e1de5f91239c1fb7efc265abbb9c0 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 8 Dec 2016 16:52:42 +0800 Subject: [PATCH 19/25] fix tests --- swagger_py_codegen/parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/swagger_py_codegen/parser.py b/swagger_py_codegen/parser.py index dbbd3d1..e4c7b9c 100644 --- a/swagger_py_codegen/parser.py +++ b/swagger_py_codegen/parser.py @@ -153,4 +153,3 @@ def process_references(swagger, pool): elif isinstance(next_ref, list): idx = int(path[-1]) next_ref[idx] = node - print " " From f3ae84926ec6fb48f7ba7b300882a6c9dd885cf1 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 8 Dec 2016 17:04:52 +0800 Subject: [PATCH 20/25] [Mod] update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f0efff6..5a3b40b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ dist/ *.egg-info *.egg .idea +*~ +*.pyc From 206e44d7f63442ac6d0070d1e5f49b83bf5409d3 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 8 Dec 2016 18:16:21 +0800 Subject: [PATCH 21/25] bump 0.2.2 --- swagger_py_codegen/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swagger_py_codegen/__init__.py b/swagger_py_codegen/__init__.py index 8cc0d97..ef35fb3 100644 --- a/swagger_py_codegen/__init__.py +++ b/swagger_py_codegen/__init__.py @@ -2,4 +2,4 @@ from .command import generate -__version__ = '0.2.1' +__version__ = '0.2.2' From 53231ec63fac2b171e0b1d6d5f61e352f5418277 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 9 Dec 2016 11:14:25 +0800 Subject: [PATCH 22/25] [Add] version command option --- setup.py | 2 +- swagger_py_codegen/__init__.py | 3 +-- swagger_py_codegen/_version.py | 4 ++++ swagger_py_codegen/command.py | 11 +++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 swagger_py_codegen/_version.py diff --git a/setup.py b/setup.py index c800eb1..78d6cc9 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ _version_re = re.compile(r'__version__\s+=\s+(.*)') -with open('swagger_py_codegen/__init__.py', 'rb') as f: +with open('swagger_py_codegen/_version.py', 'rb') as f: version = str(ast.literal_eval(_version_re.search( f.read().decode('utf-8')).group(1))) diff --git a/swagger_py_codegen/__init__.py b/swagger_py_codegen/__init__.py index ef35fb3..1afbf73 100644 --- a/swagger_py_codegen/__init__.py +++ b/swagger_py_codegen/__init__.py @@ -1,5 +1,4 @@ from __future__ import absolute_import +from ._version import __version__ from .command import generate - -__version__ = '0.2.2' diff --git a/swagger_py_codegen/_version.py b/swagger_py_codegen/_version.py new file mode 100644 index 0000000..43adb84 --- /dev/null +++ b/swagger_py_codegen/_version.py @@ -0,0 +1,4 @@ +"""Version information.""" + +# The following line *must* be the last in the module, exactly as formatted: +__version__ = "0.2.2" diff --git a/swagger_py_codegen/command.py b/swagger_py_codegen/command.py index 30fd96e..693d950 100644 --- a/swagger_py_codegen/command.py +++ b/swagger_py_codegen/command.py @@ -11,6 +11,7 @@ import yaml import click +from ._version import __version__ from .flask import FlaskGenerator from .parser import Swagger from .base import Template @@ -55,6 +56,13 @@ def _copy_ui_dir(ui_dest, ui_src): return status +def print_version(ctx, param, value): + if not value or ctx.resilient_parsing: + return + click.echo('current version: %s' % __version__) + ctx.exit() + + @click.command() @click.argument('destination', required=True) @click.option('-s', '--swagger', '--swagger-doc', @@ -73,6 +81,9 @@ def _copy_ui_dir(ui_dest, ui_src): help='Generate swagger ui.') @click.option('-j', '--jobs', default=4, help='Parallel jobs for processing.') +@click.option('--version', is_flag=True, callback=print_version, + expose_value=False, is_eager=True, + help='Show current version.') def generate(destination, swagger_doc, force=False, package=None, template_dir=None, specification=False, ui=False, jobs=4): pool = Pool(processes=int(jobs)) From e3bb94421d97a24d5ca69f30f7ebbc539656dea0 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 9 Dec 2016 11:25:07 +0800 Subject: [PATCH 23/25] Update _version.py --- swagger_py_codegen/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swagger_py_codegen/_version.py b/swagger_py_codegen/_version.py index 43adb84..095e93c 100644 --- a/swagger_py_codegen/_version.py +++ b/swagger_py_codegen/_version.py @@ -1,4 +1,4 @@ """Version information.""" # The following line *must* be the last in the module, exactly as formatted: -__version__ = "0.2.2" +__version__ = "0.2.3" From 63a79712605c129eed8d6e9705298f8f1cfe15a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrico=20Tr=C3=B6ger?= Date: Thu, 5 Jan 2017 17:39:10 +0100 Subject: [PATCH 24/25] Handle errors on converting invalid integer/float values When trying to convert integer/float values and the passed value is invalid (like a string containing non-digit characters), handle the resulting ValueError and use the original value which will cause a proper validation error later on. Fixes #83. --- swagger_py_codegen/templates/flask/validators.tpl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/swagger_py_codegen/templates/flask/validators.tpl b/swagger_py_codegen/templates/flask/validators.tpl index 49a2a24..b60827a 100644 --- a/swagger_py_codegen/templates/flask/validators.tpl +++ b/swagger_py_codegen/templates/flask/validators.tpl @@ -31,6 +31,12 @@ class FlaskValidatorAdaptor(object): def __init__(self, schema): self.validator = Draft4Validator(schema) + def validate_number(self, type_, value): + try: + return type_(value) + except ValueError: + return value + def type_convert(self, obj): if obj is None: return None @@ -41,10 +47,10 @@ class FlaskValidatorAdaptor(object): result = dict() convert_funs = { - 'integer': lambda v: int(v[0]), + 'integer': lambda v: self.validate_number(int, v[0]), 'boolean': lambda v: v[0].lower() not in ['n', 'no', 'false', '', '0'], 'null': lambda v: None, - 'number': lambda v: float(v[0]), + 'number': lambda v: self.validate_number(float, v[0]), 'string': lambda v: v[0] } From 841abfeb4eb7e98e9f7444b2cff1c7fb065eecb5 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 6 Jan 2017 18:47:44 +0800 Subject: [PATCH 25/25] bump to 0.2.4 --- swagger_py_codegen/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swagger_py_codegen/_version.py b/swagger_py_codegen/_version.py index 095e93c..75a44a9 100644 --- a/swagger_py_codegen/_version.py +++ b/swagger_py_codegen/_version.py @@ -1,4 +1,4 @@ """Version information.""" # The following line *must* be the last in the module, exactly as formatted: -__version__ = "0.2.3" +__version__ = "0.2.4"