Skip to content

Commit

Permalink
spec-first#33 support python 2.7
Browse files Browse the repository at this point in the history
  • Loading branch information
jmcs committed Jul 29, 2015
1 parent edf8390 commit e27cc06
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 58 deletions.
30 changes: 24 additions & 6 deletions connexion/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ class Api:
Single API that corresponds to a flask blueprint
"""

def __init__(self, swagger_yaml_path: pathlib.Path, base_url: str=None, arguments: dict=None,
swagger_ui: bool=None):
def __init__(self, swagger_yaml_path, base_url=None, arguments=None, swagger_ui=None):
"""
:type swagger_yaml_path: pathlib.Path
:type base_url: str | None
:type arguments: dict | None
:type swagger_ui: bool
"""
self.swagger_yaml_path = pathlib.Path(swagger_yaml_path)
logger.debug('Loading specification: %s', swagger_yaml_path, extra={'swagger_yaml': swagger_yaml_path,
'base_url': base_url,
Expand Down Expand Up @@ -74,7 +79,7 @@ def __init__(self, swagger_yaml_path: pathlib.Path, base_url: str=None, argument
self.add_swagger_ui()
self.add_paths()

def add_operation(self, method: str, path: str, swagger_operation: dict):
def add_operation(self, method, path, swagger_operation):
"""
Adds one operation to the api.
Expand All @@ -86,6 +91,10 @@ def add_operation(self, method: str, path: str, swagger_operation: dict):
A friendly name for the operation. The id MUST be unique among all operations described in the API.
Tools and libraries MAY use the operation id to uniquely identify an operation.
:type method: str
:type path: str
:type swagger_operation: dict
"""
operation = Operation(method=method, path=path, operation=swagger_operation,
app_produces=self.produces, app_security=self.security,
Expand All @@ -95,9 +104,11 @@ def add_operation(self, method: str, path: str, swagger_operation: dict):

self.blueprint.add_url_rule(path, operation.endpoint_name, operation.function, methods=[method])

def add_paths(self, paths: list=None):
def add_paths(self, paths=None):
"""
Adds the paths defined in the specification as endpoints
:type paths: list
"""
paths = paths or self.specification.get('paths', dict())
for path, methods in paths.items():
Expand Down Expand Up @@ -128,7 +139,11 @@ def add_swagger_ui(self):
index_endpoint_name = "{name}_swagger_ui_index".format(name=self.blueprint.name)
self.blueprint.add_url_rule('/ui/', index_endpoint_name, self.swagger_ui_index)

def create_blueprint(self, base_url: str=None) -> flask.Blueprint:
def create_blueprint(self, base_url=None):
"""
:type base_url: str | None
:rtype: flask.Blueprint
"""
base_url = base_url or self.base_url
logger.debug('Creating API blueprint: %s', base_url)
endpoint = utils.flaskify_endpoint(base_url)
Expand All @@ -139,5 +154,8 @@ def swagger_ui_index(self):
return flask.render_template('index.html', api_url=self.base_url)

@staticmethod
def swagger_ui_static(filename: str):
def swagger_ui_static(filename):
"""
:type filename: str
"""
return flask.send_from_directory(str(SWAGGER_UI_PATH), filename)
30 changes: 24 additions & 6 deletions connexion/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

import logging
import pathlib
import types

import flask
import tornado.wsgi
Expand All @@ -28,16 +27,23 @@


class App:
def __init__(self, import_name: str, port: int=5000, specification_dir: pathlib.Path='', server: str=None,
arguments: dict=None, debug: bool=False, swagger_ui: bool=True):
def __init__(self, import_name, port=5000, specification_dir='', server=None, arguments=None, debug=False,
swagger_ui=True):
"""
:param import_name: the name of the application package
:type import_name: str
:param port: port to listen to
:type port: int
:param specification_dir: directory where to look for specifications
:type specification_dir: pathlib.Path | str
:param server: which wsgi server to use
:type server: str | None
:param arguments: arguments to replace on the specification
:type arguments: dict | None
:param debug: include debugging information
:type debug: bool
:param swagger_ui: whether to include swagger ui or not
:type swagger_ui: bool
"""
self.app = flask.Flask(import_name)

Expand All @@ -64,17 +70,24 @@ def __init__(self, import_name: str, port: int=5000, specification_dir: pathlib.
self.swagger_ui = swagger_ui

@staticmethod
def common_error_handler(e: werkzeug.exceptions.HTTPException):
def common_error_handler(e):
"""
:type e: Exception
"""
if not isinstance(e, werkzeug.exceptions.HTTPException):
e = werkzeug.exceptions.InternalServerError()
return problem(title=e.name, detail=e.description, status=e.code)

def add_api(self, swagger_file: pathlib.Path, base_path: str=None, arguments: dict=None, swagger_ui: bool=None):
def add_api(self, swagger_file, base_path=None, arguments=None, swagger_ui=None):
"""
:param swagger_file: swagger file with the specification
:type swagger_file: pathlib.Path
:param base_path: base path where to add this api
:type base_path: str | None
:param arguments: api version specific arguments to replace on the specification
:type arguments: dict | None
:param swagger_ui: whether to include swagger ui or not
:type swagger_ui: bool
"""
swagger_ui = swagger_ui if swagger_ui is not None else self.swagger_ui
logger.debug('Adding API: %s', swagger_file)
Expand All @@ -85,7 +98,12 @@ def add_api(self, swagger_file: pathlib.Path, base_path: str=None, arguments: di
api = connexion.api.Api(yaml_path, base_path, arguments, swagger_ui)
self.app.register_blueprint(api.blueprint)

def add_error_handler(self, error_code: int, function: types.FunctionType):
def add_error_handler(self, error_code, function):
"""
:type error_code: int
:type function: types.FunctionType
"""
self.app.error_handler_spec[None][error_code] = function

def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
Expand Down
46 changes: 37 additions & 9 deletions connexion/decorators/produces.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,23 @@
import functools
import json
import logging
import types


logger = logging.getLogger('connexion.decorators.produces')


class BaseSerializer:
def __init__(self, mimetype='text/plain'):
"""
:type mimetype: str
"""
self.mimetype = mimetype

@staticmethod
def get_data_status_code(data) -> ('Any', int):
def get_data_status_code(data):
"""
:type data: flask.Response | (object, int) | object
:rtype: (object, int)
"""
url = flask.request.url
logger.debug('Getting data and status code', extra={'data': data, 'data_type': type(data), 'url': url})
if isinstance(data, flask.Response):
Expand All @@ -42,15 +47,27 @@ def get_data_status_code(data) -> ('Any', int):
'url': url})
return data, status_code

def __call__(self, function: types.FunctionType) -> types.FunctionType:
def __call__(self, function):
"""
:type function: types.FunctionType
:rtype: types.FunctionType
"""
return function

def __repr__(self) -> str:
def __repr__(self):
"""
:rtype: str
"""
return '<BaseSerializer: {}>'.format(self.mimetype)


class Produces(BaseSerializer):
def __call__(self, function: types.FunctionType) -> types.FunctionType:
def __call__(self, function):
"""
:type function: types.FunctionType
:rtype: types.FunctionType
"""

@functools.wraps(function)
def wrapper(*args, **kwargs):
url = flask.request.url
Expand All @@ -65,12 +82,20 @@ def wrapper(*args, **kwargs):

return wrapper

def __repr__(self) -> str:
def __repr__(self):
"""
:rtype: str
"""
return '<Produces: {}>'.format(self.mimetype)


class Jsonifier(BaseSerializer):
def __call__(self, function: types.FunctionType) -> types.FunctionType:
def __call__(self, function):
"""
:type function: types.FunctionType
:rtype: types.FunctionType
"""

@functools.wraps(function)
def wrapper(*args, **kwargs):
url = flask.request.url
Expand All @@ -89,5 +114,8 @@ def wrapper(*args, **kwargs):

return wrapper

def __repr__(self) -> str:
def __repr__(self):
"""
:rtype: str
"""
return '<Jsonifier: {}>'.format(self.mimetype)
16 changes: 13 additions & 3 deletions connexion/decorators/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,31 @@
import functools
import logging
import requests
import types

from connexion.problem import problem


logger = logging.getLogger('connexion.api.security')


def security_passthrough(function: types.FunctionType) -> types.FunctionType:
def security_passthrough(function):
"""
:type function: types.FunctionType
:rtype: types.FunctionType
"""
return function


def verify_oauth(token_info_url: str, allowed_scopes: set, function: types.FunctionType) -> types.FunctionType:
def verify_oauth(token_info_url, allowed_scopes, function):
"""
Decorator to verify oauth
:param token_info_url: Url to get information about the token
:type token_info_url: str
:param allowed_scopes: Set with scopes that are allowed to access the endpoint
:type allowed_scopes: set
:type function: types.FunctionType
:rtype: types.FunctionType
"""

@functools.wraps(function)
Expand Down
20 changes: 14 additions & 6 deletions connexion/decorators/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,22 @@
import logging
import numbers
import re
import types
import six

from connexion.utils import parse_datetime
from connexion.problem import problem

logger = logging.getLogger('connexion.decorators.parameters')
logger = logging.getLogger('connexion.decorators.validation')


# https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#data-types
TYPE_MAP = {'integer': int,
'number': numbers.Number,
'string': str,
'string': six.string_types[0],
'boolean': bool,
'array': list,
'object': dict} # map of swagger types to python types


FORMAT_MAP = {('string', 'date-time'): parse_datetime}


Expand Down Expand Up @@ -73,7 +72,12 @@ class RequestBodyValidator:
def __init__(self, schema):
self.schema = schema

def __call__(self, function: types.FunctionType) -> types.FunctionType:
def __call__(self, function):
"""
:type function: types.FunctionType
:rtype: types.FunctionType
"""

@functools.wraps(function)
def wrapper(*args, **kwargs):
data = flask.request.json
Expand All @@ -88,7 +92,11 @@ def wrapper(*args, **kwargs):

return wrapper

def validate_schema(self, data, schema) -> flask.Response:
def validate_schema(self, data, schema):
"""
:type schema: dict
:rtype: flask.Response | None
"""
schema_type = schema.get('type')
log_extra = {'url': flask.request.url, 'schema_type': schema_type}

Expand Down
8 changes: 6 additions & 2 deletions connexion/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@


class ConnexionException(BaseException):
...
pass


class InvalidSpecification(ConnexionException):
def __init__(self, reason: str='Unknown Reason'):
def __init__(self, reason='Unknown Reason'):
"""
:param reason: Reason why the specification is invalid
:type reason: str
"""
self.reason = reason

def __str__(self):
Expand Down
Loading

0 comments on commit e27cc06

Please sign in to comment.