Skip to content
This repository has been archived by the owner on Apr 10, 2023. It is now read-only.

Commit

Permalink
Make Tag.lookup_tag default to module name + qualname
Browse files Browse the repository at this point in the history
  • Loading branch information
rossmacarthur committed Feb 8, 2020
1 parent dbf28eb commit 26963d6
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 35 deletions.
12 changes: 6 additions & 6 deletions README.rst
Expand Up @@ -285,7 +285,7 @@ serialization.
.. code-block:: python
>>> Cat(name='Fluffy', hates_dogs=True).to_dict()
OrderedDict([('name', 'Fluffy'), ('hates_dogs', True), ('species', 'Cat')])
OrderedDict([('name', 'Fluffy'), ('hates_dogs', True), ('species', '__main__.Cat')])
When deserializing, tag deserialization is done first to determine which model
to use for the deserialization.
Expand All @@ -295,7 +295,7 @@ to use for the deserialization.
>>> milo = Pet.from_dict({
... 'name': 'Milo',
... 'hates_cats': False,
... 'species': 'Dog'
... 'species': '__main__.Dog'
... })
>>> milo.__class__
<class '__main__.Dog'>
Expand All @@ -313,10 +313,10 @@ An invalid or missing tag will raise a ``DeserializationError``.
...
serde.exceptions.DeserializationError: expected tag 'species'
>>>
>>> Pet.from_dict({'name': 'Duke', 'species': 'Horse'})
>>> Pet.from_dict({'name': 'Duke', 'species': '__main__.Horse'})
Traceback (most recent call last):
...
serde.exceptions.DeserializationError: no variant found for tag 'Horse'
serde.exceptions.DeserializationError: no variant found for tag '__main__.Horse'
Externally tagged
^^^^^^^^^^^^^^^^^
Expand All @@ -337,7 +337,7 @@ tagged example above.
... hates_cats: fields.Bool()
...
>>> Dog(name='Max', hates_cats=True).to_dict()
OrderedDict([('Dog', OrderedDict([('name', 'Max'), ('hates_cats', True)]))])
OrderedDict([('__main__.Dog', OrderedDict([('name', 'Max'), ('hates_cats', True)]))])
Adjacently tagged
^^^^^^^^^^^^^^^^^
Expand All @@ -358,7 +358,7 @@ example.
... hates_cats: fields.Bool()
...
>>> Dog(name='Max', hates_cats=True).to_dict()
OrderedDict([('species', 'Dog'), ('data', OrderedDict([('name', 'Max'), ('hates_cats', True)]))])
OrderedDict([('species', '__main__.Dog'), ('data', OrderedDict([('name', 'Max'), ('hates_cats', True)]))])
Abstract models
^^^^^^^^^^^^^^^
Expand Down
7 changes: 7 additions & 0 deletions RELEASES.rst
@@ -1,6 +1,13 @@
Releases
========

0.8.0
-----

*Unreleased*

- Make ``Tag.lookup_tag`` default to module + qualname.

0.7.3
-----

Expand Down
5 changes: 3 additions & 2 deletions src/serde/tags.py
Expand Up @@ -56,9 +56,10 @@ def lookup_tag(self, variant):
variant (Model): the model class.
Returns:
tag: the corresponding tag value.
str: the corresponding tag value.
"""
return variant.__name__
name = getattr(variant, '__qualname__', variant.__name__)
return '{}.{}'.format(variant.__module__, name)

def lookup_variant(self, tag):
"""
Expand Down
43 changes: 30 additions & 13 deletions tests/test_model.py
Expand Up @@ -15,6 +15,23 @@
from tests import py3


class LookupTagMixin(object):
def lookup_tag(self, variant):
return variant.__name__


class External(LookupTagMixin, tags.External):
pass


class Internal(LookupTagMixin, tags.Internal):
pass


class Adjacent(LookupTagMixin, tags.Adjacent):
pass


class TestModel:
def test___new___empty(self):
# Check that a Model with no Fields can be created. There should still
Expand Down Expand Up @@ -122,9 +139,9 @@ def test___new___meta_class(self):

class Example(Model):
class Meta:
tag = tags.Internal(tag='kind')
tag = Internal(tag='kind')

assert Example.__tags__ == [tags.Internal(tag='kind')]
assert Example.__tags__ == [Internal(tag='kind')]

def test___init___empty(self):
# An empty Model with no Fields should work just fine.
Expand Down Expand Up @@ -483,7 +500,7 @@ class NestedExample(Model):

class Example(Model):
class Meta:
tag = tags.External()
tag = External()

nested = fields.Nested(NestedExample)

Expand All @@ -500,7 +517,7 @@ class NestedExample(Model):

class Example(Model):
class Meta:
tag = tags.Internal(tag='kind')
tag = Internal(tag='kind')

nested = fields.Nested(NestedExample)

Expand All @@ -517,7 +534,7 @@ class NestedExample(Model):

class Example(Model):
class Meta:
tag = tags.Adjacent(tag='kind', content='data')
tag = Adjacent(tag='kind', content='data')

nested = fields.Nested(NestedExample)

Expand Down Expand Up @@ -629,7 +646,7 @@ def test_from_dict_externally_tagged(self):

class Example(Model):
class Meta:
tag = tags.External()
tag = External()

a = fields.Int()

Expand Down Expand Up @@ -667,7 +684,7 @@ def test_from_dict_internally_tagged(self):

class Example(Model):
class Meta:
tag = tags.Internal(tag='kind')
tag = Internal(tag='kind')

a = fields.Int()

Expand Down Expand Up @@ -697,7 +714,7 @@ def test_from_dict_adjacently_tagged(self):

class Example(Model):
class Meta:
tag = tags.Adjacent(tag='kind', content='data')
tag = Adjacent(tag='kind', content='data')

a = fields.Int()

Expand Down Expand Up @@ -733,7 +750,7 @@ class SubExample(Example):
def test_from_dict_override_tag_for(self):
# Check that from_dict() works when you modify the Meta.tag_for() method

class ExampleTag(tags.Internal):
class ExampleTag(Internal):
def lookup_tag(self, variant):
return variant.__name__.lower()

Expand Down Expand Up @@ -867,7 +884,7 @@ def test_to_dict_externally_tagged(self):

class Example(Model):
class Meta:
tag = tags.External()
tag = External()

a = fields.Int()

Expand All @@ -885,7 +902,7 @@ def test_to_dict_internally_tagged(self):

class Example(Model):
class Meta:
tag = tags.Internal(tag='kind')
tag = Internal(tag='kind')

a = fields.Int()

Expand All @@ -907,7 +924,7 @@ def test_to_dict_adjacently_tagged(self):

class Example(Model):
class Meta:
tag = tags.Adjacent(tag='kind', content='data')
tag = Adjacent(tag='kind', content='data')

a = fields.Int()

Expand All @@ -923,7 +940,7 @@ class SubExample(Example):

def test_to_dict_override_tag_for(self):
# Check that to_dict() works when you modify the Meta.tag_for() method
class ExampleTag(tags.Internal):
class ExampleTag(Internal):
def lookup_tag(self, variant):
return variant.__name__.lower()

Expand Down
54 changes: 40 additions & 14 deletions tests/test_tags.py
@@ -1,3 +1,5 @@
import mock
import six
from pytest import raises

from serde import Model
Expand Down Expand Up @@ -70,7 +72,11 @@ def test_lookup_tag(self):
class Example(Model):
pass

assert Tag().lookup_tag(Example) == 'Example'
if six.PY2:
prefix = 'tests.test_tags'
else:
prefix = 'tests.test_tags.TestTag.test_lookup_tag.<locals>'
assert Tag().lookup_tag(Example) == prefix + '.Example'

def test_lookup_variant(self):
class Example(Model):
Expand All @@ -84,16 +90,24 @@ class Example3(Example):

tag = Tag(recurse=True)
tag._bind(Example)
assert tag.lookup_variant('Example') is Example
assert tag.lookup_variant('Example2') is Example2
assert tag.lookup_variant('Example3') is Example3
assert tag.lookup_variant('Example4') is None
if six.PY2:
prefix = 'tests.test_tags'
else:
prefix = 'tests.test_tags.TestTag.test_lookup_variant.<locals>'
assert tag.lookup_variant(prefix + '.Example') is Example
assert tag.lookup_variant(prefix + '.Example2') is Example2
assert tag.lookup_variant(prefix + '.Example3') is Example3
assert tag.lookup_variant(prefix + '.Example4') is None

def test_serialize(self):
class Example(Model):
pass

assert Tag().serialize(Example) == 'Example'
if six.PY2:
prefix = 'tests.test_tags'
else:
prefix = 'tests.test_tags.TestTag.test_serialize.<locals>'
assert Tag().serialize(Example) == prefix + '.Example'

def test_deserialize(self):
class Example(Model):
Expand All @@ -107,22 +121,32 @@ class Example3(Example):

tag = Tag(recurse=True)
tag._bind(Example)

assert tag.deserialize('Example') is Example
assert tag.deserialize('Example2') is Example2
assert tag.deserialize('Example3') is Example3

if six.PY2:
prefix = 'tests.test_tags'
else:
prefix = 'tests.test_tags.TestTag.test_deserialize.<locals>'
assert tag.deserialize(prefix + '.Example') is Example
assert tag.deserialize(prefix + '.Example2') is Example2
assert tag.deserialize(prefix + '.Example3') is Example3

tag_value = prefix + '.Example4'
with raises(DeserializationError) as e:
tag.deserialize('Example4')
tag.deserialize(tag_value)

assert (
e.value.pretty()
== """\
DeserializationError: no variant found for tag 'Example4'
Due to => value 'Example4' for tag 'Tag' on model 'Example'"""
DeserializationError: no variant found for tag '{}'
Due to => value {} for tag 'Tag' on model 'Example'""".format(
tag_value,
"'tests.test_tags.Example4'"
if six.PY2
else "'tests.test_tags.TestTag.t... ",
)
)


@mock.patch('serde.tags.Tag.lookup_tag', lambda _, variant: variant.__name__)
class TestExternal:
def test__serialize_with(self):
class Example(Model):
Expand Down Expand Up @@ -161,6 +185,7 @@ def test__deserialize_with_untagged(self):
)


@mock.patch('serde.tags.Tag.lookup_tag', lambda _, variant: variant.__name__)
class TestInternal:
def test___init___basic(self):
tag = Internal()
Expand Down Expand Up @@ -222,6 +247,7 @@ class Example2(Example):
)


@mock.patch('serde.tags.Tag.lookup_tag', lambda _, variant: variant.__name__)
class TestAdjacent:
def test___init___basic(self):
tag = Adjacent()
Expand Down

0 comments on commit 26963d6

Please sign in to comment.