Skip to content

Commit

Permalink
Fix compatibility with marshmallow>=2.0.0a1
Browse files Browse the repository at this point in the history
[close #16]
[close #15]
  • Loading branch information
sloria committed Apr 28, 2015
1 parent d6d28e9 commit bacddcc
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 21 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

0.5.1 (unreleased)
******************

* Fix compatibility with marshmallow>=2.0.0a1.

0.5.0 (2015-03-29)
******************

Expand Down
35 changes: 26 additions & 9 deletions flask_marshmallow/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,21 @@
import re
import sys

from marshmallow import fields, utils
from marshmallow.exceptions import ForcedError
from flask import url_for
from werkzeug.routing import BuildError
from marshmallow import fields, utils
try:
from marshmallow import missing
except ImportError: # marshmallow 1.2 support
from marshmallow.fields import missing

try:
# marshmallow 1.2
from marshmallow.exceptions import ForcedError
except ImportError:
has_forced_error = False
else: # marshmallow 2.0
has_forced_error = True

# Py2/3 compatibility
PY2 = sys.version_info[0] == 2
Expand Down Expand Up @@ -75,21 +86,27 @@ def _serialize(self, value, key, obj):
for name, attr_tpl in iteritems(self.params):
attr_name = _tpl(str(attr_tpl))
if attr_name:
attribute_value = utils.get_value(attr_name, obj, default=fields.missing)
if attribute_value is not fields.missing:
attribute_value = utils.get_value(attr_name, obj, default=missing)
if attribute_value is not missing:
param_values[name] = attribute_value
else:
raise ForcedError(AttributeError(
err = AttributeError(
'{attr_name!r} is not a valid '
'attribute of {obj!r}'.format(
attr_name=attr_name, obj=obj,
)))
'attribute of {obj!r}'.format(attr_name=attr_name, obj=obj)
)
if has_forced_error:
raise ForcedError(err)
else:
raise err
else:
param_values[name] = attr_tpl
try:
return url_for(self.endpoint, **param_values)
except BuildError as err: # Make sure BuildErrors are raised
raise ForcedError(err)
if has_forced_error:
raise ForcedError(err)
else:
raise err

UrlFor = URLFor

Expand Down
35 changes: 23 additions & 12 deletions test_flask_marshmallow.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
from flask import Flask, url_for
from werkzeug.routing import BuildError
from werkzeug.wrappers import BaseResponse

from flask_marshmallow import Marshmallow
from flask_marshmallow.fields import _tpl

import marshmallow

IS_MARSHMALLOW_2 = int(marshmallow.__version__.split('.')[0]) >= 2

_app = Flask(__name__)

@_app.route('/author/<int:id>')
Expand Down Expand Up @@ -44,19 +47,25 @@ def ma(app):
return Marshmallow(app)


class MockModel(dict):
def __init__(self, *args, **kwargs):
super(MockModel, self).__init__(*args, **kwargs)
self.__dict__ = self

class Author(MockModel):
pass

class Book(MockModel):
pass

@pytest.fixture
def mockauthor():
author = mock.Mock(spec=['id', 'name'])
author.id = 123
author.name = 'Fred Douglass'
author = Author(id=123, name='Fred Douglass')
return author

@pytest.fixture
def mockbook(mockauthor):
book = mock.Mock(spec=['id', 'author', 'title'])
book.id = 42
book.author = mockauthor
book.title = 'Legend of Bagger Vance'
book = Book(id=42, author=mockauthor, title='Legend of Bagger Vance')
return book


Expand Down Expand Up @@ -91,7 +100,7 @@ def test_url_field_with_invalid_attribute(ma, mockauthor):
assert expected_msg in str(excinfo)

def test_url_field_deserialization(ma):
field = ma.URLFor('author', id='<not-an-attr>')
field = ma.URLFor('author', id='<not-an-attr>', allow_none=True)
# noop
assert field.deserialize('foo') == 'foo'
assert field.deserialize(None) is None
Expand Down Expand Up @@ -136,7 +145,7 @@ def test_hyperlinks_field_recurses(ma, mockauthor):
def test_hyperlinks_field_deserialization(ma):
field = ma.Hyperlinks({
'href': ma.URLFor('author', id='<id>')
})
}, allow_none=True)
# noop
assert field.deserialize('/author') == '/author'
assert field.deserialize(None) is None
Expand All @@ -147,7 +156,7 @@ def test_absolute_url(ma, mockauthor):
assert result == url_for('authors', _external=True)

def test_absolute_url_deserialization(ma):
field = ma.AbsoluteURLFor('authors')
field = ma.AbsoluteURLFor('authors', allow_none=True)
assert field.deserialize('foo') == 'foo'
assert field.deserialize(None) is None

Expand Down Expand Up @@ -196,9 +205,11 @@ def test_schema(app, mockauthor):
assert links['self'] == url_for('author', id=mockauthor.id)
assert links['collection'] == url_for('authors')

@pytest.mark.skipif(IS_MARSHMALLOW_2, reason='jsonify will not work with marshmallow 2 '
'because Schema.data was removed')
def test_jsonify(app, mockauthor):
s = AuthorSchema(mockauthor)
resp = s.jsonify()
resp = s.jsonify(mockauthor)
assert isinstance(resp, BaseResponse)
assert resp.content_type == 'application/json'

Expand Down

0 comments on commit bacddcc

Please sign in to comment.