8000 Release v3.1 by jacebrowning · Pull Request #155 · flask-api/flask-api · GitHub
[go: up one dir, main page]

Skip to content

Release v3.1 #155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ env:
global:
- RANDOM_SEED=0
matrix:
- FLASK_VERSION=2.0.1
- FLASK_VERSION=2.3.2

before_install:
- pip install pipenv
Expand Down
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ nose = "*"
coveragespace = "~=4.1"

# Documentation
mkdocs = "~=0.17.2"
mkdocs = "~=1.2.3"
docutils = "*"

# Release
Expand Down
892 changes: 600 additions & 292 deletions Pipfile.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions docs/about/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Release Notes

## Version 3.1

- Fixed support for Flask `2.3`.

## Version 3.0

* Dropped support for Flask `<2.0`.
Expand Down
29 changes: 15 additions & 14 deletions example.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
from flask import request, url_for
from flask.ext.api import FlaskAPI, status, exceptions
from flask.ext.api import FlaskAPI, exceptions, status

app = FlaskAPI(__name__)


notes = {
0: 'do the shopping',
1: 'build the codez',
2: 'paint the door',
0: "do the shopping",
1: "build the codez",
2: "paint the door",
}


def note_repr(key):
return {
'url': request.host_url.rstrip('/') + url_for('notes_detail', key=key),
'text': notes[key]
"url": request.host_url.rstrip("/") + url_for("notes_detail", key=key),
"text": notes[key],
}


@app.route("/", methods=['GET', 'POST'])
@app.route("/", methods=["GET", "POST"])
def notes_list():
"""
List or create notes.
"""
if request.method == 'POST':
note = str(request.data.get('text', ''))
if request.method == "POST":
note = str(request.data.get("text", ""))
idx = max(notes.keys()) + 1
notes[idx] = note
return note_repr(idx), status.HTTP_201_CREATED
Expand All @@ -32,19 +33,19 @@ def notes_list():
return [note_repr(idx) for idx in sorted(notes.keys())]


@app.route("/<int:key>/", methods=['GET', 'PUT', 'DELETE'])
@app.route("/<int:key>/", methods=["GET", "PUT", "DELETE"])
def notes_detail(key):
"""
Retrieve, update or delete note instances.
"""
if request.method == 'PUT':
note = str(request.data.get('text', ''))
if request.method == "PUT":
note = str(request.data.get("text", ""))
notes[key] = note
return note_repr(key)

elif request.method == 'DELETE':
elif request.method == "DELETE":
notes.pop(key, None)
return '', status.HTTP_204_NO_CONTENT
return "", status.HTTP_204_NO_CONTENT

# request.method == 'GET'
if key not in notes:
Expand Down
2 changes: 1 addition & 1 deletion flask_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from flask_api.app import FlaskAPI

__version__ = '3.0.post1'
__version__ = "3.1"
54 changes: 29 additions & 25 deletions flask_api/app.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
# coding: utf8
from __future__ import unicode_literals
from flask import request, Flask, Blueprint
import re
import sys
from itertools import chain

from flask import Blueprint, Flask, request
from werkzeug.exceptions import HTTPException

from flask_api.compat import is_flask_legacy
from flask_api.exceptions import APIException
from flask_api.request import APIRequest
from flask_api.response import APIResponse
from flask_api.settings import APISettings
from flask_api.status import HTTP_204_NO_CONTENT
from itertools import chain
from werkzeug.exceptions import HTTPException
import re
import sys
from flask_api.compat import is_flask_legacy


api_resources = Blueprint(
'flask-api', __name__,
url_prefix='/flask-api',
template_folder='templates', static_folder='static'
"flask-api",
__name__,
url_prefix="/flask-api",
template_folder="templates",
static_folder="static",
)
F438

Expand All @@ -32,7 +33,7 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.api_settings = APISettings(self.config)
self.register_blueprint(api_resources)
self.jinja_env.filters['urlize_quoted_links'] = urlize_quoted_links
self.jinja_env.filters["urlize_quoted_links"] = urlize_quoted_links

def preprocess_request(self):
request.parser_classes = self.api_settings.DEFAULT_PARSERS
Expand All @@ -49,10 +50,10 @@ def make_response(self, rv):
rv, status_or_headers, headers = rv + (None,) * (3 - len(rv))

if rv is None and status_or_headers == HTTP_204_NO_CONTENT:
rv = ''
rv = ""

if rv is None and status_or_headers:
raise ValueError('View function did not return a response')
raise ValueError("View function did not return a response")

if isinstance(status_or_headers, (dict, list)):
headers, status_or_headers = status_or_headers, None
Expand Down Expand Up @@ -98,15 +99,16 @@ def handle_user_exception(self, e):
if isinstance(e, typecheck):
return handler(e)
else:
for typecheck, handler in chain(dict(blueprint_handlers).items(),
dict(app_handlers).items()):
for typecheck, handler in chain(
dict(blueprint_handlers).items(), dict(app_handlers).items()
):
if isinstance(e, typecheck):
return handler(e)

raise e

def handle_api_exception(self, exc):
content = {'message': exc.detail}
content = {"message": exc.detail}
status = exc.status_code
return self.response_class(content, status=status)

Expand All @@ -119,13 +121,15 @@ def create_url_adapter(self, request):
"""
if request is not None:
environ = request.environ.copy()
environ['REQUEST_METHOD'] = request.method
return self.url_map.bind_to_environ(environ,
server_name=self.config['SERVER_NAME'])
environ["REQUEST_METHOD"] = request.method
return self.url_map.bind_to_environ(
environ, server_name=self.config["SERVER_NAME"]
)
# We need at the very least the server name to be set for this
# to work.
if self.config['SERVER_NAME'] is not None:
if self.config["SERVER_NAME"] is not None:
return self.url_map.bind(
self.config['SERVER_NAME'],
script_name=self.config['APPLICATION_ROOT'] or '/',
url_scheme=self.config['PREFERRED_URL_SCHEME'])
self.config["SERVER_NAME"],
script_name=self.config["APPLICATION_ROOT"] or "/",
url_scheme=self.config["PREFERRED_URL_SCHEME"],
)
3 changes: 0 additions & 3 deletions flask_api/compat.py
< 10000 th scope="col">Diff line change
Original file line number Diff line number
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import
from flask import __version__ as flask_version

# Markdown is optional
Expand All @@ -17,7 +15,6 @@ def apply_markdown(text):
md = markdown.Markdown(extensions=extensions)
return md.convert(text)


except ImportError: # pragma: no cover - markdown installed for tests
apply_markdown = None

Expand Down
5 changes: 5 additions & 0 deletions flask_api/decorators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from functools import wraps

from flask import request


Expand All @@ -11,7 +12,9 @@ def decorated_function(*args, **kwargs):
else:
request.parser_classes = parsers
return func(*args, **kwargs)

return decorated_function

return decorator


Expand All @@ -24,5 +27,7 @@ def decorated_function(*args, **kwargs):
else:
request.renderer_classes = renderers
return func(*args, **kwargs)

return decorated_function

return decorator
21 changes: 11 additions & 10 deletions flask_api/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from __future__ import unicode_literals
from flask_api import status


class APIException(Exception):
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
detail = ''
detail = ""

def __init__(self, detail=None):
if detail is not None:
Expand All @@ -16,27 +15,28 @@ def __str__(self):

class ParseError(APIException):
status_code = status.HTTP_400_BAD_REQUEST
detail = 'Malformed request.'
detail = "Malformed request."


class AuthenticationFailed(APIException):
status_code = status.HTTP_401_UNAUTHORIZED
detail = 'Incorrect authentication credentials.'
detail = "Incorrect authentication credentials."


class NotAuthenticated(APIException):
status_code = status.HTTP_401_UNAUTHORIZED
detail = 'Authentication credentials were not provided.'
detail = "Authentication credentials were not provided."


class PermissionDenied(APIException):
status_code = status.HTTP_403_FORBIDDEN
detail = 'You do not have permission to perform this action.'
detail = "You do not have permission to perform this action."


class NotFound(APIException):
status_code = status.HTTP_404_NOT_FOUND
detail = 'This resource does not exist.'
detail = "This resource does not exist."


# class MethodNotAllowed(APIException):
# status_code = status.HTTP_405_METHOD_NOT_ALLOWED
Expand All @@ -48,17 +48,18 @@ class NotFound(APIException):

class NotAcceptable(APIException):
status_code = status.HTTP_406_NOT_ACCEPTABLE
detail = 'Could not satisfy the request Accept header.'
detail = "Could not satisfy the request Accept header."


class UnsupportedMediaType(APIException):
status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
detail = 'Unsupported media type in the request Content-Type header.'
detail = "Unsupported media type in the request Content-Type header."


class Throttled(APIException):
status_code = status.HTTP_429_TOO_MANY_REQUESTS
detail = 'Request was throttled.'
detail = "Request was throttled."


# def __init__(self, wait=None, detail=None):
# if wait is None:
Expand Down
Loading
0