Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Fix mypy (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuathompsonlindley authored Oct 4, 2023
2 parents 20d05c8 + 647b11f commit 8177478
Show file tree
Hide file tree
Showing 13 changed files with 458 additions and 248 deletions.
2 changes: 1 addition & 1 deletion ci/scripts/codescan.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ case $TOOL in
flake8 $REPO/src/flask_openapi --config $REPO/ci/config/.flake8rc $REPO/src
;;
'mypy')
mypy --config-file $REPO/ci/config/.mypyrc $REPO/src || true
mypy --config-file $REPO/ci/config/.mypyrc $REPO/src
;;
'safety')
safety check --full-report --file $REPO/requirements.txt
Expand Down
5 changes: 2 additions & 3 deletions ci/tests/test_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pytest

from flask_openapi.openapi import Swagger


Expand All @@ -14,7 +13,7 @@ def __init__(self, config=None, merge=False):
assert t.config == Swagger.DEFAULT_CONFIG

# Empty dict passed to arguments will be overriden with default_config
empty_dict = dict()
empty_dict = {}
t = Swagger(config=empty_dict, merge=False)
assert t.config == Swagger.DEFAULT_CONFIG
assert t.config is not empty_dict
Expand All @@ -34,7 +33,7 @@ def __init__(self, config=None, merge=False):
assert all(t.config[k] == v for k, v in Swagger.DEFAULT_CONFIG.items())

# Config will be merged
empty_dict = dict()
empty_dict = {}
t = Swagger(config=empty_dict, merge=True)
assert t.config == Swagger.DEFAULT_CONFIG

Expand Down
1 change: 1 addition & 0 deletions src/flask_openapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from flask_openapi.core.validation import validate # noqa: F401
from flask_openapi.openapi import Flasgger, OpenAPI, Swagger # noqa: F401
from flask_openapi.utils.constants import OPTIONAL_FIELDS # noqa: F401
from flask_openapi.utils.files import load_from_file # noqa: F401
from flask_openapi.utils.sanitizers import ( # noqa: F401
BR_SANITIZER,
MK_SANITIZER,
Expand Down
9 changes: 4 additions & 5 deletions src/flask_openapi/core/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
from functools import wraps

from flask import request
from six import string_types

from flask_openapi.core.marshmallow_apispec import Schema
from flask_openapi.core.validation import validate
from flask_openapi.utils.constants import DEFAULT_FIELDS
from flask_openapi.utils.paths import get_root_path
from six import string_types


def swag_from(
Expand Down Expand Up @@ -114,7 +113,7 @@ def wrapper(*args, **kwargs):
schema_id or definition,
validation_function=validation_function,
validation_error_handler=validation_error_handler,
**validate_args,
**validate_args, # type: ignore
)
return function(*args, **kwargs)

Expand Down Expand Up @@ -192,8 +191,8 @@ def wrapper(*args, **kwargs):
)

function.specs_dict = specs
args = list(args)
args[2] = function
args = list(args) # type: ignore
args[2] = function # type: ignore
args = tuple(args)

return f(*args, **kwargs)
Expand Down
124 changes: 73 additions & 51 deletions src/flask_openapi/core/marshmallow_apispec.py
Original file line number Diff line number Diff line change
@@ -1,97 +1,112 @@
# coding: utf-8
import inspect
from typing import Any, Callable, Optional

from flask import Flask
from flask.views import MethodView

import flask_openapi
from flask_openapi.core.validation import validate
from flask_openapi.utils.constants import OPTIONAL_FIELDS

try:
import marshmallow
from apispec import APISpec as BaseAPISpec
from apispec.ext.marshmallow import openapi
from marshmallow import fields
from marshmallow import fields # noqa

# Note that openapi_converter is initialized with trivial
# schema_name_resolver. Resolving circular reference is not
# supported for now. See issue #314 .
# Also see: https://github.com/marshmallow-code/apispec/pull/447
openapi_converter = openapi.OpenAPIConverter(
openapi_converter: openapi.OpenAPIConverter = openapi.OpenAPIConverter(
openapi_version="2.0",
schema_name_resolver=lambda schema: None,
spec=BaseAPISpec,
spec=BaseAPISpec, # type: ignore
)
schema2jsonschema = openapi_converter.schema2jsonschema
schema2parameters = openapi_converter.schema2parameters

class Schema(marshmallow.Schema):
swag_in = "body"
swag_validate = True
swag_validation_function = None
swag_validation_error_handler = None
swag_require_data = True
swag_in: str = "body"
swag_validate: bool = True
swag_validation_function: Optional[Callable] = None
swag_validation_error_handler: Optional[Callable] = None
swag_require_data: bool = True

def to_specs_dict(self):
def to_specs_dict(self) -> dict[str, type["Schema"]]:
specs = {"parameters": self.__class__}
definitions: dict = {}
specs.update(convert_schemas(specs, definitions))
specs["definitions"] = definitions
specs["definitions"] = definitions # type: ignore
return specs

except ImportError:
Schema = None
fields = None
schema2jsonschema = lambda schema: {} # noqa
schema2parameters = lambda schema, location: [] # noqa
BaseAPISpec = object
Schema = None # type: ignore
fields = None # type: ignore
schema2jsonschema = lambda schema: {} # type: ignore # noqa
schema2parameters = lambda schema, location: [] # type: ignore # noqa
BaseAPISpec = object # type: ignore


class APISpec(BaseAPISpec):
"""
Wrapper around APISpec to add `to_flasgger` method
"""

def to_flasgger(self, app=None, definitions=None, paths=None):
def to_flasgger(
self,
app: Optional[Flask] = None,
definitions: Optional[list] = None,
paths: Optional[list] = None,
) -> dict:
"""
Converts APISpec dict to flasgger suitable dict
also adds definitions and paths (optional)
:param app: Flask app
:type app: Flask
:param definitions: a list of [Schema, ..] or [('Name', Schema), ..]
:type definitions: list
:param paths: A list of flask views
:type paths: list
:return: Flasgger suitable dict
:rtype: dict
"""
if Schema is None:
raise RuntimeError("Please install marshmallow and apispec")

return flask_openapi.utils.apispec_to_template(
app, self, definitions=definitions, paths=paths
)
from flask_openapi.core.specs import apispec_to_template

return apispec_to_template(app, self, definitions=definitions, paths=paths)


class SwaggerView(MethodView):
"""
A Swagger view
"""

parameters = []
responses = {}
definitions = {}
tags = []
consumes = ["application/json"]
produces = ["application/json"]
schemes = []
security = []
deprecated = False
operationId = None
externalDocs = {}
summary = None
description = None
validation = False
validation_function = None
validation_error_handler = None
parameters: list = []
responses: dict = {}
definitions: dict = {}
tags: list = []
consumes: list[str] = ["application/json"]
produces: list[str] = ["application/json"]
schemes: list = []
security: list = []
deprecated: bool = False
operationId: Optional[Any] = None
externalDocs: dict = {}
summary: Optional[Any] = None
description: Optional[str] = None
validation: bool = False
validation_function: Optional[Callable] = None
validation_error_handler: Optional[Callable] = None

def dispatch_request(self, *args, **kwargs):
"""
If validation=True perform validation
"""
if self.validation:
specs = {}
attrs = flask_openapi.constants.OPTIONAL_FIELDS + [
specs: dict = {}
attrs: list[str] = OPTIONAL_FIELDS + [
"parameters",
"definitions",
"responses",
Expand All @@ -100,34 +115,41 @@ def dispatch_request(self, *args, **kwargs):
]
for attr in attrs:
specs[attr] = getattr(self, attr)
definitions = {}
definitions: dict = {}
specs.update(convert_schemas(specs, definitions))
specs["definitions"] = definitions
flask_openapi.utils.validate(
validate(
specs=specs,
validation_function=self.validation_function,
validation_error_handler=self.validation_error_handler,
)
return super(SwaggerView, self).dispatch_request(*args, **kwargs)


def convert_schemas(d, definitions=None):
def convert_schemas(d: dict, definitions: dict = {}) -> dict:
"""
Convert Marshmallow schemas to dict definitions
Also updates the optional definitions argument with any definitions
entries contained within the schema.
:param d: dict to convert
:type d: dict
:param definitions: dict of definitions
:type definitions: dict
:return: converted dict
:rtype: dict
"""
if definitions is None:
definitions = {}
definitions.update(d.get("definitions", {}))
new: dict = {}

new = {}
for k, v in d.items():
if isinstance(v, dict):
v = convert_schemas(v, definitions)
if isinstance(v, (list, tuple)):
new_v = []
new_v: list = []
for item in v:
if isinstance(item, dict):
new_v.append(convert_schemas(item, definitions))
Expand All @@ -139,7 +161,7 @@ def convert_schemas(d, definitions=None):
raise RuntimeError("Please install marshmallow and apispec")

definitions[v.__name__] = schema2jsonschema(v)
ref = {"$ref": "#/definitions/{0}".format(v.__name__)}
ref: dict[str, str] = {"$ref": "#/definitions/{0}".format(v.__name__)}
if k == "parameters":
new[k] = schema2parameters(v, location=v.swag_in)
new[k][0]["schema"] = ref
Expand Down
Loading

0 comments on commit 8177478

Please sign in to comment.