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

Commit

Permalink
Support Python 2.7 (#35)
Browse files Browse the repository at this point in the history
- Always extend object where required.
- Use explicit super() calls everywhere.
- Handle string and unicode problems in tests.
- Only lint and codecov on Python 3.7.
  • Loading branch information
rossmacarthur committed Dec 9, 2018
1 parent fca9879 commit 560dd07
Show file tree
Hide file tree
Showing 13 changed files with 93 additions and 48 deletions.
7 changes: 4 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
language: python
matrix:
include:
- python: 2.7
- python: 3.4
- python: 3.5
- python: 3.6
Expand All @@ -15,9 +16,9 @@ install:

# command to run tests
script:
- make lint
- make test
- if [[ $TRAVIS_PYTHON_VERSION == 3.7 ]]; then make lint; fi
- if [[ $TRAVIS_PYTHON_VERSION != 2.7 ]]; then make test; else make test-plain; fi

# command to upload coverage
after_success:
- codecov
- if [[ $TRAVIS_PYTHON_VERSION == 3.7 ]]; then codecov; fi
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.PHONY: help clean venv install install-travis install-all lint sort-imports \
test docs docs-clean docs-open docs-test dist release
test test-plain docs docs-clean docs-open docs-test dist release

PYTHON := python3
VIRTUAL_ENV := $(or $(VIRTUAL_ENV), $(VIRTUAL_ENV), venv)
Expand Down Expand Up @@ -35,6 +35,9 @@ test: ## Run all tests.
$(VIRTUAL_ENV)/bin/pytest -vv --cov=serde --cov-report term-missing --cov-fail-under 100 \
--doctest-modules --doctest-import "*<serde"

test-plain: ## Run tests excluding doctests.
$(VIRTUAL_ENV)/bin/pytest -vv --cov=serde --cov-report term-missing

docs: ## Compile docs.
$(MAKE) -C docs html

Expand Down
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

install_requires = [
'isodate>=0.6.0<0.7.0',
'six>=1.0.0<2.0.0',
'validators>=0.12.0<0.13.0'
]

Expand All @@ -43,6 +44,7 @@
]

test_requires = [
'mock',
'pytest>=3.3.0',
'pytest-cov',
'pytest-doctest-import'
Expand All @@ -67,7 +69,7 @@
'documenting': document_requires,
'packaging': package_requires
},
python_requires='>=3.4',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
packages=find_packages('src'),
package_dir={'': 'src'},
py_modules=['serde'],
Expand Down
2 changes: 1 addition & 1 deletion src/serde/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
found in `~serde.field`.
"""

from .model import Model
from serde.model import Model


__all__ = ['Model', 'error', 'field', 'validate']
Expand Down
6 changes: 3 additions & 3 deletions src/serde/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def __init__(self, message, cause=None, value=None, field=None, model=None):
field (~serde.field.Field): the Field context.
model (~serde.model.Model): the Model context.
"""
super().__init__(message)
super(SerdeError, self).__init__(message)
self.cause = None
self.value = None
self.field = None
Expand All @@ -51,7 +51,7 @@ def add_context(self, cause=None, value=None, field=None, model=None):
field (~serde.field.Field): the Field context.
model (~serde.model.Model): the Model context.
"""
from .model import Model
from serde.model import Model

if cause is not None:
self.cause = cause
Expand All @@ -74,7 +74,7 @@ def __repr__(self):
"""
return '<{}.{}: {}>'.format(
self.__class__.__module__,
self.__class__.__qualname__,
self.__class__.__name__,
self.message
)

Expand Down
64 changes: 38 additions & 26 deletions src/serde/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@

import isodate

from . import validate
from .error import SerdeError
from .util import zip_equal
from serde import validate
from serde.error import SerdeError
from serde.util import zip_equal


__all__ = [
Expand Down Expand Up @@ -132,7 +132,7 @@ def _resolve_to_field_instance(thing, none_allowed=True):
Field: a field instance.
"""
# We import Model here to avoid circular dependency problems.
from .model import Model
from serde.model import Model

# If the thing is None then return a generic Field instance.
if none_allowed and thing is None:
Expand Down Expand Up @@ -180,7 +180,7 @@ def _resolve_to_field_instance(thing, none_allowed=True):
)


class Field:
class Field(object):
"""
A field on a `~serde.model.Model`.
Expand Down Expand Up @@ -218,7 +218,7 @@ def __init__(
to validate as an argument. The functions need to raise an
`Exception` if they fail.
"""
super().__init__()
super(Field, self).__init__()

self.id = Field.__counter
Field.__counter += 1
Expand Down Expand Up @@ -256,7 +256,7 @@ def __setattr__(self, name, value):
if name == '_name' and hasattr(self, '_name'):
raise SerdeError('field instance used multiple times')

super().__setattr__(name, value)
super(Field, self).__setattr__(name, value)

def _serialize(self, value):
"""
Expand Down Expand Up @@ -471,7 +471,7 @@ def __init__(self, type, **kwargs):
type: the type that this Field wraps.
**kwargs: keyword arguments for the `Field` constructor.
"""
super().__init__(**kwargs)
super(Instance, self).__init__(**kwargs)
self.type = type

def validate(self, value):
Expand All @@ -481,7 +481,7 @@ def validate(self, value):
Args:
value: the value to validate.
"""
super().validate(value)
super(Instance, self).validate(value)
validate.instance(self.type)(value)


Expand Down Expand Up @@ -548,7 +548,7 @@ def __init__(self, model, dict=None, strict=True, **kwargs):
unknown dictionary keys are present when deserializing.
**kwargs: keyword arguments for the `Field` constructor.
"""
super().__init__(model, **kwargs)
super(Nested, self).__init__(model, **kwargs)
self.dict = dict
self.strict = strict

Expand All @@ -563,7 +563,7 @@ def serialize(self, value):
dict: the serialized dictionary.
"""
value = value.to_dict(dict=self.dict)
return super().serialize(value)
return super(Nested, self).serialize(value)

def deserialize(self, value):
"""
Expand All @@ -576,7 +576,7 @@ def deserialize(self, value):
Model: the deserialized model.
"""
value = self.type.from_dict(value, strict=self.strict)
return super().deserialize(value)
return super(Nested, self).deserialize(value)


class Dict(Instance):
Expand Down Expand Up @@ -629,7 +629,7 @@ def __init__(self, key=None, value=None, **kwargs):
value (Field): the Field class/instance for values in this Dict.
**kwargs: keyword arguments for the `Field` constructor.
"""
super().__init__(dict, **kwargs)
super(Dict, self).__init__(dict, **kwargs)
self.key = _resolve_to_field_instance(key)
self.value = _resolve_to_field_instance(value)

Expand All @@ -647,7 +647,7 @@ def serialize(self, value):
dict: the serialized dictionary.
"""
value = {self.key.serialize(k): self.value.serialize(v) for k, v in value.items()}
return super().serialize(value)
return super(Dict, self).serialize(value)

def deserialize(self, value):
"""
Expand All @@ -662,7 +662,7 @@ def deserialize(self, value):
Returns:
dict: the deserialized dictionary.
"""
value = super().deserialize(value)
value = super(Dict, self).deserialize(value)
return {self.key.deserialize(k): self.value.deserialize(v) for k, v in value.items()}

def validate(self, value):
Expand All @@ -675,7 +675,7 @@ def validate(self, value):
Args:
value (dict): the dictionary to validate.
"""
super().validate(value)
super(Dict, self).validate(value)

for k, v in value.items():
self.key.validate(k)
Expand Down Expand Up @@ -724,7 +724,7 @@ def __init__(self, element=None, **kwargs):
element (Field): the Field class/instance for elements in the List.
**kwargs: keyword arguments for the `Field` constructor.
"""
super().__init__(list, **kwargs)
super(List, self).__init__(list, **kwargs)
self.element = _resolve_to_field_instance(element)

def serialize(self, value):
Expand All @@ -741,7 +741,7 @@ def serialize(self, value):
list: the serialized list.
"""
value = [self.element.serialize(v) for v in value]
return super().serialize(value)
return super(List, self).serialize(value)

def deserialize(self, value):
"""
Expand All @@ -756,7 +756,7 @@ def deserialize(self, value):
Returns:
list: the deserialized list.
"""
value = super().deserialize(value)
value = super(List, self).deserialize(value)
return [self.element.deserialize(v) for v in value]

def validate(self, value):
Expand All @@ -769,7 +769,7 @@ def validate(self, value):
Args:
value (list): the list to validate.
"""
super().validate(value)
super(List, self).validate(value)

for v in value:
self.element.validate(v)
Expand Down Expand Up @@ -822,7 +822,7 @@ def __init__(self, *elements, **kwargs):
Tuple.
**kwargs: keyword arguments for the `Field` constructor.
"""
super().__init__(tuple, **kwargs)
super(Tuple, self).__init__(tuple, **kwargs)
self.elements = tuple(_resolve_to_field_instance(e, none_allowed=False) for e in elements)

def serialize(self, value):
Expand Down Expand Up @@ -853,7 +853,7 @@ def deserialize(self, value):
Returns:
tuple: the deserialized tuple.
"""
value = super().deserialize(value)
value = super(Tuple, self).deserialize(value)
return tuple(e.deserialize(v) for e, v in zip_equal(self.elements, value))

def validate(self, value):
Expand All @@ -866,7 +866,7 @@ def validate(self, value):
Args:
value (tuple): the tuple to validate.
"""
super().validate(value)
super(Tuple, self).validate(value)

for e, v in zip_equal(self.elements, value):
e.validate(v)
Expand All @@ -890,6 +890,18 @@ def validate(self, value):
#: This field represents the built-in `str` type.
Str = create('Str', base=Instance, args=(str,))

try:
#: This field represents the built-in `basestring` type.
BaseString = create('BaseString', base=Instance, args=(basestring,))
except NameError:
pass

try:
#: This field represents the built-in `unicode` type.
Unicode = create('Unicode', base=Instance, args=(unicode,))
except NameError:
pass

# Str types with extra validation.
Domain = create('Domain', base=Str, validators=[validate.domain])
Email = create('Email', base=Str, validators=[validate.email])
Expand Down Expand Up @@ -938,7 +950,7 @@ def __init__(self, choices, **kwargs):
choices: a list/range/tuple of allowed values.
**kwargs: keyword arguments for the `Field` constructor.
"""
super().__init__(**kwargs)
super(Choice, self).__init__(**kwargs)
self.choices = choices

def validate(self, value):
Expand Down Expand Up @@ -982,7 +994,7 @@ def __init__(self, format='iso8601', **kwargs):
ISO 8601 datetimes.
**kwargs: keyword arguments for the `Field` constructor.
"""
super().__init__(self.__class__.type, **kwargs)
super(DateTime, self).__init__(self.__class__.type, **kwargs)
self.format = format

def serialize(self, value):
Expand Down Expand Up @@ -1096,7 +1108,7 @@ def __init__(self, **kwargs):
Args:
**kwargs: keyword arguments for the `Field` constructor.
"""
super().__init__(uuid.UUID, **kwargs)
super(Uuid, self).__init__(uuid.UUID, **kwargs)

def serialize(self, value):
"""
Expand Down
16 changes: 9 additions & 7 deletions src/serde/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@
from collections import OrderedDict
from functools import wraps

from .error import DeserializationError, SerdeError, SerializationError, ValidationError
from .field import Field
from .util import try_import, zip_until_right
from six import with_metaclass

from serde.error import DeserializationError, SerdeError, SerializationError, ValidationError
from serde.field import Field
from serde.util import try_import, zip_until_right


toml = try_import('toml')
Expand Down Expand Up @@ -190,7 +192,7 @@ def __getattr__(self, name):
try:
return self[name]
except KeyError:
return super().__getattribute__(name)
return super(Fields, self).__getattribute__(name)


class ModelType(type):
Expand Down Expand Up @@ -235,10 +237,10 @@ def __new__(cls, cname, bases, attrs):
# they were defined on the Models. We add these to the Model.
final_attrs['_fields'] = Fields(sorted(fields.items(), key=lambda x: x[1].id))

return super().__new__(cls, cname, bases, final_attrs)
return super(ModelType, cls).__new__(cls, cname, bases, final_attrs)


class Model(metaclass=ModelType):
class Model(with_metaclass(ModelType, object)):
"""
The base Model to be subclassed.
"""
Expand All @@ -251,7 +253,7 @@ def __init__(self, *args, **kwargs):
*args: positional arguments values for each Fields on the Model. If
these are given they will be interpreted as corresponding to the
Fields in the order the Fields are defined on the Model.
**kwargs: keyboard argument values for each Field on the Model.
**kwargs: keyword argument values for each Field on the Model.
"""
try:
named_args = list(zip_until_right(self._fields.keys(), args))
Expand Down

0 comments on commit 560dd07

Please sign in to comment.