mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-07-04 10:59:27 -05:00
Add server version to master api
Add IsEvadedOffense to EFPenalty Fix remote log reading in not Windows
This commit is contained in:
729
Master/env_master/Lib/site-packages/flask_restful/__init__.py
Normal file
729
Master/env_master/Lib/site-packages/flask_restful/__init__.py
Normal file
@ -0,0 +1,729 @@
|
||||
from __future__ import absolute_import
|
||||
import difflib
|
||||
from functools import wraps, partial
|
||||
import re
|
||||
from flask import request, url_for, current_app
|
||||
from flask import abort as original_flask_abort
|
||||
from flask import make_response as original_flask_make_response
|
||||
from flask.views import MethodView
|
||||
from flask.signals import got_request_exception
|
||||
from werkzeug.datastructures import Headers
|
||||
from werkzeug.exceptions import HTTPException, MethodNotAllowed, NotFound, NotAcceptable, InternalServerError
|
||||
from werkzeug.http import HTTP_STATUS_CODES
|
||||
from werkzeug.wrappers import Response as ResponseBase
|
||||
from flask_restful.utils import http_status_message, unpack, OrderedDict
|
||||
from flask_restful.representations.json import output_json
|
||||
import sys
|
||||
from flask.helpers import _endpoint_from_view_func
|
||||
from types import MethodType
|
||||
import operator
|
||||
from collections import Mapping
|
||||
|
||||
|
||||
__all__ = ('Api', 'Resource', 'marshal', 'marshal_with', 'marshal_with_field', 'abort')
|
||||
|
||||
|
||||
def abort(http_status_code, **kwargs):
|
||||
"""Raise a HTTPException for the given http_status_code. Attach any keyword
|
||||
arguments to the exception for later processing.
|
||||
"""
|
||||
#noinspection PyUnresolvedReferences
|
||||
try:
|
||||
original_flask_abort(http_status_code)
|
||||
except HTTPException as e:
|
||||
if len(kwargs):
|
||||
e.data = kwargs
|
||||
raise
|
||||
|
||||
DEFAULT_REPRESENTATIONS = [('application/json', output_json)]
|
||||
|
||||
|
||||
class Api(object):
|
||||
"""
|
||||
The main entry point for the application.
|
||||
You need to initialize it with a Flask Application: ::
|
||||
|
||||
>>> app = Flask(__name__)
|
||||
>>> api = restful.Api(app)
|
||||
|
||||
Alternatively, you can use :meth:`init_app` to set the Flask application
|
||||
after it has been constructed.
|
||||
|
||||
:param app: the Flask application object
|
||||
:type app: flask.Flask
|
||||
:type app: flask.Blueprint
|
||||
:param prefix: Prefix all routes with a value, eg v1 or 2010-04-01
|
||||
:type prefix: str
|
||||
:param default_mediatype: The default media type to return
|
||||
:type default_mediatype: str
|
||||
:param decorators: Decorators to attach to every resource
|
||||
:type decorators: list
|
||||
:param catch_all_404s: Use :meth:`handle_error`
|
||||
to handle 404 errors throughout your app
|
||||
:param serve_challenge_on_401: Whether to serve a challenge response to
|
||||
clients on receiving 401. This usually leads to a username/password
|
||||
popup in web browers.
|
||||
:param url_part_order: A string that controls the order that the pieces
|
||||
of the url are concatenated when the full url is constructed. 'b'
|
||||
is the blueprint (or blueprint registration) prefix, 'a' is the api
|
||||
prefix, and 'e' is the path component the endpoint is added with
|
||||
:type catch_all_404s: bool
|
||||
:param errors: A dictionary to define a custom response for each
|
||||
exception or error raised during a request
|
||||
:type errors: dict
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, app=None, prefix='',
|
||||
default_mediatype='application/json', decorators=None,
|
||||
catch_all_404s=False, serve_challenge_on_401=False,
|
||||
url_part_order='bae', errors=None):
|
||||
self.representations = OrderedDict(DEFAULT_REPRESENTATIONS)
|
||||
self.urls = {}
|
||||
self.prefix = prefix
|
||||
self.default_mediatype = default_mediatype
|
||||
self.decorators = decorators if decorators else []
|
||||
self.catch_all_404s = catch_all_404s
|
||||
self.serve_challenge_on_401 = serve_challenge_on_401
|
||||
self.url_part_order = url_part_order
|
||||
self.errors = errors or {}
|
||||
self.blueprint_setup = None
|
||||
self.endpoints = set()
|
||||
self.resources = []
|
||||
self.app = None
|
||||
self.blueprint = None
|
||||
|
||||
if app is not None:
|
||||
self.app = app
|
||||
self.init_app(app)
|
||||
|
||||
def init_app(self, app):
|
||||
"""Initialize this class with the given :class:`flask.Flask`
|
||||
application or :class:`flask.Blueprint` object.
|
||||
|
||||
:param app: the Flask application or blueprint object
|
||||
:type app: flask.Flask
|
||||
:type app: flask.Blueprint
|
||||
|
||||
Examples::
|
||||
|
||||
api = Api()
|
||||
api.add_resource(...)
|
||||
api.init_app(app)
|
||||
|
||||
"""
|
||||
# If app is a blueprint, defer the initialization
|
||||
try:
|
||||
app.record(self._deferred_blueprint_init)
|
||||
# Flask.Blueprint has a 'record' attribute, Flask.Api does not
|
||||
except AttributeError:
|
||||
self._init_app(app)
|
||||
else:
|
||||
self.blueprint = app
|
||||
|
||||
def _complete_url(self, url_part, registration_prefix):
|
||||
"""This method is used to defer the construction of the final url in
|
||||
the case that the Api is created with a Blueprint.
|
||||
|
||||
:param url_part: The part of the url the endpoint is registered with
|
||||
:param registration_prefix: The part of the url contributed by the
|
||||
blueprint. Generally speaking, BlueprintSetupState.url_prefix
|
||||
"""
|
||||
parts = {
|
||||
'b': registration_prefix,
|
||||
'a': self.prefix,
|
||||
'e': url_part
|
||||
}
|
||||
return ''.join(parts[key] for key in self.url_part_order if parts[key])
|
||||
|
||||
@staticmethod
|
||||
def _blueprint_setup_add_url_rule_patch(blueprint_setup, rule, endpoint=None, view_func=None, **options):
|
||||
"""Method used to patch BlueprintSetupState.add_url_rule for setup
|
||||
state instance corresponding to this Api instance. Exists primarily
|
||||
to enable _complete_url's function.
|
||||
|
||||
:param blueprint_setup: The BlueprintSetupState instance (self)
|
||||
:param rule: A string or callable that takes a string and returns a
|
||||
string(_complete_url) that is the url rule for the endpoint
|
||||
being registered
|
||||
:param endpoint: See BlueprintSetupState.add_url_rule
|
||||
:param view_func: See BlueprintSetupState.add_url_rule
|
||||
:param **options: See BlueprintSetupState.add_url_rule
|
||||
"""
|
||||
|
||||
if callable(rule):
|
||||
rule = rule(blueprint_setup.url_prefix)
|
||||
elif blueprint_setup.url_prefix:
|
||||
rule = blueprint_setup.url_prefix + rule
|
||||
options.setdefault('subdomain', blueprint_setup.subdomain)
|
||||
if endpoint is None:
|
||||
endpoint = _endpoint_from_view_func(view_func)
|
||||
defaults = blueprint_setup.url_defaults
|
||||
if 'defaults' in options:
|
||||
defaults = dict(defaults, **options.pop('defaults'))
|
||||
blueprint_setup.app.add_url_rule(rule, '%s.%s' % (blueprint_setup.blueprint.name, endpoint),
|
||||
view_func, defaults=defaults, **options)
|
||||
|
||||
def _deferred_blueprint_init(self, setup_state):
|
||||
"""Synchronize prefix between blueprint/api and registration options, then
|
||||
perform initialization with setup_state.app :class:`flask.Flask` object.
|
||||
When a :class:`flask_restful.Api` object is initialized with a blueprint,
|
||||
this method is recorded on the blueprint to be run when the blueprint is later
|
||||
registered to a :class:`flask.Flask` object. This method also monkeypatches
|
||||
BlueprintSetupState.add_url_rule with _blueprint_setup_add_url_rule_patch.
|
||||
|
||||
:param setup_state: The setup state object passed to deferred functions
|
||||
during blueprint registration
|
||||
:type setup_state: flask.blueprints.BlueprintSetupState
|
||||
|
||||
"""
|
||||
|
||||
self.blueprint_setup = setup_state
|
||||
if setup_state.add_url_rule.__name__ != '_blueprint_setup_add_url_rule_patch':
|
||||
setup_state._original_add_url_rule = setup_state.add_url_rule
|
||||
setup_state.add_url_rule = MethodType(Api._blueprint_setup_add_url_rule_patch,
|
||||
setup_state)
|
||||
if not setup_state.first_registration:
|
||||
raise ValueError('flask-restful blueprints can only be registered once.')
|
||||
self._init_app(setup_state.app)
|
||||
|
||||
def _init_app(self, app):
|
||||
"""Perform initialization actions with the given :class:`flask.Flask`
|
||||
object.
|
||||
|
||||
:param app: The flask application object
|
||||
:type app: flask.Flask
|
||||
"""
|
||||
app.handle_exception = partial(self.error_router, app.handle_exception)
|
||||
app.handle_user_exception = partial(self.error_router, app.handle_user_exception)
|
||||
|
||||
if len(self.resources) > 0:
|
||||
for resource, urls, kwargs in self.resources:
|
||||
self._register_view(app, resource, *urls, **kwargs)
|
||||
|
||||
def owns_endpoint(self, endpoint):
|
||||
"""Tests if an endpoint name (not path) belongs to this Api. Takes
|
||||
in to account the Blueprint name part of the endpoint name.
|
||||
|
||||
:param endpoint: The name of the endpoint being checked
|
||||
:return: bool
|
||||
"""
|
||||
|
||||
if self.blueprint:
|
||||
if endpoint.startswith(self.blueprint.name):
|
||||
endpoint = endpoint.split(self.blueprint.name + '.', 1)[-1]
|
||||
else:
|
||||
return False
|
||||
return endpoint in self.endpoints
|
||||
|
||||
def _should_use_fr_error_handler(self):
|
||||
""" Determine if error should be handled with FR or default Flask
|
||||
|
||||
The goal is to return Flask error handlers for non-FR-related routes,
|
||||
and FR errors (with the correct media type) for FR endpoints. This
|
||||
method currently handles 404 and 405 errors.
|
||||
|
||||
:return: bool
|
||||
"""
|
||||
adapter = current_app.create_url_adapter(request)
|
||||
|
||||
try:
|
||||
adapter.match()
|
||||
except MethodNotAllowed as e:
|
||||
# Check if the other HTTP methods at this url would hit the Api
|
||||
valid_route_method = e.valid_methods[0]
|
||||
rule, _ = adapter.match(method=valid_route_method, return_rule=True)
|
||||
return self.owns_endpoint(rule.endpoint)
|
||||
except NotFound:
|
||||
return self.catch_all_404s
|
||||
except:
|
||||
# Werkzeug throws other kinds of exceptions, such as Redirect
|
||||
pass
|
||||
|
||||
def _has_fr_route(self):
|
||||
"""Encapsulating the rules for whether the request was to a Flask endpoint"""
|
||||
# 404's, 405's, which might not have a url_rule
|
||||
if self._should_use_fr_error_handler():
|
||||
return True
|
||||
# for all other errors, just check if FR dispatched the route
|
||||
if not request.url_rule:
|
||||
return False
|
||||
return self.owns_endpoint(request.url_rule.endpoint)
|
||||
|
||||
def error_router(self, original_handler, e):
|
||||
"""This function decides whether the error occured in a flask-restful
|
||||
endpoint or not. If it happened in a flask-restful endpoint, our
|
||||
handler will be dispatched. If it happened in an unrelated view, the
|
||||
app's original error handler will be dispatched.
|
||||
In the event that the error occurred in a flask-restful endpoint but
|
||||
the local handler can't resolve the situation, the router will fall
|
||||
back onto the original_handler as last resort.
|
||||
|
||||
:param original_handler: the original Flask error handler for the app
|
||||
:type original_handler: function
|
||||
:param e: the exception raised while handling the request
|
||||
:type e: Exception
|
||||
|
||||
"""
|
||||
if self._has_fr_route():
|
||||
try:
|
||||
return self.handle_error(e)
|
||||
except Exception:
|
||||
pass # Fall through to original handler
|
||||
return original_handler(e)
|
||||
|
||||
def handle_error(self, e):
|
||||
"""Error handler for the API transforms a raised exception into a Flask
|
||||
response, with the appropriate HTTP status code and body.
|
||||
|
||||
:param e: the raised Exception object
|
||||
:type e: Exception
|
||||
|
||||
"""
|
||||
got_request_exception.send(current_app._get_current_object(), exception=e)
|
||||
|
||||
if not isinstance(e, HTTPException) and current_app.propagate_exceptions:
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
if exc_value is e:
|
||||
raise
|
||||
else:
|
||||
raise e
|
||||
|
||||
headers = Headers()
|
||||
if isinstance(e, HTTPException):
|
||||
code = e.code
|
||||
default_data = {
|
||||
'message': getattr(e, 'description', http_status_message(code))
|
||||
}
|
||||
headers = e.get_response().headers
|
||||
else:
|
||||
code = 500
|
||||
default_data = {
|
||||
'message': http_status_message(code),
|
||||
}
|
||||
|
||||
# Werkzeug exceptions generate a content-length header which is added
|
||||
# to the response in addition to the actual content-length header
|
||||
# https://github.com/flask-restful/flask-restful/issues/534
|
||||
remove_headers = ('Content-Length',)
|
||||
|
||||
for header in remove_headers:
|
||||
headers.pop(header, None)
|
||||
|
||||
data = getattr(e, 'data', default_data)
|
||||
|
||||
if code >= 500:
|
||||
exc_info = sys.exc_info()
|
||||
if exc_info[1] is None:
|
||||
exc_info = None
|
||||
current_app.log_exception(exc_info)
|
||||
|
||||
help_on_404 = current_app.config.get("ERROR_404_HELP", True)
|
||||
if code == 404 and help_on_404:
|
||||
rules = dict([(re.sub('(<.*>)', '', rule.rule), rule.rule)
|
||||
for rule in current_app.url_map.iter_rules()])
|
||||
close_matches = difflib.get_close_matches(request.path, rules.keys())
|
||||
if close_matches:
|
||||
# If we already have a message, add punctuation and continue it.
|
||||
if "message" in data:
|
||||
data["message"] = data["message"].rstrip('.') + '. '
|
||||
else:
|
||||
data["message"] = ""
|
||||
|
||||
data['message'] += 'You have requested this URI [' + request.path + \
|
||||
'] but did you mean ' + \
|
||||
' or '.join((
|
||||
rules[match] for match in close_matches)
|
||||
) + ' ?'
|
||||
|
||||
error_cls_name = type(e).__name__
|
||||
if error_cls_name in self.errors:
|
||||
custom_data = self.errors.get(error_cls_name, {})
|
||||
code = custom_data.get('status', 500)
|
||||
data.update(custom_data)
|
||||
|
||||
if code == 406 and self.default_mediatype is None:
|
||||
# if we are handling NotAcceptable (406), make sure that
|
||||
# make_response uses a representation we support as the
|
||||
# default mediatype (so that make_response doesn't throw
|
||||
# another NotAcceptable error).
|
||||
supported_mediatypes = list(self.representations.keys())
|
||||
fallback_mediatype = supported_mediatypes[0] if supported_mediatypes else "text/plain"
|
||||
resp = self.make_response(
|
||||
data,
|
||||
code,
|
||||
headers,
|
||||
fallback_mediatype = fallback_mediatype
|
||||
)
|
||||
else:
|
||||
resp = self.make_response(data, code, headers)
|
||||
|
||||
if code == 401:
|
||||
resp = self.unauthorized(resp)
|
||||
return resp
|
||||
|
||||
def mediatypes_method(self):
|
||||
"""Return a method that returns a list of mediatypes
|
||||
"""
|
||||
return lambda resource_cls: self.mediatypes() + [self.default_mediatype]
|
||||
|
||||
def add_resource(self, resource, *urls, **kwargs):
|
||||
"""Adds a resource to the api.
|
||||
|
||||
:param resource: the class name of your resource
|
||||
:type resource: :class:`Resource`
|
||||
|
||||
:param urls: one or more url routes to match for the resource, standard
|
||||
flask routing rules apply. Any url variables will be
|
||||
passed to the resource method as args.
|
||||
:type urls: str
|
||||
|
||||
:param endpoint: endpoint name (defaults to :meth:`Resource.__name__.lower`
|
||||
Can be used to reference this route in :class:`fields.Url` fields
|
||||
:type endpoint: str
|
||||
|
||||
:param resource_class_args: args to be forwarded to the constructor of
|
||||
the resource.
|
||||
:type resource_class_args: tuple
|
||||
|
||||
:param resource_class_kwargs: kwargs to be forwarded to the constructor
|
||||
of the resource.
|
||||
:type resource_class_kwargs: dict
|
||||
|
||||
Additional keyword arguments not specified above will be passed as-is
|
||||
to :meth:`flask.Flask.add_url_rule`.
|
||||
|
||||
Examples::
|
||||
|
||||
api.add_resource(HelloWorld, '/', '/hello')
|
||||
api.add_resource(Foo, '/foo', endpoint="foo")
|
||||
api.add_resource(FooSpecial, '/special/foo', endpoint="foo")
|
||||
|
||||
"""
|
||||
if self.app is not None:
|
||||
self._register_view(self.app, resource, *urls, **kwargs)
|
||||
else:
|
||||
self.resources.append((resource, urls, kwargs))
|
||||
|
||||
def resource(self, *urls, **kwargs):
|
||||
"""Wraps a :class:`~flask_restful.Resource` class, adding it to the
|
||||
api. Parameters are the same as :meth:`~flask_restful.Api.add_resource`.
|
||||
|
||||
Example::
|
||||
|
||||
app = Flask(__name__)
|
||||
api = restful.Api(app)
|
||||
|
||||
@api.resource('/foo')
|
||||
class Foo(Resource):
|
||||
def get(self):
|
||||
return 'Hello, World!'
|
||||
|
||||
"""
|
||||
def decorator(cls):
|
||||
self.add_resource(cls, *urls, **kwargs)
|
||||
return cls
|
||||
return decorator
|
||||
|
||||
def _register_view(self, app, resource, *urls, **kwargs):
|
||||
endpoint = kwargs.pop('endpoint', None) or resource.__name__.lower()
|
||||
self.endpoints.add(endpoint)
|
||||
resource_class_args = kwargs.pop('resource_class_args', ())
|
||||
resource_class_kwargs = kwargs.pop('resource_class_kwargs', {})
|
||||
|
||||
# NOTE: 'view_functions' is cleaned up from Blueprint class in Flask 1.0
|
||||
if endpoint in getattr(app, 'view_functions', {}):
|
||||
previous_view_class = app.view_functions[endpoint].__dict__['view_class']
|
||||
|
||||
# if you override the endpoint with a different class, avoid the collision by raising an exception
|
||||
if previous_view_class != resource:
|
||||
raise ValueError('This endpoint (%s) is already set to the class %s.' % (endpoint, previous_view_class.__name__))
|
||||
|
||||
resource.mediatypes = self.mediatypes_method() # Hacky
|
||||
resource.endpoint = endpoint
|
||||
resource_func = self.output(resource.as_view(endpoint, *resource_class_args,
|
||||
**resource_class_kwargs))
|
||||
|
||||
for decorator in self.decorators:
|
||||
resource_func = decorator(resource_func)
|
||||
|
||||
for url in urls:
|
||||
# If this Api has a blueprint
|
||||
if self.blueprint:
|
||||
# And this Api has been setup
|
||||
if self.blueprint_setup:
|
||||
# Set the rule to a string directly, as the blueprint is already
|
||||
# set up.
|
||||
self.blueprint_setup.add_url_rule(url, view_func=resource_func, **kwargs)
|
||||
continue
|
||||
else:
|
||||
# Set the rule to a function that expects the blueprint prefix
|
||||
# to construct the final url. Allows deferment of url finalization
|
||||
# in the case that the associated Blueprint has not yet been
|
||||
# registered to an application, so we can wait for the registration
|
||||
# prefix
|
||||
rule = partial(self._complete_url, url)
|
||||
else:
|
||||
# If we've got no Blueprint, just build a url with no prefix
|
||||
rule = self._complete_url(url, '')
|
||||
# Add the url to the application or blueprint
|
||||
app.add_url_rule(rule, view_func=resource_func, **kwargs)
|
||||
|
||||
def output(self, resource):
|
||||
"""Wraps a resource (as a flask view function), for cases where the
|
||||
resource does not directly return a response object
|
||||
|
||||
:param resource: The resource as a flask view function
|
||||
"""
|
||||
@wraps(resource)
|
||||
def wrapper(*args, **kwargs):
|
||||
resp = resource(*args, **kwargs)
|
||||
if isinstance(resp, ResponseBase): # There may be a better way to test
|
||||
return resp
|
||||
data, code, headers = unpack(resp)
|
||||
return self.make_response(data, code, headers=headers)
|
||||
return wrapper
|
||||
|
||||
def url_for(self, resource, **values):
|
||||
"""Generates a URL to the given resource.
|
||||
|
||||
Works like :func:`flask.url_for`."""
|
||||
endpoint = resource.endpoint
|
||||
if self.blueprint:
|
||||
endpoint = '{0}.{1}'.format(self.blueprint.name, endpoint)
|
||||
return url_for(endpoint, **values)
|
||||
|
||||
def make_response(self, data, *args, **kwargs):
|
||||
"""Looks up the representation transformer for the requested media
|
||||
type, invoking the transformer to create a response object. This
|
||||
defaults to default_mediatype if no transformer is found for the
|
||||
requested mediatype. If default_mediatype is None, a 406 Not
|
||||
Acceptable response will be sent as per RFC 2616 section 14.1
|
||||
|
||||
:param data: Python object containing response data to be transformed
|
||||
"""
|
||||
default_mediatype = kwargs.pop('fallback_mediatype', None) or self.default_mediatype
|
||||
mediatype = request.accept_mimetypes.best_match(
|
||||
self.representations,
|
||||
default=default_mediatype,
|
||||
)
|
||||
if mediatype is None:
|
||||
raise NotAcceptable()
|
||||
if mediatype in self.representations:
|
||||
resp = self.representations[mediatype](data, *args, **kwargs)
|
||||
resp.headers['Content-Type'] = mediatype
|
||||
return resp
|
||||
elif mediatype == 'text/plain':
|
||||
resp = original_flask_make_response(str(data), *args, **kwargs)
|
||||
resp.headers['Content-Type'] = 'text/plain'
|
||||
return resp
|
||||
else:
|
||||
raise InternalServerError()
|
||||
|
||||
def mediatypes(self):
|
||||
"""Returns a list of requested mediatypes sent in the Accept header"""
|
||||
return [h for h, q in sorted(request.accept_mimetypes,
|
||||
key=operator.itemgetter(1), reverse=True)]
|
||||
|
||||
def representation(self, mediatype):
|
||||
"""Allows additional representation transformers to be declared for the
|
||||
api. Transformers are functions that must be decorated with this
|
||||
method, passing the mediatype the transformer represents. Three
|
||||
arguments are passed to the transformer:
|
||||
|
||||
* The data to be represented in the response body
|
||||
* The http status code
|
||||
* A dictionary of headers
|
||||
|
||||
The transformer should convert the data appropriately for the mediatype
|
||||
and return a Flask response object.
|
||||
|
||||
Ex::
|
||||
|
||||
@api.representation('application/xml')
|
||||
def xml(data, code, headers):
|
||||
resp = make_response(convert_data_to_xml(data), code)
|
||||
resp.headers.extend(headers)
|
||||
return resp
|
||||
"""
|
||||
def wrapper(func):
|
||||
self.representations[mediatype] = func
|
||||
return func
|
||||
return wrapper
|
||||
|
||||
def unauthorized(self, response):
|
||||
""" Given a response, change it to ask for credentials """
|
||||
|
||||
if self.serve_challenge_on_401:
|
||||
realm = current_app.config.get("HTTP_BASIC_AUTH_REALM", "flask-restful")
|
||||
challenge = u"{0} realm=\"{1}\"".format("Basic", realm)
|
||||
|
||||
response.headers['WWW-Authenticate'] = challenge
|
||||
return response
|
||||
|
||||
|
||||
class Resource(MethodView):
|
||||
"""
|
||||
Represents an abstract RESTful resource. Concrete resources should
|
||||
extend from this class and expose methods for each supported HTTP
|
||||
method. If a resource is invoked with an unsupported HTTP method,
|
||||
the API will return a response with status 405 Method Not Allowed.
|
||||
Otherwise the appropriate method is called and passed all arguments
|
||||
from the url rule used when adding the resource to an Api instance. See
|
||||
:meth:`~flask_restful.Api.add_resource` for details.
|
||||
"""
|
||||
representations = None
|
||||
method_decorators = []
|
||||
|
||||
def dispatch_request(self, *args, **kwargs):
|
||||
|
||||
# Taken from flask
|
||||
#noinspection PyUnresolvedReferences
|
||||
meth = getattr(self, request.method.lower(), None)
|
||||
if meth is None and request.method == 'HEAD':
|
||||
meth = getattr(self, 'get', None)
|
||||
assert meth is not None, 'Unimplemented method %r' % request.method
|
||||
|
||||
if isinstance(self.method_decorators, Mapping):
|
||||
decorators = self.method_decorators.get(request.method.lower(), [])
|
||||
else:
|
||||
decorators = self.method_decorators
|
||||
|
||||
for decorator in decorators:
|
||||
meth = decorator(meth)
|
||||
|
||||
resp = meth(*args, **kwargs)
|
||||
|
||||
if isinstance(resp, ResponseBase): # There may be a better way to test
|
||||
return resp
|
||||
|
||||
representations = self.representations or OrderedDict()
|
||||
|
||||
#noinspection PyUnresolvedReferences
|
||||
mediatype = request.accept_mimetypes.best_match(representations, default=None)
|
||||
if mediatype in representations:
|
||||
data, code, headers = unpack(resp)
|
||||
resp = representations[mediatype](data, code, headers)
|
||||
resp.headers['Content-Type'] = mediatype
|
||||
return resp
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
def marshal(data, fields, envelope=None):
|
||||
"""Takes raw data (in the form of a dict, list, object) and a dict of
|
||||
fields to output and filters the data based on those fields.
|
||||
|
||||
:param data: the actual object(s) from which the fields are taken from
|
||||
:param fields: a dict of whose keys will make up the final serialized
|
||||
response output
|
||||
:param envelope: optional key that will be used to envelop the serialized
|
||||
response
|
||||
|
||||
|
||||
>>> from flask_restful import fields, marshal
|
||||
>>> data = { 'a': 100, 'b': 'foo' }
|
||||
>>> mfields = { 'a': fields.Raw }
|
||||
|
||||
>>> marshal(data, mfields)
|
||||
OrderedDict([('a', 100)])
|
||||
|
||||
>>> marshal(data, mfields, envelope='data')
|
||||
OrderedDict([('data', OrderedDict([('a', 100)]))])
|
||||
|
||||
"""
|
||||
|
||||
def make(cls):
|
||||
if isinstance(cls, type):
|
||||
return cls()
|
||||
return cls
|
||||
|
||||
if isinstance(data, (list, tuple)):
|
||||
return (OrderedDict([(envelope, [marshal(d, fields) for d in data])])
|
||||
if envelope else [marshal(d, fields) for d in data])
|
||||
|
||||
items = ((k, marshal(data, v) if isinstance(v, dict)
|
||||
else make(v).output(k, data))
|
||||
for k, v in fields.items())
|
||||
return OrderedDict([(envelope, OrderedDict(items))]) if envelope else OrderedDict(items)
|
||||
|
||||
|
||||
class marshal_with(object):
|
||||
"""A decorator that apply marshalling to the return values of your methods.
|
||||
|
||||
>>> from flask_restful import fields, marshal_with
|
||||
>>> mfields = { 'a': fields.Raw }
|
||||
>>> @marshal_with(mfields)
|
||||
... def get():
|
||||
... return { 'a': 100, 'b': 'foo' }
|
||||
...
|
||||
...
|
||||
>>> get()
|
||||
OrderedDict([('a', 100)])
|
||||
|
||||
>>> @marshal_with(mfields, envelope='data')
|
||||
... def get():
|
||||
... return { 'a': 100, 'b': 'foo' }
|
||||
...
|
||||
...
|
||||
>>> get()
|
||||
OrderedDict([('data', OrderedDict([('a', 100)]))])
|
||||
|
||||
see :meth:`flask_restful.marshal`
|
||||
"""
|
||||
def __init__(self, fields, envelope=None):
|
||||
"""
|
||||
:param fields: a dict of whose keys will make up the final
|
||||
serialized response output
|
||||
:param envelope: optional key that will be used to envelop the serialized
|
||||
response
|
||||
"""
|
||||
self.fields = fields
|
||||
self.envelope = envelope
|
||||
|
||||
def __call__(self, f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
resp = f(*args, **kwargs)
|
||||
if isinstance(resp, tuple):
|
||||
data, code, headers = unpack(resp)
|
||||
return marshal(data, self.fields, self.envelope), code, headers
|
||||
else:
|
||||
return marshal(resp, self.fields, self.envelope)
|
||||
return wrapper
|
||||
|
||||
|
||||
class marshal_with_field(object):
|
||||
"""
|
||||
A decorator that formats the return values of your methods with a single field.
|
||||
|
||||
>>> from flask_restful import marshal_with_field, fields
|
||||
>>> @marshal_with_field(fields.List(fields.Integer))
|
||||
... def get():
|
||||
... return ['1', 2, 3.0]
|
||||
...
|
||||
>>> get()
|
||||
[1, 2, 3]
|
||||
|
||||
see :meth:`flask_restful.marshal_with`
|
||||
"""
|
||||
def __init__(self, field):
|
||||
"""
|
||||
:param field: a single field with which to marshal the output.
|
||||
"""
|
||||
if isinstance(field, type):
|
||||
self.field = field()
|
||||
else:
|
||||
self.field = field
|
||||
|
||||
def __call__(self, f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
resp = f(*args, **kwargs)
|
||||
|
||||
if isinstance(resp, tuple):
|
||||
data, code, headers = unpack(resp)
|
||||
return self.field.format(data), code, headers
|
||||
return self.field.format(resp)
|
||||
|
||||
return wrapper
|
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__version__ = '0.3.6'
|
417
Master/env_master/Lib/site-packages/flask_restful/fields.py
Normal file
417
Master/env_master/Lib/site-packages/flask_restful/fields.py
Normal file
@ -0,0 +1,417 @@
|
||||
from datetime import datetime
|
||||
from calendar import timegm
|
||||
import pytz
|
||||
from decimal import Decimal as MyDecimal, ROUND_HALF_EVEN
|
||||
from email.utils import formatdate
|
||||
import six
|
||||
try:
|
||||
from urlparse import urlparse, urlunparse
|
||||
except ImportError:
|
||||
# python3
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
from flask_restful import inputs, marshal
|
||||
from flask import url_for, request
|
||||
|
||||
__all__ = ["String", "FormattedString", "Url", "DateTime", "Float",
|
||||
"Integer", "Arbitrary", "Nested", "List", "Raw", "Boolean",
|
||||
"Fixed", "Price"]
|
||||
|
||||
|
||||
class MarshallingException(Exception):
|
||||
"""
|
||||
This is an encapsulating Exception in case of marshalling error.
|
||||
"""
|
||||
|
||||
def __init__(self, underlying_exception):
|
||||
# just put the contextual representation of the error to hint on what
|
||||
# went wrong without exposing internals
|
||||
super(MarshallingException, self).__init__(six.text_type(underlying_exception))
|
||||
|
||||
|
||||
def is_indexable_but_not_string(obj):
|
||||
return not hasattr(obj, "strip") and hasattr(obj, "__iter__")
|
||||
|
||||
|
||||
def get_value(key, obj, default=None):
|
||||
"""Helper for pulling a keyed value off various types of objects"""
|
||||
if isinstance(key, int):
|
||||
return _get_value_for_key(key, obj, default)
|
||||
elif callable(key):
|
||||
return key(obj)
|
||||
else:
|
||||
return _get_value_for_keys(key.split('.'), obj, default)
|
||||
|
||||
|
||||
def _get_value_for_keys(keys, obj, default):
|
||||
if len(keys) == 1:
|
||||
return _get_value_for_key(keys[0], obj, default)
|
||||
else:
|
||||
return _get_value_for_keys(
|
||||
keys[1:], _get_value_for_key(keys[0], obj, default), default)
|
||||
|
||||
|
||||
def _get_value_for_key(key, obj, default):
|
||||
if is_indexable_but_not_string(obj):
|
||||
try:
|
||||
return obj[key]
|
||||
except (IndexError, TypeError, KeyError):
|
||||
pass
|
||||
return getattr(obj, key, default)
|
||||
|
||||
|
||||
def to_marshallable_type(obj):
|
||||
"""Helper for converting an object to a dictionary only if it is not
|
||||
dictionary already or an indexable object nor a simple type"""
|
||||
if obj is None:
|
||||
return None # make it idempotent for None
|
||||
|
||||
if hasattr(obj, '__marshallable__'):
|
||||
return obj.__marshallable__()
|
||||
|
||||
if hasattr(obj, '__getitem__'):
|
||||
return obj # it is indexable it is ok
|
||||
|
||||
return dict(obj.__dict__)
|
||||
|
||||
|
||||
class Raw(object):
|
||||
"""Raw provides a base field class from which others should extend. It
|
||||
applies no formatting by default, and should only be used in cases where
|
||||
data does not need to be formatted before being serialized. Fields should
|
||||
throw a :class:`MarshallingException` in case of parsing problem.
|
||||
|
||||
:param default: The default value for the field, if no value is
|
||||
specified.
|
||||
:param attribute: If the public facing value differs from the internal
|
||||
value, use this to retrieve a different attribute from the response
|
||||
than the publicly named value.
|
||||
"""
|
||||
|
||||
def __init__(self, default=None, attribute=None):
|
||||
self.attribute = attribute
|
||||
self.default = default
|
||||
|
||||
def format(self, value):
|
||||
"""Formats a field's value. No-op by default - field classes that
|
||||
modify how the value of existing object keys should be presented should
|
||||
override this and apply the appropriate formatting.
|
||||
|
||||
:param value: The value to format
|
||||
:exception MarshallingException: In case of formatting problem
|
||||
|
||||
Ex::
|
||||
|
||||
class TitleCase(Raw):
|
||||
def format(self, value):
|
||||
return unicode(value).title()
|
||||
"""
|
||||
return value
|
||||
|
||||
def output(self, key, obj):
|
||||
"""Pulls the value for the given key from the object, applies the
|
||||
field's formatting and returns the result. If the key is not found
|
||||
in the object, returns the default value. Field classes that create
|
||||
values which do not require the existence of the key in the object
|
||||
should override this and return the desired value.
|
||||
|
||||
:exception MarshallingException: In case of formatting problem
|
||||
"""
|
||||
|
||||
value = get_value(key if self.attribute is None else self.attribute, obj)
|
||||
|
||||
if value is None:
|
||||
return self.default
|
||||
|
||||
return self.format(value)
|
||||
|
||||
|
||||
class Nested(Raw):
|
||||
"""Allows you to nest one set of fields inside another.
|
||||
See :ref:`nested-field` for more information
|
||||
|
||||
:param dict nested: The dictionary to nest
|
||||
:param bool allow_null: Whether to return None instead of a dictionary
|
||||
with null keys, if a nested dictionary has all-null keys
|
||||
:param kwargs: If ``default`` keyword argument is present, a nested
|
||||
dictionary will be marshaled as its value if nested dictionary is
|
||||
all-null keys (e.g. lets you return an empty JSON object instead of
|
||||
null)
|
||||
"""
|
||||
|
||||
def __init__(self, nested, allow_null=False, **kwargs):
|
||||
self.nested = nested
|
||||
self.allow_null = allow_null
|
||||
super(Nested, self).__init__(**kwargs)
|
||||
|
||||
def output(self, key, obj):
|
||||
value = get_value(key if self.attribute is None else self.attribute, obj)
|
||||
if value is None:
|
||||
if self.allow_null:
|
||||
return None
|
||||
elif self.default is not None:
|
||||
return self.default
|
||||
|
||||
return marshal(value, self.nested)
|
||||
|
||||
|
||||
class List(Raw):
|
||||
"""
|
||||
Field for marshalling lists of other fields.
|
||||
|
||||
See :ref:`list-field` for more information.
|
||||
|
||||
:param cls_or_instance: The field type the list will contain.
|
||||
"""
|
||||
|
||||
def __init__(self, cls_or_instance, **kwargs):
|
||||
super(List, self).__init__(**kwargs)
|
||||
error_msg = ("The type of the list elements must be a subclass of "
|
||||
"flask_restful.fields.Raw")
|
||||
if isinstance(cls_or_instance, type):
|
||||
if not issubclass(cls_or_instance, Raw):
|
||||
raise MarshallingException(error_msg)
|
||||
self.container = cls_or_instance()
|
||||
else:
|
||||
if not isinstance(cls_or_instance, Raw):
|
||||
raise MarshallingException(error_msg)
|
||||
self.container = cls_or_instance
|
||||
|
||||
def format(self, value):
|
||||
# Convert all instances in typed list to container type
|
||||
if isinstance(value, set):
|
||||
value = list(value)
|
||||
|
||||
return [
|
||||
self.container.output(idx,
|
||||
val if (isinstance(val, dict)
|
||||
or (self.container.attribute
|
||||
and hasattr(val, self.container.attribute)))
|
||||
and not isinstance(self.container, Nested)
|
||||
and not type(self.container) is Raw
|
||||
else value)
|
||||
for idx, val in enumerate(value)
|
||||
]
|
||||
|
||||
def output(self, key, data):
|
||||
value = get_value(key if self.attribute is None else self.attribute, data)
|
||||
# we cannot really test for external dict behavior
|
||||
if is_indexable_but_not_string(value) and not isinstance(value, dict):
|
||||
return self.format(value)
|
||||
|
||||
if value is None:
|
||||
return self.default
|
||||
|
||||
return [marshal(value, self.container.nested)]
|
||||
|
||||
|
||||
class String(Raw):
|
||||
"""
|
||||
Marshal a value as a string. Uses ``six.text_type`` so values will
|
||||
be converted to :class:`unicode` in python2 and :class:`str` in
|
||||
python3.
|
||||
"""
|
||||
def format(self, value):
|
||||
try:
|
||||
return six.text_type(value)
|
||||
except ValueError as ve:
|
||||
raise MarshallingException(ve)
|
||||
|
||||
|
||||
class Integer(Raw):
|
||||
""" Field for outputting an integer value.
|
||||
|
||||
:param int default: The default value for the field, if no value is
|
||||
specified.
|
||||
"""
|
||||
def __init__(self, default=0, **kwargs):
|
||||
super(Integer, self).__init__(default=default, **kwargs)
|
||||
|
||||
def format(self, value):
|
||||
try:
|
||||
if value is None:
|
||||
return self.default
|
||||
return int(value)
|
||||
except ValueError as ve:
|
||||
raise MarshallingException(ve)
|
||||
|
||||
|
||||
class Boolean(Raw):
|
||||
"""
|
||||
Field for outputting a boolean value.
|
||||
|
||||
Empty collections such as ``""``, ``{}``, ``[]``, etc. will be converted to
|
||||
``False``.
|
||||
"""
|
||||
def format(self, value):
|
||||
return bool(value)
|
||||
|
||||
|
||||
class FormattedString(Raw):
|
||||
"""
|
||||
FormattedString is used to interpolate other values from
|
||||
the response into this field. The syntax for the source string is
|
||||
the same as the string :meth:`~str.format` method from the python
|
||||
stdlib.
|
||||
|
||||
Ex::
|
||||
|
||||
fields = {
|
||||
'name': fields.String,
|
||||
'greeting': fields.FormattedString("Hello {name}")
|
||||
}
|
||||
data = {
|
||||
'name': 'Doug',
|
||||
}
|
||||
marshal(data, fields)
|
||||
"""
|
||||
def __init__(self, src_str):
|
||||
"""
|
||||
:param string src_str: the string to format with the other
|
||||
values from the response.
|
||||
"""
|
||||
super(FormattedString, self).__init__()
|
||||
self.src_str = six.text_type(src_str)
|
||||
|
||||
def output(self, key, obj):
|
||||
try:
|
||||
data = to_marshallable_type(obj)
|
||||
return self.src_str.format(**data)
|
||||
except (TypeError, IndexError) as error:
|
||||
raise MarshallingException(error)
|
||||
|
||||
|
||||
class Url(Raw):
|
||||
"""
|
||||
A string representation of a Url
|
||||
|
||||
:param endpoint: Endpoint name. If endpoint is ``None``,
|
||||
``request.endpoint`` is used instead
|
||||
:type endpoint: str
|
||||
:param absolute: If ``True``, ensures that the generated urls will have the
|
||||
hostname included
|
||||
:type absolute: bool
|
||||
:param scheme: URL scheme specifier (e.g. ``http``, ``https``)
|
||||
:type scheme: str
|
||||
"""
|
||||
def __init__(self, endpoint=None, absolute=False, scheme=None):
|
||||
super(Url, self).__init__()
|
||||
self.endpoint = endpoint
|
||||
self.absolute = absolute
|
||||
self.scheme = scheme
|
||||
|
||||
def output(self, key, obj):
|
||||
try:
|
||||
data = to_marshallable_type(obj)
|
||||
endpoint = self.endpoint if self.endpoint is not None else request.endpoint
|
||||
o = urlparse(url_for(endpoint, _external=self.absolute, **data))
|
||||
if self.absolute:
|
||||
scheme = self.scheme if self.scheme is not None else o.scheme
|
||||
return urlunparse((scheme, o.netloc, o.path, "", "", ""))
|
||||
return urlunparse(("", "", o.path, "", "", ""))
|
||||
except TypeError as te:
|
||||
raise MarshallingException(te)
|
||||
|
||||
|
||||
class Float(Raw):
|
||||
"""
|
||||
A double as IEEE-754 double precision.
|
||||
ex : 3.141592653589793 3.1415926535897933e-06 3.141592653589793e+24 nan inf
|
||||
-inf
|
||||
"""
|
||||
|
||||
def format(self, value):
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError as ve:
|
||||
raise MarshallingException(ve)
|
||||
|
||||
|
||||
class Arbitrary(Raw):
|
||||
"""
|
||||
A floating point number with an arbitrary precision
|
||||
ex: 634271127864378216478362784632784678324.23432
|
||||
"""
|
||||
|
||||
def format(self, value):
|
||||
return six.text_type(MyDecimal(value))
|
||||
|
||||
|
||||
class DateTime(Raw):
|
||||
"""
|
||||
Return a formatted datetime string in UTC. Supported formats are RFC 822
|
||||
and ISO 8601.
|
||||
|
||||
See :func:`email.utils.formatdate` for more info on the RFC 822 format.
|
||||
|
||||
See :meth:`datetime.datetime.isoformat` for more info on the ISO 8601
|
||||
format.
|
||||
|
||||
:param dt_format: ``'rfc822'`` or ``'iso8601'``
|
||||
:type dt_format: str
|
||||
"""
|
||||
def __init__(self, dt_format='rfc822', **kwargs):
|
||||
super(DateTime, self).__init__(**kwargs)
|
||||
self.dt_format = dt_format
|
||||
|
||||
def format(self, value):
|
||||
try:
|
||||
if self.dt_format == 'rfc822':
|
||||
return _rfc822(value)
|
||||
elif self.dt_format == 'iso8601':
|
||||
return _iso8601(value)
|
||||
else:
|
||||
raise MarshallingException(
|
||||
'Unsupported date format %s' % self.dt_format
|
||||
)
|
||||
except AttributeError as ae:
|
||||
raise MarshallingException(ae)
|
||||
|
||||
ZERO = MyDecimal()
|
||||
|
||||
|
||||
class Fixed(Raw):
|
||||
"""
|
||||
A decimal number with a fixed precision.
|
||||
"""
|
||||
def __init__(self, decimals=5, **kwargs):
|
||||
super(Fixed, self).__init__(**kwargs)
|
||||
self.precision = MyDecimal('0.' + '0' * (decimals - 1) + '1')
|
||||
|
||||
def format(self, value):
|
||||
dvalue = MyDecimal(value)
|
||||
if not dvalue.is_normal() and dvalue != ZERO:
|
||||
raise MarshallingException('Invalid Fixed precision number.')
|
||||
return six.text_type(dvalue.quantize(self.precision, rounding=ROUND_HALF_EVEN))
|
||||
|
||||
|
||||
"""Alias for :class:`~fields.Fixed`"""
|
||||
Price = Fixed
|
||||
|
||||
|
||||
def _rfc822(dt):
|
||||
"""Turn a datetime object into a formatted date.
|
||||
|
||||
Example::
|
||||
|
||||
fields._rfc822(datetime(2011, 1, 1)) => "Sat, 01 Jan 2011 00:00:00 -0000"
|
||||
|
||||
:param dt: The datetime to transform
|
||||
:type dt: datetime
|
||||
:return: A RFC 822 formatted date string
|
||||
"""
|
||||
return formatdate(timegm(dt.utctimetuple()))
|
||||
|
||||
|
||||
def _iso8601(dt):
|
||||
"""Turn a datetime object into an ISO8601 formatted date.
|
||||
|
||||
Example::
|
||||
|
||||
fields._iso8601(datetime(2012, 1, 1, 0, 0)) => "2012-01-01T00:00:00"
|
||||
|
||||
:param dt: The datetime to transform
|
||||
:type dt: datetime
|
||||
:return: A ISO 8601 formatted date string
|
||||
"""
|
||||
return dt.isoformat()
|
282
Master/env_master/Lib/site-packages/flask_restful/inputs.py
Normal file
282
Master/env_master/Lib/site-packages/flask_restful/inputs.py
Normal file
@ -0,0 +1,282 @@
|
||||
from calendar import timegm
|
||||
from datetime import datetime, time, timedelta
|
||||
from email.utils import parsedate_tz, mktime_tz
|
||||
import re
|
||||
|
||||
import aniso8601
|
||||
import pytz
|
||||
|
||||
# Constants for upgrading date-based intervals to full datetimes.
|
||||
START_OF_DAY = time(0, 0, 0, tzinfo=pytz.UTC)
|
||||
END_OF_DAY = time(23, 59, 59, 999999, tzinfo=pytz.UTC)
|
||||
|
||||
# https://code.djangoproject.com/browser/django/trunk/django/core/validators.py
|
||||
# basic auth added by frank
|
||||
|
||||
url_regex = re.compile(
|
||||
r'^(?:http|ftp)s?://' # http:// or https://
|
||||
r'(?:[^:@]+?:[^:@]*?@|)' # basic auth
|
||||
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+'
|
||||
r'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
|
||||
r'localhost|' # localhost...
|
||||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4
|
||||
r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6
|
||||
r'(?::\d+)?' # optional port
|
||||
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||||
|
||||
|
||||
def url(value):
|
||||
"""Validate a URL.
|
||||
|
||||
:param string value: The URL to validate
|
||||
:returns: The URL if valid.
|
||||
:raises: ValueError
|
||||
"""
|
||||
if not url_regex.search(value):
|
||||
message = u"{0} is not a valid URL".format(value)
|
||||
if url_regex.search('http://' + value):
|
||||
message += u". Did you mean: http://{0}".format(value)
|
||||
raise ValueError(message)
|
||||
return value
|
||||
|
||||
|
||||
class regex(object):
|
||||
"""Validate a string based on a regular expression.
|
||||
|
||||
Example::
|
||||
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument('example', type=inputs.regex('^[0-9]+$'))
|
||||
|
||||
Input to the ``example`` argument will be rejected if it contains anything
|
||||
but numbers.
|
||||
|
||||
:param pattern: The regular expression the input must match
|
||||
:type pattern: str
|
||||
:param flags: Flags to change expression behavior
|
||||
:type flags: int
|
||||
"""
|
||||
|
||||
def __init__(self, pattern, flags=0):
|
||||
self.pattern = pattern
|
||||
self.re = re.compile(pattern, flags)
|
||||
|
||||
def __call__(self, value):
|
||||
if not self.re.search(value):
|
||||
message = 'Value does not match pattern: "{0}"'.format(self.pattern)
|
||||
raise ValueError(message)
|
||||
return value
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return regex(self.pattern)
|
||||
|
||||
|
||||
def _normalize_interval(start, end, value):
|
||||
"""Normalize datetime intervals.
|
||||
|
||||
Given a pair of datetime.date or datetime.datetime objects,
|
||||
returns a 2-tuple of tz-aware UTC datetimes spanning the same interval.
|
||||
|
||||
For datetime.date objects, the returned interval starts at 00:00:00.0
|
||||
on the first date and ends at 00:00:00.0 on the second.
|
||||
|
||||
Naive datetimes are upgraded to UTC.
|
||||
|
||||
Timezone-aware datetimes are normalized to the UTC tzdata.
|
||||
|
||||
Params:
|
||||
- start: A date or datetime
|
||||
- end: A date or datetime
|
||||
"""
|
||||
if not isinstance(start, datetime):
|
||||
start = datetime.combine(start, START_OF_DAY)
|
||||
end = datetime.combine(end, START_OF_DAY)
|
||||
|
||||
if start.tzinfo is None:
|
||||
start = pytz.UTC.localize(start)
|
||||
end = pytz.UTC.localize(end)
|
||||
else:
|
||||
start = start.astimezone(pytz.UTC)
|
||||
end = end.astimezone(pytz.UTC)
|
||||
|
||||
return start, end
|
||||
|
||||
|
||||
def _expand_datetime(start, value):
|
||||
if not isinstance(start, datetime):
|
||||
# Expand a single date object to be the interval spanning
|
||||
# that entire day.
|
||||
end = start + timedelta(days=1)
|
||||
else:
|
||||
# Expand a datetime based on the finest resolution provided
|
||||
# in the original input string.
|
||||
time = value.split('T')[1]
|
||||
time_without_offset = re.sub('[+-].+', '', time)
|
||||
num_separators = time_without_offset.count(':')
|
||||
if num_separators == 0:
|
||||
# Hour resolution
|
||||
end = start + timedelta(hours=1)
|
||||
elif num_separators == 1:
|
||||
# Minute resolution:
|
||||
end = start + timedelta(minutes=1)
|
||||
else:
|
||||
# Second resolution
|
||||
end = start + timedelta(seconds=1)
|
||||
|
||||
return end
|
||||
|
||||
|
||||
def _parse_interval(value):
|
||||
"""Do some nasty try/except voodoo to get some sort of datetime
|
||||
object(s) out of the string.
|
||||
"""
|
||||
try:
|
||||
return sorted(aniso8601.parse_interval(value))
|
||||
except ValueError:
|
||||
try:
|
||||
return aniso8601.parse_datetime(value), None
|
||||
except ValueError:
|
||||
return aniso8601.parse_date(value), None
|
||||
|
||||
|
||||
def iso8601interval(value, argument='argument'):
|
||||
"""Parses ISO 8601-formatted datetime intervals into tuples of datetimes.
|
||||
|
||||
Accepts both a single date(time) or a full interval using either start/end
|
||||
or start/duration notation, with the following behavior:
|
||||
|
||||
- Intervals are defined as inclusive start, exclusive end
|
||||
- Single datetimes are translated into the interval spanning the
|
||||
largest resolution not specified in the input value, up to the day.
|
||||
- The smallest accepted resolution is 1 second.
|
||||
- All timezones are accepted as values; returned datetimes are
|
||||
localized to UTC. Naive inputs and date inputs will are assumed UTC.
|
||||
|
||||
Examples::
|
||||
|
||||
"2013-01-01" -> datetime(2013, 1, 1), datetime(2013, 1, 2)
|
||||
"2013-01-01T12" -> datetime(2013, 1, 1, 12), datetime(2013, 1, 1, 13)
|
||||
"2013-01-01/2013-02-28" -> datetime(2013, 1, 1), datetime(2013, 2, 28)
|
||||
"2013-01-01/P3D" -> datetime(2013, 1, 1), datetime(2013, 1, 4)
|
||||
"2013-01-01T12:00/PT30M" -> datetime(2013, 1, 1, 12), datetime(2013, 1, 1, 12, 30)
|
||||
"2013-01-01T06:00/2013-01-01T12:00" -> datetime(2013, 1, 1, 6), datetime(2013, 1, 1, 12)
|
||||
|
||||
:param str value: The ISO8601 date time as a string
|
||||
:return: Two UTC datetimes, the start and the end of the specified interval
|
||||
:rtype: A tuple (datetime, datetime)
|
||||
:raises: ValueError, if the interval is invalid.
|
||||
"""
|
||||
|
||||
try:
|
||||
start, end = _parse_interval(value)
|
||||
|
||||
if end is None:
|
||||
end = _expand_datetime(start, value)
|
||||
|
||||
start, end = _normalize_interval(start, end, value)
|
||||
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"Invalid {arg}: {value}. {arg} must be a valid ISO8601 "
|
||||
"date/time interval.".format(arg=argument, value=value),
|
||||
)
|
||||
|
||||
return start, end
|
||||
|
||||
|
||||
def date(value):
|
||||
"""Parse a valid looking date in the format YYYY-mm-dd"""
|
||||
date = datetime.strptime(value, "%Y-%m-%d")
|
||||
return date
|
||||
|
||||
|
||||
def _get_integer(value):
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
raise ValueError('{0} is not a valid integer'.format(value))
|
||||
|
||||
|
||||
def natural(value, argument='argument'):
|
||||
""" Restrict input type to the natural numbers (0, 1, 2, 3...) """
|
||||
value = _get_integer(value)
|
||||
if value < 0:
|
||||
error = ('Invalid {arg}: {value}. {arg} must be a non-negative '
|
||||
'integer'.format(arg=argument, value=value))
|
||||
raise ValueError(error)
|
||||
return value
|
||||
|
||||
|
||||
def positive(value, argument='argument'):
|
||||
""" Restrict input type to the positive integers (1, 2, 3...) """
|
||||
value = _get_integer(value)
|
||||
if value < 1:
|
||||
error = ('Invalid {arg}: {value}. {arg} must be a positive '
|
||||
'integer'.format(arg=argument, value=value))
|
||||
raise ValueError(error)
|
||||
return value
|
||||
|
||||
|
||||
class int_range(object):
|
||||
""" Restrict input to an integer in a range (inclusive) """
|
||||
def __init__(self, low, high, argument='argument'):
|
||||
self.low = low
|
||||
self.high = high
|
||||
self.argument = argument
|
||||
|
||||
def __call__(self, value):
|
||||
value = _get_integer(value)
|
||||
if value < self.low or value > self.high:
|
||||
error = ('Invalid {arg}: {val}. {arg} must be within the range {lo} - {hi}'
|
||||
.format(arg=self.argument, val=value, lo=self.low, hi=self.high))
|
||||
raise ValueError(error)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def boolean(value):
|
||||
"""Parse the string ``"true"`` or ``"false"`` as a boolean (case
|
||||
insensitive). Also accepts ``"1"`` and ``"0"`` as ``True``/``False``
|
||||
(respectively). If the input is from the request JSON body, the type is
|
||||
already a native python boolean, and will be passed through without
|
||||
further parsing.
|
||||
"""
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
|
||||
if not value:
|
||||
raise ValueError("boolean type must be non-null")
|
||||
value = value.lower()
|
||||
if value in ('true', '1',):
|
||||
return True
|
||||
if value in ('false', '0',):
|
||||
return False
|
||||
raise ValueError("Invalid literal for boolean(): {0}".format(value))
|
||||
|
||||
|
||||
def datetime_from_rfc822(datetime_str):
|
||||
"""Turns an RFC822 formatted date into a datetime object.
|
||||
|
||||
Example::
|
||||
|
||||
inputs.datetime_from_rfc822("Wed, 02 Oct 2002 08:00:00 EST")
|
||||
|
||||
:param datetime_str: The RFC822-complying string to transform
|
||||
:type datetime_str: str
|
||||
:return: A datetime
|
||||
"""
|
||||
return datetime.fromtimestamp(mktime_tz(parsedate_tz(datetime_str)), pytz.utc)
|
||||
|
||||
|
||||
def datetime_from_iso8601(datetime_str):
|
||||
"""Turns an ISO8601 formatted date into a datetime object.
|
||||
|
||||
Example::
|
||||
|
||||
inputs.datetime_from_iso8601("2012-01-01T23:30:00+02:00")
|
||||
|
||||
:param datetime_str: The ISO8601-complying string to transform
|
||||
:type datetime_str: str
|
||||
:return: A datetime
|
||||
"""
|
||||
return aniso8601.parse_datetime(datetime_str)
|
29
Master/env_master/Lib/site-packages/flask_restful/paging.py
Normal file
29
Master/env_master/Lib/site-packages/flask_restful/paging.py
Normal file
@ -0,0 +1,29 @@
|
||||
from flask_restful.utils.crypto import decrypt, encrypt
|
||||
DEFAULT_PAGE_SIZE = 50
|
||||
|
||||
|
||||
def retrieve_next_page(key, seed, args, callback, initial_bookmark=None):
|
||||
"""
|
||||
A helper for the bookmark pager.
|
||||
|
||||
:param key: the private key of you API
|
||||
:param seed: the crypo seed for this session
|
||||
:param args: the verbatim filtering+paging arguments
|
||||
:param callback: a function that takes (a dictionary of filters, the current bookmark, the page size)
|
||||
and return a tuple (next_results, dictionary_ready_for_next_iteration, approx_number_of_element_left)
|
||||
:param initial_bookmark: pass here an optional initial bookmark for the first request
|
||||
:return: the tuple result_list and new encrypted bookmark
|
||||
"""
|
||||
filter = dict(args)
|
||||
if 'pager_info' in filter:
|
||||
initial_bookmark = decrypt(filter.pop('pager_info'), key, seed)
|
||||
|
||||
page_size = filter.pop('page_size', DEFAULT_PAGE_SIZE)
|
||||
|
||||
result_list, new_bookmark, approx_result_size = callback(filter, initial_bookmark, page_size)
|
||||
|
||||
# restore for the next iteration
|
||||
filter['pager_info'] = encrypt(new_bookmark, key, seed)
|
||||
filter['page_size'] = page_size
|
||||
|
||||
return result_list, filter, approx_result_size
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,25 @@
|
||||
from __future__ import absolute_import
|
||||
from flask import make_response, current_app
|
||||
from flask_restful.utils import PY3
|
||||
from json import dumps
|
||||
|
||||
|
||||
def output_json(data, code, headers=None):
|
||||
"""Makes a Flask response with a JSON encoded body"""
|
||||
|
||||
settings = current_app.config.get('RESTFUL_JSON', {})
|
||||
|
||||
# If we're in debug mode, and the indent is not set, we set it to a
|
||||
# reasonable value here. Note that this won't override any existing value
|
||||
# that was set. We also set the "sort_keys" value.
|
||||
if current_app.debug:
|
||||
settings.setdefault('indent', 4)
|
||||
settings.setdefault('sort_keys', not PY3)
|
||||
|
||||
# always end the json dumps with a new line
|
||||
# see https://github.com/mitsuhiko/flask/pull/1262
|
||||
dumped = dumps(data, **settings) + "\n"
|
||||
|
||||
resp = make_response(dumped, code)
|
||||
resp.headers.extend(headers or {})
|
||||
return resp
|
344
Master/env_master/Lib/site-packages/flask_restful/reqparse.py
Normal file
344
Master/env_master/Lib/site-packages/flask_restful/reqparse.py
Normal file
@ -0,0 +1,344 @@
|
||||
from copy import deepcopy
|
||||
|
||||
import collections
|
||||
from flask import current_app, request
|
||||
from werkzeug.datastructures import MultiDict, FileStorage
|
||||
from werkzeug import exceptions
|
||||
import flask_restful
|
||||
import decimal
|
||||
import six
|
||||
|
||||
|
||||
class Namespace(dict):
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self[name]
|
||||
except KeyError:
|
||||
raise AttributeError(name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
self[name] = value
|
||||
|
||||
_friendly_location = {
|
||||
u'json': u'the JSON body',
|
||||
u'form': u'the post body',
|
||||
u'args': u'the query string',
|
||||
u'values': u'the post body or the query string',
|
||||
u'headers': u'the HTTP headers',
|
||||
u'cookies': u'the request\'s cookies',
|
||||
u'files': u'an uploaded file',
|
||||
}
|
||||
|
||||
text_type = lambda x: six.text_type(x)
|
||||
|
||||
|
||||
class Argument(object):
|
||||
|
||||
"""
|
||||
:param name: Either a name or a list of option strings, e.g. foo or
|
||||
-f, --foo.
|
||||
:param default: The value produced if the argument is absent from the
|
||||
request.
|
||||
:param dest: The name of the attribute to be added to the object
|
||||
returned by :meth:`~reqparse.RequestParser.parse_args()`.
|
||||
:param bool required: Whether or not the argument may be omitted (optionals
|
||||
only).
|
||||
:param action: The basic type of action to be taken when this argument
|
||||
is encountered in the request. Valid options are "store" and "append".
|
||||
:param ignore: Whether to ignore cases where the argument fails type
|
||||
conversion
|
||||
:param type: The type to which the request argument should be
|
||||
converted. If a type raises an exception, the message in the
|
||||
error will be returned in the response. Defaults to :class:`unicode`
|
||||
in python2 and :class:`str` in python3.
|
||||
:param location: The attributes of the :class:`flask.Request` object
|
||||
to source the arguments from (ex: headers, args, etc.), can be an
|
||||
iterator. The last item listed takes precedence in the result set.
|
||||
:param choices: A container of the allowable values for the argument.
|
||||
:param help: A brief description of the argument, returned in the
|
||||
response when the argument is invalid. May optionally contain
|
||||
an "{error_msg}" interpolation token, which will be replaced with
|
||||
the text of the error raised by the type converter.
|
||||
:param bool case_sensitive: Whether argument values in the request are
|
||||
case sensitive or not (this will convert all values to lowercase)
|
||||
:param bool store_missing: Whether the arguments default value should
|
||||
be stored if the argument is missing from the request.
|
||||
:param bool trim: If enabled, trims whitespace around the argument.
|
||||
:param bool nullable: If enabled, allows null value in argument.
|
||||
"""
|
||||
|
||||
def __init__(self, name, default=None, dest=None, required=False,
|
||||
ignore=False, type=text_type, location=('json', 'values',),
|
||||
choices=(), action='store', help=None, operators=('=',),
|
||||
case_sensitive=True, store_missing=True, trim=False,
|
||||
nullable=True):
|
||||
self.name = name
|
||||
self.default = default
|
||||
self.dest = dest
|
||||
self.required = required
|
||||
self.ignore = ignore
|
||||
self.location = location
|
||||
self.type = type
|
||||
self.choices = choices
|
||||
self.action = action
|
||||
self.help = help
|
||||
self.case_sensitive = case_sensitive
|
||||
self.operators = operators
|
||||
self.store_missing = store_missing
|
||||
self.trim = trim
|
||||
self.nullable = nullable
|
||||
|
||||
def source(self, request):
|
||||
"""Pulls values off the request in the provided location
|
||||
:param request: The flask request object to parse arguments from
|
||||
"""
|
||||
if isinstance(self.location, six.string_types):
|
||||
value = getattr(request, self.location, MultiDict())
|
||||
if callable(value):
|
||||
value = value()
|
||||
if value is not None:
|
||||
return value
|
||||
else:
|
||||
values = MultiDict()
|
||||
for l in self.location:
|
||||
value = getattr(request, l, None)
|
||||
if callable(value):
|
||||
value = value()
|
||||
if value is not None:
|
||||
values.update(value)
|
||||
return values
|
||||
|
||||
return MultiDict()
|
||||
|
||||
def convert(self, value, op):
|
||||
# Don't cast None
|
||||
if value is None:
|
||||
if self.nullable:
|
||||
return None
|
||||
else:
|
||||
raise ValueError('Must not be null!')
|
||||
|
||||
# and check if we're expecting a filestorage and haven't overridden `type`
|
||||
# (required because the below instantiation isn't valid for FileStorage)
|
||||
elif isinstance(value, FileStorage) and self.type == FileStorage:
|
||||
return value
|
||||
|
||||
try:
|
||||
return self.type(value, self.name, op)
|
||||
except TypeError:
|
||||
try:
|
||||
if self.type is decimal.Decimal:
|
||||
return self.type(str(value), self.name)
|
||||
else:
|
||||
return self.type(value, self.name)
|
||||
except TypeError:
|
||||
return self.type(value)
|
||||
|
||||
def handle_validation_error(self, error, bundle_errors):
|
||||
"""Called when an error is raised while parsing. Aborts the request
|
||||
with a 400 status and an error message
|
||||
|
||||
:param error: the error that was raised
|
||||
:param bundle_errors: do not abort when first error occurs, return a
|
||||
dict with the name of the argument and the error message to be
|
||||
bundled
|
||||
"""
|
||||
error_str = six.text_type(error)
|
||||
error_msg = self.help.format(error_msg=error_str) if self.help else error_str
|
||||
msg = {self.name: error_msg}
|
||||
|
||||
if current_app.config.get("BUNDLE_ERRORS", False) or bundle_errors:
|
||||
return error, msg
|
||||
flask_restful.abort(400, message=msg)
|
||||
|
||||
def parse(self, request, bundle_errors=False):
|
||||
"""Parses argument value(s) from the request, converting according to
|
||||
the argument's type.
|
||||
|
||||
:param request: The flask request object to parse arguments from
|
||||
:param do not abort when first error occurs, return a
|
||||
dict with the name of the argument and the error message to be
|
||||
bundled
|
||||
"""
|
||||
source = self.source(request)
|
||||
|
||||
results = []
|
||||
|
||||
# Sentinels
|
||||
_not_found = False
|
||||
_found = True
|
||||
|
||||
for operator in self.operators:
|
||||
name = self.name + operator.replace("=", "", 1)
|
||||
if name in source:
|
||||
# Account for MultiDict and regular dict
|
||||
if hasattr(source, "getlist"):
|
||||
values = source.getlist(name)
|
||||
else:
|
||||
values = source.get(name)
|
||||
if not isinstance(values, collections.MutableSequence):
|
||||
values = [values]
|
||||
|
||||
for value in values:
|
||||
if hasattr(value, "strip") and self.trim:
|
||||
value = value.strip()
|
||||
if hasattr(value, "lower") and not self.case_sensitive:
|
||||
value = value.lower()
|
||||
|
||||
if hasattr(self.choices, "__iter__"):
|
||||
self.choices = [choice.lower()
|
||||
for choice in self.choices]
|
||||
|
||||
try:
|
||||
value = self.convert(value, operator)
|
||||
except Exception as error:
|
||||
if self.ignore:
|
||||
continue
|
||||
return self.handle_validation_error(error, bundle_errors)
|
||||
|
||||
if self.choices and value not in self.choices:
|
||||
if current_app.config.get("BUNDLE_ERRORS", False) or bundle_errors:
|
||||
return self.handle_validation_error(
|
||||
ValueError(u"{0} is not a valid choice".format(
|
||||
value)), bundle_errors)
|
||||
self.handle_validation_error(
|
||||
ValueError(u"{0} is not a valid choice".format(
|
||||
value)), bundle_errors)
|
||||
|
||||
if name in request.unparsed_arguments:
|
||||
request.unparsed_arguments.pop(name)
|
||||
results.append(value)
|
||||
|
||||
if not results and self.required:
|
||||
if isinstance(self.location, six.string_types):
|
||||
error_msg = u"Missing required parameter in {0}".format(
|
||||
_friendly_location.get(self.location, self.location)
|
||||
)
|
||||
else:
|
||||
friendly_locations = [_friendly_location.get(loc, loc)
|
||||
for loc in self.location]
|
||||
error_msg = u"Missing required parameter in {0}".format(
|
||||
' or '.join(friendly_locations)
|
||||
)
|
||||
if current_app.config.get("BUNDLE_ERRORS", False) or bundle_errors:
|
||||
return self.handle_validation_error(ValueError(error_msg), bundle_errors)
|
||||
self.handle_validation_error(ValueError(error_msg), bundle_errors)
|
||||
|
||||
if not results:
|
||||
if callable(self.default):
|
||||
return self.default(), _not_found
|
||||
else:
|
||||
return self.default, _not_found
|
||||
|
||||
if self.action == 'append':
|
||||
return results, _found
|
||||
|
||||
if self.action == 'store' or len(results) == 1:
|
||||
return results[0], _found
|
||||
return results, _found
|
||||
|
||||
|
||||
class RequestParser(object):
|
||||
"""Enables adding and parsing of multiple arguments in the context of a
|
||||
single request. Ex::
|
||||
|
||||
from flask_restful import reqparse
|
||||
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument('foo')
|
||||
parser.add_argument('int_bar', type=int)
|
||||
args = parser.parse_args()
|
||||
|
||||
:param bool trim: If enabled, trims whitespace on all arguments in this
|
||||
parser
|
||||
:param bool bundle_errors: If enabled, do not abort when first error occurs,
|
||||
return a dict with the name of the argument and the error message to be
|
||||
bundled and return all validation errors
|
||||
"""
|
||||
|
||||
def __init__(self, argument_class=Argument, namespace_class=Namespace,
|
||||
trim=False, bundle_errors=False):
|
||||
self.args = []
|
||||
self.argument_class = argument_class
|
||||
self.namespace_class = namespace_class
|
||||
self.trim = trim
|
||||
self.bundle_errors = bundle_errors
|
||||
|
||||
def add_argument(self, *args, **kwargs):
|
||||
"""Adds an argument to be parsed.
|
||||
|
||||
Accepts either a single instance of Argument or arguments to be passed
|
||||
into :class:`Argument`'s constructor.
|
||||
|
||||
See :class:`Argument`'s constructor for documentation on the
|
||||
available options.
|
||||
"""
|
||||
|
||||
if len(args) == 1 and isinstance(args[0], self.argument_class):
|
||||
self.args.append(args[0])
|
||||
else:
|
||||
self.args.append(self.argument_class(*args, **kwargs))
|
||||
|
||||
#Do not know what other argument classes are out there
|
||||
if self.trim and self.argument_class is Argument:
|
||||
#enable trim for appended element
|
||||
self.args[-1].trim = kwargs.get('trim', self.trim)
|
||||
|
||||
return self
|
||||
|
||||
def parse_args(self, req=None, strict=False):
|
||||
"""Parse all arguments from the provided request and return the results
|
||||
as a Namespace
|
||||
|
||||
:param strict: if req includes args not in parser, throw 400 BadRequest exception
|
||||
"""
|
||||
if req is None:
|
||||
req = request
|
||||
|
||||
namespace = self.namespace_class()
|
||||
|
||||
# A record of arguments not yet parsed; as each is found
|
||||
# among self.args, it will be popped out
|
||||
req.unparsed_arguments = dict(self.argument_class('').source(req)) if strict else {}
|
||||
errors = {}
|
||||
for arg in self.args:
|
||||
value, found = arg.parse(req, self.bundle_errors)
|
||||
if isinstance(value, ValueError):
|
||||
errors.update(found)
|
||||
found = None
|
||||
if found or arg.store_missing:
|
||||
namespace[arg.dest or arg.name] = value
|
||||
if errors:
|
||||
flask_restful.abort(400, message=errors)
|
||||
|
||||
if strict and req.unparsed_arguments:
|
||||
raise exceptions.BadRequest('Unknown arguments: %s'
|
||||
% ', '.join(req.unparsed_arguments.keys()))
|
||||
|
||||
return namespace
|
||||
|
||||
def copy(self):
|
||||
""" Creates a copy of this RequestParser with the same set of arguments """
|
||||
parser_copy = self.__class__(self.argument_class, self.namespace_class)
|
||||
parser_copy.args = deepcopy(self.args)
|
||||
parser_copy.trim = self.trim
|
||||
parser_copy.bundle_errors = self.bundle_errors
|
||||
return parser_copy
|
||||
|
||||
def replace_argument(self, name, *args, **kwargs):
|
||||
""" Replace the argument matching the given name with a new version. """
|
||||
new_arg = self.argument_class(name, *args, **kwargs)
|
||||
for index, arg in enumerate(self.args[:]):
|
||||
if new_arg.name == arg.name:
|
||||
del self.args[index]
|
||||
self.args.append(new_arg)
|
||||
break
|
||||
return self
|
||||
|
||||
def remove_argument(self, name):
|
||||
""" Remove the argument matching the given name. """
|
||||
for index, arg in enumerate(self.args[:]):
|
||||
if name == arg.name:
|
||||
del self.args[index]
|
||||
break
|
||||
return self
|
@ -0,0 +1,35 @@
|
||||
import sys
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from ordereddict import OrderedDict
|
||||
|
||||
from werkzeug.http import HTTP_STATUS_CODES
|
||||
|
||||
PY3 = sys.version_info > (3,)
|
||||
|
||||
|
||||
def http_status_message(code):
|
||||
"""Maps an HTTP status code to the textual status"""
|
||||
return HTTP_STATUS_CODES.get(code, '')
|
||||
|
||||
|
||||
def unpack(value):
|
||||
"""Return a three tuple of data, code, and headers"""
|
||||
if not isinstance(value, tuple):
|
||||
return value, 200, {}
|
||||
|
||||
try:
|
||||
data, code, headers = value
|
||||
return data, code, headers
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
data, code = value
|
||||
return data, code, {}
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return value, 200, {}
|
@ -0,0 +1,54 @@
|
||||
from datetime import timedelta
|
||||
from flask import make_response, request, current_app
|
||||
from functools import update_wrapper
|
||||
|
||||
|
||||
def crossdomain(origin=None, methods=None, headers=None, expose_headers=None,
|
||||
max_age=21600, attach_to_all=True,
|
||||
automatic_options=True, credentials=False):
|
||||
"""
|
||||
http://flask.pocoo.org/snippets/56/
|
||||
"""
|
||||
if methods is not None:
|
||||
methods = ', '.join(sorted(x.upper() for x in methods))
|
||||
if headers is not None and not isinstance(headers, str):
|
||||
headers = ', '.join(x.upper() for x in headers)
|
||||
if expose_headers is not None and not isinstance(expose_headers, str):
|
||||
expose_headers = ', '.join(x.upper() for x in expose_headers)
|
||||
if not isinstance(origin, str):
|
||||
origin = ', '.join(origin)
|
||||
if isinstance(max_age, timedelta):
|
||||
max_age = max_age.total_seconds()
|
||||
|
||||
def get_methods():
|
||||
if methods is not None:
|
||||
return methods
|
||||
|
||||
options_resp = current_app.make_default_options_response()
|
||||
return options_resp.headers['allow']
|
||||
|
||||
def decorator(f):
|
||||
def wrapped_function(*args, **kwargs):
|
||||
if automatic_options and request.method == 'OPTIONS':
|
||||
resp = current_app.make_default_options_response()
|
||||
else:
|
||||
resp = make_response(f(*args, **kwargs))
|
||||
if not attach_to_all and request.method != 'OPTIONS':
|
||||
return resp
|
||||
|
||||
h = resp.headers
|
||||
|
||||
h['Access-Control-Allow-Origin'] = origin
|
||||
h['Access-Control-Allow-Methods'] = get_methods()
|
||||
h['Access-Control-Max-Age'] = str(max_age)
|
||||
if credentials:
|
||||
h['Access-Control-Allow-Credentials'] = 'true'
|
||||
if headers is not None:
|
||||
h['Access-Control-Allow-Headers'] = headers
|
||||
if expose_headers is not None:
|
||||
h['Access-Control-Expose-Headers'] = expose_headers
|
||||
return resp
|
||||
|
||||
f.provide_automatic_options = False
|
||||
return update_wrapper(wrapped_function, f)
|
||||
return decorator
|
@ -0,0 +1,35 @@
|
||||
import pickle
|
||||
from Crypto.Cipher import AES
|
||||
from base64 import b64encode, b64decode
|
||||
|
||||
|
||||
__all__ = "encrypt", "decrypt"
|
||||
|
||||
BLOCK_SIZE = 16
|
||||
INTERRUPT = b'\0' # something impossible to put in a string
|
||||
PADDING = b'\1'
|
||||
|
||||
|
||||
def pad(data):
|
||||
return data + INTERRUPT + PADDING * (BLOCK_SIZE - (len(data) + 1) % BLOCK_SIZE)
|
||||
|
||||
|
||||
def strip(data):
|
||||
return data.rstrip(PADDING).rstrip(INTERRUPT)
|
||||
|
||||
|
||||
def create_cipher(key, seed):
|
||||
if len(seed) != 16:
|
||||
raise ValueError("Choose a seed of 16 bytes")
|
||||
if len(key) != 32:
|
||||
raise ValueError("Choose a key of 32 bytes")
|
||||
return AES.new(key, AES.MODE_CBC, seed)
|
||||
|
||||
|
||||
def encrypt(plaintext_data, key, seed):
|
||||
plaintext_data = pickle.dumps(plaintext_data, pickle.HIGHEST_PROTOCOL) # whatever you give me I need to be able to restitute it
|
||||
return b64encode(create_cipher(key, seed).encrypt(pad(plaintext_data)))
|
||||
|
||||
|
||||
def decrypt(encrypted_data, key, seed):
|
||||
return pickle.loads(strip(create_cipher(key, seed).decrypt(b64decode(encrypted_data))))
|
Reference in New Issue
Block a user