#1040 frontend: fix api_2 for marshmallow 3+
Merged a month ago by praiskup. Opened a month ago by praiskup.
copr/ praiskup/copr fix-frontend-marshmallow  into  master

@@ -37,7 +37,7 @@ 

      if self_params is None:

          self_params = {}

      return {

-         "build": BuildSchema().dump(build)[0],

+         "build": mm_serialize_one(BuildSchema, build),

          "_links": {

              "self": {"href": url_for(".buildr", build_id=build.id, **self_params)},

              "project": {"href": url_for(".projectr", project_id=build.copr_id)},

@@ -79,7 +79,7 @@ 

          """

          :return: if of the created build or raise Exception

          """

-         build_params = mm_deserialize(BuildCreateFromUrlSchema(), req.data.decode("utf-8")).data

+         build_params = mm_deserialize(BuildCreateFromUrlSchema(), req.data.decode("utf-8"))

          project = get_project_safe(build_params["project_id"])

  

          chroot_names = build_params.pop("chroots")

@@ -117,7 +117,7 @@ 

              raise MalformedRequest("Missing srpm file in the request")

          srpm_handle = req.files["srpm"]

  

-         build_params = mm_deserialize(BuildCreateSchema(), metadata).data

+         build_params = mm_deserialize(BuildCreateSchema(), metadata)

          project_id = build_params["project_id"]

  

          project = get_project_safe(project_id)

@@ -207,7 +207,7 @@ 

      @rest_api_auth_required

      def put(self, build_id):

          build = get_build_safe(build_id)

-         build_dict = mm_deserialize(BuildSchema(), flask.request.data.decode("utf-8")).data

+         build_dict = mm_deserialize(BuildSchema(), flask.request.data.decode("utf-8"))

          try:

              if not build.canceled and build_dict["state"] == "canceled":

                  BuildsLogic.cancel_build(flask.g.user, build)

@@ -6,12 +6,12 @@ 

  from ...logic.coprs_logic import MockChrootsLogic

  

  from ..schemas import MockChrootSchema

- from ..util import get_one_safe, get_request_parser, arg_bool

+ from ..util import get_one_safe, get_request_parser, arg_bool, mm_serialize_one

  

  

  def render_mock_chroot(chroot):

      return {

-         "chroot": MockChrootSchema().dump(chroot)[0],

+         "chroot": mm_serialize_one(MockChrootSchema, chroot),

          "_links": {

              "self": {"href": url_for(".mockchrootr", name=chroot.name)},

          },

@@ -29,9 +29,7 @@ 

          Creates new copr

          """

          user = flask.g.user

-         result = mm_deserialize(ProjectCreateSchema(), flask.request.data.decode("utf-8"))

- 

-         req = result.data

+         req = mm_deserialize(ProjectCreateSchema(), flask.request.data.decode("utf-8"))

          name = req.pop("name")

  

          selected_chroots = req.pop("chroots", None)

@@ -158,8 +156,7 @@ 

          """

          project = get_project_safe(project_id)

  

-         project_dict = mm_deserialize(ProjectSchema(), flask.request.data.decode("utf-8")).data

-         # pprint(project_dict)

+         project_dict = mm_deserialize(ProjectSchema(), flask.request.data.decode("utf-8"))

  

          for k, v in project_dict.items():

              setattr(project, k, v)

@@ -42,10 +42,8 @@ 

      def post(cls, project_id):

          project = get_project_safe(project_id)

  

-         chroot_data = mm_deserialize(CoprChrootCreateSchema(),

-                                      flask.request.data.decode("utf-8"))

- 

-         req = chroot_data.data

+         req = mm_deserialize(CoprChrootCreateSchema(),

+                              flask.request.data.decode("utf-8"))

          name = req.pop("name")

  

          try:

@@ -111,7 +109,7 @@ 

              updated_chroot = CoprChrootsLogic.update_chroot(

                  user=flask.g.user,

                  copr_chroot=chroot,

-                 **chroot_data.data

+                 **chroot_data

              )

          except InsufficientRightsException as err:

              raise AccessForbidden("Failed to update copr chroot: {}".format(err))

@@ -1,10 +1,40 @@ 

  # coding: utf-8

  

  from collections.abc import Iterable

- from marshmallow import Schema, fields, ValidationError, validate

+ from marshmallow import Schema as _Schema, fields, ValidationError, validate

  from six import string_types

  

  

+ loads_kwargs = {}

+ try:

+     from marshmallow.utils import EXCLUDE

+     loads_kwargs = {"unknown": EXCLUDE}

+ except ImportError:

+     pass

+ 

+ 

+ class Schema(_Schema):

+     # In marshmallow v3+ there's no data/errors attributes as it was

+     # in v2.  So we need to have compat wrapper.

+ 

+     def wrap_function(self, name, *args, **kwargs):

+         super_object = super(Schema, self)

+         super_result = getattr(super_object, name)(*args, **kwargs)

+         if hasattr(super_result, 'data') and hasattr(super_result, 'errors'):

+             # old marshmallow

+             if super_result.errors:

+                 raise ValidationError(super_result.errors)

+             return super_result.data

+         else:

+             return super_result

+ 

+     def dump(self, *args, **kwargs):

+         return self.wrap_function('dump', *args, **kwargs)

+ 

+     def loads(self, *args, **kwargs):

+         kwargs.update(loads_kwargs)

+         return self.wrap_function('loads', *args, **kwargs)

+ 

  def validate_any(fn_list):

      """

      :param fn_list: list of callable functions, each takes one param

@@ -28,12 +58,12 @@ 

  

  

  class SpaceSeparatedList(fields.Field):

-     def _serialize(self, value, attr, obj):

+     def _serialize(self, value, attr, obj, **kwargs):

          if value is None:

              return []

          return value.split()

  

-     def _deserialize(self, value, attr=None, data=None):

+     def _deserialize(self, value, attr=None, data=None, **kwargs):

          if value is None:

              return ""

          elif not isinstance(value, Iterable) or isinstance(value, string_types):

@@ -50,7 +80,7 @@ 

      { name: "pkg", version: "pkg version" }

      we implement only the serialization, since field is read-only

      """

-     def _serialize(self, value, attr, obj):

+     def _serialize(self, value, attr, obj, **kwargs):

          if value is None:

              return []

          result = []

@@ -5,6 +5,7 @@ 

  

  from flask_restful.reqparse import Argument, RequestParser

  

+ from marshmallow.exceptions import ValidationError

  from .exceptions import ObjectNotFoundError, MalformedRequest

  from .schemas import AllowedMethodSchema

  

@@ -18,7 +19,8 @@ 

  

  

  def render_allowed_method(method, doc, require_auth=True, params=None):

-     return AllowedMethodSchema().dump(AllowedMethod(method, doc, require_auth, params))[0]

+     method = AllowedMethod(method, doc, require_auth, params)

+     return mm_serialize_one(AllowedMethodSchema, method)

  

  

  def get_one_safe(query, msg=None, data=None):

@@ -44,26 +46,18 @@ 

  

  def mm_deserialize(schema, json_string):

      try:

-         result = schema.loads(json_string)

-     except ValueError as err:

+         return schema.loads(json_string)

+     except (ValueError, ValidationError) as err:

          raise MalformedRequest(

              msg="Failed to parse request: {}".format(err),

              data={"request_string": json_string}

          )

  

-     if result.errors:

-         raise MalformedRequest(

-             msg="Failed to parse request: Validation Error",

-             data={

-                 "validation_errors": result.errors

-             }

-         )

- 

-     return result

- 

- 

  def mm_serialize_one(schema, obj):

-     return schema().dump(obj)[0]

+     try:

+         return schema().dump(obj)

+     except ValidationError as err:

+         raise MalformedRequest("Failed to parse request")

  

  

  class MyArg(Argument):