Commit 1a8fcd5d authored by Ziirish's avatar Ziirish

use upstream Wildcard fields now that it is merged

parent 30a25503
......@@ -11,8 +11,7 @@
import flask_restplus.fields
from flask_restplus.fields import * # noqa # pylint: disable=locally-disabled, wildcard-import, unused-wildcard-import
from .my_fields import DateTime, DateTimeHuman, BackupNumber, SafeString, LocalizedString, Wildcard # noqa
from .my_fields2 import Nested # noqa
from .my_fields import DateTime, DateTimeHuman, BackupNumber, SafeString, LocalizedString # noqa
__all__ = flask_restplus.fields.__all__ + \
(DateTime, DateTimeHuman, BackupNumber, SafeString, LocalizedString, Wildcard)
(DateTime, DateTimeHuman, BackupNumber, SafeString, LocalizedString)
......@@ -8,11 +8,8 @@
"""
import re
import datetime
import inspect
import arrow
import fnmatch
from ...ext.i18n import get_locale
......@@ -97,100 +94,3 @@ class LocalizedString(fields.String):
def format(self, value):
"""Format the value"""
return fields.String.format(self, _(value))
class Wildcard(fields.Raw):
exclude = set()
# cache the flat object
_flat = None
_obj = None
_cache = set()
_last = None
def __init__(self, cls_or_instance, **kwargs):
super(Wildcard, self).__init__(**kwargs)
error_msg = 'The type of the wildcard elements must be a subclass of fields.Raw'
if isinstance(cls_or_instance, type):
if not issubclass(cls_or_instance, fields.Raw):
raise fields.MarshallingError(error_msg)
self.container = cls_or_instance()
else:
if not isinstance(cls_or_instance, fields.Raw):
raise fields.MarshallingError(error_msg)
self.container = cls_or_instance
def _flatten(self, obj):
if obj is None:
return None
if obj == self._obj and self._flat is not None:
return self._flat
if isinstance(obj, dict):
# self._flat needs to implement pop()
self._flat = [x for x in obj.items()]
else:
def __match_attributes(attribute):
attr_name, attr_obj = attribute
if inspect.isroutine(attr_obj) or \
(attr_name.startswith('__') and attr_name.endswith('__')):
return False
return True
attributes = inspect.getmembers(obj)
self._flat = [x for x in attributes if __match_attributes(x)]
self._cache = set()
self._obj = obj
return self._flat
@property
def key(self):
return self._last
def reset(self):
self.exclude = set()
self._flat = None
self._obj = None
self._cache = set()
self._last = None
def output(self, key, obj, ordered=False, **kwargs):
value = None
reg = fnmatch.translate(key)
if self._flatten(obj):
while True:
try:
# we are using pop() so that we don't
# loop over the whole object every time dropping the
# complexity to O(n)
(objkey, val) = self._flat.pop()
if objkey not in self._cache and \
objkey not in self.exclude and \
re.match(reg, objkey, re.IGNORECASE):
value = val
self._cache.add(objkey)
self._last = objkey
break
except IndexError:
break
if value is None:
if self.default is not None:
return self.container.format(self.default)
return None
return self.container.format(value)
def schema(self):
schema = super(Wildcard, self).schema()
schema['type'] = 'object'
schema['additionalProperties'] = self.container.__schema__
return schema
def clone(self, mask=None):
kwargs = self.__dict__.copy()
model = kwargs.pop('container')
if mask:
model = mask.apply(model)
return self.__class__(model, **kwargs)
# -*- coding: utf8 -*-
"""
.. module:: burpui.api.custom.my_fields2
:platform: Unix
:synopsis: Burp-UI api custom fields module.
.. moduleauthor:: Ziirish <hi+burpui@ziirish.me>
"""
# Monkey patching flask-restplus to handle our own marshalling/wildcard implementation
from flask_restplus import fields
from .my_marshalling import marshal
class Nested(fields.Nested):
def output(self, key, obj, ordered=False, **kwargs):
value = fields.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, skip_none=self.skip_none, ordered=ordered)
# -*- coding: utf8 -*-
"""
.. module:: burpui.api.custom.my_marshalling
:platform: Unix
:synopsis: Burp-UI api custom marshalling module.
.. moduleauthor:: Ziirish <hi+burpui@ziirish.me>
"""
from flask_restplus.mask import apply as apply_mask
from collections import OrderedDict
from .my_fields import Wildcard
def make(cls):
if isinstance(cls, type):
return cls()
return cls
def marshal(data, fields, envelope=None, skip_none=False, mask=None, ordered=False):
"""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
:param bool skip_none: optional key will be used to eliminate fields
which value is None or the field's key not
exist in data
:param bool ordered: Wether or not to preserve order
>>> from flask_restplus import fields, marshal
>>> data = { 'a': 100, 'b': 'foo', 'c': None }
>>> mfields = { 'a': fields.Raw, 'c': fields.Raw, 'd': fields.Raw }
>>> marshal(data, mfields)
{'a': 100, 'c': None, 'd': None}
>>> marshal(data, mfields, envelope='data')
{'data': {'a': 100, 'c': None, 'd': None}}
>>> marshal(data, mfields, skip_none=True)
{'a': 100}
>>> marshal(data, mfields, ordered=True)
OrderedDict([('a', 100), ('c', None), ('d', None)])
>>> marshal(data, mfields, envelope='data', ordered=True)
OrderedDict([('data', OrderedDict([('a', 100), ('c', None), ('d', None)]))])
>>> marshal(data, mfields, skip_none=True, ordered=True)
OrderedDict([('a', 100)])
"""
out, has_wildcards = _marshal(data, fields, envelope, skip_none, mask, ordered)
if has_wildcards:
mask = mask or getattr(fields, '__mask__', None)
fields = getattr(fields, 'resolved', fields)
if mask:
fields = apply_mask(fields, mask, skip=True)
wild_items = []
keys = []
for dkey, val in fields.items():
key = dkey
if isinstance(val, dict):
value = marshal(data, val, skip_none=skip_none, ordered=ordered)
else:
field = make(val)
# exclude already parsed keys from the wildcard
is_wildcard = isinstance(field, Wildcard)
if is_wildcard:
field.reset()
if keys:
field.exclude |= set(keys)
keys = []
value = field.output(dkey, data, ordered=ordered)
if is_wildcard:
def _append(k, v):
if skip_none and (v is None or v == OrderedDict() or v == {}):
return
wild_items.append((k, v))
key = field.key or key
_append(key, value)
while True:
value = field.output(dkey, data, ordered=ordered)
if value is None or \
value == field.container.format(field.default):
break
key = field.key
_append(key, value)
continue
keys.append(key)
if skip_none and (value is None or value == OrderedDict() or value == {}):
continue
wild_items.append((key, value))
items = tuple(wild_items)
out = OrderedDict(items) if ordered else dict(items)
if envelope:
out = OrderedDict([(envelope, out)]) if ordered else {envelope: out}
return out
return out
def _marshal(data, fields, envelope=None, skip_none=False, mask=None, ordered=False):
"""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
:param bool skip_none: optional key will be used to eliminate fields
which value is None or the field's key not
exist in data
:param bool ordered: Wether or not to preserve order
>>> from flask_restplus import fields, marshal
>>> data = { 'a': 100, 'b': 'foo', 'c': None }
>>> mfields = { 'a': fields.Raw, 'c': fields.Raw, 'd': fields.Raw }
>>> marshal(data, mfields)
{'a': 100, 'c': None, 'd': None}
>>> marshal(data, mfields, envelope='data')
{'data': {'a': 100, 'c': None, 'd': None}}
>>> marshal(data, mfields, skip_none=True)
{'a': 100}
>>> marshal(data, mfields, ordered=True)
OrderedDict([('a', 100), ('c', None), ('d', None)])
>>> marshal(data, mfields, envelope='data', ordered=True)
OrderedDict([('data', OrderedDict([('a', 100), ('c', None), ('d', None)]))])
>>> marshal(data, mfields, skip_none=True, ordered=True)
OrderedDict([('a', 100)])
"""
mask = mask or getattr(fields, '__mask__', None)
fields = getattr(fields, 'resolved', fields)
if mask:
fields = apply_mask(fields, mask, skip=True)
if isinstance(data, (list, tuple)):
out = [marshal(d, fields, skip_none=skip_none, ordered=ordered) for d in data]
if envelope:
out = OrderedDict([(envelope, out)]) if ordered else {envelope: out}
return out, False
has_wildcards = {'present': False}
def __format_field(key, val):
field = make(val)
if isinstance(field, Wildcard):
has_wildcards['present'] = True
value = field.output(key, data, ordered=ordered)
return (key, value)
items = (
(k, marshal(data, v, skip_none=skip_none, ordered=ordered))
if isinstance(v, dict)
else __format_field(k, v)
for k, v in fields.items()
)
if skip_none:
items = ((k, v) for k, v in items
if v is not None and v != OrderedDict() and v != {})
out = OrderedDict(items) if ordered else dict(items)
if envelope:
out = OrderedDict([(envelope, out)]) if ordered else {envelope: out}
return out, has_wildcards['present']
......@@ -4,7 +4,7 @@ Flask-Login==0.4.1
Flask-Bower==1.3.0
Flask-Babel==0.12.2
Flask-WTF==0.14.2
flask-restplus==0.12.1
flask-restplus==0.13.0
Flask-Caching==1.7.2
Flask-Session==0.3.1
WTForms==2.1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment