Skip to content

Commit

Permalink
Fixes and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
alfred82santa committed Jun 22, 2019
1 parent 354becd commit d762c21
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 31 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ run-tests:
@echo "Running tests..."
nosetests --with-coverage -d --cover-package=dirty_models --cover-erase -x

publish:
publish: clean build
@echo "Publishing new version on Pypi..."
python setup.py bdist_wheel upload
twine upload dist/*

clean:
@echo "Cleaning compiled files..."
Expand Down
8 changes: 8 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ Features
Changelog
---------

Version 0.11.3
--------------

- Fix bug casting string negative float.
- Fix exception casting non valid values to enumerations.
- Added `title` property to fields.
- Added `metadata` property to fields. It could be used to store anything.
- Improved model formatter.

Version 0.11.2
--------------
Expand Down
2 changes: 1 addition & 1 deletion dirty_models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
from .fields import *
from .utils import *

__version__ = '0.11.2'
__version__ = '0.11.3'
38 changes: 24 additions & 14 deletions dirty_models/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
Fields to be used with dirty models.
"""

from datetime import date, datetime, time, timedelta

from collections import Mapping
from dateutil.parser import parse as dateutil_parse
from datetime import date, datetime, time, timedelta
from enum import Enum
from functools import wraps

from dateutil.parser import parse as dateutil_parse

from .model_types import ListModel

__all__ = ['IntegerField', 'FloatField', 'BooleanField', 'StringField', 'StringIdField',
Expand All @@ -19,12 +19,14 @@
class BaseField:
"""Base field descriptor."""

def __init__(self, name=None, alias=None, getter=None, setter=None, read_only=False, default=None, doc=None):
def __init__(self, name=None, alias=None, getter=None, setter=None, read_only=False,
default=None, title=None, doc=None):
self._name = None
self.name = name
self.alias = alias
self.read_only = read_only
self.default = default
self.title = title
self._getter = getter
self._setter = setter
self.__doc__ = doc or self.get_field_docstring()
Expand Down Expand Up @@ -212,9 +214,12 @@ def check_value(self, value):

@can_use_enum
def can_use_value(self, value):
return isinstance(value, int) \
or (isinstance(value, str) and
value.replace('.', '', 1).isnumeric())
try:
float(value)
except (ValueError, TypeError):
return False
else:
return True


class BooleanField(BaseField):
Expand Down Expand Up @@ -642,12 +647,13 @@ class than model who define field.
"""

def __init__(self, model_class=None, **kwargs):
self._model_class = None
self.model_class = model_class
self._model_setter = None
if 'setter' in kwargs:
self._model_setter = kwargs['setter']
del (kwargs['setter'])
self._model_class = model_class

try:
self._model_setter = kwargs.pop('setter')
except KeyError:
self._model_setter = None

super(ModelField, self).__init__(**kwargs)

def export_definition(self):
Expand All @@ -669,6 +675,7 @@ def model_class(self):
@model_class.setter
def model_class(self, model_class):
"""Model_class setter: model class used on field"""

self._model_class = model_class

def convert_value(self, value):
Expand Down Expand Up @@ -930,7 +937,10 @@ def can_use_value(self, value):
except ValueError:
pass

return value in self.enum_class.__members__.keys()
try:
return value in self.enum_class.__members__.keys()
except Exception:
return False


class BytesField(BaseField):
Expand Down
26 changes: 18 additions & 8 deletions dirty_models/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from datetime import date, datetime, time, timedelta
from abc import abstractmethod
from enum import Enum
from json.encoder import JSONEncoder as BaseJSONEncoder

import re
from enum import Enum
from datetime import date, datetime, time, timedelta

from .fields import MultiTypeField
from .model_types import ListModel
Expand Down Expand Up @@ -52,7 +53,10 @@ def keys(self):


class BaseFormatterIter:
pass

@abstractmethod
def format(self): # pragma: no cover
pass


class BaseFieldtypeFormatterIter(BaseFormatterIter):
Expand All @@ -69,6 +73,9 @@ def __iter__(self):
for item in self.obj:
yield self.parent_formatter.format_field(self.field, item)

def format(self):
return list(self)


class BaseModelFormatterIter(BaseModelIterator, BaseFormatterIter):
"""
Expand All @@ -92,6 +99,9 @@ def format_field(self, field, value):

return value

def format(self):
return {k: v.format() if isinstance(v, BaseFormatterIter) else v for k, v in self}


class ModelFormatterIter(BaseModelFormatterIter):
"""
Expand Down Expand Up @@ -119,11 +129,11 @@ class JSONEncoder(BaseJSONEncoder):

def default(self, obj):
if isinstance(obj, BaseModel):
return {k: v for k, v in self.default_model_iter(obj)}
elif isinstance(obj, (BaseModelFormatterIter)):
return {k: v for k, v in obj}
elif isinstance(obj, ListFormatterIter):
return list(obj)
return self.default(self.default_model_iter(obj))
elif isinstance(obj, BaseFormatterIter):
return obj.format()
else:
return super(JSONEncoder, self).default(obj)


class Factory:
Expand Down
32 changes: 32 additions & 0 deletions tests/dirty_models/tests_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from unittest import TestCase, skip

from dirty_models import BaseModel, IntegerField


class TestModel(BaseModel):
inner_doc_field = IntegerField(doc='Inner doc')

post_doc_field = IntegerField()
"""Post doc"""

#: Pre doc
pre_doc_field = IntegerField()

titled_field = IntegerField(title='Titled field')


class DocStringTests(TestCase):

def test_inner_docstring(self):
self.assertEquals(TestModel.inner_doc_field.__doc__, 'Inner doc')

@skip('No way')
def test_post_docstring(self):
self.assertEquals(TestModel().post_doc_field.__doc__, 'Post doc', TestModel().post_doc_field.__doc__)

@skip('No way')
def test_pre_docstring(self):
self.assertEquals(TestModel().pre_doc_field.__doc__, 'Pre doc', TestModel().pre_doc_field.__doc__)

def test_title(self):
self.assertEquals(TestModel.titled_field.title, 'Titled field')
60 changes: 56 additions & 4 deletions tests/dirty_models/tests_fields.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import sys
from datetime import date, datetime, time, timedelta, timezone
from enum import Enum
from unittest import TestCase

import iso8601
import sys
from dateutil import tz
from enum import Enum

from dirty_models.fields import ArrayField, BooleanField, BytesField, DateField, DateTimeField, EnumField, FloatField, \
HashMapField, IntegerField, ModelField, MultiTypeField, StringField, StringIdField, TimeField, TimedeltaField
Expand All @@ -31,6 +31,12 @@ def test_float_field_using_str(self):
self.assertTrue(field.can_use_value("3.0"))
self.assertEqual(field.use_value("3.0"), 3.0)

def test_float_field_using_str_negative(self):
field = FloatField()
self.assertFalse(field.check_value("-3.0"))
self.assertTrue(field.can_use_value("-3.0"))
self.assertEqual(field.use_value("-3.0"), -3.0)

def test_float_field_using_dict(self):
field = FloatField()
self.assertFalse(field.check_value({}))
Expand Down Expand Up @@ -1294,7 +1300,6 @@ def test_array_field_no_autolist(self):


class IntegerFieldTests(TestCase):

class TestEnum(Enum):
value_1 = 1
value_2 = '2'
Expand Down Expand Up @@ -1645,7 +1650,6 @@ def test_export_definition(self):


class EnumFieldTests(TestCase):

class TestEnum(Enum):
value_1 = 'value1'
value_2 = 2
Expand Down Expand Up @@ -1690,6 +1694,54 @@ def test_export_definition(self):
'name': 'test_field', 'read_only': False},
self.field.export_definition())

def test_export_data(self):
class Model(BaseModel):
field = EnumField(enum_class=self.TestEnum)

model = Model(field=self.TestEnum.value_1)

self.assertEqual(model.export_data(), {'field': self.TestEnum.value_1})

def test_multitype_export_data(self):
class Model(BaseModel):
field = MultiTypeField(field_types=[EnumField(enum_class=self.TestEnum),
ArrayField(field_type=EnumField(enum_class=self.TestEnum))])

model = Model()
model.field = self.TestEnum.value_1

self.assertEqual(model.export_data(), {'field': self.TestEnum.value_1})

def test_multitype_export_data_inverted(self):
class Model(BaseModel):
field = MultiTypeField(field_types=[ArrayField(field_type=EnumField(enum_class=self.TestEnum)),
EnumField(enum_class=self.TestEnum)])

model = Model()
model.field = self.TestEnum.value_1

self.assertEqual(model.export_data(), {'field': self.TestEnum.value_1})

def test_multitype_export_data_array(self):
class Model(BaseModel):
field = MultiTypeField(field_types=[EnumField(enum_class=self.TestEnum),
ArrayField(field_type=EnumField(enum_class=self.TestEnum))])

model = Model()
model.field = [self.TestEnum.value_1, ]

self.assertEqual(model.export_data(), {'field': [self.TestEnum.value_1, ]})

def test_multitype_export_data_array_inverted(self):
class Model(BaseModel):
field = MultiTypeField(field_types=[ArrayField(field_type=EnumField(enum_class=self.TestEnum)),
EnumField(enum_class=self.TestEnum)])

model = Model()
model.field = [self.TestEnum.value_1, ]

self.assertEqual(model.export_data(), {'field': [self.TestEnum.value_1, ]})


class BytesFieldTests(TestCase):

Expand Down
19 changes: 17 additions & 2 deletions tests/dirty_models/tests_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from datetime import date, datetime, timedelta
from enum import Enum
from json import dumps, loads
from unittest.case import TestCase

from enum import Enum
from datetime import date, datetime, timedelta

from dirty_models.fields import ArrayField, DateField, DateTimeField, EnumField, HashMapField, IntegerField, \
ModelField, MultiTypeField, StringIdField, TimedeltaField
Expand Down Expand Up @@ -191,11 +191,26 @@ def test_model_json(self):

self.assertEqual(loads(json_str), data)

def test_model_json_enum_str(self):
model = TestModel(data={'test_enum': TestModel.TestEnum.value_2})

json_str = dumps(model, cls=JSONEncoder)

data = {'test_enum': '2'}

self.assertEqual(loads(json_str), data)

def test_general_use_json(self):
data = {'foo': 3, 'bar': 'str'}
json_str = dumps(data, cls=JSONEncoder)
self.assertEqual(loads(json_str), data)

def test_fail_unknown_type(self):
data = {'foo': {2, 3}}

with self.assertRaises(TypeError):
dumps(data, cls=JSONEncoder)


class ModelIteratorTests(TestCase):

Expand Down

0 comments on commit d762c21

Please sign in to comment.