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

Commit

Permalink
Improve Uuid field normalization
Browse files Browse the repository at this point in the history
  • Loading branch information
rossmacarthur committed Feb 15, 2020
1 parent 6d1db18 commit 34783b0
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 21 deletions.
36 changes: 23 additions & 13 deletions RELEASES.rst
Expand Up @@ -6,11 +6,18 @@ Releases

*Unreleased*

- Add ``IpAddress`` field.
- Extended string types now subclass ``Text`` not ``Str``.
- Remove `fields.create()` method
- Completely rework error handling.
- Make ``Tag.lookup_tag`` default to module + qualname.
- Improve ``Uuid`` field normalization.
- Add ``IpAddress`` field. (`667ca96`_)
- Extended string types now subclass ``Text`` not ``Str``. (`5862375`_)
- Remove `fields.create()` method. (`2a54886`_)
- Completely rework error handling. (`c95b6b3`_)
- Make ``Tag.lookup_tag`` default to module + qualname. (`26963d6`_)

.. _26963d6: https://github.com/rossmacarthur/serde/commit/26963d65b45229db50444665a3160bd0f1358421
.. _c95b6b3: https://github.com/rossmacarthur/serde/commit/c95b6b329125164e3301a7e43292c7a858d5e34f
.. _2a54886: https://github.com/rossmacarthur/serde/commit/2a548865106b1cfc98f5dea8bf00084f62e6334e
.. _5862375: https://github.com/rossmacarthur/serde/commit/5862375ac82a98364d7cf7ef82c6bb668d9abded
.. _667ca96: https://github.com/rossmacarthur/serde/commit/667ca9629028b29a6d420a9ce4eb14460baca448

0.7.3
-----
Expand All @@ -26,15 +33,18 @@ Releases

*Released on December 2nd, 2019*

- Add ``FrozenSet`` field.
- Add ``Deque`` field.
- Add ``FrozenSet`` field. (`1d2f0cd`_)
- Add ``Deque`` field. (`ec44671`_)
- Add ``default`` keyword argument to ``Field``. (`#111`_)
- Fix bug in ``Uuid.normalize()``.
- Fix bug in ``Uuid.normalize()``. (`408bc83`_)
- Rename ``Constant`` field to ``Literal``. (`#118`_)

.. _#118: https://github.com/rossmacarthur/serde/pull/118
.. _408bc83: https://github.com/rossmacarthur/serde/commit/408bc834ee8c4dbf92438ebcc6e94989927e2d89
.. _ec44671: https://github.com/rossmacarthur/serde/commit/ec44671704529aca505fc10716cde030c9295188
.. _1d2f0cd: https://github.com/rossmacarthur/serde/commit/1d2f0cd09cc49e350ac36ba2697e8023a2f9e47f

.. _#111: https://github.com/rossmacarthur/serde/pull/111
.. _#118: https://github.com/rossmacarthur/serde/pull/118

0.7.1
-----
Expand Down Expand Up @@ -201,8 +211,8 @@ Releases

*Released on December 19th, 2018*

- Fix a bug where overriding ``Model.__init__()`` method affected ``Model.from_dict``.
(`#45`_, `#46`_)
- Fix a bug where overriding ``Model.__init__()`` method affected
``Model.from_dict``. (`#45`_, `#46`_)

.. _#46: https://github.com/rossmacarthur/serde/pull/46

Expand Down Expand Up @@ -300,11 +310,11 @@ Releases

*Released on October 27th, 2018*

- Initial release
- Initial release, fixed.

0.1.0
-----

*Released on October 27th, 2018*

- This release is broken and was yanked.
- Initial release, yanked.
33 changes: 27 additions & 6 deletions src/serde/fields.py
Expand Up @@ -8,7 +8,7 @@
import uuid

import isodate
from six import binary_type, integer_types, text_type
from six import PY3, binary_type, integer_types, text_type
from six.moves.collections_abc import Mapping as MappingType

from serde.exceptions import ContextError, ValidationError
Expand Down Expand Up @@ -1090,31 +1090,52 @@ class Uuid(Instance):
A `~uuid.UUID` field.
A `Uuid` field validates that the input data is a UUID. It serializes the
UUID as a hex string, and deserializes hex strings or integers as UUIDs.
UUID into the specified output form and deserializes hex strings, bytes,
fields, or integers as UUIDs.
Args:
output_form (str): the type of output form to serialize to. Possible
values include 'str', 'urn', 'hex', 'int', 'bytes', or 'fields'.
**kwargs: keyword arguments for the `Field` constructor.
"""

def __init__(self, **kwargs):
def __init__(self, output_form='str', **kwargs):
"""
Create a new `Uuid`.
"""
if output_form not in ('str', 'urn', 'hex', 'int', 'bytes', 'fields'):
raise ValueError('invalid output form')
super(Uuid, self).__init__(uuid.UUID, **kwargs)
self.output_form = output_form

def serialize(self, value):
"""
Serialize the given `~uuid.UUID` as a string.
"""
return str(value)
if self.output_form == 'str':
return str(value)
else:
return getattr(value, self.output_form)

def normalize(self, value):
"""
Normalize the value into a `~uuid.UUID`.
"""
if not isinstance(value, uuid.UUID):
input_form = 'int' if isinstance(value, integer_types) else 'hex'
return uuid.UUID(**{input_form: value})
input_form = None
if isinstance(value, text_type):
input_form = 'hex'
elif isinstance(value, binary_type):
input_form = 'bytes' if PY3 or len(value) == 16 else 'hex'
elif isinstance(value, integer_types):
input_form = 'int'
elif isinstance(value, (list, tuple)):
input_form = 'fields'
if input_form:
try:
return uuid.UUID(**{input_form: value})
except ValueError:
pass
return value


Expand Down
54 changes: 52 additions & 2 deletions tests/test_fields.py
Expand Up @@ -1357,17 +1357,44 @@ def test_validate(self):


class TestUuid:
def test___init__(self):
def test___init___basic(self):
# Construct a basic Uuid and check values are set correctly.
field = Uuid()
assert field.ty == uuid.UUID
assert field.output_form == 'str'

def test___init___options(self):
# Construct a Uuid with extra options and check values are set correctly.
field = Uuid(output_form='hex', validators=[None])
assert field.ty == uuid.UUID
assert field.output_form == 'hex'

def test___init___invalid_output_form(self):
# Check that an invalid output form is denied.
with raises(ValueError):
Uuid(output_form='invalid')

def test_serialize(self):
# A Uuid should serialize a uuid.UUID as a string.
field = Uuid()
value = uuid.UUID('2d7026c8-cc58-11e8-bd7a-784f4386978e')
assert field.serialize(value) == '2d7026c8-cc58-11e8-bd7a-784f4386978e'

def test_serialize_output_form(self):
# A Uuid should serialize a uuid.UUID based on the output form.
value = uuid.UUID('c07fb668-b3cb-4719-9b3d-0881d5eeba3b')
cases = [
('str', 'c07fb668-b3cb-4719-9b3d-0881d5eeba3b'),
('urn', 'urn:uuid:c07fb668-b3cb-4719-9b3d-0881d5eeba3b'),
('hex', 'c07fb668b3cb47199b3d0881d5eeba3b'),
('int', 255874896585658101253640125750883301947),
('bytes', b'\xc0\x7f\xb6h\xb3\xcbG\x19\x9b=\x08\x81\xd5\xee\xba;'),
('fields', (3229595240, 46027, 18201, 155, 61, 9353732995643)),
]
for output_form, expected in cases:
field = Uuid(output_form=output_form)
assert field.serialize(value) == expected

def test_normalize_uuid(self):
# A Uuid should normalize a uuid.UUID as a uuid.UUID
field = Uuid()
Expand All @@ -1382,14 +1409,37 @@ def test_normalize_str(self):
'2d7026c8-cc58-11e8-bd7a-784f4386978e'
)

def test_normalize_bytes(self):
# A Uuid should normalize a byte string a a uuid.UUID.
field = Uuid()
value = b'\x99\x1a\xf7\xc7\xee\x17G\x02\xb6C\xe2\x93<\xe8:\x01'
field.normalize(value) == uuid.UUID('991af7c7-ee17-4702-b643-e2933ce83a01')

def test_normalize_int(self):
# A Uuid should normalize a string as a uuid.UUID.
# A Uuid should normalize an integer as a uuid.UUID.
field = Uuid()
value = 255874896585658101253640125750883301947
assert field.normalize(value) == uuid.UUID(
'c07fb668-b3cb-4719-9b3d-0881d5eeba3b'
)

def test_normalize_fields(self):
# A Uuid should normalize a tuple/list as a uuid.UUID.
field = Uuid()
value = (3375074170, 20614, 19730, 172, 202, 2390245548685)
assert field.normalize(value) == uuid.UUID(
'c92b8b7a-5086-4d12-acca-022c85bca28d'
)
assert field.normalize(list(value)) == uuid.UUID(
'c92b8b7a-5086-4d12-acca-022c85bca28d'
)

def test_normalize_invalid(self):
# A Uuid should not raise an error on a normalization failure.
field = Uuid()
value = b'\x99'
assert field.normalize(value) == value

def test_validate(self):
# A Uuid should validate that the value is an instance of uuid.UUID.
field = Uuid()
Expand Down

0 comments on commit 34783b0

Please sign in to comment.